[
  {
    "path": ".gitignore",
    "content": "# Built application files\n*.apk\n*.ap_\n\n# Files for the ART/Dalvik VM\n*.dex\n.idea\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\nout/\n\n# Gradle files\n.gradle/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Log Files\n*.log\n\n# Android Studio Navigation editor temp files\n.navigation/\n\n# Android Studio captures folder\ncaptures/\n\n# Intellij\n*.iml\n.idea/workspace.xml\n\n# Keystore files\n*.jks\n"
  },
  {
    "path": "README.md",
    "content": "\n[English Doc](README_eng.md \"English\")\n\n<h1><p align=\"center\">VA产品说明&开发指导</p></h1> \n\n## VA是什么? ##\nVirtualApp(简称：VA)是一款运行于Android系统的沙盒产品，可以理解为轻量级的“Android虚拟机”。其产品形态为高可扩展，可定制的集成SDK，您可以基于VA或者使用VA定制开发各种看似不可能完成的项目。VA目前被广泛应用于APP多开、小游戏合集、手游加速器、手游租号、手游手柄免激活、VR程序移植、区块链、移动办公安全、军队政府数据隔离、手机模拟信息、脚本自动化、插件化开发、无感知热更新、云控等技术领域。<br> **Github上代码已在2017年12月份停止更新，商业版代码在持续更新中，如需授权获得最新代码，请联系微信：10890**\n\n\n## VA中的术语 ##\n术语 | 解释\n---- | ---\n宿主 | 集成VirtualApp类库（lib）的App叫做宿主  \n宿主插件 | 用于在同一个手机,运行另一种ABI的宿主包,又称做插件包,扩展包,宿主插件包,宿主扩展包\n虚拟App/VApp | VA的虚拟环境多开的app\n外部App | 手机真实环境安装的app\n<br/>\n\n## VA技术架构 ##\n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/va_architecture.jpg)  \nVA技术一共涉及到了Android的APP层，Framework层以及Native层。  \n一个APP想要在Android系统上运行，必须要安装后系统才会接纳。安装到VA内部的APP实际上并没有安装到系统中，所以正常情况下是无法运行的。那如何才能让它运行呢？  \n答：那就只有“欺骗”系统，让系统认为已经安装。而这个“欺骗”过程就是VA Framework的核心工作内容，也是整个VA的核心技术原理。  \n\n**下面介绍下在这3个层次分别做了什么事情：**\n\n层次 | 主要工作\n---- | ---\nVA Space | 由VA提供了一个内部的空间，用于安装要在其内部运行的APP，这个空间是系统隔离的。\nVA Framework | 这一层主要给Android Framework和VAPP做代理，这也是VA的核心。VA提供了一套自己的VA Framework，处于Android Framework与VA APP之间。</br>1. 对于VAPP，其访问的所有系统Service均已被 `VA Framework` 代理，它会修改VAPP的请求参数，将其中与VAPP安装信息相关的全部参数修改为宿主的参数之后发送给Android Framework（有部分请求会发送给自己的VA Server直接处理而不再发送给Android系统）。这样Android Framework收到VAPP请求后检查参数就会认为没有问题。</br>2. 待Android系统对该请求处理完成返回结果时，VA Framework同样也会拦截住该返回结果，此时再将原来修改过的参数全部还原为VAPP请求时发送的。</br>这样VAPP与Android系统的交互也就能跑通了。\nVA Native | 在这一层主要为了完成2个工作，IO重定向和VA APP与Android系统交互的请求修改。</br>1. IO重定向是因为可能有部分APP会通过写死的绝对路径访问，但是如果APP没有安装到系统，这个路径是不存在的，通过IO重定向，则将其转向VA内部安装的路径。</br>2. 另外有部分jni函数在VA Framework中无法hook的，所以需要在native层来做hook。\n</br>\n\n总结：\n通过上面技术架构可以看到，VA内部的APP实际是跑在VA自己的VA Framework之上。\nVA已将其内部APP的全部系统请求进行拦截，通过这项技术也能对APP进行全面控制，而不仅仅只是多开。并且为了方便开发者，VA还提供了SDK以及Hook SDK。  \n\n\n## VA进程架构 #\n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/va_process.jpg)    \nVA运行时有5类进程：CHILD进程，VA Host Main进程，VA Host Plugin进程，VAPP Client进程，VAServer进程。\nVA为了同时支持32位APP与64位APP，需要安装2个包：一个主包，一个插件包(在本文档中主包是32位，插件包是64位)。\n2个包也是必须的，因为一个包只能运行在一种模式下，要么32位，要么64位。所以对于32位的APP，VA使用32位的主包去运行，对于64位的APP，VA则使用64位的插件包去运行。\n在主包中含了VA的所有代码，插件包中只有一段加载主包代码执行的代码，无其他代码。所以插件包几乎不用更新，只需要更新主包即可。\n另外主包是选择用32位还是64位，可以在配置文件中修改(比如对于要上GooglePlay的用户，会修改为主包64位，插件包32位)。\n\n**各类进程的作用与解释如下：**</br>\n\n进程类型 | 作用\n---- | ---\nCHILD | 由VA Host集成的其他进程，比如：保活进程，推送进程等。\nVA Host Main | VA主包的UI主界面所在的进程。默认主包是32位，插件包是64位，可在配置文件中修改切换。\nVA Host Plugin | 支持64位APP的插件包所在进程。默认主包是32位，插件包是64位，可在配置文件中修改切换。\nVAPP Client | 安装到VA中的APP启动后产生的进程，在运行时会将io.busniess.va:pxxx进程名修改VAPP的真实进程名。\nVAServer | VA Server的所在的进程，用于处理VA中不交予系统处理的请求。比如APP的安装处理。\n<br/>\n\n## VA几乎能满足您的一切需求 ##\n通过上面的技术架构，我们可以了解到VA可以对APP进行全面的控制，并且提供了Hook SDK，几乎能满足您在各个领域的一切需求：  \n1. 可以满足您的**双开/多开**需求    \nVA可以让您在同一部手机上安装多个微信/QQ/WhatsApp/Facebook等APP，实现一部手机，多个账号同时登录。  \n\n2. 可以满足您的**移动安全**需求  \nVA提供了一整套内部与外部的隔离机制，包括但不限于(文件隔离/组件隔离/进程通讯隔离)，简单的说VA内部就是一个“完全独立的空间”。\n通过VA可将工作事务与个人事务安全的隔离，互不干扰。稍作定制即可实现应用行为审计、数据加密、数据采集、数据防泄漏、防攻击泄密等移动安全相关的需求。    \n    **2.1 应用行为审计**  \n通过VA提供的HOOK能力可以实现实时监测用户使用行为，将违规信息上传到服务器；并能轻易实现诸如时间围栏(在某个时间段内能否使用应用的某个功能)、地理围栏(在某个区域内能否使用应用的某个功能)、敏感关键字过滤拦截等功能需求。  \n\t**2.2 数据加密**  \n通过VA提供的HOOK能力可以实现对应用的全部数据/文件加密，保证数据/文件落地安全。   \n\t**2.3 数据采集**  \n通过VA提供的HOOK能力可以实现应用数据的实时无感上传需求，如聊天记录、转账记录等，防止事后删除无法追溯。  \n\t**2.4 数据防泄漏**  \n通过VA提供的HOOK能力可以实现应用防复制/粘贴、防截屏/录屏、防分享/转发、水印溯源等需求。   \n\t**2.5 防攻击泄密**  \n通过VA提供的应用管控能力可以将APP获取短信/通讯录/通话记录/后台录音/后台拍照/浏览历史/位置信息等隐私相关的行为完全控制在沙盒中，防止木马/恶意APP获取到用户真实的隐私数据，造成泄密等严重后果。  \n\n3. 可以满足您的**免ROOT HOOK**需求  \nVA提供了Java与Native的Hook能力，通过VA，您可以轻易实现诸如虚拟定位、改机、APP监控管理、移动安全等各种场景需要的功能。  \n\n4. 可以满足您的**APP静默安装**需求  \nVA提供了APP静默安装，静默升级，静默卸载的能力。如应用商店或游戏中心在集成VA后可以避免需要用户手动点击确认安装的操作，做到下载后立即安装到VA内，给用户带来“小程序”搬的体验，彻底避免了应用不易被用户安装上的问题。  \n\n5. 可以满足您的**APP管控**需求  \n您可以通过VA清楚的掌握APP访问了哪些系统API，哪些敏感数据，哪些设备信息等。比如APP是否访问了联系人，相册，通话记录，是否访问了用户的地理位置等信息。\n当然，您还可以通过VA控制或者构造自定义的信息给这些APP。不仅于此，您还可以获取到APP的私有数据，比如聊天数据库等。总之通过VA提供的应用管控能力，您可以轻易控制APP的一切行为，甚至修改APP与服务器交互内容等。  </br>\n\n\n6. 可以满足您的**海外市场**需求  \nVA实现了对Google服务的支持，以支持海外的App运行，比如Twitter、Messenger、WhatsApp、Instagram、FaceBook、Youtube等。  \n\n\n7. 可以满足您的**VR程序移植**需求  \n可以通过VA的Hook能力拦截VR设备的API，让您无需改动代码即可将VR程序移植到新的设备。  \n\n8. 可以满足您**几乎一切能想到**的需求  \nVA对于内部的App具有完全的监管和控制能力，几乎能满足您的一切需求！\n\n9. 同时VA也是该技术领域**唯一一款**对外商业授权的产品    \n截止目前已有**上百家**授权客户在付费使用VirtualApp商业版代码，集成VirtualApp代码的APP**日启动**次数**超过2亿次**，众多安卓工程师向我们提供不同场景下的用户反馈，通过我们技术团队不断优化迭代，不断提升产品性能与兼容性！\n\n\nVA的特有能力\n---\n\n- 克隆能力<br/>\n可以克隆外部系统中已经安装的App，并在内部运行，互不干扰。典型应用场景为App双开。\n\n- 免安装能力<br/>\n除了克隆已安装之外，VA可以直接在内部安装(外部无感知)apk，并在内部直接运行。典型应用场景为插件化，独立应用市场等。\n\n- 多开能力<br/>\nVA不仅可以“双开”，独特的多用户模式支持用户在内部无限多开同一个App。\n\n- 内外隔离能力<br/>\nVA是一个标准的沙盒，或者说“虚拟机”，提供了一整套内部与外部的隔离机制，包括但不限于(文件隔离/组件隔离/进程通讯隔离)，简单的说VA内部就是一个“完全独立的空间”。在此基础之上，稍作定制即可实现一部手机上的“虚拟手机”。当然您也可以发挥想象，定制成应用于数据加密，数据隔离，隐私保护，企业管理的应用系统。\n\n- 对于内部App的完全控制能力<br/>\nVA对于内部的App具有完全的监控和控制能力，这点在未Root的外部环境中是绝对无法实现的。\n<details>\n<summary>详细(下拉打开)</summary>\n  1. 服务请求控制，首先VA直接提供了一些服务请求的拦截，您可以在集成VA的时候方便的定制这些服务请求，包括但远不限于(App请求安装apk/App请求打开某些文件/App请求定位数据/App请求手机信息等等)<br/><br/>\n  2. 系统API控制，VA虚拟并实现了整个安卓系统框架，这也是VA可以免安装在内部运行apk的原理，您可以对虚拟框架的实现进行修改以动态监测分析App的行为等；除此之外，您还可模拟一些系统行为以实现一些在外部难以实现的需求(例如游戏手柄)。<br/><br/>\n  3. 内存读写，VA可以无需Root读写内部App进程的内存。<br/><br/>\n  4. 免Root调试，VA可以免Root调试(ptrace)内部的App进程，基于此您还可以实现免Root的进程注入。<br/><br/>\n  5. 加载任意“插件”和“行为”，VA内部的App进程由VA的框架Client端代码派生而来，所以您可以在进程的入口代码插入任何“加载”以及“控制”逻辑。这些实现都非常简单。<br/><br/>\n  6. 方法Hook，VA内置了一套运行于Android各个版本(直到AndroidQ)的Xposed框架以及native hook框架，基于此您可以方便的Hook任意内部App的任意Java/Native方法。可以轻松实现一个免Root的Xposed环境(有实例代码)。<br/><br/>\n  7. 文件控制，VA内置了完整的文件重定向，可以方便的控制内部App的文件的读写，基于此可以实现对文件的保护加密等功能。<br/><br/>\n  8. 注：以上控制能力均有实现代码或者实例以作参考。\n</details>\n\n\nVA的其他特性\n---\n\n- 高性能<br/>\n进程级“虚拟机”，VA独特的实现方式让其性能几乎于原生App一致，更不需要普通虚拟机漫长的启动。\n\n- 全版本支持<br/>\n支持5.0-17.0，支持32位/64位app，支持ARM以及X86处理器。并且支持未来将更新的Android版本。\n\n- 易扩展与集成<br/>\nVA 的集成方式与普通Android库类似，即使您的App已经完成上线，您也方便的可以集成VA，享受VA带来的能力。\n\n- 支持Google服务<br/>\n提供Google服务的支持，以支持海外的App\n\n\n## VA与其他技术方案对比 ##\n在做企业级移动安全时，往往需要对APP进行管控，以下是列出的可能技术方案对比： \n\n技术方案 | 原理简介 | 点评 |  运行性能 | 兼容稳定性 | 项目维护成本\n---- | --- | ---  | ---  | ---  | --- \n二次打包 | 通过反编译目标APP，加入自己的控制代码，重新打包 | 1.现在的APP几乎都有加固或防篡改保护，重打包已是一件非常困难的事</br> 2.手机系统也会检测APP是否被重打包，如果重打包，会直接提示用户存在安全风险，甚至不让安装</br>3.针对每一个APP，甚至每一个版本都要深入去逆向分析，耗时耗力，难于维护  | 优秀  | 差  | 高\n定制ROM | 通过定制系统源码，编译刷到指定手机 | 只能针对指定的内部手机，局限性太大，无法扩展  | 优秀  | 优秀  | 高\nROOT手机 | 通过ROOT手机，刷入xposed等类似框架 | 1.ROOT手机现在本身已是一件不太可能的事</br> 2.现实中也很难让用户能去ROOT自己的手机  | 优秀  | 差  | 高\nVA | 轻量级虚拟机，速度快，对设备要求低 | 无上述风险点  | 优秀  | 优秀，有上百家企业在同时测试反馈  | 低，VA提供了API并有专业的技术团队保障项目稳定运行\n<br/>\n通过以上对比可以看出，VA是一款优秀的产品，并且能降低您的开发维护成本！\n\n## 集成VA步骤 ##\n第1步：在您的Application中调用VA接口```VirtualCore.get().startup()```来启动VA引擎  \n第2步:调用VA接口```VirtualCore.get().installPackageAsUser(userId, packageName)```将目标APP安装到VA中  \n第3步:调用VA接口```VActivityManager.get().launchApp(userId, packageName)```启动APP    \n**仅通过以上3个API就完成了基础使用，VA已屏蔽了复杂的技术细节，并提供了接口API，让您的开发变得很简单！**\n\n## VA的兼容稳定性 ##\nVA已被**上百家**企业进行了广泛测试，包含**数十家上市公司高标准**的测试及反馈，几乎涵盖了海内外的各种机型设备和场景！\n为您的稳定运行提供了充分的保障！  \n\n截止目前，支持的系统版本:\n\n系统版本 | 是否支持\n---- | ---\n5.0 | 支持\n5.1 | 支持\n6.0 | 支持\n7.0 | 支持\n8.0 | 支持\n9.1 | 支持\n10.0 | 支持\n11.0 | 支持\n12.0 | 支持\n13.0 | 支持\n14.0 | 支持\n15.0 | 支持\n16.0 | 支持\n17.0 | 支持\n<br/>\n\n\n支持的APP类型:\n\nAPP类型 | 是否支持\n---- | ---\n32位APP | 支持\n64位APP | 支持\n<br/>\n\n支持的HOOK类型:\n\nHook类型 | 是否支持\n---- | ---\nJava Hook | 支持\nNative Hook | 支持\n\n支持的CPU类型:\n\nHook类型 | 是否支持\n---- | ---\nARM 32 | 支持\nARM 64 | 支持\n<br/>\n\n## 集成VA遇到问题如何反馈？ ##\n购买授权后我们会建立微信群，有任何问题可以随时反馈给我们，并根据优先级在第一时间处理！\n\n## VA开发文档 ##\nVA开发文档请参考：[开发文档](doc/VADev.md)\n\n\n\n授权说明\n------\nVirtualApp虚拟机技术归属于：山东盒一网络科技有限公司（原：济宁市罗盒网络科技有限公司），于2015年至2026年申请多项VirtualApp知识产权，`受中华人民共和国知识产权法保护`。当您需要使用Github上的代码时，**请购买商业授权**，获取商业授权后将可以收到最新VirtualApp商业版全部源代码。上百家授权客户在付费使用VirtualApp商业版代码，集成VirtualApp代码的APP日启动次数超过2亿次，众多安卓工程师向我们提供不同场景下的用户反馈，通过我们技术团队不断优化迭代，VirtualApp商业版代码性能更好、兼容性更高。`当您的公司获取授权后，将成为其中一员，享受这些不断迭代完善后的技术成果。并可以和我们的授权客户进行运营、技术及商业上的互动合作。`\n\n<br/>\n负责人：张总 <br/>\n微信：10890 <br/>\n<br/>\n\n\n严重声明\n------\n您如果未经授权将VirtualApp用于**内部使用、商业牟利或上传应用市场**，我们将取证后报警（侵犯著作权罪）或起诉，这将对您所属公司造成刑事责任及法律诉讼，影响到您公司的商誉和投资。`购买商业授权为您节省大量开发、测试和完善兼容性的时间，让您更多时间用于创新及盈利。`罗盒科技已在2020年报警和起诉了一些个人及公司。<br/>\n\n**为响应国家对于知识产权的保护号召！凡举报自己所在公司或其他公司未经授权，违法使用VirtualApp代码开发产品的，一经核实给予现金奖励。我们会对举报人身份保密！举报联系微信：10890**\n\n  <br/>\n\n商业版主要更新\n------\n\n1. 兼容最新Android 17.0\n2. 支持Binder拦截，不再使用动态代理\n3. 支持Seccomp-Bpf拦截\n4. 不易被杀毒软件误报\n5. 框架优化，性能大幅提升\n6. 手机系统及APP兼容性大幅提升\n7. 完美运行Google服务\n8. 支持运行纯64位App\n9. 内置`XPosed Hook`框架\n10. 增加定位模拟代码\n11. 增加改机代码\n12. 其他600+项问题的修复和改进，详情请见下表\n\n<br>\n\n2017年-2026年商业版代码更新详细\n------\n\n**2026年3月5号 至 2026年 3月16号 商业版代码更新内容**\n\n663、17.0 beta2上的报错修复<br/>\n662、 Seccomp-bpf only相关的调整<br/>\n661、反射代码调整<br/>\n\n**2026年2月12号 至 2026年 3月4号 商业版代码更新内容**\n\n660、增加对部分加固的支持<br/>\n659、 fp指针调整避免某些App无法打开<br/>\n\n<details>\n<summary>2017年 12月 至 2026年 2 月 11 日 商业版代码更新内容(下拉打开)</summary>  <br/>\n\n**2026年2月1号 至 2026年 2月11号 商业版代码更新内容**\n\n658、 修复NativeEngine.onGetCallingUid在VisitRoots的时候crash<br/>\n657、 Secomp-Bpf开启后，执行execve时支持fd<br/>\n656、fix  getFromLocation crash on 15.0+<br/>\n\n\n**2025年12月31号 至 2026年 1月30号 商业版代码更新内容**\n\n655、增强环境检测方面的处理<br/>\n654、.0上AdvancedProtectionService以及ISupervisionManager适配<br/>\n653、fix AttributionSource子类验证报错：AttributionSource should be unparceled during a binder<br/>\n652、fix ParceledListSlice->getList返回null<br/>\n651、对于一些没有权限的函数调用进行处理<br/>\n650、maps处理方式更新<br/>\n649、对fp指令范围进行判断,避免crash<br/>\n\n**2025年12月19号 至 2025年 12月30号 商业版代码更新内容**\n\n648、从处理openat2与faccessat2，seccomp等调用<br/>\n647、适配APP中对seccomp-bpf的调用<br/>\n646、处理某些应用因为检测打不开<br/>\n\n\n**2025年12月3号 至 2025年 12月18号 商业版代码更新内容**\n\n645、某些svc处理<br/>\n644、系统路径调整<br/>\n643、maps调整<br/>\n\n\n**2025年11月12号 至 2025年 12月2号 商业版代码更新内容**\n\n642、新功能：Native层增加机型模拟支持<br/>\n641、支持对应用强制开启机型模拟/强制使用真机信息<br/>\n640、seccomp-bpf对ptrace的一些处理<br/>\n639、修复执行Java命令时进程可能崩溃的问题<br/>\n638、修复其他几处崩溃的问题<br/>\n\n\n**2025年10月29号 至 2025年 11月11号 商业版代码更新内容**\n\n637、新功能:Seccomp-Bpf下增加了对execve的调用支持<br/>\n636、处理了与反射相关的几个地方<br/>\n635、对几个内存文件的chmod处理<br/>\n\n**2025年10月11号 至 2025年 10月28号 商业版代码更新内容**\n\n634、修复IBatteryStats相关的空指针<br/>\n\n**2025年9月18号 至 2025年 10月10号 商业版代码更新内容**\n\n633、处理onNewIntent()中的activity Referrer<br/>\n632、适配NotificationProviderPublic<br/>\n631、修复15.0+上isDirectlyHandlingTransaction()的多线程处理<br/>\n630、隐藏Libcore.os的反射获取<br/>\n629、binderproxy模式支持IBatteryStats<br/>\n628、适配某些加固APP<br/>\n\n\n**2025年9月2号 至 2025年 9月17号 商业版代码更新内容**\n\n627、Android 16kb page size 适配<br/>\n626、fix蓝牙几个代理类的代码错误<br/>\n625、处理某些APP在Application->attach中获取到宿主Application的场景<br/>\n624、处理Activity referrer相关的<br/>\n623、处理processOutsideIntent时intent包含vapp class的情况<br/>\n622、针对某些oppo 13.0机型适配<br/>\n621、处理调用ArrayUtils.indexOf时，有的地方按0开始起步有的地方按1开始起步导致异常，都统一为1<br/>\n620、适配LockSettings/WifiScanner/NetworkScoreManager/WifiManager/SensitiveContentProtectionManager等几个manager<br/>\n619、新功能模式下BinderProxyInjectManager.addInjector增加判断，避免有些因为class不存在导致crash<br/>\n618、新功能模式下支持Instrumentation注入<br/>\n617、增加对native层获取宿主信息的处理<br/>\n\n\n**2025年8月9号 至 2025年 9月1号 商业版代码更新内容**\n\n616、增加对setxattr/lsetxattr/bind/connect/syscall等几个libc api处理<br/>\n615、增加对execve启动的进程实现inline hook<br/>\n614、增加可对application中的全部路径模拟为系统安装路径<br/>\n613、增加对logcat等命令的过滤处理<br/>\n612、修复app 崩溃/anr等系列问题<br/>\n611、修复ParceledListSlice.getList返回Null的问题<br/>\n\n**2025年7月22号 至 2025年 8月8号 商业版代码更新内容**\n\n610、增加对INetworkScoreService的处理<br/>\n609、修复新功能在release下的一些错误<br/>\n608、修复某些手机上新功能无法打开APP\t<br/>\n607、修复Tethering相关的crash\n\n\n**2025年7月3号 至 2025年 7月21 商业版代码更新内容**\n\n606、与新功能相关的路径处理<br/>\n605、一些bug处理<br/>\n\n**2025年5月1号 至 2025年 7月2号 商业版代码更新内容**\n\n604、新功能开发:基于拦截Binder的方式来实现对系统AIDL调用的拦截。通过这种方式可不再使用动态代理，对于稳定性将会有比较大提升<br/>\n603、16.0最新版本继续适配<br/>\n\n\n**2025年4月16号 至 2025年 4月30号 商业版代码更新内容**\n\n602、对Android 16.0 beta 4 适配<br/>\n601、完善seccomp-bpf重定向相关的功能<br/>\n600、增加配置可以让APP只使用seccomp-bpf,不使用inline hook<br/>\n599、将target sdk升级到34<br/>\n598、修复va core进程由于client为空导致的crash<br/>\n597、启动进程的时候增加重试，避免在某些机型上由于进程死亡太频繁导致无法启动进程<br/>\n596、对于某类型加固后是否需要安装provider的部分改为动态判断<br/>\n595、修复demo在某些设备上由于title为null导致的crash<br/>\n\n\n**2025年3月27号 至 2025年 4月15号 商业版代码更新内容**\n\n594、修复GMS无法调起登录的问题<br/>\n593、增加对IInputMethodManagerGlobalInvoker的hook<br/>\n592、修复工作空间中无法打开VAPP的问题<br/>\n591、适配微信8.0.57<br/>\n\n**2025年3月20号 至 2025年 3月26号 商业版代码更新内容**\n\n590、增加对AppSearchManager的适配<br/>\n589、增加对DomainVerificationManager的适配<br/>\n588、增加对SystemUpdateManager的适配<br/>\n587、修复多个进程同时启动同一个进程时的crash问题<br/>\n\n\n**2025年2月28号 至 2025年 3月19号 商业版代码更新内容**\n\n586、seccomp 相关的调整<br/>\n585、修复微信在14.0+上开启seccomp-bpf无法打开的问题<br/>\n584、新增对StorageStatsManager的适配<br/>\n583、UsageStatsManager相关API适配<br/>\n\n**2025年2月11号 至 2025年 2月27号 商业版代码更新内容**\n\n582、适配最新版微信<br/>\n\n\n**2025年1月24号 至 2025年 2月10号 商业版代码更新内容**\n\n581、对IO进行inline hook时暂停所有Java线程,避免冷启动时因多线程导致的低概率crash<br/>\n\n\n**2025年1月8号 至 2025年 1月23号 商业版代码更新内容**\n\n580、installer静默安装部分适配<br/>\n579、修复静态广播收不到消息<br/>\n578、修复pending intent数据丢失问题<br/>\n577、input manager 14.0+上的适配<br/>\n576、蓝牙适配<br/>\n575、queryStatsForPackage适配<br/>\n574、修复有些手机上显示不出应用列表<br/>\n573、其他一些小问题适配<br/>\n\n\n**2024年12月21号 至 2025年 1月7号 商业版代码更新内容**\n\n572、Seccomp-bpf支持32位<br/>\n571、修复某些情况下路径多次重定向的问题<br/>\n570、修复抖音人脸识别时可能白屏的问题<br/>\n569、去掉几年前为抖音打不开做的一些修改<br/>\n\n\n**2024年12月7号 至 2024年 12月20号 商业版代码更新内容**\n\n568、修复startIntentSenderFoeResult无法工作的问题<br/>\n567、修复GMS由于StatsManager无法hook导致crash的问题<br/>\n\n\n**2024年11月27号 至 2024年 12月6号 商业版代码更新内容**\n\n566、修复release打包后IJobService中的onNetworkChanged等几个函数被混淆导致找不到crash的问题<br/>\n565、修复由于BluetoothAdapter.sService为null导致IBluetooth hook失败的问题<br/>\n564、修复packagesettings被覆盖的问题<br/>\n563、删除getCallingUid()中的缓存代码<br/>\n562、Seccomp条件判断时处理Application.name为Null的情况<br/>\n561、AGP升级到8.2.0<br/>\n\n**2024年11月12号 至 2024年 11月26号 商业版代码更新内容**\n\n560、重定向路径调整，区分/data/data/com.xxx以及/data/user/0/com.xxx，使其更符合APP使用实际使用的路径<br/>\n559、适配fixupAppDir<br/>\n558、reverseRedirectedPath增加检查，防止路径多次重复转换<br/>\n557、修复splitNames信息缺失导致部分应用无法正常使用<br/>\n556、补充一批normal权限,解决部分APP因为权限丢失无法正常使用的问题<br/>\n\n\n**2024年10月31号 至 2024年 11月11号 商业版代码更新内容**\n\n555、新功能：增加Seccomp-Bpf支持，实现更底层的拦截<br/>\n554、增加对部分加固APP的支持<br/>\n553、系统OTA升级时对Split Apks重新安装<br/>\n\n\n**2024年10月14号 至 2024年 10月29号 商业版代码更新内容**\n\n552、所有手机package.ini版本升级到7，避免某些情况下出现应用丢失<br/>\n551、修改Demo包名<br/>\n\n\n**2024年9月15号 至 2024年 10月13号 商业版代码更新内容**\n\n550、移除对Sandhook的依赖<br/>\n549、移除几处hook，避免部分机型crash<br/>\n548、移除未使用到的goAsync，避免ANR<br/>\n547、移除部分对1.X的升级处理废弃代码<br/>\n546、修复手机系统升级到13.0+上后，应用列表消失的问题<br/>\n\n\n**2024年8月23号 至 2024年 9月14号 商业版代码更新内容**\n\n545、修复IO重定向中一系列函数未对dfd正确处理<br/>\n\n\n**2024年8月9号 至 2024年 8月22号 商业版代码更新内容**\n\n544、修复Native的一个空指针问题<br/>\n543、修复Native的某个函数由于栈上随机数导致判断出错<br/>\n542、修复unity中的检测问题<br/>\n541、修复publishService crash问题<br/>\n540、修复getPid死循环崩溃问题<br/>\n\n**2024年8月3号 至 2024年 8月8号 商业版代码更新内容**\n\n539、修复微信等APP因为webview导致的crash问题<br/>\n\n**2024年7月19号 至 2024年 8月2号 商业版代码更新内容**\n\n538、修复sandhook崩溃问题<br/>\n537、修复sandhook hook不起效问题<br/>\n536、替换sandhook中inline hook部分<br/>\n535、修复android.permission.DETECT_SCREEN_CAPTURE权限导致的crash问题<br/>\n534、修复静态广播导致的crash问题<br/>\n533、修复百度搜索crash的问题<br/>\n532、修复ResolveActivity跳转到外部应用时没有过滤export为false的场景导致crash<br/>\n531、修复在某些华为设备上微信白屏的问题<br/>\n530、修复微信流量异常的问题<br/>\n529、class_linker适配android 15<br/>\n528、修复 readlinkat参数问题导致的crash<br/>\n527、解决某些unity路径检测的问题<br/>\n\n\n**2024年7月3号 至 2024年 7月18号 商业版代码更新内容**\n\n526、适配了几十个API,很大程度提升了稳定性<br/>\n525、调整stopService不再走initProcess流程，解决了某些情况下的死锁问题<br/>\n524、修复 startprocess启动App后再次进入VActivityManagerService导致死锁的问题<br/>\n523、修复锁屏/亮屏广播引起的crash问题<br/>\n\n\n**2024年6月17号 至 2024年 7月2号 商业版代码更新内容**\n\n522、AttributionSoure中的uid调整 <br/>\n521、修复微信注册，找回账号等几个界面白屏的问题<br/>\n\n\n**2024年6月4号 至 2024年 6月16号 商业版代码更新内容**\n\n520、将内置的Java hook框架SandHook调整为可选配置 <br/>\n519、修复VA_ENABLE_EXT_FULL_SOURCE_CODE功能选项开启时，加载so错误的问题<br/>\n\n\n**2024年5月8号 至 2024年 6月3号 商业版代码更新内容**\n\n518、修复微信在鸿蒙4.0+上无法使用的问题 <br/>\n517、调整VA demo package name<br/>\n\n\n\n**2024年4月20号 至 2024年 5月7号 商业版代码更新内容**\n\n516、适配华为账户登录和授权登录等功能 <br/>\n515、适配荣耀账户登录和授权登录等功能 <br/>\n514、修复Service中getApplicationContext返回Null的问题<br/>\n\n\n**2024年4月4号 至 2024年 4月19号 商业版代码更新内容**\n\n513、修复link&unlink参数没有处理重定向的问题 <br/>\n512、修复AutoFillManagerStub未生效问题 <br/>\n511、适配高版本ShadowJobService <br/>\n\n\t\n**2024年3月7号 至 2024年 4月2号 商业版代码更新内容**\n\n510、修复数款游因为戏二次注册provider导致无法打开 <br/>\n\n\n\n**2024年2月19号 至 2024年 3月6号 商业版代码更新内容**\n\n509、修通知跳转Crash <br/>\n508、AMS API适配<br/>\n507、DevicePolicyManager API适配<br/>\n506、BlueTooth API适配<br/>\n505、修复抖音crash问题<br/>\n\n\n**2024年1月25号 至 2024年 2月18号 商业版代码更新内容**\n\n504、修复抖音在部分手机无法打开的问题<br/>\n503、修复抖音在部分手机运行一小段时间后崩溃的问题<br/>\n502、修复抖音在部分手机crash后一直打不开的问题<br/>\n501、修复抖音极速版在部分手机无法打开的问题<br/>\n500、修复抖音极速版在部分手机运行一小段时间后崩溃的问题<br/>\n499、修复抖音极速版在部分手机crash后一直打不开的问题<br/>\n498、UserManager相关API适配<br/>\n497、PackageManager相关API适配<br/>\n496、Notification相关API适配<br/>\n495、FingerprintManager相关API适配<br/>\n\n\n**2024年1月5号 至 2024年 1月24号 商业版代码更新内容**\n\n494、Activity Token获取适配<br/>\n493、适配最新版微信<br/>\n\n**2023年12月21号 至 2024年 1月4号 商业版代码更新内容**\n\n492、适配libc可能没有R权限的情况<br/>\n\n\n**2023年12月5号 至 2023年 12月20号 商业版代码更新内容**\n\n491、修复储存空间异常的问题<br/>\n\n\n**2023年10月24号 至 2023年 12月4号 商业版代码更新内容**\n\n490、取消对Xposed依赖<br/>\n489、适配最新版微信<br/>\n488、适配setCompatibilityVersion<br/>\n487、取消hookGetCallingUid对xposed的依赖<br/>\n486、蓝牙适配<br/>\n485、AddToDisplayAsUser处理<br/>\n478、PendingIntent适配<br/>\n484、MediaRecorder适配<br/>\n483、处理dispatchVolumeKeyEvent API<br/>\n482、修复AttributionSource cast crash<br/>\n481、增加配置：是否优先使用外部app<br/>\n480、修复启动前台service crash<br/>\n479、修复14.0上renameat太短导致hook后覆盖其他函数的问题<br/>\n\n\n**2023年10月8号 至 2023年 10月23号 商业版代码更新内容**\n\n478、修复Annotation依赖包为空的问题<br/>\n477、修复抖音等APP由于动态框架导致无法打开Activity的问题<br/>\n476、修复纯Java APP在64位下以32位模式安装的问题<br/>\n475、修复了13.0+上的class linker偏移检测问题<br/>\n474、调整默认使用isUseRealDataDir模拟真实路径<br/>\n473、JobServiceStub适配<br/>\n472、IO重定向增加对renameat2的hook<br/>\n471、修复APK安装模式下某些APP拍照黑屏<br/>\n470、修复APK安装模式下微信小程序无法使用的问题<br/>\n\n\t\n**2023年9月16号 至 2023年 10月7号 商业版代码更新内容**\n\n469、移除已经废弃的虚拟定位代码<br/>\n468、修复WhatsApp 来电通知bug<br/>\n467、修复GMS相关问题<br/>\n466、修复WhatsApp无法跳过短息验证界面问题<br/>\n465、修复WhatsApp等部分APP启动后界面白屏问题<br/>\n464、适配Alarms 相关API\n\n\n**2023年9月6号 至 2023年 9月15号 商业版代码更新内容**\n\n463、14.0上JobScheduler API适配<br/>\n462、修复从sdcard上安装时signature可能获取的可能不是最旧的问题<br/>\n461、LocaleManager适配<br/>\n\n\n**2023年8月16号 至 2023年 9月5号 商业版代码更新内容**\n\n460、14.0上JobScheduler适配<br/>\n459、修复API broadcastIntentWithFeature<br/>\n458、修复WhatsApp验证跳转的问题<br/>\n457、内部Provider访问适配<br/>\n\n\n**2023年8月2号 至 2023年 8月15号 商业版代码更新内容**\n\n456、修复Twitter白屏的问题<br/>\n455、修复ContentProvider在12.0+上的适配问题<br/>\n454、修复微信在nova9z上崩溃的问题<br/>\n453、修复微信等APP发送定位时黑屏的问题<br/>\n452、编译SDK版本升级到14.0<br/>\n\n**2023年7月13号 至 2023年 8月1号 商业版代码更新内容**\n\n451、适配12.0+上蓝牙相关的10来个API<br/>\n450、适配UserManager相关的10来个API<br/>\n\n\n**2023年6月30号 至 2023年 7月12号 商业版代码更新内容**\n\n449、修复JobService unbind崩溃问题<br/>\n448、修复JobService persisted崩溃问题<br/>\n\n**2023年5月26号 至 2023年 6月29号 商业版代码更新内容**\n\n447、修复部分APP无法录音的问题<br/>\n446、修复从Sdcard安装APK失败的问题<br/>\n445、更改VA Demo包名<br/>\n\n**2023年4月28号 至 2023年 5月25号 商业版代码更新内容**\n\n444、适配Android 14.0<br/>\n\n**2023年3月18号 至 2023年 4月27号 商业版代码更新内容**\n\n443、修复GMS支持，修复各种crash，权限等问题<br/>\n442、修复GooglePlay无法打开的问题<br/>\n441、修复GooglePlay无法登录Google账号的问题<br/>\n440、修复Youtube,WhatsApp等APP无法登录Google账号的问题<br/>\n439、修复Facebook无法打开的问题<br/>\n\n\n**2023年2月17号 至 2023年 3月17号 商业版代码更新内容**\n\n438、修setPictureInPictureParams crash<br/>\n\n**2023年1月27号 至 2023年 2月16号 商业版代码更新内容**\n\n437、修复mOpPackageName空指针<br/>\n436、修复13.0上PackageManager几个flags参数导致的crash<br/>\n435、修复VAPP返回主页的Intent crash<br/>\n434、TelecomManagerStub API适配<br/>\n\t\n**2022年12月9号 至 2023年 1月26号 商业版代码更新内容**\n\n433、修复PendingIntent flag处理问题<br/>\n\n**2022年11月9号 至 2022年 12月8号 商业版代码更新内容**\n\n432、修复Facebook在某些情况下无法启动的问题<br/>\n431、启动外部App时，排除对VA自身的判断<br/>\n430、修复queryIntentServices过滤规则<br/>\n\n\n**2022年10月9号 至 2022年 11月8号 商业版代码更新内容**\n\n429、修复当VA_AUTHORITY_PREFIX不等于包名时找不到Provider的问题：\"Failed to find provider info ...\"<br/>\n428、getPermissionActivityIntent处理<br/>\n427、修复特殊情况下,检查权限无限弹窗<br/>\n426、强调Intent使用外部通讯录(如果被第三方接管,建议使用外部可见)<br/>\n425、新增几个Java API适配<br/>\n424、修复修复部分后台Activity跳转问题<br/>\n423、修复在10.0+上后台Activity无法启动的问题<br/>\n\n\n**2022年 8月20号 至 2022年 10月8号 商业版代码更新内容**\n\n422、新功能:增加启动插件Activity代理，绕过后台5s限制<br/>\n421、修复Provider在10.0+上crash的问题<br/>\n420、适配最新版微信<br/>\n419、适配克隆时不显示sdcard上的APK<br/>\n418、适配12.0+上PendingIntent Flags必须为FLAG_UPDATE_CURRENT/FLAG_IMMUTABLE<br/>\n417、修复MediaProvider因为ACCESS_MEDIA_LOCATION 权限检查导致的crash<br/>\n416、修复12.0+上debug模式下hook失败的问题<br/>\n415、适配在Multi User账户下crash的问题<br/>\n414、适配由于后台限制导致VA Core启动插件中Activity失败的问题<br/>\n\n\n**2022年 7月27号 至 2022年 8月19号 商业版代码更新内容**\n\n413、Android 13.0继续适配<br/>\n412、主版调整为64bit<br/>\n411、修复某些华为手机上App无法打开的问题<br/>\n410、修复OPPO 13.0上无法打开应用的问题<br/>\n409、修复百度语音TTS的调用问题<br/>\n408、修复数据隔离后仍可以访问sdcard根目录的问题<br/>\n407、修复鸿蒙手机上的崩溃问题<br/>\n406、修复Debug模式下Hook失效问题<br/>\n405、添加对BinderInvocationStub的混淆处理，避免混淆后崩溃问题<br/>\n404、修复Native层调用free函数可能导致崩溃的问题<br/>\n403、修复微信由于虚拟文件系统导致的崩溃问题<br/>\n\n\n**2022年 7月9号 至 2022年 7月26号 商业版代码更新内容**\n\n402、Android 13.0适配<br/>\n401、修复开启虚拟储存后文件路径的处理问题<br/>\n400、修复12.0上Notification没有提示的问题<br/>\n\n\n**2022年 4月28号 至 2022年 5月31号 商业版代码更新内容**\n\n399、修复onGetCallingUid hook引起的崩溃问题<br/>\n398、修复微信8.0.22启动崩溃的问题<br/>\n\n\n**2022年 4月5号 至 2022年 4月27号 商业版代码更新内容**\n\n397、去掉sandhook中一些多余的hook代码，避免某些APP无法启动<br/>\n\n\n**2022年 3月13号 至 2022年 4月5号 商业版代码更新内容**\n\n396、新增功能:在VA中实现内置media provider，以支持媒体库隔离等需求<br/>\n395、修复微信/QQ使用语音时崩溃<br/>\n394、蓝牙崩溃问题适配<br/>\n393、增加部分Log<br/>\n392、删除一些无用代码<br/>\n\n\n**2022年 1月22号 至 2022年 3月12号 商业版代码更新内容**\n\n391、修复华为手机上StorageManager没有被hook的问题<br/>\n390、修复最新版微信无法从SD卡启动的问题<br/>\n389、PackageInfo中增加对requestedPermissionsFlags字段的处理<br/>\n388、新增VSettingsProvider，避免内置应用没有权限操作settings provider导致异常<br/>\n387、修复微信等APP启动黑屏，ANR等问题<br/>\n386、新增对MediaProvider的hook<br/>\n385、新增对插件shareUserId的处理，从而可以配置将插件的数据放到主包中<br/>\n384、新增可以配置是否将Tinker禁用<br/>\n383、修复Android 12权限处理适配<br/>\n\n\n**2021年 12月30号 至 2022年 1月21号 商业版代码更新内容**\n\n382、Sandhook适配12.0<br/>\n381、修复Sandhook在部分11.0上不生效的问题<br/>\n380、增加编译选项VA_FULL_EXT控制是否将VA源码编译到插件,满足加固场景。<br/>\n\n\n**2021年 11月24号 至 2021年 12月29号 商业版代码更新内容**\n\n379、Android 12.0第一轮适配已完成<br/>\n378、Demo App在11.0上增加文件权限检测<br/>\n377、修复静态广播接收者在独立进程无法接收广播的问题<br/>\n376、修复微信第一次登录可能crash问题<br/>\n375、修复部分APP无法显示头像问题<br/>\n374、修复在部分OPPO手机上打不开问题<br/>\n\n\n**2021年 9月21号 至 2021年 11月23号 商业版代码更新内容**\n\n373、修复WhatsApp在360手机上黑屏问题<br/>\n372、增加VA内外广播通信测试demo<br/>\n371、修复抖音极速版兼容性问题<br/>\n370、修复readlinkat返回值精度<br/>\n369、修复从外部安装app,没有引用org.apache.http.legacy的问题<br/>\n368、修复华为Nova 5i, 64位主包兼容性<br/>\n367、修复11.0上外部存储重定向问题<br/>\n366、修复11.0上GMS登录问题<br/>\n365、修复11.0 部分APP读写sdcard报错的问题<br/>\n364、修复va core进程死亡后，APP可能打不开的问题<br/>\n363、增加未安装插件时无法启动的错误日志<br/>\n\n**2021年 8月22号 至 2021年 9月20号 商业版代码更新内容**\n\n362、横屏重新适配<br/>\n361、修复部分APP通过file协议安装后无法打开的问题<br/>\n360、修复传递给JobIntentService中Intent数据丢失问题<br/>\n359、修复JobIntentService第二次调用无法工作的问题<br/>\n358、修复华为手机上某些APP奔溃的问题<br/>\n357、修复小米手机上游戏登录问题<br/>\n356、修复某些应用加固后无法打开的问题<br/>\n355、增加对关联启动权限检测<br/>\n354、targetSdk 30适配<br/>\n353、修复targetSdk为30时，某些应用无法上网的问题<br/>\n352、修复targetSdk为30时，sdcard无法访问的问题<br/>\n351、编译脚本中使用cmake替换gradle task<br/>\n350、移除过时文档<br/>\n\t\n**2021年 8月7号 至 2021年 8月21号 商业版代码更新内容**\n\n349、调整优化gradle脚本<br/>\n348、hidedenApiBypass支持Android R+<br/>\n347、targetSdk 30 支持<br/>\n346、修复VIVO系统服务bug<br/>\n345、修复VIVO手机无法使用摄像头的bug<br/>\n344、修复dex加载异常状态的获取<br/>\n343、修复Android R上libart.so路径问题<br/>\n342、修复Andoid Q+ 删除通知的bug<br/>\n341、修复APN uri的权限检查<br/>\n340、修复Android R暂停恢复线程状态<br/>\n339、修复debug模式下部分hook失效情况<br/>\n338、修复hook在R之后的一些bug<br/>\n\n**2021年 4月25号 至 2021年 8月6号 商业版代码更新内容**\n\n337、修复探探部分手机不能上传头像问题<br/>\n336、修复Android 10 华为设备IO重定向问题<br/>\n335、调整横竖屏逻辑,减少异常情况发生<br/>\n334、添加Activity生命周期的回调接口<br/>\n333、修复Android 12的广播问题<br/>\n332、修复微信部分界面状态异常的BUG<br/>\n331、修复Outlook、One drive、Teams、Zoom等海外app的支持<br/>\n330、修复Android 11 一个权限请求BUG<br/>\n329、修复部分cocos2d引擎只显示半屏的问题<br/>\n328、修复微信在多用户下不能发送文件的问题<br/>\n327、split apk 支持<br/>\n326、Android S 支持<br/>\n\n**2021年 2月24号 至 2021年 4月24号 商业版代码更新内容**\n\n325、适配多用户环境<br/>\n324、修复新版微信的兼容问题<br/>\n323、兼容更多企业级加固<br/>\n322、支持VAPP设置电源优化<br/>\n321、修复缺失权限声明<br/>\n320、修复Android 11上android.test.base库的引用<br/>\n319、优化ext插件判断<br/>\n318、优化安装时ABI的选择<br/>\n317、修复Google文档在Android 11上崩溃的问题<br/>\n\n**2020年 10月15号 至 2021年 2月23号 商业版代码更新内容**\n\n316、解决新版爱加密、邦邦等加固的兼容性<br/>\n315、修复WhatsApp不显示冷启动Splash的问题<br/>\n314、优化对系统app的识别<br/>\n313、完善多用户环境下的支持<br/>\n312、解决ext插件部分情况下卡死的问题<br/>\n311、支持Google Play在容器中下载APP<br/>\n310、修复Android 11 QQ无法显示图片的问题<br/>\n309、兼容Android 11运行Google Service<br/>\n308、解决Android 11无法运行chromium<br/>\n307、支持Hook @CriticalNative Method<br/>\n306、修复JDK 13无法编译运行的问题<br/>\n305、修复Service部分情况可能crash的问题<br/>\n304、修复Android 11无法加载外部存储私有数据的问题<br/>\n303、修复低版本app无法使用org.apache.http.legacy的问题<br/>\n302、修复某些情况系统任务栈只显示最后一个的问题<br/>\n301、完善不同平台的构建脚本<br/>\n300、修复Android 11无法读取obb的问题<br/>\n299、解决软件无法向后兼容的问题<br/>\n298、重构VApp安装框架<br/>\n297、重构virtual文件系统<br/>\n296、修复某些情况下WebView无法启动的问题<br/>\n295、修复VApp卸载重装的BUG<br/>\n294、修复LOL手游的登录异常问题<br/>\n293、支持安装Splits APK<br/>\n292、支持动态配置主包环境<br/>\n291、修复32位QQ调用64位微信卡顿的问题<br/>\n290、修复Messenger调用Facebook崩溃的问题<br/>\n289、优化对Google服务框架的支持<br/>\n288、实现新的扩展包同步机制<br/>\n287、修复Android 11正式版的异常问题<br/>\n286、添加系统Package缓存，优化性能<br/>\n285、修复disabled组件还能被PMS查询的BUG<br/>\n284、修复微信部分界面Launch行为异常的问题<br/>\n283、修复ContentProvider.getCallingPackage返回Host包名的BUG<br/>\n282、修复uid虚拟化的BUG，解决部分app权限检查失败的问题<br/>\n281、重写PendingIntent, IntentSender的实现<br/>\n280、优化进程管理，修复长期存在的概率性进程死锁问题<br/>\n279、重写Service实现，Service生命周期更准确，不容易被杀死<br/>\n\n\n**2020年 9月13号 至 2020年 10月15号 商业版代码更新内容**\n\n278、修复 64 位 App 无法调用 32 位 App 的问题<br/>\n277、修复 Android R 加载 HttpClient 的问题 <br/>\n276、修复 Android R debug 模式下的崩溃问题<br/>\n\n**2020年 8月23号 至 2020年 9月12号 商业版代码更新内容**\n\n275、添加缺失的 service hook<br/>\n274、修复百度翻译无法启动的问题 <br/>\n273、修复 GP 下载的 split app 无法启动的问题<br/>\n\n**2020年 7月10号 至 2020年 8月22号 商业版代码更新内容**\n\n272、修复 Service 创建<br/>\n271、添加 NotificationService 缺失的 Hook<br/>\n270、修复 Yotube 崩溃<br/>\n\n**2020年 5月19号 至 2020年 7月9号 商业版代码更新内容**\n\n269、初步适配 Android 11 beta1<br/>\n268、修复小红书多开闪退的问题<br/>\n267、修复某些 App 多开报“应用签名被篡改”的问题<br/>\n\n**2020年 4月24号 至 2020年 5月18号 商业版代码更新内容**\n\n266、修复 sh 调用错误<br/>\n265、修复 9.0 以上最新版 Facebook 无法登陆的问题<br/>\n264、帮助企业微信修复启动虚拟存储的情况下无法拍照的问题<br/>\n263、修复某些情况下 64位 app 打不开 Activity 的问题<br/>\n\n**2020年 3月24号 至 2020年 4月23号 商业版代码更新内容**\n\n262、修复 Vivo 设备提示安装游戏 SDK 的问题<br/>\n261、修复 Android Q 无法加载部分系统 so 的问题<br/>\n260、修复华为设备微博未响应<br/>\n259、忽略不必要的权限检查造成的崩溃<br/>\n258、修复 WPS 分享文件崩溃的问题<br/>\n257、部分 10.0 设备的闪退问题<br/>\n\n**2020年 3月7号 至 2020年 3月23号 商业版代码更新内容**\n\n256、修复微信同时打开两个页面问题<br/>\n255、修复微信登陆成功但是返回登陆页面的问题<br/>\n254、修复最新版 QQ 无法下载附件的问题<br/>\n253、更新 SandHook 版本<br/>\n252、修复 9.0 以上安装未签名Apk问题 <br/>\n251、修复 10.0 的定位问题<br/>\n\n**2020年 1月16号 至 2020年 3月6号 商业版代码更新内容**\n\n250、调整 lib 重定向逻辑<br/>\n249、修复三星 10.0 系统上的崩溃问题<br/>\n248、修复 release build 的 hook 异常<br/>\n247、增加 SandHook 的 proguard 规则<br/>\n246、修复对部分 App 中 VirtualApk 的兼容问题 <br/>\n245、修复 VA 内部请求安装 apk 失败的问题<br/>\n\n**2019年 12月26号 至 2020年 1月15号 商业版代码更新内容**\n\n244、修复 Android Q 遗漏的 hook<br/>\n243、禁用 Emui10 的 AutoFill<br/>\n242、增加新 api 结束所有 activity<br/>\n\n**2019年 12月15号 至 2019年 12月25号 商业版代码更新内容**\n\n241、修复 Emui10 上企业微信等 App 无法启动的问题<br/>\n240、修复在 4.x 可能导致的崩溃<br/>\n239、升级 SandHook 修复对 Thread 类的 Hook<br/>\n238、修复 Android Q 某些接口导致的权限问题<br/>\n\n**2019年 11月20号 至 2019年 12月14号 商业版代码更新内容**\n\n237、修复 Notification 缓存导致的崩溃<br/>\n236、修复高版本 Notification 的 classloader 问题<br/>\n\n**2019年 11月9号 至 2019年 11月19号 商业版代码更新内容**\n\n235、修复 Android 5.x 的 ART Hook <br/>\n234、修复 ART Hook 可能导致的死锁问题 <br/>\n\n**2019年 11月2号 至 2019年 11月8号 商业版代码更新内容**\n\n233、修复 WPS, 网易邮箱等在 Q 设备上崩溃的问题 <br/>\n232、修复汤姆猫跑酷在部分 Q 设备上崩溃的问题 <br/>\n231、修复 QQ 在部分 Q 设备上崩溃的问题 <br/>\n\n**2019年 10月25号 至 2019年 11月1号 商业版代码更新内容**\n\n230、修复克隆 Google Play 下载的 64位 App<br/>\n229、修复企业微信 <br/>\n228、修复 Telegram <br/>\n\n**2019年 10月8号 至 2019年 10月24号 商业版代码更新内容**\n\n227、修复 Android P 下 AppOspManager 的异常 <br/>\n226、添加 Android P 下 ActivityTaskManager 丢失的 Hook <br/>\n225、修复 Android P 下 Activity Top Resume 异常 <br/>\n224、支持在系统多用户模式下运行! <br/>\n\n**2019年 10月8号 商业版代码更新内容**\n\n223、修复Android P 以上内部 app 返回桌面异常的问题 <br/>\n222、64位分支支持 Android Q <br/>\n\n**2019年 9月20号 至 2019年 10月7号 商业版代码更新内容**\n\n221、修复安装在扩展插件中的 apk 无法正确显示图标和名称的问题 <br/>\n220、修复 twitter 无法打开的问题 <br/>\n219、正式兼容 Android Q 正式版! <br/>\n218、修复 Android Q 某些 Activity 无法再次打开的问题 <br/>\n217、初步适配 Android Q 正式版 <br/>\n216、修复数个64位分支的 Bug <br/>\n215、新增加支持32位插件的64位分支，该分支支持32位旧设备并且64位设备在32位插件的情况下可以支持32位旧应用 <br/>\n\n**2017年 12月 至 2019年 7月 30 日 商业版代码更新内容**\n\n214、改进 App 层提示信息 <br/>\n213、改进部分编码 <br/>\n212、修复从宿主向插件发送广播的方法 <br/>\n211、兼容最新版 gradle 插件 <br/>\n210、增加广播命名空间以避免多个使用 VA 技术的 App 互相干扰 <br/>\n209、修复 IMO 打不开的问题 <br/>\n208、修复部分 ContentProvider 找不到的问题 <br/>\n207、支持纯32位模式，以兼容老设备 <br/>\n206、初步支持纯64位模式，以应对8月份的谷歌市场的策略变化 <br/>\n205、适配到 Android Q beta4 <br/>\n204、修复了货拉拉无法安装的问题<br/>\n203、优化了64位apk的判定逻辑<br/>\n202、修复配置网络证书的 App 的联网<br/>\n201、重构组件状态管理<br/>\n200、优化 MIUI/EMUI ContentProvider 兼容性<br/>\n199、修复 StorageStats Hook<br/>\n198、修复快手无法登陆<br/>\n197、修复 YY 无法启动，更好的兼容插件化框架<br/>\n196、修复 Facebook 登陆<br/>\n195、修复 Google Play 下载的 App 无法找到 so 的问题(皇室战争)<br/>\n194、修复 split apk 支持<br/>\n193、修复 Youtube 无法启动<br/>\n192、修复优酷无法启动的问题<br/>\n191、修复多开时app间可能存在广播namespace冲突的BUG<br/>\n190、采用新的策略绕过Android P以后的Hidden Policy API<br/>\n189、适配Android Q(beta1)<br/>\n188、修复华为设备部分app无法识别存储的问题<br/>\n187、修复启动进程可能失败导致app无法运行的问题<br/>\n186、修复4.4设备部分native符号无法找到的问题<br/>\n185、修复部分设备WebView包名获取失败的问题<br/>\n184、修复Service细节处理的问题<br/>\n183、优化启动速度<br/>\n182、修复WebView在少数机型加载失败的情况<br/>\n181、修复Lib决策的问题<br/>\n180、修复部分华为机型无法读取内存卡的问题<br/>\n179、修复Service可能存在的问题<br/>\n178、允许根据intent判断Activity是否在外部启动<br/>\n177、修复部分机型上Gms和Google Play启动到了不正确的环境<br/>\n176、修复新实现的StaticBroadcast导致的兼容性问题<br/>\n175、修复Android P上无法使用apache.http.legacy的问题<br/>\n174、实现Native trace<br/>\n173、优化IO Redirect性能<br/>\n172、修复wechat部分时候出现网络无法连接的问题<br/>\n171、修复小概率process attach不正确的BUG<br/>\n170、开始下一阶段的ROADMAP<br/>\n169、解决Android P无法注册超过1000个广播导致的问题<br/>\n168、修复可能导致ANR的DeadLock<br/>\n167、修复部分app动态加载so失败的问题<br/>\n166、修复免安装运行环境下部分机型第一次打开出现黑屏的问题<br/>\n165、兼容适配多款主流的Android模拟器<br/>\n164、优化启动性能<br/>\n163、解决多个内存泄露问题<br/>\n162、修复IO Redirect优先级的问题<br/>\n161、修复8.0以下设备Messenger无网络连接的问题<br/>\n160、修复双开时外部app卸载时内部app仍然保留的BUG<br/>\n159、修复部分腾讯加固无法运行的问题<br/>\n158、修复Instagram无法登录Facebook的BUG<br/>\n157、修复进程小概率可能重复启动的BUG<br/>\n156、修复GET_PERMISSIONS没有获取权限的BUG<br/>\n155、修复startActivityIntentSender的BUG<br/>\n154、修复vivo设备部分Activity无法启动的问题<br/>\n153、修复app无法调用外部app选择文件的问题<br/>\n152、完善Android P的兼容<br/>\n151、兼容Android P的Google服务<br/>\n150、解决Messenger部分功能异常<br/>\n149、完善IO Redirect<br/>\n148、大量适配Gms, 修复Gms运行过程中进程无限重启的问题<br/>\n147、重新实现Service的运行机制<br/>\n146、完善64bit，提供了部分ROM配置64bit Engine权限的API<br/>\n145、修复了4.4设备上的Activity启动问题<br/>\n144、支持excludeFromRecent属性<br/>\n143、修复Instagram无法Facebook登录的问题<br/>\n142、修复Facebook第一次登录闪退的问题<br/>\n141、支持以64位模式运行Gms、Google play、Play game<br/>\n140、支持在双开/免安装运行的Google play中下载和安装app<br/>\n139、修复DownloadManager的BUG<br/>\n138、修复Google play返回上层时重启界面的BUG<br/>\n137、修复免安装模式下so决策问题<br/>\n136、优化构建脚本，便于引入项目<br/>\n135、修复移动MM SDK无法启动的问题<br/>\n134、修复微信摇一摇的BUG<br/>\n133、修复中兴设备不稳定的BUG<br/>\n132、支持ARM64下的IO Redirect<br/>\n131、修复USE_OUTSIDE模式下外部app更新时，内部app没有更新的BUG<br/>\n130、兼容最新Android 9.0(代号: pie) 及正式版之前发布的四个Preview版本<br/>\n129、兼容内置houdini的x86设备<br/>\n128、WindowPreview技术，使app启动与真实app达到一样的速度<br/>\n127、新的ActivityStack以提高app运行质量<br/>\n126、解决接入Atlas Framework的app运行异常的问题<br/>\n125、现在可以定义虚拟app返回桌面的具体行为<br/>\n124、现在双开模式下app随系统动态更新，不需要手动检查<br/>\n123、支持targetSdkVersion >= 26时仍可正常运行低版本的app<br/>\n122、兼容腾讯游戏管家的QDroid虚拟引擎 (beta)<br/>\n121、大量重构底层代码，大幅提升运行速度<br/>\n120、修复网易新闻分享到微博后无法取消的问题<br/>\n119、修复App自定义权限无法识别的问题<br/>\n118、修复墨迹天气app无法启动的问题<br/>\n117、修复部分政府app无法启动的问题<br/>\n116、API的变动详见代码<br/>\n115、修复三星系列应用的相互调用问题<br/>\n114、修复小米应用在非小米系统的账号问题<br/>\n113、修复分享/发送等第三方调用，返回页面不正常<br/>\n112、修复应用宝提示不能安装<br/>\n111、调用第三方app，对uri进行加密<br/>\n110、适配前刘海<br/>\n109、适配小米rom的hook<br/>\n108、适配努比亚录音问题<br/>\n107、内部悬浮窗权限控制<br/>\n106、优化自定义通知栏的处理<br/>\n105、修复Context的INCLUDE_CODE权限问题<br/>\n104、适配华为，oppo的角标<br/>\n103、修复百度视频的进程重启问题<br/>\n102、修复某些snapchat的无法启动问题<br/>\n101、适配autofill服务，例如piexl系列<br/>\n100、完善64位的io hook<br/>\n99、优化hook库的兼容性，加回dlopen<br/>\n98、64位扩展包的so移到32位主包。（jni代码改动后，在Run之前，请先build一次）<br/>\n97、通知栏改动：适配8.1的通知渠道；移除应用时，移除应用的全部通知<br/>\n96、兼容部分app，需要设置android:largeHeap=true<br/>\n95、修复ffmpeg库的视频无法播放问题<br/>\n94、优化横竖屏切换<br/>\n93、降低通过Intent.ACTION_VIEW调用外部Activity限制。<br/>\n92、兼容MG SDK<br/>\n91、64位支持还在开发阶段<br/>\n90、更新混淆配置app/proguard-rules.pro，必须加规则-dontshrink<br/>\n89、优化模拟机型，例如：模拟后，某些app不出现设备验证<br/>\n88、提高dex2oat兼容性<br/>\n87、优化模拟定位<br/>\n86、移除dlopen<br/>\n85、targetVersion可以改为26：支持targetVersion<23的app动态权限申请，支持targetVersion<24的文件Uri<br/>\n84、installPackage改为默认异步形式<br/>\n83、为了支持64位模式，换回aidl<br/>\n82、去掉SettingHandler现在可以动态设置特殊规则，规则会存储，不需要重复设置<br/>\n81、增加2个native_setup<br/>\n80、提高jobService兼容性<br/>\n79、ShortcutService相关：关联VASettings.ENABLE_INNER_SHORTCUT<br/>\n78、为了稳定性和运行效率，去掉上个版本的蓝牙，wifi，不声明权限的适配。<br/>\n77、增加app启动异常的广播Constants.ACTION_PROCESS_ERROR<br/>\n76、修复少数游戏横屏判断问题<br/>\n75、demo增加机型模拟<br/>\n74、适配vivo一个自定义权限（后台弹窗）VA是把一个历史acitivty返回前台，vivo需要这个权限。<br/>\n73、如果没有蓝牙权限，返回默认值（海外用）<br/>\n72、修复uid权限检查问题<br/>\n71、安全性更新，内部应用的文件权限控制<br/>\n70、提高内部app调用的兼容性，第三方登录，分享<br/>\n69、自动过滤没权限的外部ContentProvider<br/>\n68、增加功能：内部app的权限检查（默认关闭）<br/>\n67、机型模拟:Build类和build.prop<br/>\n66、提高对乐固加固的app兼容性<br/>\n65、适配三星wifimanager<br/>\n64、修复ipc框架一个参数传递问题（IPCMethod这个类必须更新）<br/>\n63、补全7.0通知栏的hook<br/>\n62、修正8.0动态快捷菜单的hook<br/>\n61、SettingHandler新增一个适配接口，主要适配各种游戏<br/>\n60、功能改动：google自动安装改为手动安装，避免第一次启动时间过久<br/>\n59、可以禁止访问外部某个ContentProvider<br/>\n58、适配华为桌面图标数量<br/>\n57、权限分类注释，标注可删除权限。<br/>\n56、增加双开模式的app跟随外部升级的开关。<br/>\n55、提高app的jni兼容性。<br/>\n54、提高对app集成其他插件框架的兼容性。<br/>\n53、增加设置接口，根据包名进行设置。<br/>\n52、增加Uri的适配范围，支持通过Uri分享和查看文件。<br/>\n51、修复一个在三星8.0的问题。<br/>\n50、提高对系统自带的app组件兼容性，更好兼容chrome webview，google service。<br/>\n49、提高ART稳定性<br/>\n48、增加相机适配范围<br/>\n47、支持内部App在8.0下的快捷方式管理<br/>\n46、修复exec异常<br/>\n45、提高稳定性（修复微信登录闪退）<br/>\n44、解决微信数据库崩溃问题<br/>\n43、修复部分4.4设备崩溃问题<br/>\n42、修复后台应用易被杀死，土豆视频黑屏，新浪微博无法打开，优酷两次返回无法退出。<br/>\n41、增加应用的保活机制，双开APP更不易被杀死。<br/>\n40、优化虚拟引擎启动性能。<br/>\n39、兼容了大部分的加固，第三方APP兼容性对比上一版提升40%+。<br/>\n38、修复某些rom下，快捷方式图标不正确<br/>\n37、兼容以前组件StubFileProvider<br/>\n36、适配部分新ROM的虚拟IMEI<br/>\n35、改善进程初始化代码，增加稳定性<br/>\n34、添加内部发送Intent.ACTION_BOOT_COMPLETED的广播，可以设置开关<br/>\n33、适配关联google play游戏，支持游戏使用google登录<br/>\n32、适配android O的google service框架<br/>\n31、适配android O 快捷方式<br/>\n30、适配耳机模式<br/>\n29、某些rom对intent的大小限制，demo添加缩放快捷方式图标代码<br/>\n28、修复多开情况下一个bug<br/>\n27、修复某些情况下MediaController的bug<br/>\n26、修复4.1.2的StubFileProvider报错<br/>\n25、分享的uri处理<br/>\n24、修复跨app调用Activity的回调<br/>\n23、前台服务的通知栏拦截开关<br/>\n22、附带doc<br/>\n21、完善VA内部的intent的CHOOSE回调<br/>\n20、Android O的通知栏适配2<br/>\n19、ipc框架优化, 提高判断binder的存活准确性<br/>\n18、jni的log开关 Android.mk:LOCAL_CFLAGS += -DLOG_ENABLE<br/>\n17、混淆配置<br/>\n16、Android O的通知栏适配<br/>\n15、修复部分app网络卡的问题<br/>\n14、适配 android 8.0的dl_open（jni加载）<br/>\n13、修复华为emui8.0的一个bug<br/>\n12、完善定位<br/>\n11、设置手机信息，imei伪装算法<br/>\n10、适配8.0某个功能（主要app：whatsapp）<br/>\n9、修复内部微信等应用，无法更新图片，视频<br/>\n8、demo增加安装监听，自动升级克隆模式的应用<br/>\n7、7.0的file provider适配<br/>\n6、增加了定位代码<br/>\n5、代码进行了架构优化<br/>\n4、与开源版不同的特征<br/>\n3、解决了微信被封的一些问题<br/>\n2、修复了部分机型兼容性<br/>\n1、修复了12个小BUG<br/>\n</details>\n\n\n\n\n\n\n"
  },
  {
    "path": "README_eng.md",
    "content": "\n[中文文档](README.md \"中文\")\n\n<h1><p align=\"center\">VA Product description & Development guidance</p></h1> \n\n## What is VA? ##\nVirtualAPP (abbreviation: VA) is a sandbox product running on Android system, which can be understood as a lightweight \"Android virtual machine\". Its product form is a highly extensible, customizable, integrated SDK that allows you to develop a variety of seemingly impossible projects based on or using VA. Now, VA is widely used in many technology fields as following: mini game collection, blockchain, cloud control, silent hot fix and so on. On the one hand, you can realize cloud control mobile office security and achieve military and government data isolation with VA. On the other hand, you can implement script automation, device-info-mock, and plug-in development. Meanwhile, you can realize multi space and games booster. You can also rent the mobile game account and use the mobile controller without activation by VA. <br> **The code on Github has stopped updating in December 2017. The code of business version is continuously being updated. If you need license to obtain the latest code, please contact WeChat: 10890.**\n\n\n## Terminology in VA ##\nTerminology | Explanation\n---- | ---\nHost | The APP that integrates the VirtualAPP SDK is called  host.  \nHost Plug-in | A host package is used to run another ABI on the same device. It also called plug-in package,extension package, host plug-in package, host extension package.\nVirtual APP / VAPP | App installed in the VA space\nExternal APP | App installed in the device\n<br/>\n\n## VA Technical architecture ##\n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/va_architecture.jpg)  \nVA technology involves the APP layer, Framework layer and Native layer of Android in total.\nApp must be installed on the system before it can run. The APP installed inside the VA  space is not actually installed into the system, so it cannot run. Then how to get it to run?\nAnswer: The only way to do this is to \"cheat\" the system into thinking it has been installed. This \"cheat\" process is the core work of the VA Framework, and is also the core technical principle of the VA.  \n\n**Here is the description of what did each layer do:**\n\nLayer | Main work\n---- | ---\nVA Space | An internal space is provided by the VA for the installation of the APP to be run inside it, and this space is system isolated.\nVA Framework | This layer is mainly a proxy for Android Framework and VAPP, which is the core of VA. And VA provides a set of VA Framework of its own, which is between Android Framework and VA APP. </br>1. For VAPP, all the system services it accesses have been proxied by VA Framework, which will modify the request parameters of VAPP and send all the parameters related to VAPP installation information to Android Framework after changing them to the parameters of the host （Some of the requests will be sent to their own VA Server to be processed directly, and no longer send to the Android system）. This way Android Framework receives the VAPP request and checks the parameters, and it will think there is no problem.</br>2. When the Android system finishes processing the request and returns the result, the VA Framework will also intercept the return result and restore all the parameters that have been original modified to those that were sent during the VAPP request. This way the interaction between VAPP and Android system can work.\nVA Native | The main purpose of this layer is to accomplish 2 tasks: IO redirection and the request modification for VA APP to interact with Android system. </br>1. IO redirection is some APPs may be accessed through the hard code absolute path. But if the APP is not installed to the system, this path does not exist. Through IO redirection, it will be redirected to the path to install inside VA.</br>2. In addition, there are some jni functions that cannot be hooked in VA Framework, so they need to be hooked in the native layer.\n</br>\n\nIn summary:\nAs you can see from the above technical architecture, the internal VA APP actually runs on top of VA's own VA Framework. VA has intercepted all system requests from its internal APP, and through this technology it can also have full control over the APP, not just the multi space. And for the convenience of developers, VA also provides SDK and Hook SDK.\n\n\n## VA Process architecture#\n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/va_process.jpg)    \nThere are five types of processes in the VA’s runtime: CHILD process, VA Host Main process, VA Host Plugin process, VAPP Client process, and VAServer process. \nTo support both 32-bit and 64-bit APPs, VA needs to install two packages: a master package and a plug-in package ( In this document, the main package is 32 bits and the plug-in package is 64 bit ).\nTwo packages are also necessary because a package can only run in one mode, either 32-bit or 64-bit. So for 32-bit APPs, VA uses the 32-bit main package to run, and for 64-bit APPs, VA uses the 64-bit plug-in package to run.\nThe main package contains all the code of VA, and the plug-in package contains only one piece of code that loads the main package code for execution, no other code. So plug-in package rarely needs to be updated, just the main package. \nIn addition, whether the main package is chosen to use 32-bit or 64-bit can be modified in the configuration file ( For example, for users who want to access GooglePlay, it will be modified to 64-bit for the main package and 32-bit for the plug-in package ).\n\n**The functions and explanations of the each type of process are as follows:**</br>\n\nProcess Type | Function\n---- | ---\nCHILD | Other processes integrated by VA Host, such as: keepalive process, push process, etc.\nVA Host Main | The process where the UI main interface of the VA main package is located. The default main package is 32-bit and the plug-in package is 64-bit, which can be modified and switched in the configuration file\nVA Host Plugin | The process that supports the plug-in package of 64-bit APP. The default main package is 32-bit and the plug-in package is 64-bit, which can be modified and switched in the configuration file.\nVAPP Client | The process generated by the APP installed into VA after it starts, it will modify io.busniess.va:pxxx process name to the real process name of VAPP when it runs.\nVAServer | The process where the VA Server is located, it is used to handle requests in VA that are not assigned to the system for processing, such as APP installation processing.\n<br/>\n\n## VA can satisfy almost all your needs ##\nThrough the above technical architecture, we can know that VA can fully control APP and provide Hook SDK, which can satisfy almost all your needs in various fields: \n\n1. Satisfy the need of **dual/multi space**   \nVA allows you to install multiple WeChat/QQ/WhatsAPP/Facebook and other APPs on the same mobile phone, so you can have one phone with multiple accounts logged in at the same time.  \n\n2. Satisfy the need of **mobile security**  \nVA provides a set of internal and external isolation mechanisms, including but not limited to (file isolation / component isolation / process communication isolation). Simply speaking, VA internal is a \"completely independent space\". \nThrough VA, work affairs and personal affairs can be safely separated without mutual interference. With a little customization, you can achieve mobile security-related needs such as application behavior audit, data encryption, data acquisition, data leakage prevention, anti-attack leaks and so on.    \n    **2.1 Application behavior audit**  \nThe HOOK capability provided by VA can realize real-time monitoring of user usage behavior and upload violation information to the server. And it's easy to implement things like Time Fence ( whether a feature of the APP can be used in a certain time ), Geo Fence ( whether a feature of the APP can be used in a certain area ), sensitive keyword filtering interception and other functional requirements.    \n    **2.2 Data encryption**    \nThe HOOK capability provided by VA can realize all data/file encryption of the application, ensuring data/file landing security.  \n    **2.3 Data acquisition**           \nThe HOOK capability provided by VA can realize the demand for real-time silent upload of application data, such as chat records and transfer records, preventing them from being deleted afterwards without traceability.  \n\t**2.4 Data leakage prevention**  \nThe HOOK capability provided by VA can realize application anti-copy/paste, anti-screenshot/recording, anti-sharing/forwarding, watermark traceability and other requirements.   \n\t**2.5 Anti-attack leaks**  \nWith the application control capability provided by VA, privacy-related behaviors such as SMS/ address book/call log/ background recording/background photo/ browsing history and location information can be completely controlled in sandbox, prevent Trojan horses/malicious APPs from acquiring users' real private data, causing serious consequences such as leakage of secrets.\n3. Satisfy the need of **ROOT without HOOK**  \nVA provides Hook capability of Java and Native. With VA, you can easily achieve functions required by various scenarios, such as virtual positioning, changing device, APP monitoring and management, mobile security and so on.  \n\n4. Satisfy the need of **silent installation**  \nVA provides the ability to silently install, silently upgrade and silently uninstall APPs. For example, the application store or game center can be integrated with VA to avoid the need for users to manually click to confirm the installation operation, so that it can be installed into VA immediately after downloading, bringing users an experience like \"small program\" , completely avoiding the problem of applications not easily installed by users.  \n\n5. Satisfy the need of **APP controlled**   \nYou can clearly grasp the system API, sensitive data, device information, etc. accessed by the APP through VA. For example, whether the APP accesses the contacts, photo albums, call log, whether it accesses the user's geographic location and other information.\nOf course, you can also control or construct custom messages to these APPs via VA, and not only that, you can also get access to the APP's private data, such as chat database and so on. In a word, through the application control capability provided by VA, you can easily control all the behaviors of the APP, even modify the content of the APP and server interaction and so on .  </br>\n\n\n6. Satisfy the need of **overseas markets**  \nVA implements support for Google services to support overseas APPs running, such as Twitter, Messenger, WhatsAPP, Instagram, FaceBook, Youtube and so on.\n\n7. Satisfy the need of **almost everything you can think of**  \nVA has complete oversight and control over the internal APP, and can meet almost any of your needs！\n8. VA is also the only commercially licensed product in this technology area   \n**Hundreds of** licensed customers are currently paying to use the business version of VirtualAPP code, and the APP integrated with VirtualAPP code is launched more than 200 million times per day. Many Android engineers provide us with user feedback in different scenarios, and through our technical team's continuous optimization and iteration, we continue to improve product performance and compatibility.\n\n\nVA Specialized capabilities\n---\n\n- Cloning ability<br/>\nYou can clone the APP already installed in the external system and run it internally without mutual interference. Typical application scenario is double space.\n\n- Without installation ability<br/>\nIn addition to cloning already installed, VA can install (externally silent ) apk's directly internally and run them directly internally. Typical application scenarios are plug-in, standalone APP marketplace and so on.\n\n- Double space ability<br/>\nVA is not only \"double space\", but also has a unique multi-user mode that allows users to open the same APP internally for an unlimited number of times.\n\n- Internal and external isolation ability<br/>\nVA is a standard sandbox, or \"virtual machine\", that provides a set of internal and external isolation mechanisms, including but not limited to (file isolation/component isolation/process communication isolation). Simply put, the inside of a VA is a \"completely separate space\". Simply put, the inside of a VA is a \"completely separate space\". Based on it, you can realize a \"virtual phone\" on your cell phone with a little customization. Of course, you can also use your imagination to customize it for data encryption, data isolation, privacy protection, and enterprise management applications.\n\n- Full control over internal APPs ability<br/>\nVA has complete monitoring and control over the internal APP, which is absolutely impossible to achieve in an external environment without Root.\n\n<details>\n<summary>Details(Drop down to open)</summary>\n  1. Service request control. First, VA directly provides some service request interception, you can easily customize these service requests when integrating VA, including but far from limited to (APP request to install apk / APP request to open certain files / APP request for location data / APP request for phone information, etc.)<br/><br/>\n  2. System API control. VA virtualizes and implements the entire Android system framework, which is the principle that VA can run apk internally without installation. And you can through modify the virtual framework's implementation to dynamically monitor and analyze the behavior of the app, etc. In addition, you can also mock some system behavior to achieve some needs that are difficult to achieve externally (e.g. game controller).<br/><br/>\n  3. Memory read and write. VA can read and write the memory of internal APP processes without Root.<br/><br/>\n  4. Root without debugging. VA can debug (ptrace) internal APP processes without Root, based on which you can also achieve Root-free process injection.<br/><br/>\n  5. Load arbitrary \"plug-in\" and \"behaviors\". The APP process inside VA is derived from the Client side code of the VA framework, so you can insert any \"load\" and \"control\" logic into the entry code of the process. These are very simple to implement.<br/><br/>\n  6. Hook. VA has a set of built-in Xposed framework and native hook framework running on all versions of Android (until AndroidQ), based on it, you can easily Hook any Java/Native of any internal APP.<br/><br/>\n  7. File control. VA built in a complete file redirection, which allows easy control of reading and writing of files from internal apps. Based on it, you can realize many functions such as protection and encryption of files can be achieved.<br/><br/>\n  8. Note: The above control capabilities are implemented with code or examples for reference.\n</details>\n\n\nVA Other features\n---\n\n- High performance<br/>\nProcess-level \"virtual machine\", VA's unique implementation model makes its performance almost the same as that of the native APP, and does not need a long startup of ordinary virtual machines.\n\n- Full version support<br/>\nSupport 5.0-17.0, 32-bit/64-bit APP, ARM and X86 processor. And support Android version in the future which will be updated.\n\n- Easy Expansion and Integration<br/>\nThe integration of VA is similar to the normal Android library, even if your APP has been online, you can conveniently integrate VA and enjoy the capability brought by VA.\n\n- Support Google services<br/>\nProvide support for Google services in order to support overseas APPs.\n\n\n## Comparison between VA and other technical solutions ##\nWhen doing enterprise-level mobile security, it is often necessary to control the APP, and the following is a comparison of possible technical solutions listed： \n\nTechnical solution | Principle introduction | Comment |  Running performance | Compatibility stability | Project maintenance cost\n---- | --- | ---  | ---  | ---  | --- \nRepackage | Repackage the target APP by decompiling it and adding your own control code | 1. Nowadays, almost all APPs have hardened or tamper-proof protection, and repackaging is already a very difficult task</br> 2.The mobile phone system will also detect whether the APP is repackaged, if it is repackaged, it will directly prompt the user that there is a security risk, and even not allow the installation</br>3.For each APP, even each version to go deep to reverse analysis, time-consuming and difficult to maintain  | Excellent  | Poor  | High\nCustom ROM | By customizing the system source code and compiling it to flash to the designated mobile phone | Only for specified internal mobile phones, too limited to be extended  | Excellent  | Excellent  | High\nROOT the mobile phone | By rooting the mobile phone，flashing a framework which is similar to Xposed | 1.Now, root the mobile phone is an unlikely thing</br> 2.In reality, it is difficult for users to root their own mobile phones  | Excellent  | Poor  | High\nVA | Lightweight virtual machine with high speed and low device requirements | No risk point mentioned above  | Excellent  | Excellent. Hundreds of companies testing feedback at the same time  | Low. \nVA provides API and a professional technical team to ensure the stable operation of the project\n<br/>\nAs you can see from the above comparison, VA is an excellent product and can reduce your development and maintenance costs.\n\n## Integrating VA Steps ##\nStep 1: Call the VA interface```VirtualCore.get().startup()```in your application to start the VA engine.  \nStep 2: Call VA interface```VirtualCore.get().installPackageAsUser(userId, packageName)```to install the target APP into VA.\nStep 3: Call VA interface```VActivityManager.get().launchApp(userId, packageName)```to start the APP.   \n**With only the above 3 APIs to complete the basic use, VA has shielded the complex technical details and provided the interface API to make your development easy.**\n\n## VA compatible stability ##\nVA has been extensively tested by ** hundreds of **companies, including **high standards of testing and feedback of dozens of listed companies**, covering almost all types of equipment and scenarios at home and abroad, providing full protection for your stable operation!\n \n\nUp to now, the supported system versions:\n\nSystem version | Whether to support\n---- | ---\n5.0 | support\n5.1 | support\n6.0 | support\n7.0 | support\n8.0 | support\n9.1 | support\n10.0 | support\n11.0 | support\n12.0 | support\n13.0 | support\n14.0 | support\n15.0 | support\n16.0 | support\n17.0 | support\n<br/>\n\n\nSupported App Types:\n\nApp Type | Whether to support\n---- | ---\n32-bit APP | support\n64-bit APP | support\n<br/>\n\nSupported HOOK Types:\n\nHook Type | Whether to support\n---- | ---\nJava Hook | support\nNative Hook | support\n\nSupported CPU Types:\n\nHook Type | Whether to support\n---- | ---\nARM 32 | support\nARM 64 | support\n<br/>\n\n## How to give feedback on problems encountered with integrated VA ? ##\nAfter the purchase of the license we will establish a WeChat group, any problems can always feedback to us, and according to the priority in the first time to deal with.\n\n## VA Development document ##\nPlease refer to the VA development documentation：[Development document](doc/VADev_eng.md)\n\n\nLicense Instructions\n------\nVirtualApp virtual machine technology belongs to: Jining Luohe Network Technology Co., LTD. It applied for several VirtualApp intellectual property rights from 2015 to 2026 and` is protected by the Intellectual property Law of the People's Republic of China`.When you need to use the code on Github, **please purchase a business license**，and receive the full source code of the latest VirtualApp business version.Hundreds of licensed customers are paying to use the business version of VirtualApp code, and the app integrated with VirtualApp code is launched more than 200 million times a day. Many Android engineers provided us with user feedback in different scenarios, and through our technical team's continuous optimization and iteration, VirtualApp Business Edition code has better performance and higher compatibility. `The company of that year will become one of them after obtaining the license, and enjoy the technological achievements after the continuous iteration. And we can interact and collaborate with our licensed customers operationally, technically and commercially.`\n\n<br/>\nPerson in charge: Mr. Zhang <br/>\nWeChat：10890 <br/>\n<br/>\n\n\nSerious statement\n------\nIf you use VirtualApp for **internal use, business profit or upload it to the application market**without licensing. We will take evidence and then report you to the police (for copyright infringement) or prosecute you. It will cause your company to undertake criminal liability and legal action, and affect your company's goodwill and investment.`Purchasing a business license can save you a lot of time developing, testing and refining compatibility, leaving you more time for innovation and profitability.`Luo He Technology has called to the police and sued a number of individuals and companies in 2020.<br/>\n\n**In response to the national call for the protection of intellectual property rights! Anyone who reports that his or her company or other companies are using VirtualApp code to develop products without licensing will be given a cash reward upon verification. We will keep the identity of the whistleblower confidential! Reporting WeChat: 10890.**\n\n  <br/>\n\nMajor updates of the business version\n------\n\n1. Support Android 17.0\n2. Support Seccomp-Bpf.\n3. Not easily misreported by anti-virus software\n4. Framework optimization, performance greatly improved\n5. Mobile system and APP compatibility greatly improved\n6. Run Google services perfectly\n7. Supports running pure 64-bit Apps\n8. Built-in `XPosed Hook` framework\n9. Add positioning mock code\n10. Add code to change device \n11. Nearly 700 other fixes and improvements\n<br>\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "VirtualApp/.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": "VirtualApp/app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "VirtualApp/app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\n\nandroid {\n    compileSdkVersion 26\n    buildToolsVersion '26.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\n    sourceSets {\n        main{\n            jniLibs.srcDirs = ['libs']\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.2'\n    compile 'com.android.support:appcompat-v7:25.4.0'\n    compile 'com.melnykov:floatingactionbutton:1.3.0'\n    compile 'com.android.support:recyclerview-v7:25.4.0'\n    compile 'com.android.support:percent:25.4.0'\n    compile 'com.android.support:design:25.4.0'\n    compile 'com.android.support:cardview-v7:25.4.0'\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}\n"
  },
  {
    "path": "VirtualApp/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-keep   class com.amap.api.maps.**{*;}\n-keep   class com.autonavi.**{*;}\n-keep   class com.amap.api.trace.**{*;}\n\n#定位\n-keep class com.amap.api.location.**{*;}\n-keep class com.amap.api.fence.**{*;}\n-keep class com.autonavi.aps.amapapi.model.**{*;}\n\n#搜索\n-keep   class com.amap.api.services.**{*;}\n\n#2D地图\n-keep class com.amap.api.maps2d.**{*;}\n-keep class com.amap.api.mapcore2d.**{*;}\n\n#导航\n-keep class com.amap.api.navi.**{*;}\n-keep class com.autonavi.**{*;}"
  },
  {
    "path": "VirtualApp/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        <meta-data\n            android:name=\"TencentMapSDK\"\n            android:value=\"4HPBZ-2QWC6-H47SR-M6PZY-MTZB5-N2F4F\"/>\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        <activity\n            android:name=\".home.location.VirtualLocationSettings\"\n            android:screenOrientation=\"portrait\"\n            android:theme=\"@style/UITheme\"\n            />\n\n        <activity\n            android:name=\".home.location.MarkerActivity\"\n            android:screenOrientation=\"portrait\"\n            android:theme=\"@style/UITheme\"\n            />\n    </application>\n\n\n</manifest>\n"
  },
  {
    "path": "VirtualApp/app/src/main/java/io/virtualapp/VApp.java",
    "content": "package io.virtualapp;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android.support.multidex.MultiDexApplication;\n\nimport com.flurry.android.FlurryAgent;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.stub.VASettings;\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 */\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                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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/app/src/main/java/io/virtualapp/abs/ui/BaseAdapterPlus.java",
    "content": "package io.virtualapp.abs.ui;\n\nimport android.content.Context;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.SpinnerAdapter;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\npublic abstract class BaseAdapterPlus<T> extends BaseAdapter implements SpinnerAdapter {\n    protected Context context;\n    private LayoutInflater mLayoutInflater;\n    protected final List<T> mItems = new ArrayList<T>();\n\n    public BaseAdapterPlus(Context context) {\n        this.context = context;\n        mLayoutInflater = LayoutInflater.from(context);\n    }\n\n    public Context getContext() {\n        return context;\n    }\n\n    public boolean add(T item) {\n        return add(-1, item, false);\n    }\n\n    public boolean add(int pos, T item, boolean onlyone) {\n        if (item != null) {\n            if (onlyone) {\n                if (exist(item)) {\n                    return false;\n                }\n            }\n            if (pos >= 0) {\n                mItems.add(pos, item);\n            } else {\n                mItems.add(item);\n            }\n            return true;\n        }\n        return true;\n    }\n\n    public T remove(int pos) {\n        return mItems.remove(pos);\n    }\n\n    public List<T> getItems() {\n        return mItems;\n    }\n\n    protected <VW extends View> VW  inflate(int resource, ViewGroup root) {\n        return (VW) mLayoutInflater.inflate(resource, root);\n    }\n\n    protected <VW extends View> VW inflate(int resource, ViewGroup root, boolean attachToRoot) {\n        return (VW) mLayoutInflater.inflate(resource, root, attachToRoot);\n    }\n\n    public void clear() {\n        mItems.clear();\n    }\n\n    public void set(Collection<T> items) {\n        clear();\n        addAll(items);\n    }\n\n    public void addAll(Collection<T> items) {\n        if (items != null) {\n            mItems.addAll(items);\n        }\n    }\n\n    public int findItem(T item) {\n        return mItems.indexOf(item);\n    }\n\n    public boolean exist(T item) {\n        if (item == null) return false;\n        return mItems.contains(item);\n    }\n\n    @Override\n    public final int getCount() {\n        return mItems.size();\n    }\n\n    public final T getDataItem(int position) {\n        return mItems.get(position);\n    }\n\n    @Override\n    public final T getItem(int position) {\n        if (position >= 0 && position < getCount()) {\n            return mItems.get(position);\n        }\n        return null;\n    }\n\n    public final T getItemById(long id) {\n        return getItem((int) id);\n    }\n\n    @Override\n    public long getItemId(int position) {\n        return position;\n    }\n\n    @Override\n    public final View getView(int position, View convertView, ViewGroup parent) {\n        if (convertView == null) {\n            convertView = createView(position, parent);\n        }\n        T t = getItem(position);\n        attach(convertView, t, position);\n        return convertView;\n    }\n\n    @Override\n    public View getDropDownView(int position, View convertView, ViewGroup parent) {\n        if (convertView == null) {\n            convertView = createView(position, parent);\n        }\n        T t = getItem(position);\n        attach(convertView, t, position);\n        return convertView;\n    }\n\n    protected abstract View createView(int position, ViewGroup parent);\n\n    protected abstract void attach(View view, T item, int position);\n\n    public static class BaseViewHolder {\n        protected View view;\n        protected Context context;\n\n        public BaseViewHolder(View view) {\n            this.view = view;\n            this.context = view.getContext();\n        }\n\n        protected <T extends View> T $(int id) {\n            return (T) view.findViewById(id);\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/app/src/main/java/io/virtualapp/delegate/MyComponentDelegate.java",
    "content": "package io.virtualapp.delegate;\n\nimport android.app.Activity;\nimport android.app.Application;\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\n    @Override\n    public void beforeApplicationCreate(Application application) {\n\n    }\n\n    @Override\n    public void afterApplicationCreate(Application application) {\n\n    }\n\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": "VirtualApp/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": "VirtualApp/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.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        if (oldTaskDescription == null) {\n            return null;\n        }\n        String labelPrefix = \"[\" + VUserManager.get().getUserName() + \"] \";\n        String oldLabel = oldTaskDescription.getLabel() != null ? oldTaskDescription.getLabel() : \"\";\n\n        if (!oldLabel.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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.location.VirtualLocationSettings;\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.MultiplePackageAppData;\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(\"Virtual Location\").setIcon(R.drawable.ic_notification).setOnMenuItemClickListener(item -> {\n            startActivity(new Intent(this, VirtualLocationSettings.class));\n            return true;\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 || model instanceof MultiplePackageAppData) {\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": "VirtualApp/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": "VirtualApp/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.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.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 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    }\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        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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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(false, 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": "VirtualApp/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": "VirtualApp/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            if (!appModel.fastOpen) {\n                try {\n                    VirtualCore.get().preOpt(appModel.packageName);\n                } catch (Exception 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": "VirtualApp/app/src/main/java/io/virtualapp/home/adapters/AppLocationAdapter.java",
    "content": "package io.virtualapp.home.adapters;\n\nimport android.content.Context;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport io.virtualapp.R;\nimport io.virtualapp.abs.ui.BaseAdapterPlus;\nimport io.virtualapp.home.models.LocationData;\n\npublic class AppLocationAdapter extends BaseAdapterPlus<LocationData> {\n    public AppLocationAdapter(Context context) {\n        super(context);\n    }\n\n    @Override\n    protected View createView(int position, ViewGroup parent) {\n        View view = inflate(R.layout.item_location_app, parent, false);\n        ViewHolder viewHolder = new ViewHolder(view);\n        view.setTag(viewHolder);\n        return view;\n    }\n\n    @Override\n    protected void attach(View view, LocationData item, int position) {\n        ViewHolder viewHolder = (ViewHolder) view.getTag();\n        viewHolder.icon.setImageDrawable(item.icon);\n        viewHolder.label.setText(item.name);\n        if (item.location != null && item.mode != 0) {\n            viewHolder.location.setText(item.location.latitude + \",\" + item.location.longitude);\n        } else {\n            viewHolder.location.setText(\"real location\");\n        }\n    }\n\n    static class ViewHolder extends BaseAdapterPlus.BaseViewHolder {\n        public ViewHolder(View view) {\n            super(view);\n            icon = $(R.id.item_app_icon);\n            label = $(R.id.item_app_name);\n            location = $(R.id.item_location);\n        }\n\n        final ImageView icon;\n        final TextView label, location;\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/app/src/main/java/io/virtualapp/home/location/MarkerActivity.java",
    "content": "package io.virtualapp.home.location;\n\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.app.ProgressDialog;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.v7.app.ActionBar;\nimport android.support.v7.widget.Toolbar;\nimport android.text.TextUtils;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.remote.vloc.VLocation;\nimport com.tencent.lbssearch.TencentSearch;\nimport com.tencent.lbssearch.httpresponse.BaseObject;\nimport com.tencent.lbssearch.httpresponse.HttpResponseListener;\nimport com.tencent.lbssearch.object.Location;\nimport com.tencent.lbssearch.object.param.Geo2AddressParam;\nimport com.tencent.lbssearch.object.result.Geo2AddressResultObject;\nimport com.tencent.map.geolocation.TencentLocation;\nimport com.tencent.map.geolocation.TencentLocationListener;\nimport com.tencent.map.geolocation.TencentLocationManager;\nimport com.tencent.map.geolocation.TencentLocationRequest;\nimport com.tencent.mapsdk.raster.model.BitmapDescriptorFactory;\nimport com.tencent.mapsdk.raster.model.CameraPosition;\nimport com.tencent.mapsdk.raster.model.LatLng;\nimport com.tencent.mapsdk.raster.model.MarkerOptions;\nimport com.tencent.tencentmap.mapsdk.map.CameraUpdateFactory;\nimport com.tencent.tencentmap.mapsdk.map.MapView;\nimport com.tencent.tencentmap.mapsdk.map.TencentMap;\n\nimport io.virtualapp.abs.ui.VActivity;\nimport io.virtualapp.R;\n\npublic class MarkerActivity extends VActivity implements TencentMap.OnMapClickListener, TencentLocationListener {\n    private TencentMap mMap;\n    private MapView mapView;\n    private LatLng mLatLng = new LatLng(39.9182645956, 116.3970032689);\n    private TextView pathText;\n    private TencentSearch geocoderSearch;\n    private String mAddress;\n    private boolean isNoPoint = true;\n    private VLocation mVLocation;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_marker);\n        setResult(Activity.RESULT_CANCELED);\n        Toolbar toolbar = bind(R.id.task_top_toolbar);\n        setSupportActionBar(toolbar);\n        //地址显示，暂时不用\n        pathText = bind(R.id.address);\n        pathText.setVisibility(View.VISIBLE);\n        enableBackHome();\n        mapView = (MapView) findViewById(R.id.map);\n        mapView.onCreate(savedInstanceState); // 此方法必须重写\n        mMap = mapView.getMap();\n        mMap.setOnMapClickListener(this);\n        geocoderSearch = new TencentSearch(this);\n        //\n        Intent data = getIntent();\n        if (data != null) {\n            mVLocation = data.getParcelableExtra(EXTRA_LOCATION);\n            if (mVLocation != null && mVLocation.latitude != 0 && mVLocation.longitude != 0) {\n                mLatLng = new LatLng(mVLocation.latitude, mVLocation.longitude);\n                isNoPoint = false;\n            }\n        }\n\n        if (isNoPoint) {\n            startLocation();\n        } else {\n            onMapClick(mLatLng);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected <T extends View> T bind(int id) {\n        return (T) findViewById(id);\n    }\n\n    public void enableBackHome() {\n        ActionBar actionBar = getSupportActionBar();\n        if (actionBar != null) {\n            actionBar.setDisplayHomeAsUpEnabled(true);\n        }\n    }\n\n    private void startLocation() {\n        Toast.makeText(this, \"start location\", Toast.LENGTH_SHORT).show();\n        TencentLocationRequest request = TencentLocationRequest.create()\n                .setRequestLevel(TencentLocationRequest.REQUEST_LEVEL_GEO)\n                .setAllowGPS(true);\n        int error = TencentLocationManager.getInstance(this)\n                .requestLocationUpdates(request, this);\n        if (error != 0) {\n            VLog.w(\"TMap\", \"startLocation:error=\" + error);\n        }\n    }\n\n    @Override\n    public void onLocationChanged(TencentLocation location, int error, String msg) {\n        if (location != null) {\n            TencentLocationManager.getInstance(this).removeUpdates(this);\n            onMapClick(new LatLng(location.getLatitude(), location.getLongitude()));\n        } else {\n            String errText = \"定位失败,\" + error + \": \" + msg;\n            VLog.e(\"TMap\", errText);\n        }\n    }\n\n    @Override\n    public void onStatusUpdate(String s, int i, String s1) {\n\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        getMenuInflater().inflate(R.menu.marktet_map, menu);\n        return true;\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n            case R.id.action_clear:\n                AlertDialog.Builder builder = new AlertDialog.Builder(this);\n                builder.setTitle(\"Question\");\n                builder.setMessage(\"Clear virtual location\");\n                builder.setNegativeButton(android.R.string.ok, (d, s) -> {\n                    if (mMap != null) {\n                        mMap.clearAllOverlays();\n                    }\n                    setResultOk(null);\n                    finish();\n                    d.dismiss();\n                });\n                builder.setNeutralButton(android.R.string.cancel, (d, s) -> {\n                    d.dismiss();\n                });\n                builder.show();\n                break;\n            case R.id.action_ok:\n                if (mLatLng != null) {\n                    /**\n                     * TODO edit info\n                     * @see com.lody.virtual.remote.vloc.VLocation#altitude\n                     * @see com.lody.virtual.remote.vloc.VLocation#accuracy\n                     * @see com.lody.virtual.remote.vloc.VLocation#speed\n                     * @see com.lody.virtual.remote.vloc.VLocation#bearing\n                     */\n                    if (mVLocation == null) {\n                        mVLocation = new VLocation();\n                        mVLocation.accuracy = 50;\n                    }\n                    mVLocation.latitude = mLatLng.getLatitude();\n                    mVLocation.longitude = mLatLng.getLongitude();\n                    setResultOk(mVLocation);\n                    finish();\n                }\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    @Override\n    public void onMapClick(LatLng latLng) {\n        mLatLng = latLng;\n        MarkerOptions markerOption = new MarkerOptions()\n                .icon(BitmapDescriptorFactory.defaultMarker())\n                .position(mLatLng)\n                .draggable(true);\n        mMap.clearAllOverlays();\n        mMap.addMarker(markerOption);\n        int level = Math.min(mMap.getZoomLevel(), mMap.getMaxZoomLevel() / 3 * 2);\n        mMap.moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition(latLng, level)));\n\n        //查询地理位置\n        ProgressDialog dialog = ProgressDialog.show(this, null, \"get address of location\");\n        Geo2AddressParam param = new Geo2AddressParam()\n                .location(new Location()\n                        .lat((float) latLng.getLatitude())\n                        .lng((float) latLng.getLongitude()));\n        geocoderSearch.geo2address(param, new HttpResponseListener() {\n            @Override\n            public void onSuccess(int i, BaseObject object) {\n                Geo2AddressResultObject oj = (Geo2AddressResultObject) object;\n                if (oj.result != null) {\n                    pathText.setText(oj.result.address);\n                    mAddress = oj.result.address;\n                }\n                dialog.dismiss();\n            }\n\n            @Override\n            public void onFailure(int i, String s, Throwable throwable) {\n                dialog.dismiss();\n                pathText.setText(\"error:\" + s);\n            }\n        });\n    }\n\n    /**\n     * 方法必须重写\n     */\n    @Override\n    protected void onResume() {\n        super.onResume();\n        mapView.onResume();\n    }\n\n    /**\n     * 方法必须重写\n     */\n    @Override\n    protected void onPause() {\n        super.onPause();\n        mapView.onPause();\n    }\n\n    /**\n     * 方法必须重写\n     */\n    @Override\n    protected void onSaveInstanceState(Bundle outState) {\n        super.onSaveInstanceState(outState);\n        mapView.onSaveInstanceState(outState);\n    }\n\n    /**\n     * 方法必须重写\n     */\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        mapView.onDestroy();\n    }\n\n\n    private void setResultOk(VLocation location) {\n        Intent intent = new Intent();\n        intent.putExtra(EXTRA_LOCATION, location);\n        setResult(Activity.RESULT_OK, intent);\n    }\n\n    public static final String EXTRA_LOCATION = \"virtual_location\";\n}\n"
  },
  {
    "path": "VirtualApp/app/src/main/java/io/virtualapp/home/location/VirtualLocationSettings.java",
    "content": "package io.virtualapp.home.location;\n\nimport android.app.ProgressDialog;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.ListView;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.ipc.VirtualLocationManager;\nimport com.lody.virtual.helper.utils.VLog;\nimport com.lody.virtual.remote.InstalledAppInfo;\nimport com.lody.virtual.remote.vloc.VLocation;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport io.virtualapp.R;\nimport io.virtualapp.abs.ui.VActivity;\nimport io.virtualapp.abs.ui.VUiKit;\nimport io.virtualapp.home.adapters.AppLocationAdapter;\nimport io.virtualapp.home.models.LocationData;\nimport io.virtualapp.home.repo.AppRepository;\n\nimport static io.virtualapp.home.location.MarkerActivity.EXTRA_LOCATION;\n\npublic class VirtualLocationSettings extends VActivity implements AdapterView.OnItemClickListener {\n    private static final int REQUSET_CODE = 1001;\n    private AppRepository mRepo;\n    private ListView mListView;\n    private AppLocationAdapter mAppLocationAdapter;\n    private LocationData mSelectData;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_location_settings);\n        mListView = (ListView) findViewById(R.id.appdata_list);\n        mRepo = new AppRepository(this);\n        mAppLocationAdapter = new AppLocationAdapter(this);\n        mListView.setAdapter(mAppLocationAdapter);\n        mListView.setOnItemClickListener(this);\n        loadData();\n    }\n\n    private void readLocation(LocationData locationData) {\n        locationData.mode = VirtualLocationManager.get().getMode(locationData.userId, locationData.packageName);\n        locationData.location = VirtualLocationManager.get().getLocation(locationData.userId, locationData.packageName);\n    }\n\n    private void saveLocation(LocationData locationData) {\n        if(locationData.location == null||locationData.location.isEmpty()){\n            VirtualLocationManager.get().setMode(locationData.userId, locationData.packageName, 0);\n        }else if(locationData.mode != 2){\n            VirtualLocationManager.get().setMode(locationData.userId, locationData.packageName, 2);\n        }\n        VirtualLocationManager.get().setLocation(locationData.userId, locationData.packageName, locationData.location);\n    }\n\n    private void loadData() {\n        ProgressDialog dialog = ProgressDialog.show(this, null, \"loading\");\n        VUiKit.defer().when(() -> {\n            List<InstalledAppInfo> infos = VirtualCore.get().getInstalledApps(0);\n            List<LocationData> models = new ArrayList<>();\n            for (InstalledAppInfo info : infos) {\n                if (!VirtualCore.get().isPackageLaunchable(info.packageName)) {\n                    continue;\n                }\n                int[] userIds = info.getInstalledUsers();\n                for (int userId : userIds) {\n                    LocationData data = new LocationData(this, info, userId);\n                    readLocation(data);\n                    models.add(data);\n                }\n            }\n            return models;\n        }).done((list) -> {\n            dialog.dismiss();\n            mAppLocationAdapter.set(list);\n            mAppLocationAdapter.notifyDataSetChanged();\n        }).fail((e) -> {\n            dialog.dismiss();\n        });\n    }\n\n    @Override\n    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n        mSelectData = mAppLocationAdapter.getItem(position);\n        Intent intent = new Intent(this, MarkerActivity.class);\n        if (mSelectData.location != null) {\n            intent.putExtra(EXTRA_LOCATION, mSelectData.location);\n        }\n        startActivityForResult(intent, REQUSET_CODE);\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        if (requestCode == REQUSET_CODE) {\n            if (resultCode == RESULT_OK) {\n                VLocation location = data.getParcelableExtra(EXTRA_LOCATION);\n                if (mSelectData != null) {\n                    mSelectData.location = location;\n                    VLog.i(\"kk\", \"set\" + mSelectData);\n                    saveLocation(mSelectData);\n                    mSelectData = null;\n                    loadData();\n                }\n            }\n        }\n        super.onActivityResult(requestCode, resultCode, data);\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/app/src/main/java/io/virtualapp/home/models/LocationData.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;\nimport com.lody.virtual.remote.vloc.VLocation;\n\n/**\n * @see android.location.Location\n */\npublic class LocationData {\n    public String packageName;\n    public int userId;\n    public String name;\n    public Drawable icon;\n    public int mode;\n    public VLocation location;\n\n    public LocationData() {\n    }\n\n    public LocationData(Context context, InstalledAppInfo installedAppInfo, int userId) {\n        this.packageName = installedAppInfo.packageName;\n        this.userId = userId;\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 String toString() {\n        return \"LocationData{\" +\n                \"packageName='\" + packageName + '\\'' +\n                \", userId=\" + userId +\n                \", location=\" + location +\n                '}';\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.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\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        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\n    private void doActionInThread() {\n        if (!VirtualCore.get().isEngineLaunched()) {\n            VirtualCore.get().waitForEngine();\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/app/src/main/java/io/virtualapp/widgets/fittext/BaseTextView.java",
    "content": "package io.virtualapp.widgets.fittext;\n\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.os.Build;\nimport android.text.Layout;\nimport android.text.TextPaint;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.widget.TextView;\n\n\nclass BaseTextView extends TextView {\n    protected boolean mSingleLine = false;\n    protected boolean mIncludeFontPadding = true;\n    protected float mLineSpacingMult = 1;\n    protected float mLineSpacingAdd = 0;\n    protected int mMaxLines = Integer.MAX_VALUE;\n    protected boolean mLineEndNoSpace = true;\n    protected boolean mJustify = false;\n\n    /***\n     * 不拆分单词\n     */\n    protected boolean mKeepWord = true;\n    @SuppressWarnings(\"deprecation\")\n    private static final int[] ANDROID_ATTRS = new int[]{\n            android.R.attr.includeFontPadding,\n            android.R.attr.lineSpacingMultiplier,\n            android.R.attr.lineSpacingExtra,\n            android.R.attr.maxLines,\n            android.R.attr.singleLine,\n            };\n\n    public BaseTextView(Context context) {\n        this(context, null);\n    }\n\n    public BaseTextView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        if (attrs != null) {\n            TypedArray a = context.obtainStyledAttributes(attrs, ANDROID_ATTRS);\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n                mIncludeFontPadding = a.getBoolean(a.getIndex(0),  mIncludeFontPadding);\n                mLineSpacingMult = a.getFloat(a.getIndex(1),  mLineSpacingMult);\n                mLineSpacingAdd = a.getDimensionPixelSize(a.getIndex(2),  (int) mLineSpacingAdd);\n                mMaxLines = a.getInteger(a.getIndex(3), mMaxLines);\n            }\n            mSingleLine = a.getBoolean(android.R.attr.singleLine, mSingleLine);\n            a.recycle();\n        }\n    }\n\n    public BaseTextView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs);\n    }\n\n    public boolean isKeepWord() {\n        return mKeepWord;\n    }\n\n    public void setKeepWord(boolean keepWord) {\n        mKeepWord = keepWord;\n    }\n\n    public boolean isJustify() {\n        return mJustify;\n    }\n\n    public void setJustify(boolean justify) {\n        mJustify = justify;\n    }\n\n    public boolean isLineEndNoSpace() {\n        return mLineEndNoSpace;\n    }\n\n    public void setLineEndNoSpace(boolean lineEndNoSpace) {\n        mLineEndNoSpace = lineEndNoSpace;\n    }\n\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n    public boolean getIncludeFontPaddingCompat() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            return getIncludeFontPadding();\n        } else {\n            return mIncludeFontPadding;\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n    public float getLineSpacingMultiplierCompat() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            return getLineSpacingMultiplier();\n        } else {\n            return mLineSpacingMult;\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n    public float getLineSpacingExtraCompat() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            return getLineSpacingExtra();\n        } else {\n            return mLineSpacingAdd;\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n    public int getMaxLinesCompat() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            return getMaxLines();\n        } else {\n            return mMaxLines;\n        }\n    }\n\n    @Override\n    public void setLineSpacing(float add, float mult) {\n        super.setLineSpacing(add, mult);\n        mLineSpacingAdd = add;\n        mLineSpacingMult = mult;\n    }\n\n    @Override\n    public void setIncludeFontPadding(boolean includepad) {\n        super.setIncludeFontPadding(includepad);\n        mIncludeFontPadding = includepad;\n    }\n\n    @Override\n    public void setMaxLines(int maxlines) {\n        super.setMaxLines(maxlines);\n        mMaxLines = maxlines;\n    }\n\n    @Override\n    public void setSingleLine(boolean singleLine) {\n        super.setSingleLine(singleLine);\n        mSingleLine = singleLine;\n    }\n\n\n    public int getTextWidth() {\n        return FitTextHelper.getTextWidth(this);\n    }\n\n    public int getTextHeight() {\n        return getMeasuredHeight() - getCompoundPaddingTop()\n                - getCompoundPaddingBottom();\n    }\n\n    /**\n     * 设置粗体\n     *\n     * @param bold 粗体\n     */\n    public void setBoldText(boolean bold) {\n        getPaint().setFakeBoldText(bold);\n    }\n\n    /**\n     * 设置斜体\n     *\n     * @param italic 斜体\n     */\n    public void setItalicText(boolean italic) {\n        getPaint().setTextSkewX(italic ? -0.25f : 0f);\n    }\n\n    public boolean isItalicText() {\n        return getPaint().getTextSkewX() != 0f;\n    }\n\n    public boolean isSingleLine() {\n        return mSingleLine;\n    }\n\n    public float getTextLineHeight() {\n        return getLineHeight();\n    }\n\n    public TextView getTextView() {\n        return this;\n    }\n\n    protected void onDraw(Canvas canvas) {\n        if (!mJustify || mSingleLine) {\n            super.onDraw(canvas);\n            return;\n        }\n        TextPaint paint = getPaint();\n//        paint.drawableState = getDrawableState();\n        float mViewWidth = getTextWidth();\n        if (isItalicText()) {\n            float letterW = getPaint().measureText(\"a\");\n            mViewWidth -= letterW;\n        }\n        CharSequence text = getText();\n        Layout layout = getLayout();\n        if (layout == null) {\n            layout = FitTextHelper.getStaticLayout(this, getText(), getPaint());\n        }\n        int count = layout.getLineCount();\n        for (int i = 0; i < count; i++) {\n            int lineStart = layout.getLineStart(i);\n            int lineEnd = layout.getLineEnd(i);\n//            int top = layout.getLineTop(i);\n            float x = layout.getLineLeft(i);\n            int mLineY = layout.getTopPadding() + (i + 1) * getLineHeight();\n            CharSequence line = text.subSequence(lineStart, lineEnd);\n            if (line.length() == 0) {\n                continue;\n            }\n            if (mLineEndNoSpace) {\n                if (TextUtils.equals(line.subSequence(line.length() - 1, line.length()), \" \")) {\n                    line = line.subSequence(0, line.length() - 1);\n                }\n                if (TextUtils.equals(line.subSequence(0, 1), \" \")) {\n                    line = line.subSequence(1, line.length() - 1);\n                }\n            }\n            float lineWidth = getPaint().measureText(text, lineStart, lineEnd);\n            boolean needScale = i < (count - 1) && (needScale(text.subSequence(lineEnd - 1, lineEnd)));\n//            if (i < (count - 1) && needScale(line)) {\n\n            //float width = StaticLayout.getDesiredWidth(text, lineStart, lineEnd, getPaint());\n//                drawScaledText(canvas, mViewWidth, mLineY, lineStart, line, width - getCompoundPaddingLeft() - getCompoundPaddingRight());\n//            } else {\n//                canvas.drawText(line, 0, line.length(), 0, mLineY, paint);\n//            }\n//            float x = getCompoundPaddingLeft();\n            if (needScale && mViewWidth > lineWidth) {\n//                float sc = mViewWidth / lineWidth;\n                //标点数\n                int clen = countEmpty(line);\n                float d = (mViewWidth - lineWidth) / clen;\n                for (int j = 0; j < line.length(); j++) {\n                    float cw = getPaint().measureText(line, j, j + 1);\n                    canvas.drawText(line, j, j + 1, x, mLineY, getPaint());\n                    x += cw;\n                    // 后面是标点\n                    if (isEmpty(line, j + 1, j + 2)) {\n                        x += d / 2;\n                    }\n                    //当前是标点\n                    if (isEmpty(line, j, j + 1)) {\n                        x += d / 2;\n                    }\n                }\n            } else {\n                canvas.drawText(line, 0, line.length(), x, mLineY, paint);\n            }\n        }\n    }\n\n    /**\n     * 共有多少个标点/空白字符\n     *\n     * @param text 内容\n     * @return 数量\n     */\n    protected int countEmpty(CharSequence text) {\n        int len = text.length();\n        int count = 0;\n        for (int i = 0; i < len; i++) {\n            if (isEmpty(text, i, i + 1)) {\n                count++;\n            }\n        }\n        return count;\n    }\n\n    /**\n     * 是否是标点/空白字符\n     *\n     * @param c     内容\n     * @param start 开始\n     * @param end   结束\n     */\n    protected boolean isEmpty(CharSequence c, int start, int end) {\n        if (end >= c.length()) {\n            return false;\n        }\n        CharSequence ch = c.subSequence(start, end);\n        return TextUtils.equals(ch, \"\\t\") || TextUtils.equals(ch, \" \") || FitTextHelper.sSpcaeList.contains(ch);\n    }\n\n//    private void drawScaledText(Canvas canvas, int mViewWidth, int mLineY, int lineStart, CharSequence line, float lineWidth) {\n//        float x = 0;\n//        if (isFirstLineOfParagraph(lineStart, line)) {\n//            String blanks = \"  \";\n//            canvas.drawText(blanks, x, mLineY, getPaint());\n//            float bw = StaticLayout.getDesiredWidth(blanks, getPaint());\n//            x += bw;\n//\n//            line = line.subSequence(3, line.length() - 3);\n//        }\n//\n//        float d = (mViewWidth - lineWidth) / line.length() - 1;\n//        for (int i = 0; i < line.length(); i++) {\n//            String c = String.valueOf(line.charAt(i));\n//            float cw = StaticLayout.getDesiredWidth(c, getPaint());\n//            canvas.drawText(c, x, mLineY, getPaint());\n//            x += cw + d;\n//        }\n//    }\n//\n//    private boolean isFirstLineOfParagraph(int lineStart, CharSequence line) {\n//        return line.length() > 3 && line.charAt(0) == ' ' && line.charAt(1) == ' ';\n//    }\n\n    /**\n     * 是否需要两端对齐\n     *\n     * @param end 结束字符\n     */\n    protected boolean needScale(CharSequence end) {\n        return TextUtils.equals(end, \" \");// || !TextUtils.equals(end, \"\\n\");\n    }\n\n}"
  },
  {
    "path": "VirtualApp/app/src/main/java/io/virtualapp/widgets/fittext/FitTextHelper.java",
    "content": "package io.virtualapp.widgets.fittext;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.text.Layout;\nimport android.text.SpannableStringBuilder;\nimport android.text.StaticLayout;\nimport android.text.TextPaint;\nimport android.text.TextUtils;\nimport android.view.Gravity;\nimport android.view.inputmethod.EditorInfo;\nimport android.widget.TextView;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/***\n * 两端对齐\n * 标点句尾\n */\nclass FitTextHelper {\n    protected static final float LIMIT = 0.001f;// 误差\n    private static final boolean LastNoSpace = false;\n    protected BaseTextView textView;\n\n    //region space list\n    public final static List<CharSequence> sSpcaeList = new ArrayList<>();\n\n    static {\n        sSpcaeList.add(\",\");\n        sSpcaeList.add(\".\");\n        sSpcaeList.add(\";\");\n        sSpcaeList.add(\"'\");\n        sSpcaeList.add(\"\\\"\");\n        sSpcaeList.add(\":\");\n        sSpcaeList.add(\"?\");\n        sSpcaeList.add(\"~\");\n        sSpcaeList.add(\"!\");\n        sSpcaeList.add(\"‘\");\n        sSpcaeList.add(\"’\");\n        sSpcaeList.add(\"”\");\n        sSpcaeList.add(\"“\");\n        sSpcaeList.add(\"；\");\n        sSpcaeList.add(\"：\");\n        sSpcaeList.add(\"，\");\n        sSpcaeList.add(\"。\");\n        sSpcaeList.add(\"？\");\n        sSpcaeList.add(\"！\");\n        sSpcaeList.add(\"(\");\n        sSpcaeList.add(\")\");\n        sSpcaeList.add(\"[\");\n        sSpcaeList.add(\"]\");\n        sSpcaeList.add(\"@\");\n        sSpcaeList.add(\"/\");\n        sSpcaeList.add(\"#\");\n        sSpcaeList.add(\"$\");\n        sSpcaeList.add(\"%\");\n        sSpcaeList.add(\"^\");\n        sSpcaeList.add(\"&\");\n        sSpcaeList.add(\"*\");\n//        sSpcaeList.add(\"{\");\n//        sSpcaeList.add(\"}\");\n        sSpcaeList.add(\"<\");\n        sSpcaeList.add(\">\");\n//        sSpcaeList.add(\"/\");\n//        sSpcaeList.add(\"\\\\\");\n        sSpcaeList.add(\"+\");\n        sSpcaeList.add(\"-\");\n        sSpcaeList.add(\"·\");\n//        sSpcaeList.add(\"●\");\n//        sSpcaeList.add(\"【\");\n//        sSpcaeList.add(\"】\");\n//        sSpcaeList.add(\"《\");\n//        sSpcaeList.add(\"》\");\n//        sSpcaeList.add(\"『\");\n//        sSpcaeList.add(\"』\");\n//        sSpcaeList.add(\"／\");\n    }\n    //endregion\n\n    protected volatile boolean mFittingText = false;\n\n    public FitTextHelper(BaseTextView textView) {\n        this.textView = textView;\n    }\n\n    /***\n     * @param textView textview\n     * @return 是否是单行\n     */\n    public static boolean isSingleLine(TextView textView) {\n        if (textView == null) return false;\n        if (textView instanceof BaseTextView) {\n            return ((BaseTextView) textView).isSingleLine();\n        }\n        if (textView == null) {\n            return false;\n        }\n        int type = textView.getInputType();\n        return (type & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) == EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;\n    }\n\n//    public float getLineHieght() {\n//        Paint.FontMetrics fm = textView.getPaint().getFontMetrics();\n//        float baseline = fm.descent - fm.ascent;\n//        float multi = textView.getLineSpacingMultiplierCompat();\n//        float space = textView.getLineSpacingExtraCompat();\n//        //字距\n//        return (baseline + fm.leading)\n//                * multi + space;\n//    }\n\n    /**\n     * @return 文本框的当前最大行数\n     */\n    protected int getMaxLineCount() {\n        float vspace = textView.getTextLineHeight();\n        float height = textView.getTextHeight();\n        return (int) (height / vspace);\n    }\n\n    //\n//    protected boolean isSingle(TextView textView) {\n//        int inputType = textView.getInputType();\n//        return (inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) == EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;\n//    }\n\n    /**\n     * 文本框的宽度\n     *\n     * @param textView 文本框\n     * @return 宽度\n     */\n    public static int getTextWidth(TextView textView) {\n        return textView.getMeasuredWidth() - textView.getCompoundPaddingLeft()\n                - textView.getCompoundPaddingRight();\n    }\n\n    /***\n     * @param text  文本\n     * @param paint 画笔\n     * @return 文本布局\n     */\n    public StaticLayout getStaticLayout(CharSequence text, TextPaint paint) {\n        return getStaticLayout(textView.getTextView(), text, paint);\n    }\n\n    /**\n     * @param textView 文本框\n     * @param text     文本\n     * @param paint    画笔\n     * @return 文本布局\n     */\n    public static StaticLayout getStaticLayout(TextView textView, CharSequence text, TextPaint paint) {\n        StaticLayout layout;\n        if (textView instanceof FitTextView) {\n            FitTextView fitTextView = (FitTextView) textView;\n            layout = new StaticLayout(text, paint, getTextWidth(textView),\n                    getLayoutAlignment(fitTextView), fitTextView.getLineSpacingMultiplierCompat(),\n                    fitTextView.getLineSpacingExtraCompat(), fitTextView.getIncludeFontPaddingCompat());\n        } else {\n            if (Build.VERSION.SDK_INT <= 16) {\n                layout = new StaticLayout(text, paint, getTextWidth(textView),\n                        getLayoutAlignment(textView), 0, 0, false);\n            } else {\n                layout = new StaticLayout(text, paint, getTextWidth(textView),\n                        getLayoutAlignment(textView), textView.getLineSpacingMultiplier(),\n                        textView.getLineSpacingExtra(), textView.getIncludeFontPadding());\n            }\n        }\n        if(isSingleLine(textView)) {\n            try {\n                Field field = StaticLayout.class.getDeclaredField(\"mMaximumVisibleLineCount\");\n                if (field != null) {\n                    field.setAccessible(true);\n                    field.set(layout, 1);\n                }\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n        return layout;\n    }\n\n    /**\n     * 判断内容是否在框内\n     *\n     * @param text  文本\n     * @param paint 画笔\n     * @return 没有超过框\n     */\n    protected boolean isFit(CharSequence text, TextPaint paint) {\n        // 自动换行\n        boolean mSingleLine = textView.isSingleLine();\n        int maxLines = textView.getMaxLinesCompat();\n        float multi = textView.getLineSpacingMultiplierCompat();\n        float space = textView.getLineSpacingExtraCompat();\n        space = space * multi;\n        int height = textView.getTextHeight();\n        if (!mSingleLine) {\n            if (!LastNoSpace) {\n                height += Math.round(space);\n            }\n        }\n\n        int lines = mSingleLine ? 1 : Math.max(1, maxLines);\n\n        StaticLayout layout = getStaticLayout(text, paint);\n\n        return layout.getLineCount() <= lines && layout.getHeight() <= height;\n    }\n\n    /**\n     * 调整字体大小\n     *\n     * @param oldPaint 旧画笔\n     * @param text     内容\n     * @param max      最大字体\n     * @param min      最小字体\n     * @return 适合字体大小\n     */\n    public float fitTextSize(TextPaint oldPaint, CharSequence text, float max, float min) {\n        if (TextUtils.isEmpty(text)) {\n            if (oldPaint != null) {\n                return oldPaint.getTextSize();\n            }\n            if (textView != null) {\n                return textView.getTextSize();\n            }\n        }\n        float low = min;\n        float high = max;\n        TextPaint paint = new TextPaint(oldPaint);\n        while (Math.abs(high - low) > LIMIT) {\n            paint.setTextSize((low + high) / 2.0f);\n            if (isFit(getLineBreaks(text, paint), paint)) {\n                low = paint.getTextSize();\n            } else {\n                high = paint.getTextSize();\n            }\n        }\n        return low;\n    }\n\n    /**\n     * 拆入换行符，解决中英文的换行问题\n     *\n     * @param text  内容\n     * @param paint 画笔\n     * @return 调整后的内容\n     */\n    public CharSequence getLineBreaks(\n            CharSequence text, TextPaint paint) {\n        int width = textView.getTextWidth();\n        boolean keepWord = textView.isKeepWord();\n        if (width <= 0 || keepWord)\n            return text;\n        int length = text.length();\n        int start = 0, end = 1;\n\n        SpannableStringBuilder ssb = new SpannableStringBuilder();\n        while (end <= length) {\n            CharSequence c = text.subSequence(end - 1, end);\n//            char c = text.charAt(end - 1);// cs最后一个字符\n//            boolean needCheck = false;\n            if (TextUtils.equals(c, \"\\n\")) {// 已经换行\n                ssb.append(text, start, end);\n                start = end;\n//                needCheck = true;\n            } else {\n                float lw = paint.measureText(text, start, end);\n                if (lw > width) {// 超出宽度，退回一个位置\n                    ssb.append(text, start, end - 1);\n                    start = end - 1;\n                    if (end < length) {\n                        CharSequence c2 = text.subSequence(end - 1, end);\n                        if (!TextUtils.equals(c2, \"\\n\"))\n                            ssb.append('\\n');\n                    }\n//                    needCheck = true;\n                } else if (lw == width) {\n                    ssb.append(text, start, end);\n                    start = end;\n                    if (end < length) {\n                        CharSequence c2 = text.subSequence(end, end + 1);\n                        if (!TextUtils.equals(c2, \"\\n\"))\n                            ssb.append('\\n');\n                    }\n//                    needCheck = true;\n                } else if (end == length) {\n                    // 已经是最后一个字符\n                    ssb.append(text, start, end);\n                    start = end;\n                }\n            }\n            end++;\n        }\n        return ssb;\n    }\n\n    /***\n     * 获取文本框的布局\n     *\n     * @param textView\n     * @return\n     */\n    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n    public static Layout.Alignment getLayoutAlignment(TextView textView) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            return Layout.Alignment.ALIGN_NORMAL;\n        }\n\n        Layout.Alignment alignment;\n        switch (textView.getTextAlignment()) {\n            case TextView.TEXT_ALIGNMENT_GRAVITY:\n                switch (textView.getGravity() & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {\n                    case Gravity.START:\n                        alignment = Layout.Alignment.ALIGN_NORMAL;\n                        break;\n                    case Gravity.END:\n                        alignment = Layout.Alignment.ALIGN_OPPOSITE;\n                        break;\n                    case Gravity.LEFT:\n                        alignment = (textView.getLayoutDirection() == TextView.LAYOUT_DIRECTION_RTL) ? Layout.Alignment.ALIGN_OPPOSITE\n                                : Layout.Alignment.ALIGN_NORMAL;\n                        break;\n                    case Gravity.RIGHT:\n                        alignment = (textView.getLayoutDirection() == TextView.LAYOUT_DIRECTION_RTL) ? Layout.Alignment.ALIGN_NORMAL\n                                : Layout.Alignment.ALIGN_OPPOSITE;\n                        break;\n                    case Gravity.CENTER_HORIZONTAL:\n                        alignment = Layout.Alignment.ALIGN_CENTER;\n                        break;\n                    default:\n                        alignment = Layout.Alignment.ALIGN_NORMAL;\n                        break;\n                }\n                break;\n            case TextView.TEXT_ALIGNMENT_TEXT_START:\n                alignment = Layout.Alignment.ALIGN_NORMAL;\n                break;\n            case TextView.TEXT_ALIGNMENT_TEXT_END:\n                alignment = Layout.Alignment.ALIGN_OPPOSITE;\n                break;\n            case TextView.TEXT_ALIGNMENT_CENTER:\n                alignment = Layout.Alignment.ALIGN_CENTER;\n                break;\n            case TextView.TEXT_ALIGNMENT_VIEW_START:\n                alignment = Layout.Alignment.ALIGN_NORMAL;\n                break;\n            case TextView.TEXT_ALIGNMENT_VIEW_END:\n                alignment = Layout.Alignment.ALIGN_OPPOSITE;\n                break;\n            case TextView.TEXT_ALIGNMENT_INHERIT:\n                //\n            default:\n                alignment = Layout.Alignment.ALIGN_NORMAL;\n                break;\n        }\n        return alignment;\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/app/src/main/java/io/virtualapp/widgets/fittext/FitTextView.java",
    "content": "package io.virtualapp.widgets.fittext;\n\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.text.TextPaint;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport io.virtualapp.R;\n\npublic class FitTextView extends BaseTextView {\n\n    private boolean mMeasured = false;\n    /**\n     * 不需要调整大小\n     */\n    private boolean mNeedFit = true;\n    protected float mOriginalTextSize = 0;\n    private float mMinTextSize, mMaxTextSize;\n    protected CharSequence mOriginalText;\n    /**\n     * 正在调整字体大小\n     */\n    protected volatile boolean mFittingText = false;\n    protected FitTextHelper mFitTextHelper;\n\n    public FitTextView(Context context) {\n        this(context, null);\n    }\n\n    public FitTextView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public FitTextView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        mOriginalTextSize = getTextSize();\n        if (attrs != null) {\n            TypedArray a = context.obtainStyledAttributes(attrs, new int[]{\n                    R.attr.ftMaxTextSize,\n                    R.attr.ftMinTextSize,\n                    });\n            mMaxTextSize = a.getDimension(0, mOriginalTextSize * 2.0f);\n            mMinTextSize = a.getDimension(1, mOriginalTextSize / 2.0f);\n            a.recycle();\n        } else {\n            mMinTextSize = mOriginalTextSize;\n            mMaxTextSize = mOriginalTextSize;\n        }\n    }\n\n    protected FitTextHelper getFitTextHelper() {\n        if (mFitTextHelper == null) {\n            mFitTextHelper = new FitTextHelper(this);\n        }\n        return mFitTextHelper;\n    }\n\n    /**\n     * @return 最小字体大小\n     */\n    public float getMinTextSize() {\n        return mMinTextSize;\n    }\n\n    /**\n     * @param minTextSize 最小字体大小\n     */\n    public void setMinTextSize(float minTextSize) {\n        mMinTextSize = minTextSize;\n    }\n\n    /**\n     * @return 最大字体大小\n     */\n    public float getMaxTextSize() {\n        return mMaxTextSize;\n    }\n\n    /**\n     * @param maxTextSize 最大字体大小\n     */\n    public void setMaxTextSize(float maxTextSize) {\n        mMaxTextSize = maxTextSize;\n    }\n\n    /**\n     * 是否需要调整字体\n     *\n     * @return\n     */\n    public boolean isNeedFit() {\n        return mNeedFit;\n    }\n\n    /**\n     * @param needFit 是否需要调整字体大小\n     */\n    public void setNeedFit(boolean needFit) {\n        mNeedFit = needFit;\n    }\n\n    @Override\n    public void setTextSize(int unit, float size) {\n        super.setTextSize(unit, size);\n        mOriginalTextSize = getTextSize();\n    }\n\n    public float getOriginalTextSize() {\n        return mOriginalTextSize;\n    }\n\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\n        int widthMode = View.MeasureSpec.getMode(widthMeasureSpec);\n        int heightMode = View.MeasureSpec.getMode(heightMeasureSpec);\n\n        if (widthMode == View.MeasureSpec.UNSPECIFIED\n                && heightMode == View.MeasureSpec.UNSPECIFIED) {\n            super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mOriginalTextSize);\n            mMeasured = false;\n        } else {\n            mMeasured = true;\n            fitText(getOriginalText());\n        }\n    }\n\n    @Override\n    public void setText(CharSequence text, TextView.BufferType type) {\n        mOriginalText = text;\n        super.setText(text, type);\n        fitText(text);\n    }\n\n    public CharSequence getOriginalText() {\n        return mOriginalText;\n    }\n\n    /**\n     * 调整字体大小\n     *\n     * @param text 内容\n     */\n    protected void fitText(CharSequence text) {\n        if (!mNeedFit) {\n            return;\n        }\n        if (!mMeasured || mFittingText || mSingleLine || TextUtils.isEmpty(text))\n            return;\n        mFittingText = true;\n        TextPaint oldPaint = getPaint();\n        float size = getFitTextHelper().fitTextSize(oldPaint, text, mMaxTextSize, mMinTextSize);\n        super.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);\n        super.setText(getFitTextHelper().getLineBreaks(text, getPaint()));\n        mFittingText = false;\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n    }\n\n}"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/app/src/main/res/layout/activity_location_settings.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    <ListView\n        android:id=\"@+id/appdata_list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"/>\n</LinearLayout>"
  },
  {
    "path": "VirtualApp/app/src/main/res/layout/activity_marker.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.design.widget.CoordinatorLayout\n    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    android:clipToPadding=\"true\"\n    android:fitsSystemWindows=\"true\"\n    >\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <include layout=\"@layout/content_toolbar\"/>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"vertical\">\n\n            <io.virtualapp.widgets.fittext.FitTextView\n                android:id=\"@+id/address\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"@dimen/line_height\"\n                android:layout_marginLeft=\"6dp\"\n                android:layout_marginRight=\"6dp\"\n                android:gravity=\"center_vertical\"\n                android:maxLines=\"2\"\n                app:ftMaxTextSize=\"20sp\"\n                app:ftMinTextSize=\"10sp\"\n                tools:text=\"hello\"/>\n\n            <com.tencent.tencentmap.mapsdk.map.MapView\n                android:id=\"@+id/map\"\n                android:layout_width=\"fill_parent\"\n                android:layout_height=\"fill_parent\" />\n        </LinearLayout>\n    </LinearLayout>\n</android.support.design.widget.CoordinatorLayout>"
  },
  {
    "path": "VirtualApp/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\n</LinearLayout>\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/app/src/main/res/layout/content_toolbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.design.widget.AppBarLayout\n    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=\"wrap_content\"\n    android:theme=\"@style/AppTheme.AppBarOverlay\">\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/task_top_toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:background=\"@color/colorPrimary\"\n        app:popupTheme=\"@style/AppTheme.PopupOverlay\"\n        app:theme=\"@style/AppTheme.AppBarOverlay\"/>\n</android.support.design.widget.AppBarLayout>"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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                xmlns:tools=\"http://schemas.android.com/tools\"\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            tools:src=\"@mipmap/ic_launcher\"\n            />\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            tools:text=\"App Label\"\n            android:gravity=\"center|start\" />\n\n    </LinearLayout>\n</RelativeLayout>"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/app/src/main/res/layout/item_location_app.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout 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: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            tools:src=\"@mipmap/ic_launcher\"\n            />\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_weight=\"1\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:id=\"@+id/item_app_name\"\n                style=\"@style/TextAppearance.AppCompat.Body1\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"0dp\"\n                android:layout_marginLeft=\"20dp\"\n                android:layout_marginStart=\"20dp\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center|start\"\n                tools:text=\"App Label\"/>\n\n            <TextView\n                android:id=\"@+id/item_location\"\n                style=\"@style/TextAppearance.AppCompat.Caption\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"0dp\"\n                android:layout_marginLeft=\"20dp\"\n                android:layout_marginStart=\"20dp\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center|start\"\n                tools:text=\"22,114\"/>\n\n        </LinearLayout>\n    </LinearLayout>\n</RelativeLayout>"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/app/src/main/res/menu/marktet_map.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_clear\"\n        android:title=\"Clear\"\n        app:showAsAction=\"always\"/>\n    <item\n        android:id=\"@+id/action_ok\"\n        android:title=\"@android:string/ok\"\n        app:showAsAction=\"always\"/>\n</menu>"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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    <dimen name=\"line_height\">40dp</dimen>\n\n</resources>\n"
  },
  {
    "path": "VirtualApp/app/src/main/res/values/fitTextView.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <declare-styleable name=\"FitTextView\">\n        <attr name=\"ftMinTextSize\" format=\"dimension\"/>\n        <attr name=\"ftMaxTextSize\" format=\"dimension\"/>\n    </declare-styleable>\n</resources>"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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    <style name=\"AppTheme.AppBarOverlay\" parent=\"ThemeOverlay.AppCompat.Dark.ActionBar\"/>\n\n    <style name=\"AppTheme.PopupOverlay\" parent=\"ThemeOverlay.AppCompat.Light\"/>\n\n</resources>\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/app/src/main/res/values-zh-rTW/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 個程式！</string>\n</resources>\n"
  },
  {
    "path": "VirtualApp/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        maven {\n            url 'https://maven.google.com/'\n            name 'Google'\n        }\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.0.1'\n        classpath 'com.android.tools.build:gradle-experimental:0.11.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        maven {\n            url 'https://maven.google.com/'\n            name 'Google'\n        }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "VirtualApp/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Sun Nov 19 13:36:42 CST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.1-all.zip\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/.gitignore",
    "content": "/build\n.externalNativeBuild/\nobj/\n"
  },
  {
    "path": "VirtualApp/lib/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 26\n    buildToolsVersion '26.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-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}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/aidl/android/app/IActivityManager/ContentProviderHolder.aidl",
    "content": "// ContentProviderHolder.aidl\npackage android.app.IActivityManager;\n\nparcelable ContentProviderHolder;"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/aidl/android/content/ISyncStatusObserver.aidl",
    "content": "package android.content;\n\n\ninterface ISyncStatusObserver {\n    void onStatusChanged(int which);\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/aidl/android/location/ILocationListener.aidl",
    "content": "// ILocationListener.aidl\npackage android.location;\n\nimport android.location.Location;\nimport android.os.Bundle;\n\ninterface ILocationListener\n{\n    void onLocationChanged(in Location location);\n    void onStatusChanged(String provider, int status, in Bundle extras);\n    void onProviderEnabled(String provider);\n    void onProviderDisabled(String provider);\n}"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/aidl/com/lody/virtual/os/VUserInfo.aidl",
    "content": "// VUserInfo.aidl\npackage com.lody.virtual.os;\n\nparcelable VUserInfo;"
  },
  {
    "path": "VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/AppTaskInfo.aidl",
    "content": "// AppTaskInfo.aidl\npackage com.lody.virtual.remote;\n\nparcelable AppTaskInfo;"
  },
  {
    "path": "VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/BadgerInfo.aidl",
    "content": "// BadgerInfo.aidl\npackage com.lody.virtual.remote;\n\nparcelable BadgerInfo;"
  },
  {
    "path": "VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/InstallResult.aidl",
    "content": "// InstallResult.aidl\npackage com.lody.virtual.remote;\n\nparcelable InstallResult;"
  },
  {
    "path": "VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/InstalledAppInfo.aidl",
    "content": "// AppSetting.aidl\npackage com.lody.virtual.remote;\n\nparcelable InstalledAppInfo;"
  },
  {
    "path": "VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/PendingIntentData.aidl",
    "content": "// PendingIntentData.aidl\npackage com.lody.virtual.remote;\n\nparcelable PendingIntentData;"
  },
  {
    "path": "VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/PendingResultData.aidl",
    "content": "// PendingResultData.aidl\npackage com.lody.virtual.remote;\n\nparcelable PendingResultData;"
  },
  {
    "path": "VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/Problem.aidl",
    "content": "// Problem.aidl\npackage com.lody.virtual.remote;\n\nparcelable Problem;"
  },
  {
    "path": "VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/ReceiverInfo.aidl",
    "content": "// ReceiverInfo.aidl\npackage com.lody.virtual.remote;\n\nparcelable ReceiverInfo;"
  },
  {
    "path": "VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/VDeviceInfo.aidl",
    "content": "// VDeviceInfo.aidl\npackage com.lody.virtual.remote;\n\nparcelable VDeviceInfo;"
  },
  {
    "path": "VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/VParceledListSlice.aidl",
    "content": "// VParceledListSlice.aidl\npackage com.lody.virtual.remote;\n\nparcelable VParceledListSlice;"
  },
  {
    "path": "VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/vloc/VCell.aidl",
    "content": "// VCell.aidl\npackage com.lody.virtual.remote.vloc;\n\nparcelable VCell;"
  },
  {
    "path": "VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/vloc/VLocation.aidl",
    "content": "// VLocation.aidl\npackage com.lody.virtual.remote.vloc;\n\nparcelable VLocation;"
  },
  {
    "path": "VirtualApp/lib/src/main/aidl/com/lody/virtual/remote/vloc/VWifi.aidl",
    "content": "// VWifi.aidl\npackage com.lody.virtual.remote.vloc;\n\nparcelable VWifi;"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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    public class Package {\n        public final ArrayList<Activity> activities = new ArrayList<Activity>(0);\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": "VirtualApp/lib/src/main/java/android/location/LocationRequest.java",
    "content": "package android.location;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\npublic final class LocationRequest implements Parcelable {\n\n    public String getProvider() {\n        return null;\n    }\n\n\n    public static final Creator<LocationRequest> CREATOR = new Creator<LocationRequest>() {\n        @Override\n        public LocationRequest createFromParcel(Parcel in) {\n            return null;\n        }\n\n        @Override\n        public LocationRequest[] newArray(int size) {\n            return null;\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    }\n}"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/DelegateApplication64Bit.java",
    "content": "package com.lody.virtual;\n\nimport android.annotation.TargetApi;\nimport android.app.Application;\nimport android.content.Context;\nimport android.content.pm.ApplicationInfo;\nimport android.content.res.Configuration;\nimport android.os.Build;\nimport android.util.Log;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n\n/**\n * @author Lody\n *         <p>\n *         <p>\n *         Copy the file to your Project.\n */\n@TargetApi(Build.VERSION_CODES.M)\npublic abstract class DelegateApplication64Bit extends Application {\n\n    private Application mTarget;\n\n    protected abstract String get32BitPackageName();\n\n\n    private static Field findField(Object instance, String name) throws NoSuchFieldException {\n        for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {\n            try {\n                Field field = clazz.getDeclaredField(name);\n\n\n                if (!field.isAccessible()) {\n                    field.setAccessible(true);\n                }\n\n                return field;\n            } catch (NoSuchFieldException e) {\n                // ignore and search next\n            }\n        }\n\n        throw new NoSuchFieldException(\"Field \" + name + \" not found in \" + instance.getClass());\n    }\n\n\n    private static Method findMethod(Object instance, String name, Class<?>... parameterTypes)\n            throws NoSuchMethodException {\n        for (Class<?> clazz = instance.getClass(); clazz != null; clazz = clazz.getSuperclass()) {\n            try {\n                Method method = clazz.getDeclaredMethod(name, parameterTypes);\n\n\n                if (!method.isAccessible()) {\n                    method.setAccessible(true);\n                }\n\n                return method;\n            } catch (NoSuchMethodException e) {\n                // ignore and search next\n            }\n        }\n\n        throw new NoSuchMethodException(\"Method \" + name + \" with parameters \" +\n                Arrays.asList(parameterTypes) + \" not found in \" + instance.getClass());\n    }\n\n\n    private static void expandFieldArray(Object instance, String fieldName,\n                                         Object[] extraElements) throws NoSuchFieldException, IllegalArgumentException,\n            IllegalAccessException {\n        Field jlrField = findField(instance, fieldName);\n        Object[] original = (Object[]) jlrField.get(instance);\n        Object[] combined = (Object[]) Array.newInstance(\n                original.getClass().getComponentType(), original.length + extraElements.length);\n        System.arraycopy(original, 0, combined, 0, original.length);\n        System.arraycopy(extraElements, 0, combined, original.length, extraElements.length);\n        jlrField.set(instance, combined);\n    }\n\n\n    private static void expandFieldList(Object instance, String fieldName, Object[] extraElements) throws NoSuchFieldException, IllegalAccessException {\n        Field field = findField(instance, fieldName);\n        Object[] original = ((List) field.get(instance)).toArray();\n        Object[] combined = (Object[]) Array.newInstance(original.getClass().getComponentType(), original.length + 1);\n        System.arraycopy(original, 0, combined, 0, original.length);\n        System.arraycopy(extraElements, 0, combined, original.length, 1);\n        field.set(instance, Arrays.asList(combined));\n    }\n\n    private static Object[] makeDexElements(\n            Object dexPathList, ArrayList<File> files,\n            ArrayList<IOException> suppressedExceptions)\n            throws IllegalAccessException, InvocationTargetException,\n            NoSuchMethodException {\n        Method makeDexElements;\n        if (Build.VERSION.SDK_INT >= 23) {\n            makeDexElements = findMethod(dexPathList, \"makePathElements\", List.class, File.class, List.class);\n        } else {\n            makeDexElements = findMethod(dexPathList, \"makeDexElements\", ArrayList.class, File.class, ArrayList.class);\n        }\n        return (Object[]) makeDexElements.invoke(dexPathList, files, null,\n                suppressedExceptions);\n\n    }\n\n    protected void attachBaseContext(Context context) {\n        super.attachBaseContext(context);\n        try {\n            ApplicationInfo ai = getPackageManager().getApplicationInfo(get32BitPackageName(), 0);\n            ClassLoader classLoader = getClassLoader();\n            Object dexPathList = findField(classLoader, \"pathList\").get(classLoader);\n            ArrayList<IOException> suppressedExceptions = new ArrayList<>();\n            ArrayList<File> dexFiles = new ArrayList<>();\n            dexFiles.add(new File(ai.publicSourceDir));\n            ArrayList<File> nativeLibs = new ArrayList<>();\n            nativeLibs.add(new File(ai.nativeLibraryDir));\n            if (Build.VERSION.SDK_INT > 25) {\n                expandFieldList(dexPathList, \"nativeLibraryDirectories\", new File[]{new File(ai.nativeLibraryDir)});\n                expandFieldArray(dexPathList, \"nativeLibraryPathElements\",\n                        (Object[]) findMethod(dexPathList, \"makePathElements\", List.class).invoke(dexPathList, nativeLibs));\n            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                expandFieldList(dexPathList, \"nativeLibraryDirectories\", new File[]{new File(ai.nativeLibraryDir)});\n                expandFieldArray(dexPathList, \"nativeLibraryPathElements\", makeDexElements(dexPathList, nativeLibs, suppressedExceptions));\n            } else {\n                expandFieldArray(dexPathList, \"nativeLibraryDirectories\", new File[]{new File(ai.nativeLibraryDir)});\n            }\n            expandFieldArray(dexPathList, \"dexElements\", makeDexElements(dexPathList, dexFiles, suppressedExceptions));\n            if (suppressedExceptions.size() > 0) {\n                for (IOException e : suppressedExceptions) {\n                    Log.w(getClass().getSimpleName(), \"Exception in makeDexElement\", e);\n                }\n                Field suppressedExceptionsField =\n                        findField(classLoader, \"dexElementsSuppressedExceptions\");\n                IOException[] dexElementsSuppressedExceptions =\n                        (IOException[]) suppressedExceptionsField.get(classLoader);\n\n                if (dexElementsSuppressedExceptions == null) {\n                    dexElementsSuppressedExceptions =\n                            suppressedExceptions.toArray(\n                                    new IOException[suppressedExceptions.size()]);\n                } else {\n                    IOException[] combined =\n                            new IOException[suppressedExceptions.size() +\n                                    dexElementsSuppressedExceptions.length];\n                    suppressedExceptions.toArray(combined);\n                    System.arraycopy(dexElementsSuppressedExceptions, 0, combined,\n                            suppressedExceptions.size(), dexElementsSuppressedExceptions.length);\n                    dexElementsSuppressedExceptions = combined;\n                }\n                suppressedExceptionsField.set(classLoader, dexElementsSuppressedExceptions);\n            }\n            mTarget = (Application) classLoader.loadClass(ai.className).newInstance();\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n\n    }\n\n    public void onConfigurationChanged(Configuration configuration) {\n        super.onConfigurationChanged(configuration);\n        if (mTarget != null) {\n            mTarget.onConfigurationChanged(configuration);\n        }\n    }\n\n    public void onCreate() {\n        super.onCreate();\n        if (mTarget != null) {\n            mTarget.onCreate();\n        }\n    }\n\n    public void onLowMemory() {\n        super.onLowMemory();\n        if (mTarget != null) {\n            mTarget.onLowMemory();\n        }\n    }\n\n    public void onTerminate() {\n        super.onTerminate();\n        if (mTarget != null) {\n            mTarget.onTerminate();\n        }\n    }\n\n    public void onTrimMemory(int i) {\n        super.onTrimMemory(i);\n        if (mTarget != null) {\n            mTarget.onTrimMemory(i);\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/client/NativeEngine.java",
    "content": "package com.lody.virtual.client;\n\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++\");\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 resverseRedirectedPath(String origPath) {\n        try {\n            return nativeReverseRedirectedPath(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            nativeIORedirect(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            nativeIORedirect(origPath, newPath);\n        } catch (Throwable e) {\n            VLog.e(TAG, VLog.getStackTraceString(e));\n        }\n    }\n\n    public static void whitelist(String path) {\n        try {\n            nativeIOWhitelist(path);\n        } catch (Throwable e) {\n            VLog.e(TAG, VLog.getStackTraceString(e));\n        }\n    }\n\n    public static void forbid(String path) {\n        if (!path.endsWith(\"/\")) {\n            path = path + \"/\";\n        }\n        try {\n            nativeIOForbid(path);\n        } catch (Throwable e) {\n            VLog.e(TAG, VLog.getStackTraceString(e));\n        }\n    }\n\n    public static void enableIORedirect() {\n        try {\n            String soPath = String.format(\"/data/data/%s/lib/libva++.so\", VirtualCore.get().getHostPkg());\n            if (!new File(soPath).exists()) {\n                throw new RuntimeException(\"Unable to find the so.\");\n            }\n            nativeEnableIORedirect(soPath, Build.VERSION.SDK_INT, BuildCompat.getPreviewSDKInt());\n        } catch (Throwable e) {\n            VLog.e(TAG, VLog.getStackTraceString(e));\n        }\n    }\n\n    static void launchEngine() {\n        if (sFlag) {\n            return;\n        }\n        Method[] methods = {NativeMethods.gOpenDexFileNative, NativeMethods.gCameraNativeSetup, NativeMethods.gAudioRecordNativeCheckPermission};\n        try {\n            nativeLaunchEngine(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    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 nativeLaunchEngine(Object[] method, String hostPackageName, boolean isArt, int apiLevel, int cameraMethodType);\n\n    private static native void nativeMark();\n\n    private static native String nativeReverseRedirectedPath(String redirectedPath);\n\n    private static native String nativeGetRedirectedPath(String orgPath);\n\n    private static native void nativeIORedirect(String origPath, String newPath);\n\n    private static native void nativeIOWhitelist(String path);\n\n    private static native void nativeIOForbid(String path);\n\n    private static native void nativeEnableIORedirect(String selfSoPath, int apiLevel, int previewApiLevel);\n\n    public static int onGetUid(int uid) {\n        return VClientImpl.get().getBaseVUid();\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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.content.res.Configuration;\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.Parcelable;\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;\n\nimport java.io.File;\nimport java.lang.reflect.Field;\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.ContextImplKitkat;\nimport mirror.android.app.IActivityManager;\nimport mirror.android.app.LoadedApk;\nimport mirror.android.app.LoadedApkICS;\nimport mirror.android.app.LoadedApkKitkat;\nimport mirror.android.content.ContentProviderHolderOreo;\nimport mirror.android.content.res.CompatibilityInfo;\nimport mirror.android.providers.Settings;\nimport mirror.android.renderscript.RenderScriptCacheDir;\nimport mirror.android.view.CompatibilityInfoHolder;\nimport mirror.android.view.DisplayAdjustments;\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        if (deviceInfo == null) {\n            synchronized (this) {\n                if (deviceInfo == null) {\n                    deviceInfo = VDeviceManager.get().getDeviceInfo(getUserId(vuid));\n                }\n            }\n        }\n        return deviceInfo;\n    }\n\n    public Application getCurrentApplication() {\n        return mInitialApplication;\n    }\n\n    public String getCurrentPackage() {\n        return mBoundApplication != null ?\n                mBoundApplication.appInfo.packageName : VPackageManager.get().getNameForUid(getVUid());\n    }\n\n    public ApplicationInfo getCurrentApplicationInfo() {\n        return mBoundApplication != null ? mBoundApplication.appInfo : 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    }\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    private void bindApplicationNoCheck(String packageName, String processName, ConditionVariable lock) {\n        VDeviceInfo deviceInfo = getDeviceInfo();\n        if (processName == null) {\n            processName = packageName;\n        }\n        mTempLock = lock;\n        try {\n            setupUncaughtHandler();\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n        try {\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        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        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        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.LOLLIPOP && targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {\n            mirror.android.os.Message.updateCheckRecycle.call(targetSdkVersion);\n        }\n        if (VASettings.ENABLE_IO_REDIRECT) {\n            startIOUniformer();\n        }\n        NativeEngine.launchEngine();\n        Object mainThread = VirtualCore.mainThread();\n        NativeEngine.startDexOverride();\n        Context context = createPackageContext(data.appInfo.packageName);\n        System.setProperty(\"java.io.tmpdir\", context.getCacheDir().getAbsolutePath());\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\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        Object boundApp = fixBoundApp(mBoundApplication);\n        mBoundApplication.info = ContextImpl.mPackageInfo.get(context);\n        mirror.android.app.ActivityThread.AppBindData.info.set(boundApp, data.info);\n        VMRuntime.setTargetSdkVersion.call(VMRuntime.getRuntime.call(), data.appInfo.targetSdkVersion);\n\n        Configuration configuration = context.getResources().getConfiguration();\n        Object compatInfo = CompatibilityInfo.ctor.newInstance(data.appInfo, configuration.screenLayout, configuration.smallestScreenWidthDp, false);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {\n                DisplayAdjustments.setCompatibilityInfo.call(ContextImplKitkat.mDisplayAdjustments.get(context), compatInfo);\n            }\n            DisplayAdjustments.setCompatibilityInfo.call(LoadedApkKitkat.mDisplayAdjustments.get(mBoundApplication.info), compatInfo);\n        } else {\n            CompatibilityInfoHolder.set.call(LoadedApkICS.mCompatibilityInfo.get(mBoundApplication.info), compatInfo);\n        }\n\n        boolean conflict = SpecialComponentList.isConflictingInstrumentation(packageName);\n        if (!conflict) {\n            InvocationStubManager.getInstance().checkEnv(AppInstrumentation.class);\n        }\n        mInitialApplication = LoadedApk.makeApplication.call(data.info, false, null);\n        mirror.android.app.ActivityThread.mInitialApplication.set(mainThread, mInitialApplication);\n        ContextFixer.fixContext(mInitialApplication);\n        if (Build.VERSION.SDK_INT >= 24 && \"com.tencent.mm:recovery\".equals(processName)) {\n            fixWeChatRecovery(mInitialApplication);\n        }\n        if (data.providers != null) {\n            installContentProviders(mInitialApplication, data.providers);\n        }\n        if (lock != null) {\n            lock.open();\n            mTempLock = null;\n        }\n        VirtualCore.get().getComponentDelegate().beforeApplicationCreate(mInitialApplication);\n        try {\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        VirtualCore.get().getComponentDelegate().afterApplicationCreate(mInitialApplication);\n    }\n\n    private void fixWeChatRecovery(Application app) {\n        try {\n            Field field = app.getClassLoader().loadClass(\"com.tencent.recovery.Recovery\").getField(\"context\");\n            field.setAccessible(true);\n            if (field.get(null) != null) {\n                return;\n            }\n            field.set(null, app.getBaseContext());\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\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                    if (group == newRoot) continue;\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                    if (group == newRoot) continue;\n                    ThreadGroupN.parent.set(group, newRoot);\n                }\n                ThreadGroupN.ngroups.set(root, 1);\n            }\n        }\n    }\n\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\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 = VEnvironment.getAppLibDirectory(info.packageName).getAbsolutePath();\n        String userLibPath = new File(VEnvironment.getUserSystemDirectory(userId), info.packageName + \"/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        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.enableIORedirect();\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            Context hostContext = VirtualCore.get().getContext();\n            return hostContext.createPackageContext(packageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\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                try {\n                    ActivityThread.installProvider(mainThread, app, cpi, null);\n                } catch (Throwable e) {\n                    e.printStackTrace();\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        if (!isBound()) {\n            VClientImpl.get().bindApplication(info.packageName, info.processName);\n        }\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 = 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    @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            if (!isBound()) {\n                bindApplication(data.component.getPackageName(), data.processName);\n            }\n            Context context = mInitialApplication.getBaseContext();\n            Context receiverContext = ContextImpl.getReceiverRestrictedContext.call(context);\n            String className = data.component.getClassName();\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            if (data.intent.getComponent() == null) {\n                data.intent.setComponent(data.component);\n            }\n            receiver.onReceive(receiverContext, data.intent);\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        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": "VirtualApp/lib/src/main/java/com/lody/virtual/client/badger/BadgerManager.java",
    "content": "package com.lody.virtual.client.badger;\n\nimport android.content.Intent;\n\nimport com.lody.virtual.client.ipc.VActivityManager;\nimport com.lody.virtual.remote.BadgerInfo;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Lody\n */\npublic class BadgerManager {\n\n    private static final Map<String, IBadger> BADGERS = new HashMap<>(10);\n\n    static {\n        addBadger(new BroadcastBadger1.AdwHomeBadger());\n        addBadger(new BroadcastBadger1.AospHomeBadger());\n        addBadger(new BroadcastBadger1.LGHomeBadger());\n        addBadger(new BroadcastBadger1.NewHtcHomeBadger2());\n        addBadger(new BroadcastBadger1.OPPOHomeBader());\n        addBadger(new BroadcastBadger2.NewHtcHomeBadger1());\n\n    }\n\n    private static void addBadger(IBadger badger) {\n        BADGERS.put(badger.getAction(), badger);\n    }\n\n    public static boolean handleBadger(Intent intent) {\n        IBadger badger = BADGERS.get(intent.getAction());\n        if (badger != null) {\n            BadgerInfo info = badger.handleBadger(intent);\n            VActivityManager.get().notifyBadgerChange(info);\n            return true;\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/client/badger/BroadcastBadger1.java",
    "content": "package com.lody.virtual.client.badger;\n\nimport android.content.Intent;\n\nimport com.lody.virtual.remote.BadgerInfo;\n\n/**\n * @author Lody\n */\npublic abstract class BroadcastBadger1 implements IBadger {\n\n    public abstract String getAction();\n\n    public abstract String getPackageKey();\n\n    public abstract String getClassNameKey();\n\n    public abstract String getCountKey();\n\n    @Override\n    public BadgerInfo handleBadger(Intent intent) {\n        BadgerInfo info = new BadgerInfo();\n        info.packageName = intent.getStringExtra(getPackageKey());\n        if (getClassNameKey() != null) {\n            info.className = intent.getStringExtra(getClassNameKey());\n        }\n        info.badgerCount = intent.getIntExtra(getCountKey(), 0);\n        return info;\n    }\n\n\n    static class LGHomeBadger extends BroadcastBadger1 {\n\n        @Override\n        public String getAction() {\n            return \"android.intent.action.BADGE_COUNT_UPDATE\";\n        }\n\n        @Override\n        public String getPackageKey() {\n            return \"badge_count_package_name\";\n        }\n\n        @Override\n        public String getClassNameKey() {\n            return \"badge_count_class_name\";\n        }\n\n        @Override\n        public String getCountKey() {\n            return \"badge_count\";\n        }\n    }\n\n    static class AdwHomeBadger extends BroadcastBadger1 {\n\n        @Override\n        public String getAction() {\n            return \"org.adw.launcher.counter.SEND\";\n        }\n\n        @Override\n        public String getPackageKey() {\n            return \"PNAME\";\n        }\n\n        @Override\n        public String getClassNameKey() {\n            return \"CNAME\";\n        }\n\n        @Override\n        public String getCountKey() {\n            return \"COUNT\";\n        }\n    }\n\n    static class AospHomeBadger extends BroadcastBadger1 {\n\n        @Override\n        public String getAction() {\n            return \"android.intent.action.BADGE_COUNT_UPDATE\";\n        }\n\n        @Override\n        public String getPackageKey() {\n            return \"badge_count_package_name\";\n        }\n\n        @Override\n        public String getClassNameKey() {\n            return \"badge_count_class_name\";\n        }\n\n        @Override\n        public String getCountKey() {\n            return \"badge_count\";\n        }\n    }\n\n\n    static class NewHtcHomeBadger2 extends BroadcastBadger1 {\n\n        @Override\n        public String getAction() {\n            return \"com.htc.launcher.action.UPDATE_SHORTCUT\";\n        }\n\n        @Override\n        public String getPackageKey() {\n            return \"packagename\";\n        }\n\n        @Override\n        public String getClassNameKey() {\n            return null;\n        }\n\n        @Override\n        public String getCountKey() {\n            return \"count\";\n        }\n    }\n\n\n    static class OPPOHomeBader extends BroadcastBadger1 {\n\n        @Override\n        public String getAction() {\n            return \"com.oppo.unsettledevent\";\n        }\n\n        @Override\n        public String getPackageKey() {\n            return \"pakeageName\";\n        }\n\n        @Override\n        public String getClassNameKey() {\n            return null;\n        }\n\n        @Override\n        public String getCountKey() {\n            return \"number\";\n        }\n    }\n\n\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/client/badger/BroadcastBadger2.java",
    "content": "package com.lody.virtual.client.badger;\n\nimport android.content.ComponentName;\nimport android.content.Intent;\n\nimport com.lody.virtual.remote.BadgerInfo;\n\n/**\n * @author Lody\n */\npublic abstract class BroadcastBadger2 implements IBadger {\n\n    public abstract String getAction();\n\n    public abstract String getComponentKey();\n\n    public abstract String getCountKey();\n\n    @Override\n    public BadgerInfo handleBadger(Intent intent) {\n        BadgerInfo info = new BadgerInfo();\n        String componentName = intent.getStringExtra(getComponentKey());\n        ComponentName component = ComponentName.unflattenFromString(componentName);\n        if (component != null) {\n            info.packageName = component.getPackageName();\n            info.className = component.getClassName();\n            info.badgerCount = intent.getIntExtra(getCountKey(), 0);\n            return info;\n        }\n        return null;\n    }\n\n\n    static class NewHtcHomeBadger1 extends BroadcastBadger2 {\n\n        @Override\n        public String getAction() {\n            return \"com.htc.launcher.action.SET_NOTIFICATION\";\n        }\n\n        @Override\n        public String getComponentKey() {\n            return \"com.htc.launcher.extra.COMPONENT\";\n        }\n\n\n        @Override\n        public String getCountKey() {\n            return \"com.htc.launcher.extra.COUNT\";\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/client/badger/IBadger.java",
    "content": "package com.lody.virtual.client.badger;\n\nimport android.content.Intent;\n\nimport com.lody.virtual.remote.BadgerInfo;\n\n/**\n * @author Lody\n */\npublic interface IBadger {\n\n    String getAction();\n\n    BadgerInfo handleBadger(Intent intent);\n\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.devicepolicy.DevicePolicyManagerStub;\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.fingerprint.FingerprintManagerStub;\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.usage.UsageStatsManagerStub;\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.view.AutoFillManagerStub;\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\tif (VirtualCore.get().isMainProcess()) {\n\t\t\treturn;\n\t\t}\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\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\taddInjector(new UsageStatsManagerStub());\n\t\t\t}\n\t\t\tif (Build.VERSION.SDK_INT >= M) {\n\t\t\t\taddInjector(new FingerprintManagerStub());\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                addInjector(new DevicePolicyManagerStub());\n            }\n            if (Build.VERSION.SDK_INT >= 26) {\n\t\t\t\taddInjector(new AutoFillManagerStub());\n\t\t\t}\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": "VirtualApp/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.IBinder;\nimport android.os.Looper;\nimport android.os.Process;\nimport android.os.RemoteException;\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.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.ipcbus.IPCBus;\nimport com.lody.virtual.helper.ipcbus.IPCSingleton;\nimport com.lody.virtual.helper.ipcbus.IServerCache;\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.interfaces.IAppManager;\nimport com.lody.virtual.server.ServiceCache;\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 IPCSingleton<IAppManager> singleton = new IPCSingleton<>(IAppManager.class);\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            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            mainThread = ActivityThread.currentActivityThread.call();\n            unHookPackageManager = context.getPackageManager();\n            hostPkgInfo = unHookPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_PROVIDERS);\n            IPCBus.initialize(new IServerCache() {\n                @Override\n                public void join(String serverName, IBinder binder) {\n                    ServiceCache.addService(serverName, binder);\n                }\n\n                @Override\n                public IBinder query(String serverName) {\n                    return ServiceManagerNative.getService(serverName);\n                }\n            });\n            detectProcessType();\n            InvocationStubManager invocationStubManager = InvocationStubManager.getInstance();\n            invocationStubManager.init();\n            invocationStubManager.injectAll();\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        return singleton.get();\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    @Deprecated\n    public void preOpt(String pkg) throws IOException {\n        InstalledAppInfo info = getInstalledAppInfo(pkg, 0);\n        if (info != null && !info.dependSystem) {\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            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_\", userId);\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    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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/client/env/GPSStateline.java",
    "content": "package com.lody.virtual.client.env;\n\n\nclass GPSStateline {\n    private double mAzimuth;\n    private double mElevation;\n    private boolean mHasAlmanac;\n    private boolean mHasEphemeris;\n    private int mPnr;\n    private double mSnr;\n    private boolean mUseInFix;\n\n\n    public double getAzimuth() {\n        return this.mAzimuth;\n    }\n\n    public double getElevation() {\n        return this.mElevation;\n    }\n\n    public int getPnr() {\n        return this.mPnr;\n    }\n\n    public double getSnr() {\n        return this.mSnr;\n    }\n\n    public boolean isHasAlmanac() {\n        return this.mHasAlmanac;\n    }\n\n    public boolean isHasEphemeris() {\n        return this.mHasEphemeris;\n    }\n\n    public boolean isUseInFix() {\n        return this.mUseInFix;\n    }\n\n    public void setAzimuth(double azimuth) {\n        this.mAzimuth = azimuth;\n    }\n\n    public void setElevation(double elevation) {\n        this.mElevation = elevation;\n    }\n\n    public void setHasAlmanac(boolean hasAlmanac) {\n        this.mHasAlmanac = hasAlmanac;\n    }\n\n    public void setHasEphemeris(boolean hasEphemeris) {\n        this.mHasEphemeris = hasEphemeris;\n    }\n\n    public void setPnr(int pnr) {\n        this.mPnr = pnr;\n    }\n\n    public void setSnr(double snr) {\n        this.mSnr = snr;\n    }\n\n    public void setUseInFix(boolean useInFix) {\n        this.mUseInFix = useInFix;\n    }\n\n    public GPSStateline(int pnr, double snr, double elevation, double azimuth, boolean useInFix, boolean hasAlmanac, boolean hasEphemeris) {\n        this.mPnr = pnr;\n        this.mSnr = snr;\n        this.mElevation = elevation;\n        this.mAzimuth = azimuth;\n        this.mUseInFix = useInFix;\n        this.mHasAlmanac = hasAlmanac;\n        this.mHasEphemeris = hasEphemeris;\n    }\n}"
  },
  {
    "path": "VirtualApp/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    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(Intent.ACTION_USER_PRESENT);\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\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": "VirtualApp/lib/src/main/java/com/lody/virtual/client/env/VirtualGPSSatalines.java",
    "content": "package com.lody.virtual.client.env;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class VirtualGPSSatalines {\n    private static VirtualGPSSatalines INSTANCE;\n    private int mAlmanacMask;\n    private float[] mAzimuths;\n    private float[] mElevations;\n    private int mEphemerisMask;\n    private float[] mSnrs;\n    private int mUsedInFixMask;\n    private int[] pnrs;\n    private int[] prnWithFlags;\n    private int svCount;\n\n    static {\n        INSTANCE = new VirtualGPSSatalines();\n    }\n\n    public int getAlmanacMask() {\n        return this.mAlmanacMask;\n    }\n\n    public float[] getAzimuths() {\n        return this.mAzimuths;\n    }\n\n    public float[] getElevations() {\n        return this.mElevations;\n    }\n\n    public int getEphemerisMask() {\n        return this.mEphemerisMask;\n    }\n\n    public int[] getPrns() {\n        return this.pnrs;\n    }\n\n    public float[] getSnrs() {\n        return this.mSnrs;\n    }\n\n    public int getUsedInFixMask() {\n        return this.mUsedInFixMask;\n    }\n\n    public static VirtualGPSSatalines get() {\n        return INSTANCE;\n    }\n\n    private VirtualGPSSatalines() {\n        List<GPSStateline> statelines = new ArrayList<>();\n        statelines.add(new GPSStateline(5, 1.0d, 5.0d, 112.0d, false, true, true));\n        statelines.add(new GPSStateline(13, 13.5d, 23.0d, 53.0d, true, true, true));\n        statelines.add(new GPSStateline(14, 19.1d, 6.0d, 247.0d, true, true, true));\n        statelines.add(new GPSStateline(15, 31.0d, 58.0d, 45.0d, true, true, true));\n        statelines.add(new GPSStateline(18, 0.0d, 52.0d, 309.0d, false, true, true));\n        statelines.add(new GPSStateline(20, 30.1d, 54.0d, 105.0d, true, true, true));\n        statelines.add(new GPSStateline(21, 33.2d, 56.0d, 251.0d, true, true, true));\n        statelines.add(new GPSStateline(22, 0.0d, 14.0d, 299.0d, false, true, true));\n        statelines.add(new GPSStateline(24, 25.9d, 57.0d, 157.0d, true, true, true));\n        statelines.add(new GPSStateline(27, 18.0d, 3.0d, 309.0d, true, true, true));\n        statelines.add(new GPSStateline(28, 18.2d, 3.0d, 42.0d, true, true, true));\n        statelines.add(new GPSStateline(41, 28.8d, 0.0d, 0.0d, false, false, false));\n        statelines.add(new GPSStateline(50, 29.2d, 0.0d, 0.0d, false, true, true));\n        statelines.add(new GPSStateline(67, 14.4d, 2.0d, 92.0d, false, false, false));\n        statelines.add(new GPSStateline(68, 21.2d, 45.0d, 60.0d, false, false, false));\n        statelines.add(new GPSStateline(69, 17.5d, 50.0d, 330.0d, false, true, true));\n        statelines.add(new GPSStateline(70, 22.4d, 7.0d, 291.0d, false, false, false));\n        statelines.add(new GPSStateline(77, 23.8d, 10.0d, 23.0d, true, true, true));\n        statelines.add(new GPSStateline(78, 18.0d, 47.0d, 70.0d, true, true, true));\n        statelines.add(new GPSStateline(79, 22.8d, 41.0d, 142.0d, true, true, true));\n        statelines.add(new GPSStateline(83, 0.2d, 9.0d, 212.0d, false, false, false));\n        statelines.add(new GPSStateline(84, 16.7d, 30.0d, 264.0d, true, true, true));\n        statelines.add(new GPSStateline(85, 12.1d, 20.0d, 317.0d, true, true, true));\n        this.svCount = statelines.size();\n        this.pnrs = new int[statelines.size()];\n        for (int i = 0; i < statelines.size(); i++) {\n            this.pnrs[i] = statelines.get(i).getPnr();\n        }\n        this.mSnrs = new float[statelines.size()];\n        for (int i = 0; i < statelines.size(); i++) {\n            this.mSnrs[i] = (float) statelines.get(i).getSnr();\n        }\n        this.mElevations = new float[statelines.size()];\n        for (int i = 0; i < statelines.size(); i++) {\n            this.mElevations[i] = (float) statelines.get(i).getElevation();\n        }\n        this.mAzimuths = new float[statelines.size()];\n        for (int i = 0; i < statelines.size(); i++) {\n            this.mAzimuths[i] = (float) statelines.get(i).getAzimuth();\n        }\n        this.mEphemerisMask = 0;\n        for (int i = 0; i < statelines.size(); i++) {\n            if (statelines.get(i).isHasEphemeris()) {\n                this.mEphemerisMask |= 1 << (statelines.get(i).getPnr() - 1);\n            }\n        }\n        this.mAlmanacMask = 0;\n        for (int i = 0; i < statelines.size(); i++) {\n            if (statelines.get(i).isHasAlmanac()) {\n                this.mAlmanacMask |= 1 << (statelines.get(i).getPnr() - 1);\n            }\n        }\n        this.mUsedInFixMask = 0;\n        for (int i = 0; statelines.size() > i; i++) {\n            if (statelines.get(i).isUseInFix()) {\n                this.mUsedInFixMask |= 1 << (statelines.get(i).getPnr() - 1);\n            }\n        }\n        this.prnWithFlags = new int[statelines.size()];\n        for (int i = 0; i < statelines.size(); i++) {\n            GPSStateline gpsStateline = statelines.get(i);\n            this.prnWithFlags[i] =\n                    (gpsStateline.isHasEphemeris() ? 1 : 0)\n                            | (gpsStateline.isHasAlmanac() ? 1 : 0) << 1\n                            | (gpsStateline.isUseInFix() ? 1 : 0) << 2\n                            | 8\n                            | (gpsStateline.getPnr() << 7);\n        }\n    }\n\n    public int getSvCount() {\n        return this.svCount;\n    }\n\n    public int[] getPrnWithFlags() {\n        return this.prnWithFlags;\n    }\n}"
  },
  {
    "path": "VirtualApp/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.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        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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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@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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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 */\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            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": "VirtualApp/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": "VirtualApp/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.client.ipc.VirtualLocationManager;\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 */\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    public static String getAppPkg() {\n        return VClientImpl.get().getCurrentPackage();\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    public 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    protected static boolean isFakeLocationEnable() {\n        return VirtualLocationManager.get().getMode(VUserHandle.myUserId(), VClientImpl.get().getCurrentPackage()) != 0;\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": "VirtualApp/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": "VirtualApp/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\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.PersistableBundle;\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        if (icicle != null) {\n            BundleCompat.clearParcelledData(icicle);\n        }\n        VirtualCore.get().getComponentDelegate().beforeActivityCreate(activity);\n        IBinder token = mirror.android.app.Activity.mToken.get(activity);\n        ActivityClientRecord r = VActivityManager.get().getActivityRecord(token);\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        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 callActivityOnCreate(Activity activity, Bundle icicle, PersistableBundle persistentState) {\n        if (icicle != null) {\n            BundleCompat.clearParcelledData(icicle);\n        }\n        super.callActivityOnCreate(activity, icicle, persistentState);\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": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/delegate/ComponentDelegate.java",
    "content": "package com.lody.virtual.client.hook.delegate;\n\n\nimport android.app.Application;\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        @Override\n        public void beforeApplicationCreate(Application application) {\n            // Empty\n        }\n\n        @Override\n        public void afterApplicationCreate(Application application) {\n            // Empty\n        }\n    };\n\n    void beforeApplicationCreate(Application application);\n\n    void afterApplicationCreate(Application application);\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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/delegate/PhoneInfoDelegate.java",
    "content": "package com.lody.virtual.client.hook.delegate;\r\n\r\npublic interface PhoneInfoDelegate {\r\n\r\n    /***\r\n     * Fake the Device ID.\r\n     *\r\n     * @param oldDeviceId old DeviceId\r\n     * @param userId      user\r\n     */\r\n    String getDeviceId(String oldDeviceId, int userId);\r\n\r\n    /***\r\n     * Fake the BluetoothAddress\r\n     *\r\n     * @param oldBluetoothAddress old BluetoothAddress\r\n     * @param userId              user\r\n     */\r\n    String getBluetoothAddress(String oldBluetoothAddress, int userId);\r\n\r\n    /***\r\n     * Fake the macAddress\r\n     *\r\n     * @param oldMacAddress old MacAddress\r\n     * @param userId        user\r\n     */\r\n    String getMacAddress(String oldMacAddress, int userId);\r\n}\r\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/delegate/TaskDescriptionDelegate.java",
    "content": "package com.lody.virtual.client.hook.delegate;\r\n\r\nimport android.annotation.TargetApi;\r\nimport android.app.Activity;\r\nimport android.app.ActivityManager;\r\nimport android.os.Build;\r\n\r\npublic interface TaskDescriptionDelegate {\r\n    public ActivityManager.TaskDescription getTaskDescription(ActivityManager.TaskDescription oldTaskDescription);\r\n}\r\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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    public static final String QUERY_ARG_SQL_SELECTION = \"android:query-arg-sql-selection\";\n\n    public static final String QUERY_ARG_SQL_SELECTION_ARGS =\n            \"android:query-arg-sql-selection-args\";\n    public static final String QUERY_ARG_SQL_SORT_ORDER = \"android:query-arg-sql-sort-order\";\n\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, Bundle originQueryArgs) 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[start];\n                String selection = (String) args[start + 1];\n                String[] selectionArgs = (String[]) args[start + 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 = null;\n                String[] selectionArgs = null;\n                String sortOrder = null;\n                Bundle queryArgs = null;\n                if (Build.VERSION.SDK_INT >= 26) {\n                    queryArgs = (Bundle) args[start + 2];\n                    if (queryArgs != null) {\n                        selection = queryArgs.getString(QUERY_ARG_SQL_SELECTION);\n                        selectionArgs = queryArgs.getStringArray(QUERY_ARG_SQL_SELECTION_ARGS);\n                        sortOrder = queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER);\n                    }\n                } else {\n                    selection = (String) args[start + 2];\n                    selectionArgs = (String[]) args[start + 3];\n                    sortOrder = (String) args[start + 4];\n                }\n                return query(methodBox, url, projection, selection, selectionArgs, sortOrder, queryArgs);\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": "VirtualApp/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        if (!VClientImpl.get().isBound()) {\n            return methodBox.call();\n        }\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.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@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": "VirtualApp/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.ApplicationInfo;\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.InstalledAppInfo;\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    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            StubActivityRecord saveInstance = new StubActivityRecord(stubIntent);\n            if (saveInstance.intent == null) {\n                return true;\n            }\n            Intent intent = saveInstance.intent;\n            ComponentName caller = saveInstance.caller;\n            IBinder token = ActivityThread.ActivityClientRecord.token.get(r);\n            ActivityInfo info = saveInstance.info;\n            if (VClientImpl.get().getToken() == null) {\n                InstalledAppInfo installedAppInfo = VirtualCore.get().getInstalledAppInfo(info.packageName, 0);\n                if(installedAppInfo == null){\n                    return true;\n                }\n                VActivityManager.get().processRestarted(info.packageName, info.processName, saveInstance.userId);\n                getH().sendMessageAtFrontOfQueue(Message.obtain(msg));\n                return false;\n            }\n            if (!VClientImpl.get().isBound()) {\n                VClientImpl.get().bindApplication(info.packageName, info.processName);\n                getH().sendMessageAtFrontOfQueue(Message.obtain(msg));\n                return false;\n            }\n            int taskId = IActivityManager.getTaskForActivity.call(\n                    ActivityManagerNative.getDefault.call(),\n                    token,\n                    false\n            );\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            ActivityThread.ActivityClientRecord.intent.set(r, intent);\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": "VirtualApp/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.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.graphics.drawable.Icon;\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.badger.BadgerManager;\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.VNotificationManager;\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.FileUtils;\nimport com.lody.virtual.helper.utils.Reflect;\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.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\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\nimport static com.lody.virtual.client.stub.VASettings.INTERCEPT_BACK_HOME;\n\n/**\n * @author Lody\n */\n@SuppressWarnings(\"unused\")\nclass MethodProxies {\n\n\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                for (int i = 0; i < intents.length; i++) {\n                    Intent intent = intents[i];\n                    if (resolvedTypes != null && i < resolvedTypes.length) {\n                        intent.setDataAndType(intent.getData(), resolvedTypes[i]);\n                    }\n                    Intent targetIntent = redirectIntentSender(type, creator, intent);\n                    if (targetIntent != null) {\n                        intents[i] = 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\n    static class StartActivity extends MethodProxy {\n\n        private static final String SCHEME_FILE = \"file\";\n        private static final String SCHEME_PACKAGE = \"package\";\n        private static final String SCHEME_CONTENT = \"content\";\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\n            Log.d(\"Q_M\", \"---->StartActivity 类\");\n\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            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            if (intent.getScheme() != null && intent.getScheme().equals(SCHEME_PACKAGE) && intent.getData() != null) {\n                if (intent.getAction() != null && intent.getAction().startsWith(\"android.settings.\")) {\n                    intent.setData(Uri.parse(\"package:\" + getHostPkg()));\n                }\n            }\n\n            ActivityInfo activityInfo = VirtualCore.get().resolveActivityInfo(intent, userId);\n            if (activityInfo == null) {\n                VLog.e(\"VActivityManager\", \"Unable to resolve activityInfo : \" + intent);\n\n                Log.d(\"Q_M\", \"---->StartActivity who=\" + who);\n                Log.d(\"Q_M\", \"---->StartActivity intent=\" + intent);\n                Log.d(\"Q_M\", \"---->StartActivity resultTo=\" + resultTo);\n\n                if (intent.getPackage() != null && isAppPkg(intent.getPackage())) {\n                    return ActivityManagerCompat.START_INTENT_NOT_RESOLVED;\n                }\n\n                if (INTERCEPT_BACK_HOME && Intent.ACTION_MAIN.equals(intent.getAction())\n                        && intent.getCategories().contains(\"android.intent.category.HOME\")\n                        && resultTo != null) {\n                    VActivityManager.get().finishActivity(resultTo);\n                    return 0;\n                }\n\n                return method.invoke(who, args);\n            }\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            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                } else if (SCHEME_CONTENT.equals(packageUri.getScheme())) {\n                    InputStream inputStream = null;\n                    OutputStream outputStream = null;\n                    File sharedFileCopy = new File(getHostContext().getCacheDir(), packageUri.getLastPathSegment());\n                    try {\n                        inputStream = getHostContext().getContentResolver().openInputStream(packageUri);\n                        outputStream = new FileOutputStream(sharedFileCopy);\n                        byte[] buffer = new byte[1024];\n                        int count;\n                        while ((count = inputStream.read(buffer)) > 0) {\n                            outputStream.write(buffer, 0, count);\n                        }\n                        outputStream.flush();\n\n                    } catch (IOException e) {\n                        e.printStackTrace();\n                    } finally {\n                        FileUtils.closeQuietly(inputStream);\n                        FileUtils.closeQuietly(outputStream);\n                    }\n                    try {\n                        listener.onRequestInstall(sharedFileCopy.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            VNotificationManager.get().dealNotification(id, notification, getAppPkg());\n\n            /**\n             * `BaseStatusBar#updateNotification` aosp will use use\n             * `new StatusBarIcon(...notification.getSmallIcon()...)`\n             *  while in samsung SystemUI.apk ,the corresponding code comes as\n             * `new StatusBarIcon(...pkgName,notification.icon...)`\n             * the icon comes from `getSmallIcon.getResource`\n             * which will throw an exception on :x process thus crash the application\n             */\n            if (notification != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&\n                    (Build.BRAND.equalsIgnoreCase(\"samsung\") || Build.MANUFACTURER.equalsIgnoreCase(\"samsung\"))) {\n                notification.icon = getHostContext().getApplicationInfo().icon;\n                Icon icon = Icon.createWithResource(getHostPkg(), notification.icon);\n                Reflect.on(notification).call(\"setSmallIcon\", icon);\n            }\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            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            if (serviceInfo != null) {\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            ProviderInfo info = VPackageManager.get().resolveContentProvider(name, 0, userId);\n            if (info != null && info.enabled && isAppPkg(info.packageName)) {\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                if (BuildCompat.isOreo()) {\n                    IInterface provider = ContentProviderHolderOreo.provider.get(holder);\n                    if (provider != null) {\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 if (BadgerManager.handleBadger(intent)) {\n                return null;\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.StaticMethodProxy;\nimport com.lody.virtual.helper.utils.marks.FakeDeviceMark;\n\nimport java.lang.reflect.Method;\n\nimport mirror.android.bluetooth.IBluetooth;\nimport mirror.android.bluetooth.IBluetoothManager;\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(Build.VERSION.SDK_INT >= 17 ? IBluetoothManager.Stub.asInterface : IBluetooth.Stub.asInterface,\n                SERVICE_NAME);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new GetAddress());\n    }\n\n    @FakeDeviceMark(\"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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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 android.os.Build;\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, getServiceName());\n    }\n\n    private static String getServiceName() {\n        return Build.VERSION.SDK_INT >= 26 ? \"contexthub\" : \"contexthub_service\";\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new ResultStaticMethodProxy(\"registerCallback\", 0));\n    }\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/devicepolicy/DevicePolicyManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.devicepolicy;\n\nimport android.content.Context;\nimport android.util.Log;\n\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.MethodProxy;\n\nimport java.lang.reflect.Method;\n\nimport mirror.android.app.admin.IDevicePolicyManager;\n\n/**\n * Created by wy on 2017/10/20.\n */\n\npublic class DevicePolicyManagerStub extends BinderInvocationProxy{\n    public DevicePolicyManagerStub() {\n        super(IDevicePolicyManager.Stub.asInterface, Context.DEVICE_POLICY_SERVICE);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new GetStorageEncryptionStatus());\n    }\n\n    private static class GetStorageEncryptionStatus extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getStorageEncryptionStatus\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            args[0] = VirtualCore.get().getHostPkg();\n            return method.invoke(who, args);\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/fingerprint/FingerprintManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.fingerprint;\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.ReplaceLastPkgMethodProxy;\n\nimport mirror.android.hardware.fingerprint.IFingerprintService;\n\n/**\n * Created by natsuki on 12/10/2017.\n */\n\n@TargetApi(Build.VERSION_CODES.M)\npublic class FingerprintManagerStub extends BinderInvocationProxy {\n    public FingerprintManagerStub() {\n        super(IFingerprintService.Stub.asInterface, Context.FINGERPRINT_SERVICE);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"isHardwareDetected\"));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"hasEnrolledFingerprints\"));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"authenticate\"));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"cancelAuthentication\"));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"getEnrolledFingerprints\"));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"getAuthenticatorId\"));\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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 StartInputOrWindowGainedFocus {\n\n        @Override\n        public String getMethodName() {\n            return \"startInput\";\n        }\n    }\n\n    static class WindowGainedFocus extends StartInputOrWindowGainedFocus {\n\n        @Override\n        public String getMethodName() {\n            return \"windowGainedFocus\";\n        }\n\n\n    }\n\n    static class StartInputOrWindowGainedFocus extends MethodProxy {\n\n\n        @Override\n        public String getMethodName() {\n            return \"startInputOrWindowGainedFocus\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            int editorInfoIndex = ArrayUtils.indexOfFirst(args, EditorInfo.class);\n            if (editorInfoIndex != -1) {\n                EditorInfo attribute = (EditorInfo) args[editorInfoIndex];\n                attribute.packageName = getHostPkg();\n            }\n            return method.invoke(who, args);\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.content.Intent;\nimport android.os.Build;\nimport android.os.Build.VERSION;\n\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.ipc.VJobScheduler;\nimport com.lody.virtual.helper.utils.ComponentUtils;\n\nimport java.lang.reflect.Method;\n\nimport mirror.android.app.job.IJobScheduler;\nimport mirror.android.app.job.JobWorkItem;\n\n/**\n * @author Lody\n * @see android.app.job.JobScheduler\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class JobServiceStub extends BinderInvocationProxy {\n\n    public JobServiceStub() {\n        super(IJobScheduler.Stub.asInterface, Context.JOB_SCHEDULER_SERVICE);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new schedule());\n        addMethodProxy(new getAllPendingJobs());\n        addMethodProxy(new cancelAll());\n        addMethodProxy(new cancel());\n\n        if (VERSION.SDK_INT >= 24) {\n            addMethodProxy(new getPendingJob());\n        }\n        if (VERSION.SDK_INT >= 26) {\n            addMethodProxy(new enqueue());\n        }\n    }\n\n\n    private class schedule extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"schedule\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            JobInfo jobInfo = (JobInfo) args[0];\n            return VJobScheduler.get().schedule(jobInfo);\n        }\n    }\n\n    private class getAllPendingJobs extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getAllPendingJobs\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return VJobScheduler.get().getAllPendingJobs();\n        }\n    }\n\n    private class cancelAll extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"cancelAll\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            VJobScheduler.get().cancelAll();\n            return 0;\n        }\n    }\n\n    private class cancel extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"cancel\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            int jobId = (int) args[0];\n            VJobScheduler.get().cancel(jobId);\n            return 0;\n        }\n    }\n\n    private class getPendingJob extends MethodProxy {\n        private getPendingJob() {\n        }\n\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return VJobScheduler.get().getPendingJob((Integer) args[0]);\n        }\n\n        public String getMethodName() {\n            return \"getPendingJob\";\n        }\n    }\n\n    private class enqueue extends MethodProxy {\n        private enqueue() {\n        }\n\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return VJobScheduler.get().enqueue(\n                    (JobInfo) args[0],\n                    JobServiceStub.this.redirect(args[1], MethodProxy.getAppPkg())\n            );\n        }\n\n        public String getMethodName() {\n            return \"enqueue\";\n        }\n    }\n\n    private Object redirect(Object item, String pkg) {\n        if (item == null) {\n            return null;\n        }\n        Intent redirectIntentSender = ComponentUtils.redirectIntentSender(4, pkg, (Intent) JobWorkItem.getIntent.call(item, new Object[0]), null);\n        Object newInstance = JobWorkItem.ctor.newInstance(redirectIntentSender);\n        JobWorkItem.mWorkId.set(newInstance, JobWorkItem.mWorkId.get(item));\n        JobWorkItem.mGrants.set(newInstance, JobWorkItem.mGrants.get(item));\n        JobWorkItem.mDeliveryCount.set(newInstance, JobWorkItem.mDeliveryCount.get(item));\n        return newInstance;\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/location/GPSListenerThread.java",
    "content": "package com.lody.virtual.client.hook.proxies.location;\n\nimport android.location.Location;\nimport android.os.Build.VERSION;\nimport android.os.Handler;\n\nimport com.lody.virtual.client.ipc.VirtualLocationManager;\nimport com.lody.virtual.remote.vloc.VLocation;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.Timer;\nimport java.util.TimerTask;\n\nimport mirror.android.location.LocationManager;\n\n\npublic class GPSListenerThread extends TimerTask {\n    private static GPSListenerThread INSTANCE;\n    private Handler handler = new Handler();\n    private boolean isRunning = false;\n    private HashMap<Object, Long> listeners = new HashMap<>();\n    private Timer timer = new Timer();\n\n    static {\n        INSTANCE = new GPSListenerThread();\n    }\n\n    private void notifyGPSStatus(Map listeners) {\n        if (listeners != null && !listeners.isEmpty()) {\n            //noinspection unchecked\n            Set<Map.Entry> entries = listeners.entrySet();\n            for (Map.Entry entry : entries) {\n                try {\n                    Object value = entry.getValue();\n                    if (value != null) {\n                        MockLocationHelper.invokeSvStatusChanged(value);\n                    }\n                } catch (Throwable e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    private void notifyLocation(Map listeners) {\n        if (listeners != null) {\n            try {\n                if (!listeners.isEmpty()) {\n                    VLocation vLocation = VirtualLocationManager.get().getLocation();\n                    if (vLocation != null) {\n                        Location location = vLocation.toSysLocation();\n                        //noinspection unchecked\n                        Set<Map.Entry> entries = listeners.entrySet();\n                        for (Map.Entry entry : entries) {\n                            Object value = entry.getValue();\n                            if (value != null) {\n                                try {\n                                    LocationManager.ListenerTransport.onLocationChanged.call(value, location);\n                                } catch (Throwable e) {\n                                    e.printStackTrace();\n                                }\n                            }\n                        }\n                    }\n                }\n            } catch (Throwable e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    private void notifyMNmeaListener(Map listeners) {\n        if (listeners != null && !listeners.isEmpty()) {\n            //noinspection unchecked\n            Set<Map.Entry> entries = listeners.entrySet();\n            for (Map.Entry entry : entries) {\n                try {\n                    Object value = entry.getValue();\n                    if (value != null) {\n                        MockLocationHelper.invokeNmeaReceived(value);\n                    }\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    public void addListenerTransport(Object transport) {\n        this.listeners.put(transport, System.currentTimeMillis());\n        if (!isRunning) {\n            synchronized (this) {\n                if (!isRunning) {\n                    isRunning = true;\n                    timer.schedule(this, 1000, 1000);\n                }\n            }\n        }\n    }\n\n    public void removeListenerTransport(Object transport) {\n        if (transport != null) {\n            listeners.remove(transport);\n        }\n    }\n\n    public void run() {\n        if (!listeners.isEmpty()) {\n            if (VirtualLocationManager.get().getMode() == VirtualLocationManager.MODE_CLOSE) {\n                listeners.clear();\n                return;\n            }\n            for (Map.Entry entry : this.listeners.entrySet()) {\n                try {\n                    Object transport = entry.getKey();\n                    Map gpsStatusListeners;\n                    if (VERSION.SDK_INT >= 24) {\n                        Map nmeaListeners = LocationManager.mGnssNmeaListeners.get(transport);\n                        notifyGPSStatus(LocationManager.mGnssStatusListeners.get(transport));\n                        notifyMNmeaListener(nmeaListeners);\n                        gpsStatusListeners = LocationManager.mGpsStatusListeners.get(transport);\n                        notifyGPSStatus(gpsStatusListeners);\n                        notifyMNmeaListener(LocationManager.mGpsNmeaListeners.get(transport));\n                    } else {\n                        gpsStatusListeners = LocationManager.mGpsStatusListeners.get(transport);\n                        notifyGPSStatus(gpsStatusListeners);\n                        notifyMNmeaListener(LocationManager.mNmeaListeners.get(transport));\n                    }\n                    final Map listeners = LocationManager.mListeners.get(transport);\n                    if (gpsStatusListeners != null && !gpsStatusListeners.isEmpty()) {\n                        if (listeners == null || listeners.isEmpty()) {\n                            // listeners not ready\n                            handler.postDelayed(new Runnable() {\n                                public void run() {\n                                    GPSListenerThread.this.notifyLocation(listeners);\n                                }\n                            }, 100);\n                        } else {\n                            notifyLocation(listeners);\n                        }\n                    }\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    public void stop() {\n        this.timer.cancel();\n    }\n\n    public static GPSListenerThread get() {\n        return INSTANCE;\n    }\n\n    private GPSListenerThread() {\n    }\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/location/GPSStatusListenerThread.java",
    "content": "package com.lody.virtual.client.hook.proxies.location;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Timer;\nimport java.util.TimerTask;\n\npublic class GPSStatusListenerThread extends TimerTask {\n    private static GPSStatusListenerThread INSTANCE;\n    private boolean isRunning = false;\n    private Map<Object, Long> listeners = new HashMap<>();\n    private Timer timer = new Timer();\n\n    static {\n        INSTANCE = new GPSStatusListenerThread();\n    }\n\n    public void addListenerTransport(Object transport) {\n        if (!isRunning) {\n            synchronized (this) {\n                if (!isRunning) {\n                    isRunning = true;\n                    timer.schedule(this, 100, 800);\n                }\n            }\n        }\n        listeners.put(transport, System.currentTimeMillis());\n    }\n\n    public void removeListenerTransport(Object obj) {\n        if (obj != null) {\n            listeners.remove(obj);\n        }\n    }\n\n    public void run() {\n        if (!listeners.isEmpty()) {\n            for (Entry entry : listeners.entrySet()) {\n                try {\n                    Object transport = entry.getKey();\n                    MockLocationHelper.invokeSvStatusChanged(transport);\n                    MockLocationHelper.invokeNmeaReceived(transport);\n                } catch (Throwable e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    public void stop() {\n        timer.cancel();\n    }\n\n    public static GPSStatusListenerThread get() {\n        return INSTANCE;\n    }\n\n    private GPSStatusListenerThread() {\n    }\n}"
  },
  {
    "path": "VirtualApp/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.Inject;\nimport com.lody.virtual.client.hook.base.LogInvocation;\nimport com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy;\nimport com.lody.virtual.client.stub.VASettings;\n\nimport java.lang.reflect.Method;\n\nimport mirror.android.location.ILocationManager;\n\n/**\n * @author Lody\n * @see android.location.LocationManager\n */\n@LogInvocation(LogInvocation.Condition.ALWAYS)\n@Inject(MethodProxies.class)\npublic class LocationManagerStub extends BinderInvocationProxy {\n    public LocationManagerStub() {\n        super(ILocationManager.Stub.asInterface, Context.LOCATION_SERVICE);\n    }\n\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"addTestProvider\"));\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"removeTestProvider\"));\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"setTestProviderLocation\"));\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"clearTestProviderLocation\"));\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"setTestProviderEnabled\"));\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"clearTestProviderEnabled\"));\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"setTestProviderStatus\"));\n            addMethodProxy(new ReplaceLastPkgMethodProxy(\"clearTestProviderStatus\"));\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            addMethodProxy(new FakeReplaceLastPkgMethodProxy(\"addGpsMeasurementsListener\", true));\n            addMethodProxy(new FakeReplaceLastPkgMethodProxy(\"addGpsNavigationMessageListener\", true));\n            addMethodProxy(new FakeReplaceLastPkgMethodProxy(\"removeGpsMeasurementListener\", 0));\n            addMethodProxy(new FakeReplaceLastPkgMethodProxy(\"removeGpsNavigationMessageListener\", 0));\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            addMethodProxy(new FakeReplaceLastPkgMethodProxy(\"requestGeofence\", 0));\n            addMethodProxy(new FakeReplaceLastPkgMethodProxy(\"removeGeofence\", 0));\n        }\n\n        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {\n            addMethodProxy(new FakeReplaceLastPkgMethodProxy(\"addProximityAlert\", 0));\n        }\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            addMethodProxy(new FakeReplaceLastPkgMethodProxy(\"addNmeaListener\", 0));\n            addMethodProxy(new FakeReplaceLastPkgMethodProxy(\"removeNmeaListener\", 0));\n        }\n    }\n\n    private static class FakeReplaceLastPkgMethodProxy extends ReplaceLastPkgMethodProxy {\n        private Object mDefValue;\n\n        private FakeReplaceLastPkgMethodProxy(String name, Object def) {\n            super(name);\n            mDefValue = def;\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (isFakeLocationEnable()) {\n                return mDefValue;\n            }\n            return super.call(who, method, args);\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/location/MethodProxies.java",
    "content": "package com.lody.virtual.client.hook.proxies.location;\n\nimport android.location.LocationManager;\nimport android.location.LocationRequest;\nimport android.os.Build;\n\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.base.ReplaceLastPkgMethodProxy;\nimport com.lody.virtual.client.ipc.VirtualLocationManager;\nimport com.lody.virtual.helper.utils.ArrayUtils;\nimport com.lody.virtual.helper.utils.Reflect;\nimport com.lody.virtual.remote.vloc.VLocation;\n\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport mirror.android.location.LocationRequestL;\n\n/**\n * @author Lody\n */\n@SuppressWarnings(\"ALL\")\npublic class MethodProxies {\n\n    private static void fixLocationRequest(LocationRequest request) {\n        if (request != null) {\n            if (LocationRequestL.mHideFromAppOps != null) {\n                LocationRequestL.mHideFromAppOps.set(request, false);\n            }\n            if (LocationRequestL.mWorkSource != null) {\n                LocationRequestL.mWorkSource.set(request, null);\n            }\n        }\n    }\n\n    static class AddGpsStatusListener extends ReplaceLastPkgMethodProxy {\n\n        public AddGpsStatusListener() {\n            super(\"addGpsStatusListener\");\n        }\n\n        public AddGpsStatusListener(String name) {\n            super(name);\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (isFakeLocationEnable()) {\n                Object transport = ArrayUtils.getFirst(args, mirror.android.location.LocationManager.GpsStatusListenerTransport.TYPE);\n                Object locationManager = mirror.android.location.LocationManager.GpsStatusListenerTransport.this$0.get(transport);\n                mirror.android.location.LocationManager.GpsStatusListenerTransport.onGpsStarted.call(transport);\n                mirror.android.location.LocationManager.GpsStatusListenerTransport.onFirstFix.call(transport, 0);\n                if (mirror.android.location.LocationManager.GpsStatusListenerTransport.mListener.get(transport) != null) {\n                    MockLocationHelper.invokeSvStatusChanged(transport);\n                } else {\n                    MockLocationHelper.invokeNmeaReceived(transport);\n                }\n                GPSListenerThread.get().addListenerTransport(locationManager);\n                return true;\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n    static class RequestLocationUpdates extends ReplaceLastPkgMethodProxy {\n\n        public RequestLocationUpdates() {\n            super(\"requestLocationUpdates\");\n        }\n\n        @Override\n        public Object call(final Object who, Method method, Object... args) throws Throwable {\n            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {\n                LocationRequest request = (LocationRequest) args[0];\n                fixLocationRequest(request);\n            }\n            if (isFakeLocationEnable()) {\n                Object transport = ArrayUtils.getFirst(args, mirror.android.location.LocationManager.ListenerTransport.TYPE);\n                if (transport != null) {\n                    Object locationManager = mirror.android.location.LocationManager.ListenerTransport.this$0.get(transport);\n                    MockLocationHelper.setGpsStatus(locationManager);\n                    GPSListenerThread.get().addListenerTransport(locationManager);\n                }\n                return 0;\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n    static class RemoveUpdates extends ReplaceLastPkgMethodProxy {\n\n        public RemoveUpdates() {\n            super(\"removeUpdates\");\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (isFakeLocationEnable()) {\n                // TODO\n                return 0;\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n    static class GetLastLocation extends ReplaceLastPkgMethodProxy {\n\n        public GetLastLocation() {\n            super(\"getLastLocation\");\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (!(args[0] instanceof String)) {\n                LocationRequest request = (LocationRequest) args[0];\n                fixLocationRequest(request);\n            }\n            if (isFakeLocationEnable()) {\n                VLocation loc = VirtualLocationManager.get().getLocation();\n                if (loc != null) {\n                    return loc.toSysLocation();\n                } else {\n                    return null;\n                }\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n    static class GetLastKnownLocation extends GetLastLocation {\n        @Override\n        public String getMethodName() {\n            return \"getLastKnownLocation\";\n        }\n    }\n\n    static class getProviders extends MethodProxy {\n\n        static List PROVIDERS = Arrays.asList(\n                LocationManager.GPS_PROVIDER,\n                LocationManager.PASSIVE_PROVIDER,\n                LocationManager.NETWORK_PROVIDER\n        );\n\n        @Override\n        public String getMethodName() {\n            return \"getProviders\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            return PROVIDERS;\n        }\n    }\n\n    static class IsProviderEnabled extends MethodProxy {\n        @Override\n        public String getMethodName() {\n            return \"isProviderEnabled\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (isFakeLocationEnable()) {\n                String provider = (String) args[0];\n                if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {\n                    return true;\n                }\n                if (LocationManager.GPS_PROVIDER.equals(provider)) {\n                    return true;\n                }\n                if (LocationManager.NETWORK_PROVIDER.equals(provider)) {\n                    return true;\n                }\n                return false;\n\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n    static class getAllProviders extends getProviders {\n\n        @Override\n        public String getMethodName() {\n            return \"getAllProviders\";\n        }\n    }\n\n    static class GetBestProvider extends MethodProxy {\n        @Override\n        public String getMethodName() {\n            return \"getBestProvider\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (isFakeLocationEnable()) {\n                return LocationManager.GPS_PROVIDER;\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n\n    static class RemoveGpsStatusListener extends ReplaceLastPkgMethodProxy {\n        public RemoveGpsStatusListener() {\n            super(\"removeGpsStatusListener\");\n        }\n\n        public RemoveGpsStatusListener(String name) {\n            super(name);\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (isFakeLocationEnable()) {\n                return 0;\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n    static class sendExtraCommand extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"sendExtraCommand\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (isFakeLocationEnable()) {\n                return true;\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n\n    static class UnregisterGnssStatusCallback extends RemoveGpsStatusListener {\n        public UnregisterGnssStatusCallback() {\n            super(\"unregisterGnssStatusCallback\");\n        }\n    }\n\n    static class RegisterGnssStatusCallback extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"registerGnssStatusCallback\";\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (!isFakeLocationEnable()) {\n                return super.call(who, method, args);\n            }\n            Object transport = ArrayUtils.getFirst(args, mirror.android.location.LocationManager.GnssStatusListenerTransport.TYPE);\n            if (transport != null) {\n                mirror.android.location.LocationManager.GnssStatusListenerTransport.onGnssStarted.call(transport, new Object[0]);\n                if (mirror.android.location.LocationManager.GnssStatusListenerTransport.mGpsListener.get(transport) != null) {\n                    MockLocationHelper.invokeSvStatusChanged(transport);\n                } else {\n                    MockLocationHelper.invokeNmeaReceived(transport);\n                }\n                mirror.android.location.LocationManager.GnssStatusListenerTransport.onFirstFix.call(transport, Integer.valueOf(0));\n                Object locationManager = mirror.android.location.LocationManager.GnssStatusListenerTransport.this$0.get(transport);\n                GPSListenerThread.get().addListenerTransport(locationManager);\n            }\n            return true;\n        }\n    }\n\n    static class getProviderProperties extends MethodProxy {\n\n        @Override\n        public String getMethodName() {\n            return \"getProviderProperties\";\n        }\n\n        @Override\n        public Object afterCall(Object who, Method method, Object[] args, Object result) throws Throwable {\n            if (!isFakeLocationEnable()) {\n                return super.afterCall(who, method, args, result);\n            }\n            try {\n                Reflect.on(result).set(\"mRequiresNetwork\", false);\n                Reflect.on(result).set(\"mRequiresCell\", false);\n            } catch (Throwable e) {\n                e.printStackTrace();\n            }\n            return result;\n        }\n    }\n\n    static class locationCallbackFinished extends MethodProxy {\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (isFakeLocationEnable()) {\n                return true;\n            }\n            return super.call(who, method, args);\n        }\n\n        @Override\n        public String getMethodName() {\n            return \"locationCallbackFinished\";\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/location/MockLocationHelper.java",
    "content": "package com.lody.virtual.client.hook.proxies.location;\n\nimport android.util.Log;\n\nimport com.lody.virtual.client.env.VirtualGPSSatalines;\nimport com.lody.virtual.client.ipc.VirtualLocationManager;\nimport com.lody.virtual.helper.utils.Reflect;\nimport com.lody.virtual.remote.vloc.VLocation;\n\nimport java.lang.reflect.Method;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Locale;\n\nimport mirror.android.location.LocationManager;\n\n/**\n * @author Lody\n */\npublic class MockLocationHelper {\n\n    public static void invokeNmeaReceived(Object listener) {\n        if (listener != null) {\n            VirtualGPSSatalines satalines = VirtualGPSSatalines.get();\n            try {\n                VLocation location = VirtualLocationManager.get().getLocation();\n                if (location != null) {\n                    String date = new SimpleDateFormat(\"HHmmss:SS\", Locale.US).format(new Date());\n                    String lat = getGPSLat(location.latitude);\n                    String lon = getGPSLat(location.longitude);\n                    String latNW = getNorthWest(location);\n                    String lonSE = getSouthEast(location);\n                    String $GPGGA = checksum(String.format(\"$GPGGA,%s,%s,%s,%s,%s,1,%s,692,.00,M,.00,M,,,\", date, lat, latNW, lon, lonSE, satalines.getSvCount()));\n                    String $GPRMC = checksum(String.format(\"$GPRMC,%s,A,%s,%s,%s,%s,0,0,260717,,,A,\", date, lat, latNW, lon, lonSE));\n                    if (LocationManager.GnssStatusListenerTransport.onNmeaReceived != null) {\n                        LocationManager.GnssStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), \"$GPGSV,1,1,04,12,05,159,36,15,41,087,15,19,38,262,30,31,56,146,19,*73\");\n                        LocationManager.GnssStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), $GPGGA);\n                        LocationManager.GnssStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), \"$GPVTG,0,T,0,M,0,N,0,K,A,*25\");\n                        LocationManager.GnssStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), $GPRMC);\n                        LocationManager.GnssStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), \"$GPGSA,A,2,12,15,19,31,,,,,,,,,604,712,986,*27\");\n                    } else if (LocationManager.GpsStatusListenerTransport.onNmeaReceived != null) {\n                        LocationManager.GpsStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), \"$GPGSV,1,1,04,12,05,159,36,15,41,087,15,19,38,262,30,31,56,146,19,*73\");\n                        LocationManager.GpsStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), $GPGGA);\n                        LocationManager.GpsStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), \"$GPVTG,0,T,0,M,0,N,0,K,A,*25\");\n                        LocationManager.GpsStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), $GPRMC);\n                        LocationManager.GpsStatusListenerTransport.onNmeaReceived.call(listener, System.currentTimeMillis(), \"$GPGSA,A,2,12,15,19,31,,,,,,,,,604,712,986,*27\");\n                    }\n                }\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    public static void setGpsStatus(Object locationManager) {\n\n        VirtualGPSSatalines satalines = VirtualGPSSatalines.get();\n        Method setStatus = null;\n        int svCount = satalines.getSvCount();\n        float[] snrs = satalines.getSnrs();\n        int[] prns = satalines.getPrns();\n        float[] elevations = satalines.getElevations();\n        float[] azimuths = satalines.getAzimuths();\n        Object mGpsStatus = Reflect.on(locationManager).get(\"mGpsStatus\");\n        try {\n            setStatus = mGpsStatus.getClass().getDeclaredMethod(\"setStatus\", Integer.TYPE, int[].class, float[].class, float[].class, float[].class, Integer.TYPE, Integer.TYPE, Integer.TYPE);\n            setStatus.setAccessible(true);\n            int ephemerisMask = satalines.getEphemerisMask();\n            int almanacMask = satalines.getAlmanacMask();\n            int usedInFixMask = satalines.getUsedInFixMask();\n            setStatus.invoke(mGpsStatus, svCount, prns, snrs, elevations, azimuths, ephemerisMask, almanacMask, usedInFixMask);\n        } catch (Exception e) {\n            // ignore\n        }\n        if (setStatus == null) {\n            try {\n                setStatus = mGpsStatus.getClass().getDeclaredMethod(\"setStatus\", Integer.TYPE, int[].class, float[].class, float[].class, float[].class, int[].class, int[].class, int[].class);\n                setStatus.setAccessible(true);\n                svCount = satalines.getSvCount();\n                int length = satalines.getPrns().length;\n                elevations = satalines.getElevations();\n                azimuths = satalines.getAzimuths();\n                int[] ephemerisMask = new int[length];\n                for (int i = 0; i < length; i++) {\n                    ephemerisMask[i] = satalines.getEphemerisMask();\n                }\n                int[] almanacMask = new int[length];\n                for (int i = 0; i < length; i++) {\n                    almanacMask[i] = satalines.getAlmanacMask();\n                }\n                int[] usedInFixMask = new int[length];\n                for (int i = 0; i < length; i++) {\n                    usedInFixMask[i] = satalines.getUsedInFixMask();\n                }\n                setStatus.invoke(mGpsStatus, svCount, prns, snrs, elevations, azimuths, ephemerisMask, almanacMask, usedInFixMask);\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    public static void invokeSvStatusChanged(Object transport) {\n        if (transport != null) {\n            VirtualGPSSatalines satalines = VirtualGPSSatalines.get();\n            try {\n                Class<?> aClass = transport.getClass();\n                int svCount;\n                float[] snrs;\n                float[] elevations;\n                float[] azimuths;\n                if (aClass == LocationManager.GnssStatusListenerTransport.TYPE) {\n                    svCount = satalines.getSvCount();\n                    int[] prnWithFlags = satalines.getPrnWithFlags();\n                    snrs = satalines.getSnrs();\n                    elevations = satalines.getElevations();\n                    azimuths = satalines.getAzimuths();\n                    LocationManager.GnssStatusListenerTransport.onSvStatusChanged.call(transport, svCount, prnWithFlags, snrs, elevations, azimuths);\n                } else if (aClass == LocationManager.GpsStatusListenerTransport.TYPE) {\n                    svCount = satalines.getSvCount();\n                    int[] prns = satalines.getPrns();\n                    snrs = satalines.getSnrs();\n                    elevations = satalines.getElevations();\n                    azimuths = satalines.getAzimuths();\n                    int ephemerisMask = satalines.getEphemerisMask();\n                    int almanacMask = satalines.getAlmanacMask();\n                    int usedInFixMask = satalines.getUsedInFixMask();\n                    if (LocationManager.GpsStatusListenerTransport.onSvStatusChanged != null) {\n                        LocationManager.GpsStatusListenerTransport.onSvStatusChanged.call(transport, svCount, prns, snrs, elevations, azimuths, ephemerisMask, almanacMask, usedInFixMask);\n                    } else if (LocationManager.GpsStatusListenerTransportVIVO.onSvStatusChanged != null) {\n                        LocationManager.GpsStatusListenerTransportVIVO.onSvStatusChanged.call(transport, svCount, prns, snrs, elevations, azimuths, ephemerisMask, almanacMask, usedInFixMask, new long[svCount]);\n                    } else if (LocationManager.GpsStatusListenerTransportSumsungS5.onSvStatusChanged != null) {\n                        LocationManager.GpsStatusListenerTransportSumsungS5.onSvStatusChanged.call(transport, svCount, prns, snrs, elevations, azimuths, ephemerisMask, almanacMask, usedInFixMask, new int[svCount]);\n                    } else if (LocationManager.GpsStatusListenerTransportOPPO_R815T.onSvStatusChanged != null) {\n                        int len = prns.length;\n                        int[] ephemerisMasks = new int[len];\n                        for (int i = 0; i < len; i++) {\n                            ephemerisMasks[i] = satalines.getEphemerisMask();\n                        }\n                        int[] almanacMasks = new int[len];\n                        for (int i = 0; i < len; i++) {\n                            almanacMasks[i] = satalines.getAlmanacMask();\n                        }\n                        int[] usedInFixMasks = new int[len];\n                        for (int i = 0; i < len; i++) {\n                            usedInFixMasks[i] = satalines.getUsedInFixMask();\n                        }\n                        LocationManager.GpsStatusListenerTransportOPPO_R815T.onSvStatusChanged.call(transport, svCount, prns, snrs, elevations, azimuths, ephemerisMasks, almanacMasks, usedInFixMasks, svCount);\n                    }\n                }\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    private static String getSouthEast(VLocation location) {\n        if (location.longitude > 0.0d) {\n            return \"E\";\n        }\n        return \"W\";\n    }\n\n    private static String getNorthWest(VLocation location) {\n        if (location.latitude > 0.0d) {\n            return \"N\";\n        }\n        return \"S\";\n    }\n\n    public static String getGPSLat(double v) {\n        int du = (int) v;\n        double fen = (v - (double) du) * 60.0d;\n        return du + leftZeroPad((int) fen, 2) + \":\" + String.valueOf(fen).substring(2);\n    }\n\n    private static String leftZeroPad(int num, int size) {\n        return leftZeroPad(String.valueOf(num), size);\n    }\n\n    private static String leftZeroPad(String num, int size) {\n        StringBuilder sb = new StringBuilder(size);\n        int i;\n        if (num == null) {\n            for (i = 0; i < size; i++) {\n                sb.append('0');\n            }\n        } else {\n            for (i = 0; i < size - num.length(); i++) {\n                sb.append('0');\n            }\n            sb.append(num);\n        }\n        return sb.toString();\n    }\n\n    public static String checksum(String nema) {\n        String checkStr = nema;\n        if (nema.startsWith(\"$\")) {\n            checkStr = nema.substring(1);\n        }\n        int sum = 0;\n        for (int i = 0; i < checkStr.length(); i++) {\n            sum ^= (byte) checkStr.charAt(i);\n        }\n        return nema + \"*\" + String.format(\"%02X\", sum).toLowerCase();\n    }\n}"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/mount/MountServiceStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.mount;\n\nimport android.os.IInterface;\n\nimport com.lody.virtual.client.hook.base.Inject;\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.helper.compat.BuildCompat;\n\nimport mirror.RefStaticMethod;\nimport mirror.android.os.mount.IMountService;\nimport mirror.android.os.storage.IStorageManager;\n\n/**\n * @author Lody\n */\n@Inject(MethodProxies.class)\npublic class MountServiceStub extends BinderInvocationProxy {\n\n    public MountServiceStub() {\n        super(getInterfaceMethod(), \"mount\");\n    }\n\n    private static RefStaticMethod<IInterface> getInterfaceMethod() {\n        if (BuildCompat.isOreo()) {\n            return IStorageManager.Stub.asInterface;\n        } else {\n            return IMountService.Stub.asInterface;\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.marks.FakeDeviceMark;\n\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\n@SuppressWarnings(\"ALL\")\nclass MethodProxies {\n\n    @FakeDeviceMark(\"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    @FakeDeviceMark(\"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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/telephony/MethodProxies.java",
    "content": "package com.lody.virtual.client.hook.proxies.telephony;\n\nimport android.os.Bundle;\nimport android.telephony.CellIdentityCdma;\nimport android.telephony.CellIdentityGsm;\nimport android.telephony.CellInfo;\nimport android.telephony.CellInfoCdma;\nimport android.telephony.CellInfoGsm;\nimport android.telephony.CellSignalStrengthCdma;\nimport android.telephony.CellSignalStrengthGsm;\nimport android.telephony.NeighboringCellInfo;\nimport android.telephony.cdma.CdmaCellLocation;\nimport android.telephony.gsm.GsmCellLocation;\n\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.client.ipc.VirtualLocationManager;\nimport com.lody.virtual.helper.utils.marks.FakeDeviceMark;\nimport com.lody.virtual.helper.utils.marks.FakeLocMark;\nimport com.lody.virtual.remote.vloc.VCell;\n\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * @author Lody\n */\n@SuppressWarnings(\"ALL\")\nclass MethodProxies {\n\n    @FakeDeviceMark(\"fake device id.\")\n    static class GetDeviceId extends StaticMethodProxy {\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    @FakeLocMark(\"cell location\")\n    static class GetCellLocation extends ReplaceCallingPkgMethodProxy {\n\n        public GetCellLocation() {\n            super(\"getCellLocation\");\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (isFakeLocationEnable()) {\n                VCell cell = VirtualLocationManager.get().getCell(getAppUserId(), getAppPkg());\n                if (cell != null) {\n                    return getCellLocationInternal(cell);\n                }\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n    static class getAllCellInfoUsingSubId extends ReplaceCallingPkgMethodProxy {\n\n        public getAllCellInfoUsingSubId() {\n            super(\"getAllCellInfoUsingSubId\");\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (isFakeLocationEnable()) {\n                return null;\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n    @FakeLocMark(\"cell location\")\n    static class GetAllCellInfo extends ReplaceCallingPkgMethodProxy {\n\n        public GetAllCellInfo() {\n            super(\"getAllCellInfo\");\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (isFakeLocationEnable()) {\n                List<VCell> cells = VirtualLocationManager.get().getAllCell(getAppUserId(), getAppPkg());\n                if (cells != null) {\n                    List<CellInfo> result = new ArrayList<CellInfo>();\n                    for (VCell cell : cells) {\n                        result.add(createCellInfo(cell));\n                    }\n                    return result;\n                }\n\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n    @FakeLocMark(\"neb cell location\")\n    static class GetNeighboringCellInfo extends ReplaceCallingPkgMethodProxy {\n\n        public GetNeighboringCellInfo() {\n            super(\"getNeighboringCellInfo\");\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n            if (isFakeLocationEnable()) {\n                List<VCell> cells = VirtualLocationManager.get().getNeighboringCell(getAppUserId(), getAppPkg());\n                if (cells != null) {\n                    List<NeighboringCellInfo> infos = new ArrayList<>();\n                    for (VCell cell : cells) {\n                        NeighboringCellInfo info = new NeighboringCellInfo();\n                        mirror.android.telephony.NeighboringCellInfo.mLac.set(info, cell.lac);\n                        mirror.android.telephony.NeighboringCellInfo.mCid.set(info, cell.cid);\n                        mirror.android.telephony.NeighboringCellInfo.mRssi.set(info, 6);\n                        infos.add(info);\n                    }\n                    return infos;\n                }\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n    private static Bundle getCellLocationInternal(VCell cell) {\n        if (cell != null) {\n            Bundle cellData = new Bundle();\n            if (cell.type == 2) {\n                try {\n                    CdmaCellLocation cellLoc = new CdmaCellLocation();\n                    cellLoc.setCellLocationData(cell.baseStationId, Integer.MAX_VALUE, Integer.MAX_VALUE, cell.systemId, cell.networkId);\n                    cellLoc.fillInNotifierBundle(cellData);\n                } catch (Throwable e) {\n                    cellData.putInt(\"baseStationId\", cell.baseStationId);\n                    cellData.putInt(\"baseStationLatitude\", Integer.MAX_VALUE);\n                    cellData.putInt(\"baseStationLongitude\", Integer.MAX_VALUE);\n                    cellData.putInt(\"systemId\", cell.systemId);\n                    cellData.putInt(\"networkId\", cell.networkId);\n                }\n            } else {\n                try {\n                    GsmCellLocation cellLoc = new GsmCellLocation();\n                    cellLoc.setLacAndCid(cell.lac, cell.cid);\n                    cellLoc.fillInNotifierBundle(cellData);\n                } catch (Throwable e) {\n                    cellData.putInt(\"lac\", cell.lac);\n                    cellData.putInt(\"cid\", cell.cid);\n                    cellData.putInt(\"psc\", cell.psc);\n                }\n            }\n            return cellData;\n        }\n        return null;\n    }\n\n\n    private static CellInfo createCellInfo(VCell cell) {\n        if (cell.type == 2) { // CDMA\n            CellInfoCdma cdma = mirror.android.telephony.CellInfoCdma.ctor.newInstance();\n            CellIdentityCdma identityCdma = mirror.android.telephony.CellInfoCdma.mCellIdentityCdma.get(cdma);\n            CellSignalStrengthCdma strengthCdma = mirror.android.telephony.CellInfoCdma.mCellSignalStrengthCdma.get(cdma);\n            mirror.android.telephony.CellIdentityCdma.mNetworkId.set(identityCdma, cell.networkId);\n            mirror.android.telephony.CellIdentityCdma.mSystemId.set(identityCdma, cell.systemId);\n            mirror.android.telephony.CellIdentityCdma.mBasestationId.set(identityCdma, cell.baseStationId);\n            mirror.android.telephony.CellSignalStrengthCdma.mCdmaDbm.set(strengthCdma, -74);\n            mirror.android.telephony.CellSignalStrengthCdma.mCdmaEcio.set(strengthCdma, -91);\n            mirror.android.telephony.CellSignalStrengthCdma.mEvdoDbm.set(strengthCdma, -64);\n            mirror.android.telephony.CellSignalStrengthCdma.mEvdoSnr.set(strengthCdma, 7);\n            return cdma;\n        } else { // GSM\n            CellInfoGsm gsm = mirror.android.telephony.CellInfoGsm.ctor.newInstance();\n            CellIdentityGsm identityGsm = mirror.android.telephony.CellInfoGsm.mCellIdentityGsm.get(gsm);\n            CellSignalStrengthGsm strengthGsm = mirror.android.telephony.CellInfoGsm.mCellSignalStrengthGsm.get(gsm);\n            mirror.android.telephony.CellIdentityGsm.mMcc.set(identityGsm, cell.mcc);\n            mirror.android.telephony.CellIdentityGsm.mMnc.set(identityGsm, cell.mnc);\n            mirror.android.telephony.CellIdentityGsm.mLac.set(identityGsm, cell.lac);\n            mirror.android.telephony.CellIdentityGsm.mCid.set(identityGsm, cell.cid);\n            mirror.android.telephony.CellSignalStrengthGsm.mSignalStrength.set(strengthGsm, 20);\n            mirror.android.telephony.CellSignalStrengthGsm.mBitErrorRate.set(strengthGsm, 0);\n            return gsm;\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/telephony/TelephonyRegistryStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.telephony;\n\nimport android.telephony.PhoneStateListener;\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 java.lang.reflect.Method;\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\t\t@Override\n\t\t\tpublic boolean beforeCall(Object who, Method method, Object... args) {\n\t\t\t\tif (android.os.Build.VERSION.SDK_INT >= 17) {\n\t\t\t\t\tif (isFakeLocationEnable()) {\n\t\t\t\t\t\tfor (int i = args.length - 1; i > 0; i--) {\n\t\t\t\t\t\t\tif (args[i] instanceof Integer) {\n\t\t\t\t\t\t\t\tint events = (Integer) args[i];\n\t\t\t\t\t\t\t\tevents ^= PhoneStateListener.LISTEN_CELL_INFO;\n\t\t\t\t\t\t\t\tevents ^= PhoneStateListener.LISTEN_CELL_LOCATION;\n\t\t\t\t\t\t\t\targs[i] = events;\n\t\t\t\t\t\t\t\tbreak;\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\treturn super.beforeCall(who, method, args);\n\t\t\t}\n\t\t});\n\t}\n}\n"
  },
  {
    "path": "VirtualApp/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(\"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": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/usage/UsageStatsManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.usage;\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.ReplaceLastPkgMethodProxy;\n\nimport mirror.android.app.IUsageStatsManager;\n\n/**\n * Created by caokai on 2017/9/8.\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)\npublic class UsageStatsManagerStub extends BinderInvocationProxy {\n\n    public UsageStatsManagerStub() {\n        super(IUsageStatsManager.Stub.asInterface, Context.USAGE_STATS_SERVICE);\n    }\n    @Override\n    protected void onBindMethods() {\n        super.onBindMethods();\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"queryUsageStats\"));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"queryConfigurations\"));\n        addMethodProxy(new ReplaceLastPkgMethodProxy(\"queryEvents\"));\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/client/hook/proxies/view/AutoFillManagerStub.java",
    "content": "package com.lody.virtual.client.hook.proxies.view;\n\nimport android.annotation.SuppressLint;\nimport android.util.Log;\n\nimport com.lody.virtual.client.hook.base.BinderInvocationProxy;\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.client.hook.utils.MethodParameterUtils;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\nimport mirror.android.view.IAutoFillManager;\n\n/**\n * @author 陈磊.\n */\n\npublic class AutoFillManagerStub extends BinderInvocationProxy {\n\n    private static final String TAG = \"AutoFillManagerStub\";\n\n    private static final String AUTO_FILL_NAME = \"autofill\";\n    public AutoFillManagerStub() {\n        super(IAutoFillManager.Stub.asInterface, AUTO_FILL_NAME);\n    }\n\n    @SuppressLint(\"WrongConstant\")\n    @Override\n    public void inject() throws Throwable {\n        super.inject();\n        try {\n            Object AutoFillManagerInstance = getContext().getSystemService(AUTO_FILL_NAME);\n            if (AutoFillManagerInstance == null) {\n                throw new NullPointerException(\"AutoFillManagerInstance is null.\");\n            }\n            Object AutoFillManagerProxy = getInvocationStub().getProxyInterface();\n            if (AutoFillManagerProxy == null) {\n                throw new NullPointerException(\"AutoFillManagerProxy is null.\");\n            }\n            Field AutoFillManagerServiceField = AutoFillManagerInstance.getClass().getDeclaredField(\"mService\");\n            AutoFillManagerServiceField.setAccessible(true);\n            AutoFillManagerServiceField.set(AutoFillManagerInstance, AutoFillManagerProxy);\n        } catch (Throwable tr) {\n            Log.e(TAG, \"AutoFillManagerStub inject error.\", tr);\n            return;\n        }\n        addMethodProxy(new MethodProxy() {\n            @Override\n            public String getMethodName() {\n                return \"startSession\";\n            }\n            @Override\n            public boolean beforeCall(Object who, Method method, Object... args) {\n                MethodParameterUtils.replaceLastAppPkg(args);\n                return super.beforeCall(who, method, args);\n            }\n        });\n        addMethodProxy(new MethodProxy() {\n            @Override\n            public String getMethodName() {\n                return \"updateOrRestartSession\";\n            }\n            @Override\n            public boolean beforeCall(Object who, Method method, Object... args) {\n                MethodParameterUtils.replaceLastAppPkg(args);\n                return super.beforeCall(who, method, args);\n            }\n        });\n        addMethodProxy(new MethodProxy() {\n            @Override\n            public String getMethodName() {\n                return \"isServiceEnabled\";\n            }\n            @Override\n            public boolean beforeCall(Object who, Method method, Object... args) {\n                MethodParameterUtils.replaceLastAppPkg(args);\n                return super.beforeCall(who, method, args);\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/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.DhcpInfo;\nimport android.net.wifi.ScanResult;\nimport android.net.wifi.SupplicantState;\nimport android.net.wifi.WifiInfo;\nimport android.net.wifi.WifiManager;\nimport android.os.Build;\nimport android.os.Parcel;\nimport android.os.Parcelable;\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.client.ipc.VirtualLocationManager;\nimport com.lody.virtual.client.stub.VASettings;\nimport com.lody.virtual.helper.utils.ArrayUtils;\nimport com.lody.virtual.helper.utils.Reflect;\nimport com.lody.virtual.helper.utils.marks.FakeDeviceMark;\nimport com.lody.virtual.helper.utils.marks.FakeLocMark;\nimport com.lody.virtual.remote.vloc.VWifi;\n\nimport java.lang.reflect.Method;\nimport java.net.InetAddress;\nimport java.net.NetworkInterface;\nimport java.net.SocketException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\nimport mirror.android.net.wifi.IWifiManager;\nimport mirror.android.net.wifi.WifiSsid;\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 MethodProxy() {\n            @Override\n            public String getMethodName() {\n                return \"isWifiEnabled\";\n            }\n\n            @Override\n            public Object call(Object who, Method method, Object... args) throws Throwable {\n                if (VASettings.Wifi.FAKE_WIFI_STATE) {\n                    return true;\n                }\n                return super.call(who, method, args);\n            }\n        });\n        addMethodProxy(new MethodProxy() {\n            @Override\n            public String getMethodName() {\n                return \"getWifiEnabledState\";\n            }\n\n            @Override\n            public Object call(Object who, Method method, Object... args) throws Throwable {\n                if (VASettings.Wifi.FAKE_WIFI_STATE) {\n                    return WifiManager.WIFI_STATE_ENABLED;\n                }\n                return super.call(who, method, args);\n            }\n        });\n        addMethodProxy(new MethodProxy() {\n            @Override\n            public String getMethodName() {\n                return \"createDhcpInfo\";\n            }\n\n            @Override\n            public Object call(Object who, Method method, Object... args) throws Throwable {\n                if (VASettings.Wifi.FAKE_WIFI_STATE) {\n                    IPInfo ipInfo = getIPInfo();\n                    if (ipInfo != null) {\n                        return createDhcpInfo(ipInfo);\n                    }\n                }\n                return super.call(who, method, args);\n            }\n        });\n        addMethodProxy(new GetConnectionInfo());\n        addMethodProxy(new 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    @FakeLocMark(\"Fake wifi bssid\")\n    @FakeDeviceMark(\"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 (isFakeLocationEnable()) {\n                mirror.android.net.wifi.WifiInfo.mBSSID.set(wifiInfo, \"00:00:00:00:00:00\");\n                mirror.android.net.wifi.WifiInfo.mMacAddress.set(wifiInfo, \"00:00:00:00:00:00\");\n            }\n            if (VASettings.Wifi.FAKE_WIFI_STATE) {\n                return createWifiInfo();\n            }\n            if (wifiInfo != null) {\n                mirror.android.net.wifi.WifiInfo.mMacAddress.set(wifiInfo, getDeviceInfo().wifiMac);\n            }\n            return wifiInfo;\n        }\n    }\n\n    @FakeLocMark(\"fake scan result\")\n    private final class GetScanResults extends ReplaceCallingPkgMethodProxy {\n\n        public GetScanResults() {\n            super(\"getScanResults\");\n        }\n\n        @Override\n        public Object call(Object who, Method method, Object... args) throws Throwable {\n//            noinspection unchecked\n            if (isFakeLocationEnable()) {\n                new ArrayList<ScanResult>(0);\n            }\n            return super.call(who, method, args);\n        }\n    }\n\n    private static ScanResult cloneScanResult(Parcelable scanResult) {\n        Parcel p = Parcel.obtain();\n        scanResult.writeToParcel(p, 0);\n        p.setDataPosition(0);\n        ScanResult newScanResult = Reflect.on(scanResult).field(\"CREATOR\").call(\"createFromParcel\", p).get();\n        p.recycle();\n        return newScanResult;\n    }\n\n    public static class IPInfo {\n        NetworkInterface intf;\n        InetAddress addr;\n        String ip;\n        int ip_hex;\n        int netmask_hex;\n    }\n\n\n    private static IPInfo getIPInfo() {\n        try {\n            List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());\n            for (NetworkInterface intf : interfaces) {\n                List<InetAddress> addrs = Collections.list(intf.getInetAddresses());\n                for (InetAddress addr : addrs) {\n                    if (!addr.isLoopbackAddress()) {\n                        String sAddr = addr.getHostAddress().toUpperCase();\n                        boolean isIPv4 = isIPv4Address(sAddr);\n                        if (isIPv4) {\n                            IPInfo info = new IPInfo();\n                            info.addr = addr;\n                            info.intf = intf;\n                            info.ip = sAddr;\n                            info.ip_hex = InetAddress_to_hex(addr);\n                            info.netmask_hex = netmask_to_hex(intf.getInterfaceAddresses().get(0).getNetworkPrefixLength());\n                            return info;\n                        }\n                    }\n                }\n            }\n        } catch (SocketException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    private static boolean isIPv4Address(String input) {\n        Pattern IPV4_PATTERN = Pattern.compile(\"^(25[0-5]|2[0-4]\\\\d|[0-1]?\\\\d?\\\\d)(\\\\.(25[0-5]|2[0-4]\\\\d|[0-1]?\\\\d?\\\\d)){3}$\");\n        return IPV4_PATTERN.matcher(input).matches();\n    }\n\n    private static int netmask_to_hex(int netmask_slash) {\n        int r = 0;\n        int b = 1;\n        for (int i = 0; i < netmask_slash; i++, b = b << 1)\n            r |= b;\n        return r;\n    }\n\n    private static int InetAddress_to_hex(InetAddress a) {\n        int result = 0;\n        byte b[] = a.getAddress();\n        for (int i = 0; i < 4; i++)\n            result |= (b[i] & 0xff) << (8 * i);\n        return result;\n    }\n\n    private DhcpInfo createDhcpInfo(IPInfo ip) {\n        DhcpInfo i = new DhcpInfo();\n        i.ipAddress = ip.ip_hex;\n        i.netmask = ip.netmask_hex;\n        i.dns1 = 0x04040404;\n        i.dns2 = 0x08080808;\n        return i;\n    }\n\n    private static WifiInfo createWifiInfo() throws Exception {\n        WifiInfo info = mirror.android.net.wifi.WifiInfo.ctor.newInstance();\n        IPInfo ip = getIPInfo();\n        InetAddress address = (ip != null ? ip.addr : null);\n        mirror.android.net.wifi.WifiInfo.mNetworkId.set(info, 1);\n        mirror.android.net.wifi.WifiInfo.mSupplicantState.set(info, SupplicantState.COMPLETED);\n        mirror.android.net.wifi.WifiInfo.mBSSID.set(info, VASettings.Wifi.BSSID);\n        mirror.android.net.wifi.WifiInfo.mMacAddress.set(info, VASettings.Wifi.MAC);\n        mirror.android.net.wifi.WifiInfo.mIpAddress.set(info, address);\n        mirror.android.net.wifi.WifiInfo.mLinkSpeed.set(info, 65);\n        if (Build.VERSION.SDK_INT >= 21) {\n            mirror.android.net.wifi.WifiInfo.mFrequency.set(info, 5000); // MHz\n        }\n        mirror.android.net.wifi.WifiInfo.mRssi.set(info, 200); // MAX_RSSI\n        if (mirror.android.net.wifi.WifiInfo.mWifiSsid != null) {\n            mirror.android.net.wifi.WifiInfo.mWifiSsid.set(info, WifiSsid.createFromAsciiEncoded.call(VASettings.Wifi.SSID));\n        } else {\n            mirror.android.net.wifi.WifiInfo.mSSID.set(info, VASettings.Wifi.SSID);\n        }\n        return info;\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.content.Context;\nimport android.content.ServiceConnection;\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.os.RemoteException;\nimport android.util.Log;\n\nimport com.lody.virtual.client.VClientImpl;\nimport com.lody.virtual.client.core.VirtualCore;\nimport com.lody.virtual.helper.collection.ArrayMap;\nimport com.lody.virtual.server.IBinderDelegateService;\n\nimport mirror.android.app.ActivityThread;\nimport mirror.android.app.ContextImpl;\nimport mirror.android.app.IServiceConnectionO;\nimport mirror.android.app.LoadedApk;\n\n/**\n * @author Lody\n */\n\npublic class ServiceConnectionDelegate extends IServiceConnection.Stub {\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 IServiceConnection getDelegate(Context context, ServiceConnection connection,int flags) {\n        IServiceConnection sd = null;\n        if (connection == null) {\n            throw new IllegalArgumentException(\"connection is null\");\n        }\n        try {\n            Object activityThread = ActivityThread.currentActivityThread.call();\n            Object loadApk = ContextImpl.mPackageInfo.get(VirtualCore.get().getContext());\n            Handler handler = ActivityThread.getHandler.call(activityThread);\n            sd = LoadedApk.getServiceDispatcher.call(loadApk, connection, context, handler, flags);\n        } catch (Exception e) {\n            Log.e(\"ConnectionDelegate\", \"getServiceDispatcher\", e);\n        }\n        if (sd == null) {\n            throw new RuntimeException(\"Not supported in system context\");\n        }\n        return getDelegate(sd);\n    }\n\n    public static IServiceConnection removeDelegate(Context context, ServiceConnection conn) {\n        IServiceConnection connection = null;\n        try{\n            Object loadApk = ContextImpl.mPackageInfo.get(VirtualCore.get().getContext());\n            connection = LoadedApk.forgetServiceDispatcher.call(loadApk, context, conn);\n        }catch (Exception e){\n            Log.e(\"ConnectionDelegate\", \"forgetServiceDispatcher\", e);\n        }\n        if(connection == null){\n            return null;\n        }\n        return ServiceConnectionDelegate.removeDelegate(connection);\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        connected(name, service, false);\n    }\n\n    public void connected(ComponentName name, IBinder service, boolean dead) 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\n        if(Build.VERSION.SDK_INT>=26) {\n            IServiceConnectionO.connected.call(mConn, name, service, dead);\n        }else {\n            mConn.connected(name, service);\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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 <T> T getFirstParam(Object[] args, Class<T> tClass) {\n\t\tif (args == null) {\n\t\t\treturn null;\n\t\t}\n\t\tint index = ArrayUtils.indexOfFirst(args, tClass);\n\t\tif (index != -1) {\n\t\t\treturn (T) args[index];\n\t\t}\n\t\treturn null;\n\t}\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\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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    public static final String VIRTUAL_LOC = \"virtual-loc\";\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    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    public static IBinder getService(String name) {\n        if (VirtualCore.get().isServerProcess()) {\n            return ServiceCache.getService(name);\n        }\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": "VirtualApp/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.env.VirtualRuntime;\nimport com.lody.virtual.client.stub.AmsTask;\nimport com.lody.virtual.helper.ipcbus.IPCSingleton;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.server.interfaces.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 IPCSingleton<IAccountManager> singleton = new IPCSingleton<>(IAccountManager.class);\n\n    public static VAccountManager get() {\n        return sMgr;\n    }\n\n    public IAccountManager getService() {\n        return singleton.get();\n    }\n\n    public AuthenticatorDescription[] getAuthenticatorTypes() {\n        try {\n            return getService().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            getService().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            getService().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 getService().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 getService().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 getService().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 getService().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 getService().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            getService().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 getService().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            getService().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            getService().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            getService().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            getService().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            getService().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            getService().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 getService().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            getService().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            getService().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            getService().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            getService().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            getService().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            getService().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            getService().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 getService().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 getService().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    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": "VirtualApp/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.Context;\nimport android.content.Intent;\nimport android.content.ServiceConnection;\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.client.hook.secondary.ServiceConnectionDelegate;\nimport com.lody.virtual.helper.compat.ActivityManagerCompat;\nimport com.lody.virtual.helper.ipcbus.IPCSingleton;\nimport com.lody.virtual.helper.utils.ComponentUtils;\nimport com.lody.virtual.os.VUserHandle;\nimport com.lody.virtual.remote.AppTaskInfo;\nimport com.lody.virtual.remote.BadgerInfo;\nimport com.lody.virtual.remote.PendingIntentData;\nimport com.lody.virtual.remote.PendingResultData;\nimport com.lody.virtual.remote.VParceledListSlice;\nimport com.lody.virtual.server.interfaces.IActivityManager;\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 IPCSingleton<IActivityManager> singleton = new IPCSingleton<>(IActivityManager.class);\n\n    public static VActivityManager get() {\n        return sAM;\n    }\n\n    public IActivityManager getService() {\n        return singleton.get();\n    }\n\n\n    public int startActivity(Intent intent, ActivityInfo info, IBinder resultTo, Bundle options, String resultWho, int requestCode, int userId) {\n        try {\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            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(Context context, Intent service, ServiceConnection connection, int flags) {\n        try {\n            IServiceConnection conn = ServiceConnectionDelegate.getDelegate(context, connection, flags);\n            return getService().bindService(null, null, service, null, conn, flags, 0);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public boolean unbindService(Context context, ServiceConnection connection) {\n        try {\n            IServiceConnection conn = ServiceConnectionDelegate.removeDelegate(context, connection);\n            return getService().unbindService(conn, VUserHandle.myUserId());\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\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 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 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    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    public void notifyBadgerChange(BadgerInfo info) {\n        try {\n            getService().notifyBadgerChange(info);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VDeviceManager.java",
    "content": "package com.lody.virtual.client.ipc;\n\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.helper.ipcbus.IPCSingleton;\nimport com.lody.virtual.remote.VDeviceInfo;\nimport com.lody.virtual.server.interfaces.IDeviceInfoManager;\n\n/**\n * @author Lody\n */\n\npublic class VDeviceManager {\n\n    private static final VDeviceManager sInstance = new VDeviceManager();\n    private IPCSingleton<IDeviceInfoManager> singleton = new IPCSingleton<>(IDeviceInfoManager.class);\n\n\n    public static VDeviceManager get() {\n        return sInstance;\n    }\n\n\n    public IDeviceInfoManager getService() {\n        return singleton.get();\n    }\n\n    public VDeviceInfo getDeviceInfo(int userId) {\n        try {\n            return getService().getDeviceInfo(userId);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n}"
  },
  {
    "path": "VirtualApp/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.Parcelable;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.helper.ipcbus.IPCSingleton;\nimport com.lody.virtual.server.interfaces.IJobService;\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 IPCSingleton<IJobService> singleton = new IPCSingleton<>(IJobService.class);\n\n    public static VJobScheduler get() {\n        return sInstance;\n    }\n\n    public IJobService getService() {\n        return singleton.get();\n    }\n\n    public int schedule(JobInfo job) {\n        try {\n            return getService().schedule(job);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<JobInfo> getAllPendingJobs() {\n        try {\n            return getService().getAllPendingJobs();\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public void cancelAll() {\n        try {\n            getService().cancelAll();\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void cancel(int jobId) {\n        try {\n            getService().cancel(jobId);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n\n    public JobInfo getPendingJob(int jobId) {\n        try {\n            return getService().getPendingJob(jobId);\n        } catch (RemoteException e) {\n            return (JobInfo) VirtualRuntime.crash(e);\n        }\n    }\n\n\n    public int enqueue(JobInfo job, Object workItem) {\n        if (workItem == null) {\n            return -1;\n        }\n        try {\n            return getService().enqueue(job, (Parcelable) workItem);\n        } catch (RemoteException e) {\n            return (Integer) VirtualRuntime.crash(e);\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VNotificationManager.java",
    "content": "package com.lody.virtual.client.ipc;\r\n\r\nimport android.app.Notification;\r\nimport android.os.RemoteException;\r\n\r\nimport com.lody.virtual.client.core.VirtualCore;\r\nimport com.lody.virtual.helper.ipcbus.IPCSingleton;\r\nimport com.lody.virtual.server.interfaces.INotificationManager;\r\nimport com.lody.virtual.server.notification.NotificationCompat;\r\n\r\n/**\r\n * Fake notification manager\r\n */\r\npublic class VNotificationManager {\r\n    private static final VNotificationManager sInstance = new VNotificationManager();\r\n    private final NotificationCompat mNotificationCompat;\r\n    private IPCSingleton<INotificationManager> singleton = new IPCSingleton<>(INotificationManager.class);\r\n\r\n    private VNotificationManager() {\r\n        mNotificationCompat = NotificationCompat.create();\r\n    }\r\n\r\n    public static VNotificationManager get() {\r\n        return sInstance;\r\n    }\r\n\r\n    public INotificationManager getService() {\r\n        return singleton.get();\r\n    }\r\n\r\n    public boolean dealNotification(int id, Notification notification, String packageName) {\r\n        if (notification == null) return false;\r\n        return VirtualCore.get().getHostPkg().equals(packageName)\r\n                || mNotificationCompat.dealNotification(id, notification, packageName);\r\n    }\r\n\r\n    public int dealNotificationId(int id, String packageName, String tag, int userId) {\r\n        try {\r\n            return getService().dealNotificationId(id, packageName, tag, userId);\r\n        } catch (RemoteException e) {\r\n            e.printStackTrace();\r\n        }\r\n        return id;\r\n    }\r\n\r\n    public String dealNotificationTag(int id, String packageName, String tag, int userId) {\r\n        try {\r\n            return getService().dealNotificationTag(id, packageName, tag, userId);\r\n        } catch (RemoteException e) {\r\n            e.printStackTrace();\r\n        }\r\n        return tag;\r\n    }\r\n\r\n    public boolean areNotificationsEnabledForPackage(String packageName, int userId) {\r\n        try {\r\n            return getService().areNotificationsEnabledForPackage(packageName, userId);\r\n        } catch (RemoteException e) {\r\n            e.printStackTrace();\r\n            return true;\r\n        }\r\n    }\r\n\r\n    public void setNotificationsEnabledForPackage(String packageName, boolean enable, int userId) {\r\n        try {\r\n            getService().setNotificationsEnabledForPackage(packageName, enable, userId);\r\n        } catch (RemoteException e) {\r\n            e.printStackTrace();\r\n        }\r\n    }\r\n\r\n    public void addNotification(int id, String tag, String packageName, int userId) {\r\n        try {\r\n            getService().addNotification(id, tag, packageName, userId);\r\n        } catch (RemoteException e) {\r\n            e.printStackTrace();\r\n        }\r\n    }\r\n\r\n    public void cancelAllNotification(String packageName, int userId) {\r\n        try {\r\n            getService().cancelAllNotification(packageName, userId);\r\n        } catch (RemoteException e) {\r\n            e.printStackTrace();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "VirtualApp/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.RemoteException;\n\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.helper.ipcbus.IPCSingleton;\nimport com.lody.virtual.server.IPackageInstaller;\nimport com.lody.virtual.server.interfaces.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 IPCSingleton<IPackageManager> singleton = new IPCSingleton<>(IPackageManager.class);\n\n    public static VPackageManager get() {\n        return sMgr;\n    }\n\n    public IPackageManager getService() {\n        return singleton.get();\n    }\n\n    public int checkPermission(String permName, String pkgName, int userId) {\n        try {\n            return getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().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 getService().querySharedPackages(packageName);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public String[] getPackagesForUid(int uid) {\n        try {\n            return getService().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 getService().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 getService().getNameForUid(uid);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n\n    public IPackageInstaller getPackageInstaller() {\n        try {\n            return IPackageInstaller.Stub.asInterface(getService().getPackageInstaller());\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VirtualLocationManager.java",
    "content": "package com.lody.virtual.client.ipc;\n\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.client.hook.base.MethodProxy;\nimport com.lody.virtual.helper.ipcbus.IPCSingleton;\nimport com.lody.virtual.remote.vloc.VCell;\nimport com.lody.virtual.remote.vloc.VLocation;\nimport com.lody.virtual.server.interfaces.IVirtualLocationManager;\n\nimport java.util.List;\n\n/**\n * @author Lody\n */\n\npublic class VirtualLocationManager {\n\n    private static final VirtualLocationManager sInstance = new VirtualLocationManager();\n    private IPCSingleton<IVirtualLocationManager> singleton = new IPCSingleton<>(IVirtualLocationManager.class);\n\n    public static final int MODE_CLOSE = 0;\n    public static final int MODE_USE_GLOBAL = 1;\n    public static final int MODE_USE_SELF = 2;\n\n\n    public static VirtualLocationManager get() {\n        return sInstance;\n    }\n\n\n    public IVirtualLocationManager getService() {\n        return singleton.get();\n    }\n\n\n    public int getMode(int userId, String pkg) {\n        try {\n            return getService().getMode(userId, pkg);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public int getMode() {\n        return getMode(MethodProxy.getAppUserId(), MethodProxy.getAppPkg());\n    }\n\n    public void setMode(int userId, String pkg, int mode) {\n        try {\n            getService().setMode(userId, pkg, mode);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public void setCell(int userId, String pkg, VCell cell) {\n        try {\n            getService().setCell(userId, pkg, cell);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public void setAllCell(int userId, String pkg, List<VCell> cell) {\n        try {\n            getService().setAllCell(userId, pkg, cell);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public void setNeighboringCell(int userId, String pkg, List<VCell> cell) {\n        try {\n            getService().setNeighboringCell(userId, pkg, cell);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public VCell getCell(int userId, String pkg) {\n        try {\n            return getService().getCell(userId, pkg);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<VCell> getAllCell(int userId, String pkg) {\n        try {\n            return getService().getAllCell(userId, pkg);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public List<VCell> getNeighboringCell(int userId, String pkg) {\n        try {\n            return getService().getNeighboringCell(userId, pkg);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n\n    public void setGlobalCell(VCell cell) {\n        try {\n            getService().setGlobalCell(cell);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public void setGlobalAllCell(List<VCell> cell) {\n        try {\n            getService().setGlobalAllCell(cell);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public void setGlobalNeighboringCell(List<VCell> cell) {\n        try {\n            getService().setGlobalNeighboringCell(cell);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public void setLocation(int userId, String pkg, VLocation loc) {\n        try {\n            getService().setLocation(userId, pkg, loc);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public VLocation getLocation(int userId, String pkg) {\n        try {\n            return getService().getLocation(userId, pkg);\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n\n    public VLocation getLocation() {\n        return getLocation(MethodProxy.getAppUserId(), MethodProxy.getAppPkg());\n    }\n\n    public void setGlobalLocation(VLocation loc) {\n        try {\n            getService().setGlobalLocation(loc);\n        } catch (RemoteException e) {\n            VirtualRuntime.crash(e);\n        }\n    }\n\n    public VLocation getGlobalLocation() {\n        try {\n            return getService().getGlobalLocation();\n        } catch (RemoteException e) {\n            return VirtualRuntime.crash(e);\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/client/ipc/VirtualStorageManager.java",
    "content": "package com.lody.virtual.client.ipc;\n\n\nimport android.os.RemoteException;\n\nimport com.lody.virtual.client.env.VirtualRuntime;\nimport com.lody.virtual.helper.ipcbus.IPCSingleton;\nimport com.lody.virtual.server.interfaces.IVirtualStorageService;\n\n/**\n * @author Lody\n */\n\npublic class VirtualStorageManager {\n\n    private static final VirtualStorageManager sInstance = new VirtualStorageManager();\n    private IPCSingleton<IVirtualStorageService> singleton = new IPCSingleton<>(IVirtualStorageService.class);\n\n\n    public static VirtualStorageManager get() {\n        return sInstance;\n    }\n\n\n    public IVirtualStorageService getRemote() {\n        return singleton.get();\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.VClientImpl;\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\tintent.setExtrasClassLoader(VClientImpl.get().getCurrentApplication().getClassLoader());\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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;\nimport android.util.Log;\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        Log.d(\"Q_M\", \"StubJob-->onCreate\");\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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 final String ACTION_BADGER_CHANGE = \"com.lody.virtual.BADGER_CHANGE\";\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     * 是否禁止插件应用直接调用返回桌面的 intent\n     * <p>\n     * Intent home = new Intent(Intent.ACTION_MAIN);\n     * home.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);\n     * home.addCategory(Intent.CATEGORY_HOME);\n     * startActivity(home);\n     */\n    public static boolean INTERCEPT_BACK_HOME = true;\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    public static class Wifi {\n        public static boolean FAKE_WIFI_STATE = false;\n        public static String DEFAULT_BSSID = \"66:55:44:33:22:11\";\n        public static String DEFAULT_MAC = \"11:22:33:44:55:66\";\n        public static String DEFAULT_SSID = \"VirtualApp\";\n\n        public static String BSSID = DEFAULT_BSSID;\n        public static String MAC = DEFAULT_MAC;\n        public static String SSID = DEFAULT_SSID;\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/helper/ArtDexOptimizer.java",
    "content": "package com.lody.virtual.helper;\n\nimport android.os.Build;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.Executors;\n\nimport mirror.dalvik.system.VMRuntime;\n\n/**\n * @author Lody\n */\npublic class ArtDexOptimizer {\n\n    /**\n     * Optimize the dex in interpret mode.\n     *\n     * @param dexFilePath dex file path\n     * @param oatFilePath oat file path\n     * @throws IOException\n     */\n    public static void interpretDex2Oat(String dexFilePath, String oatFilePath) throws IOException {\n        final File oatFile = new File(oatFilePath);\n        if (!oatFile.exists()) {\n            oatFile.getParentFile().mkdirs();\n        }\n\n        final List<String> commandAndParams = new ArrayList<>();\n        commandAndParams.add(\"dex2oat\");\n        // for 7.1.1, duplicate class fix\n        if (Build.VERSION.SDK_INT >= 24) {\n            commandAndParams.add(\"--runtime-arg\");\n            commandAndParams.add(\"-classpath\");\n            commandAndParams.add(\"--runtime-arg\");\n            commandAndParams.add(\"&\");\n        }\n        commandAndParams.add(\"--dex-file=\" + dexFilePath);\n        commandAndParams.add(\"--oat-file=\" + oatFilePath);\n        commandAndParams.add(\"--instruction-set=\" + VMRuntime.getCurrentInstructionSet.call());\n        if (Build.VERSION.SDK_INT > 25) {\n            commandAndParams.add(\"--compiler-filter=quicken\");\n        } else {\n            commandAndParams.add(\"--compiler-filter=interpret-only\");\n        }\n\n        final ProcessBuilder pb = new ProcessBuilder(commandAndParams);\n        pb.redirectErrorStream(true);\n        final Process dex2oatProcess = pb.start();\n        StreamConsumer.consumeInputStream(dex2oatProcess.getInputStream());\n        StreamConsumer.consumeInputStream(dex2oatProcess.getErrorStream());\n        try {\n            final int ret = dex2oatProcess.waitFor();\n            if (ret != 0) {\n                throw new IOException(\"dex2oat works unsuccessfully, exit code: \" + ret);\n            }\n        } catch (InterruptedException e) {\n            throw new IOException(\"dex2oat is interrupted, msg: \" + e.getMessage(), e);\n        }\n    }\n\n    private static class StreamConsumer {\n        static final Executor STREAM_CONSUMER = Executors.newSingleThreadExecutor();\n\n        static void consumeInputStream(final InputStream is) {\n            STREAM_CONSUMER.execute(new Runnable() {\n                @Override\n                public void run() {\n                    if (is == null) {\n                        return;\n                    }\n                    final byte[] buffer = new byte[256];\n                    try {\n                        while ((is.read(buffer)) > 0) {\n                            // To satisfy checkstyle rules.\n                        }\n                    } catch (IOException ignored) {\n                        // Ignored.\n                    } finally {\n                        try {\n                            is.close();\n                        } catch (Exception ignored) {\n                            // Ignored.\n                        }\n                    }\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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\n    public abstract int getCurrentVersion();\n\n    public void writeMagic(Parcel p) {\n    }\n\n    public boolean verifyMagic(Parcel p) {\n        return true;\n    }\n\n    public abstract void writePersistenceData(Parcel p);\n\n    public abstract void readPersistenceData(Parcel p);\n\n    public boolean onVersionConflict(int fileVersion, int currentVersion) {\n        return false;\n    }\n\n    public void onPersistenceFileDamage() {\n    }\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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;\nimport android.os.Parcel;\n\nimport mirror.android.os.BaseBundle;\nimport mirror.android.os.BundleICS;\n\n/**\n * @author Lody\n */\npublic class BundleCompat {\n    public static IBinder getBinder(Bundle bundle, String key) {\n        if (Build.VERSION.SDK_INT >= 18) {\n            return bundle.getBinder(key);\n        } else {\n            return mirror.android.os.Bundle.getIBinder.call(bundle, key);\n        }\n    }\n\n    public static void putBinder(Bundle bundle, String key, IBinder value) {\n        if (Build.VERSION.SDK_INT >= 18) {\n            bundle.putBinder(key, value);\n        } else {\n            mirror.android.os.Bundle.putIBinder.call(bundle, key, value);\n        }\n    }\n\n    public static void clearParcelledData(Bundle bundle) {\n        Parcel obtain = Parcel.obtain();\n        obtain.writeInt(0);\n        obtain.setDataPosition(0);\n        Parcel parcel;\n        if (BaseBundle.TYPE != null) {\n            parcel = BaseBundle.mParcelledData.get(bundle);\n            if (parcel != null) {\n                parcel.recycle();\n            }\n            BaseBundle.mParcelledData.set(bundle, obtain);\n        } else if (BundleICS.TYPE != null) {\n            parcel = BundleICS.mParcelledData.get(bundle);\n            if (parcel != null) {\n                parcel.recycle();\n            }\n            BundleICS.mParcelledData.set(bundle, obtain);\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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 java.util.ArrayList;\nimport java.util.List;\n\nimport mirror.android.app.IApplicationThread;\nimport mirror.android.app.IApplicationThreadICSMR1;\nimport mirror.android.app.IApplicationThreadKitkat;\nimport mirror.android.app.IApplicationThreadOreo;\nimport mirror.android.app.ServiceStartArgs;\nimport mirror.android.content.res.CompatibilityInfo;\n\n/**\n * @author Lody\n */\n\npublic class IApplicationThreadCompat {\n\n    public static void scheduleCreateService(IInterface appThread, IBinder token, ServiceInfo info,\n                                             int processState) throws RemoteException {\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            IApplicationThreadKitkat.scheduleCreateService.call(appThread, token, info, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO.get(),\n                    processState);\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {\n            IApplicationThreadICSMR1.scheduleCreateService.call(appThread, token, info, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO.get());\n        } else {\n            IApplicationThread.scheduleCreateService.call(appThread, token, info);\n        }\n\n    }\n\n    public static void scheduleBindService(IInterface appThread, IBinder token, Intent intent, boolean rebind,\n                                           int processState) throws RemoteException {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            IApplicationThreadKitkat.scheduleBindService.call(appThread, token, intent, rebind, processState);\n        } else {\n            IApplicationThread.scheduleBindService.call(appThread, token, intent, rebind);\n        }\n    }\n\n    public static void scheduleUnbindService(IInterface appThread, IBinder token, Intent intent) throws RemoteException {\n        IApplicationThread.scheduleUnbindService.call(appThread, token, intent);\n    }\n\n    public static void scheduleServiceArgs(IInterface appThread, IBinder token, boolean taskRemoved,\n                                           int startId, int flags, Intent args) throws RemoteException {\n\n        if (Build.VERSION.SDK_INT >= 26) {\n            List<Object> list = new ArrayList<>(1);\n            Object serviceStartArg = ServiceStartArgs.ctor.newInstance(taskRemoved, startId, flags, args);\n            list.add(serviceStartArg);\n            IApplicationThreadOreo.scheduleServiceArgs.call(appThread, token, ParceledListSliceCompat.create(list));\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {\n            IApplicationThreadICSMR1.scheduleServiceArgs.call(appThread, token, taskRemoved, startId, flags, args);\n        } else {\n            IApplicationThread.scheduleServiceArgs.call(appThread, token, startId, flags, args);\n        }\n    }\n\n\n    public static void scheduleStopService(IInterface appThread, IBinder token) throws RemoteException {\n        IApplicationThread.scheduleStopService.call(appThread, token);\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCBus.java",
    "content": "package com.lody.virtual.helper.ipcbus;\n\n\nimport android.os.IBinder;\n\nimport java.lang.reflect.Proxy;\n\n/**\n * @author Lody\n */\npublic class IPCBus {\n\n    private static IServerCache sCache;\n\n    public static void initialize(IServerCache cache) {\n        sCache = cache;\n    }\n\n    private static void checkInitialized() {\n        if (sCache == null) {\n            throw new IllegalStateException(\"please call initialize() at first.\");\n        }\n    }\n\n    public static void register(Class<?> interfaceClass, Object server) {\n        checkInitialized();\n        ServerInterface serverInterface = new ServerInterface(interfaceClass);\n        TransformBinder binder = new TransformBinder(serverInterface, server);\n        sCache.join(serverInterface.getInterfaceName(), binder);\n    }\n\n    public static <T> T get(Class<?> interfaceClass) {\n        checkInitialized();\n        ServerInterface serverInterface = new ServerInterface(interfaceClass);\n        IBinder binder = sCache.query(serverInterface.getInterfaceName());\n        if (binder == null) {\n            return null;\n        }\n        //noinspection unchecked\n        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new IPCInvocationBridge(serverInterface, binder));\n    }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCInvocationBridge.java",
    "content": "package com.lody.virtual.helper.ipcbus;\n\nimport android.os.IBinder;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\n\n/**\n * @author Lody\n */\npublic class IPCInvocationBridge implements InvocationHandler {\n\n    private ServerInterface serverInterface;\n    private IBinder binder;\n\n    public IPCInvocationBridge(ServerInterface serverInterface, IBinder binder) {\n        this.serverInterface = serverInterface;\n        this.binder = binder;\n    }\n\n    @Override\n    public Object invoke(Object o, Method method, Object[] args) throws Throwable {\n        IPCMethod ipcMethod = serverInterface.getIPCMethod(method);\n        if (ipcMethod == null) {\n            throw new IllegalStateException(\"Can not found the ipc method : \" + method.getDeclaringClass().getName() + \"@\" +  method.getName());\n        }\n        return ipcMethod.callRemote(binder, args);\n    }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCMethod.java",
    "content": "package com.lody.virtual.helper.ipcbus;\n\nimport android.os.Binder;\nimport android.os.IBinder;\nimport android.os.IInterface;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.os.RemoteException;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\n\n/**\n * @author Lody\n */\npublic class IPCMethod {\n\n    private int code;\n    private Method method;\n    private String interfaceName;\n    private MethodParamConverter[] converters;\n    private MethodParamConverter resultConverter;\n\n\n    public IPCMethod(int code, Method method, String interfaceName) {\n        this.code = code;\n        this.method = method;\n        this.interfaceName = interfaceName;\n        Class<?>[] parameterTypes = method.getParameterTypes();\n        converters = new MethodParamConverter[parameterTypes.length];\n        for (int i = 0; i < parameterTypes.length; i++) {\n            if (isAidlParam(parameterTypes[i])) {\n                converters[i] = new AidlParamConverter(parameterTypes[i]);\n            }\n        }\n        Class<?> returnType = method.getReturnType();\n        if (isAidlParam(returnType)) {\n            resultConverter = new AidlParamConverter(returnType);\n        }\n    }\n\n    private boolean isAidlParam(Class<?> type) {\n        return type.isInterface() && IInterface.class.isAssignableFrom(type);\n    }\n\n\n    public String getInterfaceName() {\n        return interfaceName;\n    }\n\n    public Method getMethod() {\n        return method;\n    }\n\n    public void handleTransact(Object server, Parcel data, Parcel reply) {\n        data.enforceInterface(interfaceName);\n        Object[] parameters = data.readArray(getClass().getClassLoader());\n        if (parameters != null && parameters.length > 0) {\n            for (int i = 0; i < parameters.length; i++) {\n                if (converters[i] != null) {\n                    parameters[i] = converters[i].convert(parameters[i]);\n                }\n            }\n        }\n        try {\n            Object res = method.invoke(server, parameters);\n            reply.writeNoException();\n            reply.writeValue(res);\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n            reply.writeException(e);\n        } catch (InvocationTargetException e) {\n            e.printStackTrace();\n            reply.writeException(e);\n        }\n    }\n\n    private static Method findAsInterfaceMethod(Class<?> type) {\n        for (Class<?> innerClass : type.getDeclaredClasses()) {\n            // public static class Stub extends Binder implements IType\n            if (Modifier.isStatic(innerClass.getModifiers())\n                    && Binder.class.isAssignableFrom(innerClass)\n                    && type.isAssignableFrom(innerClass)) {\n                // public static IType asInterface(android.os.IBinder obj)\n                for (Method method : innerClass.getDeclaredMethods()) {\n                    if (Modifier.isStatic(method.getModifiers())) {\n                        Class<?>[] types = method.getParameterTypes();\n                        if (types.length == 1 && types[0] == IBinder.class) {\n                            return method;\n                        }\n                    }\n                }\n            }\n        }\n        throw new IllegalStateException(\"Can not found the \" + type.getName() + \"$Stub.asInterface method.\");\n    }\n\n    public Object callRemote(IBinder server, Object[] args) throws RemoteException {\n        Parcel data = Parcel.obtain();\n        Parcel reply = Parcel.obtain();\n        Object result;\n        try {\n            data.writeInterfaceToken(interfaceName);\n            data.writeArray(args);\n            server.transact(code, data, reply, 0);\n            reply.readException();\n            result = readValue(reply);\n            if (resultConverter != null) {\n                result = resultConverter.convert(result);\n            }\n        } finally {\n            data.recycle();\n            reply.recycle();\n        }\n        return result;\n    }\n\n    private Object readValue(Parcel replay) {\n        Object result = replay.readValue(getClass().getClassLoader());\n        if (result instanceof Parcelable[]) {\n            Parcelable[] parcelables = (Parcelable[]) result;\n            Object[] results = (Object[]) Array.newInstance(method.getReturnType().getComponentType(), parcelables.length);\n            System.arraycopy(parcelables, 0, results, 0, results.length);\n            return results;\n        }\n        return result;\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        IPCMethod ipcMethod = (IPCMethod) o;\n\n        return method != null ? method.equals(ipcMethod.method) : ipcMethod.method == null;\n    }\n\n    public interface MethodParamConverter {\n        Object convert(Object param);\n    }\n\n    private class AidlParamConverter implements MethodParamConverter {\n\n        private Method asInterfaceMethod;\n        private Class<?> type;\n\n        AidlParamConverter(Class<?> type) {\n            this.type = type;\n        }\n\n        @Override\n        public Object convert(Object param) {\n            if (param != null) {\n                if (asInterfaceMethod == null) {\n                    synchronized (this) {\n                        if (asInterfaceMethod == null) {\n                            asInterfaceMethod = findAsInterfaceMethod(type);\n                        }\n                    }\n                }\n                try {\n                    return asInterfaceMethod.invoke(null, param);\n                } catch (Throwable e) {\n                    throw new IllegalStateException(e);\n                }\n            }\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IPCSingleton.java",
    "content": "package com.lody.virtual.helper.ipcbus;\n\n/**\n * @author Lody\n */\npublic class IPCSingleton<T> {\n\n    private Class<?> ipcClass;\n    private T instance;\n\n    public IPCSingleton(Class<?> ipcClass) {\n        this.ipcClass = ipcClass;\n    }\n\n    public T get() {\n        if (instance == null) {\n            synchronized (this) {\n                if (instance == null) {\n                    instance = IPCBus.get(ipcClass);\n                }\n            }\n        }\n        return instance;\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/IServerCache.java",
    "content": "package com.lody.virtual.helper.ipcbus;\n\nimport android.os.IBinder;\n\n/**\n * @author Lody\n */\npublic interface IServerCache {\n    void join(String serverName, IBinder binder);\n    IBinder query(String serverName);\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/ServerInterface.java",
    "content": "package com.lody.virtual.helper.ipcbus;\n\nimport android.os.Binder;\n\nimport com.lody.virtual.helper.collection.SparseArray;\n\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * @author Lody\n */\npublic class ServerInterface {\n\n    private Class<?> interfaceClass;\n    private final SparseArray<IPCMethod> codeToInterfaceMethod;\n    private final Map<Method, IPCMethod> methodToIPCMethodMap;\n\n    public ServerInterface(Class<?> interfaceClass) {\n        this.interfaceClass = interfaceClass;\n        Method[] methods = interfaceClass.getMethods();\n        codeToInterfaceMethod = new SparseArray<>(methods.length);\n        methodToIPCMethodMap = new HashMap<>(methods.length);\n        for (int i = 0; i < methods.length; i++) {\n            int code = Binder.FIRST_CALL_TRANSACTION + i;\n            IPCMethod ipcMethod = new IPCMethod(code, methods[i], interfaceClass.getName());\n            codeToInterfaceMethod.put(code, ipcMethod);\n            methodToIPCMethodMap.put(methods[i], ipcMethod);\n        }\n    }\n\n    public Class<?> getInterfaceClass() {\n        return interfaceClass;\n    }\n\n    public String getInterfaceName() {\n        return interfaceClass.getName();\n    }\n\n    public IPCMethod getIPCMethod(int code) {\n        return codeToInterfaceMethod.get(code);\n    }\n\n    public IPCMethod getIPCMethod(Method method) {\n        return methodToIPCMethodMap.get(method);\n    }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/helper/ipcbus/TransformBinder.java",
    "content": "package com.lody.virtual.helper.ipcbus;\n\nimport android.os.Binder;\nimport android.os.Parcel;\nimport android.os.RemoteException;\n\n\n/**\n * @author Lody\n */\npublic class TransformBinder extends Binder {\n\n    private ServerInterface serverInterface;\n    private Object server;\n\n    public TransformBinder(ServerInterface serverInterface, Object server) {\n        this.serverInterface = serverInterface;\n        this.server = server;\n    }\n\n    @Override\n    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {\n        if (code == INTERFACE_TRANSACTION) {\n            reply.writeString(serverInterface.getInterfaceName());\n            return true;\n        }\n        IPCMethod method = serverInterface.getIPCMethod(code);\n        if (method != null) {\n            try {\n                method.handleTransact(server, data, reply);\n            } catch (Throwable e) {\n                e.printStackTrace();\n            }\n            return true;\n        }\n        return super.onTransact(code, data, reply, flags);\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.client.ipc.VActivityManager;\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.helper.compat.ObjectsCompat;\nimport com.lody.virtual.os.VUserHandle;\n\nimport android.os.IBinder;\nimport android.os.Parcelable;\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            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\n    public static Intent redirectIntentSender(int type, String creator, Intent intent, IBinder iBinder) {\n        Intent cloneFilter = intent.cloneFilter();\n        switch (type) {\n            case 1:\n                cloneFilter.setClass(VirtualCore.get().getContext(), StubPendingReceiver.class);\n                break;\n            case 2:\n                if (VirtualCore.get().resolveActivityInfo(intent, VUserHandle.myUserId()) != null) {\n                    cloneFilter.setClass(VirtualCore.get().getContext(), StubPendingActivity.class);\n                    cloneFilter.setFlags(intent.getFlags());\n                    if (iBinder != null) {\n                        try {\n                            Parcelable activityForToken = VActivityManager.get().getActivityForToken(iBinder);\n                            if (activityForToken != null) {\n                                cloneFilter.putExtra(\"_VA_|_caller_\", activityForToken);\n                                break;\n                            }\n                        } catch (Throwable th) {\n                            break;\n                        }\n                    }\n                }\n                break;\n            case 4:\n                if (VirtualCore.get().resolveServiceInfo(intent, VUserHandle.myUserId()) != null) {\n                    cloneFilter.setClass(VirtualCore.get().getContext(), StubPendingService.class);\n                    break;\n                }\n                break;\n            default:\n                return null;\n        }\n        cloneFilter.putExtra(\"_VA_|_user_id_\", VUserHandle.myUserId());\n        cloneFilter.putExtra(\"_VA_|_intent_\", intent);\n        cloneFilter.putExtra(\"_VA_|_creator_\", creator);\n        cloneFilter.putExtra(\"_VA_|_from_inner_\", true);\n        return cloneFilter;\n    }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/helper/utils/DrawableUtils.java",
    "content": "package com.lody.virtual.helper.utils;\r\n\r\nimport android.graphics.Bitmap;\r\nimport android.graphics.Canvas;\r\nimport android.graphics.PixelFormat;\r\nimport android.graphics.drawable.BitmapDrawable;\r\nimport android.graphics.drawable.Drawable;\r\n\r\npublic class DrawableUtils {\r\n    public static Bitmap drawableToBitMap(Drawable drawable) {\r\n        if (drawable == null) {\r\n            return null;\r\n        }\r\n        if (drawable instanceof BitmapDrawable) {\r\n            BitmapDrawable bitmapDrawable = ((BitmapDrawable) drawable);\r\n            return bitmapDrawable.getBitmap();\r\n        } else {\r\n            Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),\r\n                    drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);\r\n            Canvas canvas = new Canvas(bitmap);\r\n            drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());\r\n            drawable.draw(canvas);\r\n            return bitmap;\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/helper/utils/SchedulerTask.java",
    "content": "package com.lody.virtual.helper.utils;\n\nimport android.os.Handler;\n\n/**\n * @author Lody\n */\n\npublic abstract class SchedulerTask implements Runnable {\n    private Handler mHandler;\n    private long mDelay;\n\n    public SchedulerTask(Handler handler, long delay) {\n        this.mHandler = handler;\n        this.mDelay = delay;\n    }\n\n    public void schedule() {\n        mHandler.post(mInnerRunnable);\n    }\n\n    public void cancel() {\n        mHandler.removeCallbacks(mInnerRunnable);\n    }\n\n    private final Runnable mInnerRunnable = new Runnable() {\n        @Override\n        public void run() {\n            SchedulerTask.this.run();\n            if(mDelay > 0) {\n                mHandler.postDelayed(this, mDelay);\n            }\n        }\n    };\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/helper/utils/marks/FakeDeviceMark.java",
    "content": "package com.lody.virtual.helper.utils.marks;\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 FakeDeviceMark {\n    String value() default \"\";\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/helper/utils/marks/FakeLocMark.java",
    "content": "package com.lody.virtual.helper.utils.marks;\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 FakeLocMark {\n    String value() default \"\";\n}"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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.client.env.VirtualRuntime;\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 getVirtualLocationFile() {\n        return new File(getSystemSecureDirectory(), \"virtual-loc.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     * @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 getAppLibDirectory(String packageName) {\n        return ensureCreated(new File(getDataAppPackageDirectory(packageName), \"lib\"));\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.helper.ipcbus.IPCBus;\nimport com.lody.virtual.server.interfaces.IUserManager;\n\nimport java.util.List;\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 = IPCBus.get(IUserManager.class);\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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/remote/BadgerInfo.java",
    "content": "package com.lody.virtual.remote;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport com.lody.virtual.os.VUserHandle;\n\n/**\n * @author Lody\n */\npublic class BadgerInfo implements Parcelable {\n\n    public int userId;\n    public String packageName;\n    public int badgerCount;\n    public String className;\n\n    public BadgerInfo() {\n        userId = VUserHandle.myUserId();\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(packageName);\n        dest.writeInt(badgerCount);\n        dest.writeString(className);\n    }\n\n    protected BadgerInfo(Parcel in) {\n        userId = in.readInt();\n        packageName = in.readString();\n        badgerCount = in.readInt();\n        className = in.readString();\n    }\n\n    public static final Parcelable.Creator<BadgerInfo> CREATOR = new Parcelable.Creator<BadgerInfo>() {\n        @Override\n        public BadgerInfo createFromParcel(Parcel source) {\n            return new BadgerInfo(source);\n        }\n\n        @Override\n        public BadgerInfo[] newArray(int size) {\n            return new BadgerInfo[size];\n        }\n    };\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/remote/InstalledAppInfo.java",
    "content": "package com.lody.virtual.remote;\n\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\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 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.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 PackageInfo getPackageInfo(int userId) {\n        return VPackageManager.get().getPackageInfo(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.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.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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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        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    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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/remote/vloc/VCell.java",
    "content": "package com.lody.virtual.remote.vloc;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * @author Lody\n */\n\npublic class VCell implements Parcelable {\n\n    public int type;\n    public int mcc;\n    public int mnc;\n    public int psc;\n    public int lac;\n    public int cid;\n    public int baseStationId;\n    public int systemId;\n    public int networkId;\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.type);\n        dest.writeInt(this.mcc);\n        dest.writeInt(this.mnc);\n        dest.writeInt(this.psc);\n        dest.writeInt(this.lac);\n        dest.writeInt(this.cid);\n        dest.writeInt(this.baseStationId);\n        dest.writeInt(this.systemId);\n        dest.writeInt(this.networkId);\n    }\n\n    public VCell() {\n    }\n\n    public VCell(Parcel in) {\n        this.type = in.readInt();\n        this.mcc = in.readInt();\n        this.mnc = in.readInt();\n        this.psc = in.readInt();\n        this.lac = in.readInt();\n        this.cid = in.readInt();\n        this.baseStationId = in.readInt();\n        this.systemId = in.readInt();\n        this.networkId = in.readInt();\n    }\n\n    public static final Parcelable.Creator<VCell> CREATOR = new Parcelable.Creator<VCell>() {\n        @Override\n        public VCell createFromParcel(Parcel source) {\n            return new VCell(source);\n        }\n\n        @Override\n        public VCell[] newArray(int size) {\n            return new VCell[size];\n        }\n    };\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/remote/vloc/VLocation.java",
    "content": "package com.lody.virtual.remote.vloc;\n\nimport android.location.Location;\nimport android.location.LocationManager;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport com.lody.virtual.client.env.VirtualGPSSatalines;\nimport com.lody.virtual.helper.utils.Reflect;\n\n/**\n * @author Lody\n */\n\npublic class VLocation implements Parcelable {\n\n    public double latitude = 0.0;\n    public double longitude = 0.0;\n    public double altitude = 0.0f;\n    public float accuracy = 0.0f;\n    public float speed;\n    public float bearing;\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeDouble(latitude);\n        dest.writeDouble(longitude);\n        dest.writeDouble(altitude);\n        dest.writeFloat(accuracy);\n        dest.writeFloat(speed);\n        dest.writeFloat(bearing);\n    }\n\n    public VLocation() {\n    }\n\n    public VLocation(Parcel in) {\n        latitude = in.readDouble();\n        longitude = in.readDouble();\n        altitude = in.readDouble();\n        accuracy = in.readFloat();\n        speed = in.readFloat();\n        bearing = in.readFloat();\n    }\n\n    public boolean isEmpty() {\n        return latitude == 0 && longitude == 0;\n    }\n\n    public static final Parcelable.Creator<VLocation> CREATOR = new Parcelable.Creator<VLocation>() {\n        @Override\n        public VLocation createFromParcel(Parcel source) {\n            return new VLocation(source);\n        }\n\n        @Override\n        public VLocation[] newArray(int size) {\n            return new VLocation[size];\n        }\n    };\n\n    @Override\n    public String toString() {\n        return \"VLocation{\" +\n                \"latitude=\" + latitude +\n                \", longitude=\" + longitude +\n                \", altitude=\" + altitude +\n                \", accuracy=\" + accuracy +\n                \", speed=\" + speed +\n                \", bearing=\" + bearing +\n                '}';\n    }\n\n    public Location toSysLocation() {\n        Location location = new Location(LocationManager.GPS_PROVIDER);\n        location.setAccuracy(8f);\n        Bundle extraBundle = new Bundle();\n        location.setBearing(bearing);\n        Reflect.on(location).call(\"setIsFromMockProvider\", false);\n        location.setLatitude(latitude);\n        location.setLongitude(longitude);\n        location.setSpeed(speed);\n        location.setTime(System.currentTimeMillis());\n        location.setExtras(extraBundle);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            location.setElapsedRealtimeNanos(277000000);\n        }\n        int svCount = VirtualGPSSatalines.get().getSvCount();\n        extraBundle.putInt(\"satellites\", svCount);\n        extraBundle.putInt(\"satellitesvalue\", svCount);\n        return location;\n    }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/remote/vloc/VWifi.java",
    "content": "package com.lody.virtual.remote.vloc;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * @author Lody\n */\n\npublic class VWifi implements Parcelable {\n\n    public String ssid;\n    public String bssid;\n    public String capabilities;\n    public int level;\n    public int frequency;\n    public long timestamp;\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.ssid);\n        dest.writeString(this.bssid);\n        dest.writeString(this.capabilities);\n        dest.writeInt(this.level);\n        dest.writeInt(this.frequency);\n        dest.writeLong(this.timestamp);\n    }\n\n    public VWifi() {\n    }\n\n    public VWifi(Parcel in) {\n        this.ssid = in.readString();\n        this.bssid = in.readString();\n        this.capabilities = in.readString();\n        this.level = in.readInt();\n        this.frequency = in.readInt();\n        this.timestamp = in.readLong();\n    }\n\n    public static final Parcelable.Creator<VWifi> CREATOR = new Parcelable.Creator<VWifi>() {\n        @Override\n        public VWifi createFromParcel(Parcel source) {\n            return new VWifi(source);\n        }\n\n        @Override\n        public VWifi[] newArray(int size) {\n            return new VWifi[size];\n        }\n    };\n}\n"
  },
  {
    "path": "VirtualApp/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.stub.DaemonService;\nimport com.lody.virtual.helper.compat.BundleCompat;\nimport com.lody.virtual.helper.ipcbus.IPCBus;\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.IAccountManager;\nimport com.lody.virtual.server.interfaces.IActivityManager;\nimport com.lody.virtual.server.interfaces.IAppManager;\nimport com.lody.virtual.server.interfaces.IDeviceInfoManager;\nimport com.lody.virtual.server.interfaces.IJobService;\nimport com.lody.virtual.server.interfaces.INotificationManager;\nimport com.lody.virtual.server.interfaces.IPackageManager;\nimport com.lody.virtual.server.interfaces.IServiceFetcher;\nimport com.lody.virtual.server.interfaces.IUserManager;\nimport com.lody.virtual.server.interfaces.IVirtualLocationManager;\nimport com.lody.virtual.server.interfaces.IVirtualStorageService;\nimport com.lody.virtual.server.job.VJobSchedulerService;\nimport com.lody.virtual.server.location.VirtualLocationService;\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\nimport mirror.android.app.job.IJobScheduler;\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        DaemonService.startup(context);\n        if (!VirtualCore.get().isStartup()) {\n            return true;\n        }\n        VPackageManagerService.systemReady();\n        IPCBus.register(IPackageManager.class, VPackageManagerService.get());\n        VActivityManagerService.systemReady(context);\n        IPCBus.register(IActivityManager.class, VActivityManagerService.get());\n        IPCBus.register(IUserManager.class, VUserManagerService.get());\n        VAppManagerService.systemReady();\n        IPCBus.register(IAppManager.class, VAppManagerService.get());\n        BroadcastSystem.attach(VActivityManagerService.get(), VAppManagerService.get());\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            IPCBus.register(IJobService.class, VJobSchedulerService.get());\n        }\n        VNotificationManagerService.systemReady(context);\n        IPCBus.register(INotificationManager.class, VNotificationManagerService.get());\n        VAppManagerService.get().scanApps();\n        VAccountManagerService.systemReady();\n        IPCBus.register(IAccountManager.class, VAccountManagerService.get());\n        IPCBus.register(IVirtualStorageService.class, VirtualStorageService.get());\n        IPCBus.register(IDeviceInfoManager.class, VDeviceManagerService.get());\n        IPCBus.register(IVirtualLocationManager.class, VirtualLocationService.get());\n        return true;\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        if (\"register\".equals(method)) {\n\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    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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.am.VActivityManagerService;\nimport com.lody.virtual.server.interfaces.IAccountManager;\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 implements IAccountManager {\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 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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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\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": "VirtualApp/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.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.RemoteException;\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\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\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        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\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    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            }else{\n                Resources resources=VirtualCore.get().getResources(targetInfo.packageName);\n                if(resources!=null) {\n                    TypedArray typedArray = resources.newTheme().obtainStyledAttributes(targetInfo.theme, R_Styleable_Window);\n                    if(typedArray!=null){\n                        showWallpaper = typedArray.getBoolean(R_Styleable_Window_windowShowWallpaper, false);\n                        isTranslucent = typedArray.getBoolean(R_Styleable_Window_windowIsTranslucent, false);\n                        isFloating = typedArray.getBoolean(R_Styleable_Window_windowIsFloating, false);\n                    }\n                }\n            }\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n\n        boolean isDialogStyle = isFloating || isTranslucent || showWallpaper;\n        if (isDialogStyle) {\n            return VASettings.getStubDialogName(vpid);\n        } else {\n            return VASettings.getStubActivityName(vpid);\n        }\n    }\n\n    private Intent startActivityProcess(int userId, ActivityRecord sourceRecord, Intent intent, ActivityInfo info) {\n        intent = new Intent(intent);\n        ProcessRecord targetApp = mService.startProcessIfNeedLocked(info.processName, userId, info.packageName);\n        if (targetApp == null) {\n            return null;\n        }\n        Intent targetIntent = new Intent();\n        targetIntent.setClassName(VirtualCore.get().getHostPkg(), fetchStubActivity(targetApp.vpid, info));\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.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.HandlerThread;\nimport android.os.IBinder;\nimport android.os.Looper;\nimport android.os.Message;\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.Reflect;\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.lang.reflect.Field;\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        HandlerThread broadcastThread = new HandlerThread(\"BroadcastThread\");\n        HandlerThread anrThread = new HandlerThread(\"BroadcastAnrThread\");\n        broadcastThread.start();\n        anrThread.start();\n        mScheduler = new StaticScheduler(broadcastThread.getLooper());\n        mTimeoutHandler = new TimeoutHandler(anrThread.getLooper());\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 >= 26) {\n                        Map<Integer, List<String>> whiteListMap = Reflect.on(receiverResource).get(\"mWhiteListMap\");\n                        List<String> whiteList = whiteListMap.get(0);\n                        if (whiteList == null) {\n                            whiteList = new ArrayList<>();\n                            whiteListMap.put(0, whiteList);\n                        }\n                        whiteList.add(mContext.getPackageName());\n                    } else 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                    } 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    public void startApp(VPackage p) {\n        PackageSetting setting = (PackageSetting) p.mExtras;\n        for (VPackage.ActivityComponent receiver : p.receivers) {\n            ActivityInfo info = receiver.info;\n            List<BroadcastReceiver> receivers = mReceivers.get(p.packageName);\n            if (receivers == null) {\n                receivers = new ArrayList<>();\n                mReceivers.put(p.packageName, receivers);\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            receivers.add(r);\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                receivers.add(r);\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        StaticScheduler(Looper looper) {\n            super(looper);\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\n        TimeoutHandler(Looper looper) {\n            super(looper);\n        }\n\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            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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.app.Notification;\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.Collections;\nimport java.util.Iterator;\nimport java.util.List;\n\npublic class ServiceRecord extends Binder {\n\tpublic final List<IntentBindRecord> bindings = new ArrayList<>();\n\tpublic long activeSince;\n\tpublic long lastActivityTime;\n\tpublic ServiceInfo serviceInfo;\n\tpublic int startId;\n\tpublic ProcessRecord process;\n\tpublic int foregroundId;\n\tpublic Notification foregroundNoti;\n\n\tpublic boolean containConnection(IServiceConnection connection) {\n\t\tfor (IntentBindRecord record : bindings) {\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 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) {\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) {\n\t\t\t\tif (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\tbindings.add(record);\n\t\t\t}\n\t\t}\n\t\trecord.addConnection(connection);\n\t}\n\n\tpublic static class IntentBindRecord {\n\t\tpublic  final List<IServiceConnection> connections = Collections.synchronizedList(new ArrayList<IServiceConnection>());\n\t\tpublic IBinder binder;\n\t\tIntent intent;\n\t\tpublic boolean doRebind = false;\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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;\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.BadgerInfo;\nimport com.lody.virtual.remote.PendingIntentData;\nimport com.lody.virtual.remote.PendingResultData;\nimport com.lody.virtual.remote.VParceledListSlice;\nimport com.lody.virtual.server.interfaces.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.Collections;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicReference;\n\nimport mirror.android.app.IServiceConnectionO;\n\nimport static android.os.Process.killProcess;\nimport static com.lody.virtual.os.VBinder.getCallingPid;\nimport static com.lody.virtual.os.VUserHandle.getUserId;\n\n/**\n * @author Lody\n */\npublic class VActivityManagerService implements IActivityManager {\n\n    private static final boolean BROADCAST_NOT_STARTED_PKG = false;\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 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        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            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(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    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\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    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        if (r == null) {\n            r = new ServiceRecord();\n            r.startId = 0;\n            r.activeSince = SystemClock.elapsedRealtime();\n            r.process = targetApp;\n            r.serviceInfo = serviceInfo;\n            try {\n                IApplicationThreadCompat.scheduleCreateService(appThread, r, r.serviceInfo, 0);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n            addRecord(r);\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            stopServiceCommon(r, ComponentUtils.toComponentName(serviceInfo));\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, className);\n                return true;\n            }\n\n            return false;\n        }\n    }\n\n    private void stopServiceCommon(ServiceRecord r, ComponentName className) {\n        for (ServiceRecord.IntentBindRecord bindRecord : r.bindings) {\n            for (IServiceConnection connection : bindRecord.connections) {\n                // Report to all of the connections that the service is no longer\n                // available.\n                try {\n                    if (Build.VERSION.SDK_INT >= 26) {\n                        IServiceConnectionO.connected.call(connection, className, null, true);\n                    } else {\n                        connection.connected(className, null);\n                    }\n                } catch (RemoteException e) {\n                    e.printStackTrace();\n                }\n            }\n            try {\n                IApplicationThreadCompat.scheduleUnbindService(r.process.appThread, r, bindRecord.intent);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n        }\n        try {\n            IApplicationThreadCompat.scheduleStopService(r.process.appThread, r);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n        mHistory.remove(r);\n\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            if (r == null) {\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, false);\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\n    @Override\n    public boolean unbindService(IServiceConnection connection, int userId) {\n        synchronized (this) {\n            ServiceRecord r = findRecordLocked(connection);\n            if (r == null) {\n                return false;\n            }\n\n            for (ServiceRecord.IntentBindRecord bindRecord : r.bindings) {\n                if (!bindRecord.containConnection(connection)) {\n                    continue;\n                }\n                bindRecord.removeConnection(connection);\n                try {\n                    IApplicationThreadCompat.scheduleUnbindService(r.process.appThread, r, bindRecord.intent);\n                } catch (RemoteException e) {\n                    e.printStackTrace();\n                }\n            }\n\n            if (r.startId <= 0 && r.getConnectionCount() <= 0) {\n                try {\n                    IApplicationThreadCompat.scheduleStopService(r.process.appThread, r);\n                } catch (RemoteException e) {\n                    e.printStackTrace();\n                }\n                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n                    mHistory.remove(r);\n                }\n            }\n            return true;\n        }\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, false);\n                    }\n                }\n            }\n        }\n    }\n\n    private void connectService(IServiceConnection conn, ComponentName component, ServiceRecord.IntentBindRecord r, boolean dead) {\n        try {\n            BinderDelegateService delegateService = new BinderDelegateService(component, r.binder);\n            if (Build.VERSION.SDK_INT >= 26) {\n                IServiceConnectionO.connected.call(conn, component, delegateService, dead);\n            } else {\n                conn.connected(component, delegateService);\n            }\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.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 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    @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\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 IBinder.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        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        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        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 Collections.emptyList();\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 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(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 realIntent = intent.getParcelableExtra(\"_VA_|_intent_\");\n        ComponentName component = intent.getParcelableExtra(\"_VA_|_component_\");\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        String originAction = SpecialComponentList.unprotectAction(realIntent.getAction());\n        if (originAction != null) {\n            // restore to origin action.\n            realIntent.setAction(originAction);\n        }\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.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\n    @Override\n    public void notifyBadgerChange(BadgerInfo info) {\n        Intent intent = new Intent(VASettings.ACTION_BADGER_CHANGE);\n        intent.putExtra(\"userId\", info.userId);\n        intent.putExtra(\"packageName\", info.packageName);\n        intent.putExtra(\"badgerCount\", info.badgerCount);\n        VirtualCore.get().getContext().sendBroadcast(intent);\n    }\n}"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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.interfaces.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 implements IDeviceInfoManager {\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) {\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    @Override\n    public void updateDeviceInfo(int userId, VDeviceInfo info) {\n        synchronized (mDeviceInfos) {\n            if (info != null) {\n                mDeviceInfos.put(userId, info);\n                mPersistenceLayer.save();\n            }\n        }\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": "VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IAccountManager.java",
    "content": "package com.lody.virtual.server.interfaces;\n\nimport android.accounts.Account;\nimport android.accounts.AuthenticatorDescription;\nimport android.accounts.IAccountManagerResponse;\nimport android.os.Bundle;\nimport android.os.RemoteException;\n\n/**\n * @author Lody\n */\npublic interface IAccountManager {\n\n    AuthenticatorDescription[] getAuthenticatorTypes(int userId) throws RemoteException;\n\n    void getAccountsByFeatures(int userId, IAccountManagerResponse response, String type, String[] features) throws RemoteException;\n\n    String getPreviousName(int userId, Account account) throws RemoteException;\n\n    Account[] getAccounts(int userId, String type) throws RemoteException;\n\n    void getAuthToken(int userId, IAccountManagerResponse response, Account account, String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch, Bundle loginOptions) throws RemoteException;\n\n    void setPassword(int userId, Account account, String password) throws RemoteException;\n\n    void setAuthToken(int userId, Account account, String authTokenType, String authToken) throws RemoteException;\n\n    void setUserData(int userId, Account account, String key, String value) throws RemoteException;\n\n    void hasFeatures(int userId, IAccountManagerResponse response, Account account, String[] features) throws RemoteException;\n\n    void updateCredentials(int userId, IAccountManagerResponse response, Account account, String authTokenType, boolean expectActivityLaunch, Bundle loginOptions) throws RemoteException;\n\n    void editProperties(int userId, IAccountManagerResponse response, String accountType, boolean expectActivityLaunch) throws RemoteException;\n\n    void getAuthTokenLabel(int userId, IAccountManagerResponse response, String accountType, String authTokenType) throws RemoteException;\n\n    String getUserData(int userId, Account account, String key) throws RemoteException;\n\n    String getPassword(int userId, Account account) throws RemoteException;\n\n    void confirmCredentials(int userId, IAccountManagerResponse response, Account account, Bundle options, boolean expectActivityLaunch) throws RemoteException;\n\n    void addAccount(int userId, IAccountManagerResponse response, String accountType, String authTokenType, String[] requiredFeatures, boolean expectActivityLaunch, Bundle optionsIn) throws RemoteException;\n\n    boolean addAccountExplicitly(int userId, Account account, String password, Bundle extras) throws RemoteException;\n\n    boolean removeAccountExplicitly(int userId, Account account) throws RemoteException;\n\n    void renameAccount(int userId, IAccountManagerResponse response, Account accountToRename, String newName) throws RemoteException;\n\n    void removeAccount(int userId, IAccountManagerResponse response, Account account, boolean expectActivityLaunch) throws RemoteException;\n\n    void clearPassword(int userId, Account account) throws RemoteException;\n\n    boolean accountAuthenticated(int userId, Account account) throws RemoteException;\n\n    void invalidateAuthToken(int userId, String accountType, String authToken) throws RemoteException;\n\n    String peekAuthToken(int userId, Account account, String authTokenType) throws RemoteException;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IActivityManager.java",
    "content": "package com.lody.virtual.server.interfaces;\n\nimport android.app.*;\nimport android.content.*;\nimport android.content.pm.*;\nimport android.os.*;\n\nimport com.lody.virtual.remote.*;\n\n/**\n * @author Lody\n */\npublic interface IActivityManager {\n\n    int initProcess(String packageName, String processName, int userId) throws RemoteException;\n\n    int getFreeStubCount() throws RemoteException;\n\n    int getSystemPid() throws RemoteException;\n\n    int getUidByPid(int pid) throws RemoteException;\n\n    boolean isAppProcess(String processName) throws RemoteException;\n\n    boolean isAppRunning(String packageName, int userId) throws RemoteException;\n\n    boolean isAppPid(int pid) throws RemoteException;\n\n    String getAppProcessName(int pid) throws RemoteException;\n\n    java.util.List<String> getProcessPkgList(int pid) throws RemoteException;\n\n    void killAllApps() throws RemoteException;\n\n    void killAppByPkg(String pkg, int userId) throws RemoteException;\n\n    void killApplicationProcess(String processName, int vuid) throws RemoteException;\n\n    void dump() throws RemoteException;\n\n    String getInitialPackage(int pid) throws RemoteException;\n\n    void handleApplicationCrash() throws RemoteException;\n\n    void appDoneExecuting() throws RemoteException;\n\n    int startActivities(Intent[] intents, String[] resolvedTypes, IBinder token, Bundle options, int userId) throws RemoteException;\n\n    int startActivity(Intent intent, ActivityInfo info, IBinder resultTo, Bundle options, String resultWho, int requestCode, int userId) throws RemoteException;\n\n    void onActivityCreated(ComponentName component, ComponentName caller, IBinder token, Intent intent, String affinity, int taskId, int launchMode, int flags) throws RemoteException;\n\n    void onActivityResumed(int userId, IBinder token) throws RemoteException;\n\n    boolean onActivityDestroyed(int userId, IBinder token) throws RemoteException;\n\n    ComponentName getActivityClassForToken(int userId, IBinder token) throws RemoteException;\n\n    String getCallingPackage(int userId, IBinder token) throws RemoteException;\n\n    ComponentName getCallingActivity(int userId, IBinder token) throws RemoteException;\n\n    AppTaskInfo getTaskInfo(int taskId) throws RemoteException;\n\n    String getPackageForToken(int userId, IBinder token) throws RemoteException;\n\n    boolean isVAServiceToken(IBinder token) throws RemoteException;\n\n    ComponentName startService(IBinder caller, Intent service, String resolvedType, int userId) throws RemoteException;\n\n    int stopService(IBinder caller, Intent service, String resolvedType, int userId) throws RemoteException;\n\n    boolean stopServiceToken(ComponentName className, IBinder token, int startId, int userId) throws RemoteException;\n\n    void setServiceForeground(ComponentName className, IBinder token, int id, Notification notification, boolean removeNotification, int userId) throws RemoteException;\n\n    int bindService(IBinder caller, IBinder token, Intent service, String resolvedType, IServiceConnection connection, int flags, int userId) throws RemoteException;\n\n    boolean unbindService(IServiceConnection connection, int userId) throws RemoteException;\n\n    void unbindFinished(IBinder token, Intent service, boolean doRebind, int userId) throws RemoteException;\n\n    void serviceDoneExecuting(IBinder token, int type, int startId, int res, int userId) throws RemoteException;\n\n    IBinder peekService(Intent service, String resolvedType, int userId) throws RemoteException;\n\n    void publishService(IBinder token, Intent intent, IBinder service, int userId) throws RemoteException;\n\n    VParceledListSlice getServices(int maxNum, int flags, int userId) throws RemoteException;\n\n    IBinder acquireProviderClient(int userId, ProviderInfo info) throws RemoteException;\n\n    PendingIntentData getPendingIntent(IBinder binder) throws RemoteException;\n\n    void addPendingIntent(IBinder binder, String packageName) throws RemoteException;\n\n    void removePendingIntent(IBinder binder) throws RemoteException;\n\n    String getPackageForIntentSender(IBinder binder) throws RemoteException;\n\n    void processRestarted(String packageName, String processName, int userId) throws RemoteException;\n\n    void broadcastFinish(PendingResultData res) throws RemoteException;\n\n    void notifyBadgerChange(BadgerInfo info) throws RemoteException;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IAppManager.java",
    "content": "package com.lody.virtual.server.interfaces;\n\nimport android.os.RemoteException;\n\nimport com.lody.virtual.remote.InstallResult;\nimport com.lody.virtual.remote.InstalledAppInfo;\n\nimport java.util.List;\n\n/**\n * @author Lody\n */\npublic interface IAppManager {\n\n    int[] getPackageInstalledUsers(String packageName) throws RemoteException;\n\n    void scanApps() throws RemoteException;\n\n    void addVisibleOutsidePackage(String pkg) throws RemoteException;\n\n    void removeVisibleOutsidePackage(String pkg) throws RemoteException;\n\n    boolean isOutsidePackageVisible(String pkg) throws RemoteException;\n\n    InstalledAppInfo getInstalledAppInfo(String pkg, int flags) throws RemoteException;\n\n    InstallResult installPackage(String path, int flags) throws RemoteException;\n\n    boolean isPackageLaunched(int userId, String packageName) throws RemoteException;\n\n    void setPackageHidden(int userId, String packageName, boolean hidden) throws RemoteException;\n\n    boolean installPackageAsUser(int userId, String packageName) throws RemoteException;\n\n    boolean uninstallPackageAsUser(String packageName, int userId) throws RemoteException;\n\n    boolean uninstallPackage(String packageName) throws RemoteException;\n\n    List<InstalledAppInfo> getInstalledApps(int flags) throws RemoteException;\n\n    List<InstalledAppInfo> getInstalledAppsAsUser(int userId, int flags) throws RemoteException;\n\n    int getInstalledAppCount() throws RemoteException;\n\n    boolean isAppInstalled(String packageName) throws RemoteException;\n\n    boolean isAppInstalledAsUser(int userId, String packageName) throws RemoteException;\n\n    void registerObserver(IPackageObserver observer) throws RemoteException;\n\n    void unregisterObserver(IPackageObserver observer) throws RemoteException;\n\n    void setAppRequestListener(IAppRequestListener listener) throws RemoteException;\n\n    void clearAppRequestListener() throws RemoteException;\n\n    IAppRequestListener getAppRequestListener() throws RemoteException;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IDeviceInfoManager.java",
    "content": "package com.lody.virtual.server.interfaces;\n\nimport android.os.RemoteException;\n\nimport com.lody.virtual.remote.VDeviceInfo;\n\n/**\n * @author Lody\n */\npublic interface IDeviceInfoManager {\n\n    VDeviceInfo getDeviceInfo(int userId) throws RemoteException;\n\n    void updateDeviceInfo(int userId, VDeviceInfo info) throws RemoteException;\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IJobService.java",
    "content": "package com.lody.virtual.server.interfaces;\n\nimport android.app.job.JobInfo;\nimport android.os.Parcelable;\nimport android.os.RemoteException;\n\nimport java.util.List;\n\n/**\n * @author Lody\n */\npublic interface IJobService {\n\n    int schedule(JobInfo job) throws RemoteException;\n\n    void cancel(int jobId) throws RemoteException;\n\n    void cancelAll() throws RemoteException;\n\n    List<JobInfo> getAllPendingJobs() throws RemoteException;\n\n    int enqueue(JobInfo jobInfo, Parcelable parcelable) throws RemoteException;\n\n    JobInfo getPendingJob(int i) throws RemoteException;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/INotificationManager.java",
    "content": "package com.lody.virtual.server.interfaces;\n\nimport android.os.RemoteException;\n\n/**\n * @author Lody\n */\npublic interface INotificationManager {\n\n    int dealNotificationId(int id, String packageName, String tag, int userId) throws RemoteException;\n\n    String dealNotificationTag(int id, String packageName, String tag, int userId) throws RemoteException;\n\n    boolean areNotificationsEnabledForPackage(String packageName, int userId) throws RemoteException;\n\n    void setNotificationsEnabledForPackage(String packageName, boolean enable, int userId) throws RemoteException;\n\n    void addNotification(int id, String tag, String packageName, int userId) throws RemoteException;\n\n    void cancelAllNotification(String packageName, int userId) throws RemoteException;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IPackageManager.java",
    "content": "package com.lody.virtual.server.interfaces;\n\nimport android.content.*;\nimport android.content.pm.*;\nimport android.os.IBinder;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.remote.VParceledListSlice;\n\nimport java.util.List;\n\n/**\n * @author Lody\n */\npublic interface IPackageManager {\n\n    int getPackageUid(String packageName, int userId) throws RemoteException;\n\n    String[] getPackagesForUid(int vuid) throws RemoteException;\n\n    List<String> getSharedLibraries(String pkgName) throws RemoteException;\n\n    int checkPermission(String permName, String pkgName, int userId) throws RemoteException;\n\n    PackageInfo getPackageInfo(String packageName, int flags, int userId) throws RemoteException;\n\n    ActivityInfo getActivityInfo(ComponentName componentName, int flags, int userId) throws RemoteException;\n\n    boolean activitySupportsIntent(ComponentName component, Intent intent, String resolvedType) throws RemoteException;\n\n    ActivityInfo getReceiverInfo(ComponentName componentName, int flags, int userId) throws RemoteException;\n\n    ServiceInfo getServiceInfo(ComponentName componentName, int flags, int userId) throws RemoteException;\n\n    ProviderInfo getProviderInfo(ComponentName componentName, int flags, int userId) throws RemoteException;\n\n    ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;\n\n    List<ResolveInfo> queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;\n\n    List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;\n\n    ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;\n\n    List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;\n\n    List<ResolveInfo> queryIntentContentProviders(Intent intent, String resolvedType, int flags, int userId) throws RemoteException;\n\n    VParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) throws RemoteException;\n\n    VParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId) throws RemoteException;\n\n    PermissionInfo getPermissionInfo(String name, int flags) throws RemoteException;\n\n    List<PermissionInfo> queryPermissionsByGroup(String group, int flags) throws RemoteException;\n\n    PermissionGroupInfo getPermissionGroupInfo(String name, int flags) throws RemoteException;\n\n    List<PermissionGroupInfo> getAllPermissionGroups(int flags) throws RemoteException;\n\n    ProviderInfo resolveContentProvider(String name, int flags, int userId) throws RemoteException;\n\n    ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) throws RemoteException;\n\n    VParceledListSlice queryContentProviders(String processName, int vuid, int flags) throws RemoteException;\n\n    List<String> querySharedPackages(String packageName) throws RemoteException;\n\n    String getNameForUid(int uid) throws RemoteException;\n\n    IBinder getPackageInstaller() throws RemoteException;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IUserManager.java",
    "content": "package com.lody.virtual.server.interfaces;\n\nimport android.graphics.Bitmap;\nimport android.os.RemoteException;\n\nimport com.lody.virtual.os.VUserInfo;\n\nimport java.util.List;\n\n/**\n * @author Lody\n */\npublic interface IUserManager {\n    VUserInfo createUser(String name, int flags) throws RemoteException;\n\n    boolean removeUser(int userHandle) throws RemoteException;\n\n    void setUserName(int userHandle, String name) throws RemoteException;\n\n    void setUserIcon(int userHandle, Bitmap icon) throws RemoteException;\n\n    Bitmap getUserIcon(int userHandle) throws RemoteException;\n\n    List<VUserInfo> getUsers(boolean excludeDying) throws RemoteException;\n\n    VUserInfo getUserInfo(int userHandle) throws RemoteException;\n\n    void setGuestEnabled(boolean enable) throws RemoteException;\n\n    boolean isGuestEnabled() throws RemoteException;\n\n    void wipeUser(int userHandle) throws RemoteException;\n\n    int getUserSerialNumber(int userHandle) throws RemoteException;\n\n    int getUserHandle(int userSerialNumber) throws RemoteException;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IVirtualLocationManager.java",
    "content": "package com.lody.virtual.server.interfaces;\n\nimport android.os.RemoteException;\n\nimport com.lody.virtual.remote.vloc.VCell;\nimport com.lody.virtual.remote.vloc.VLocation;\n\nimport java.util.List;\n\n/**\n * @author Lody\n */\npublic interface IVirtualLocationManager {\n\n    int getMode(int userId, String pkg) throws RemoteException;\n\n    void setMode(int userId, String pkg, int mode) throws RemoteException;\n\n    void setCell(int userId, String pkg, VCell cell) throws RemoteException;\n\n    void setAllCell(int userId, String pkg, List<VCell> cell) throws RemoteException;\n\n    void setNeighboringCell(int userId, String pkg, List<VCell> cell) throws RemoteException;\n\n    void setGlobalCell(VCell cell) throws RemoteException;\n\n    void setGlobalAllCell(List<VCell> cell) throws RemoteException;\n\n    void setGlobalNeighboringCell(List<VCell> cell) throws RemoteException;\n\n    VCell getCell(int userId, String pkg) throws RemoteException;\n\n    List<VCell> getAllCell(int userId, String pkg) throws RemoteException;\n\n    List<VCell> getNeighboringCell(int userId, String pkg) throws RemoteException;\n\n    void setLocation(int userId, String pkg, VLocation loc) throws RemoteException;\n\n    VLocation getLocation(int userId, String pkg) throws RemoteException;\n\n    void setGlobalLocation(VLocation loc) throws RemoteException;\n\n    VLocation getGlobalLocation() throws RemoteException;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/interfaces/IVirtualStorageService.java",
    "content": "package com.lody.virtual.server.interfaces;\n\nimport android.os.RemoteException;\n\n/**\n * @author Lody\n */\npublic interface IVirtualStorageService {\n\n    void setVirtualStorage(String packageName, int userId, String vsPath) throws RemoteException;\n\n    String getVirtualStorage(String packageName, int userId) throws RemoteException;\n\n    void setVirtualStorageState(String packageName, int userId, boolean enable) throws RemoteException;\n\n    boolean isVirtualStorageEnable(String packageName, int userId) throws RemoteException;\n}\n"
  },
  {
    "path": "VirtualApp/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.app.job.JobWorkItem;\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.text.TextUtils;\nimport android.util.Log;\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.interfaces.IJobService;\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;\nimport java.util.Map.Entry;\n\n\n/**\n * @author Lody\n */\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\npublic class VJobSchedulerService implements IJobService {\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) {\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) {\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() {\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() {\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    @TargetApi(24)\n    public JobInfo getPendingJob(int jobId) {\n        int callingUid = VBinder.getCallingUid();\n        JobInfo jobInfo = null;\n        synchronized (this.mJobStore) {\n            for (Entry key : this.mJobStore.entrySet()) {\n                JobId jobId2 = (JobId) key.getKey();\n                if (jobId2.vuid == callingUid && jobId2.clientJobId == jobId) {\n                    jobInfo = this.mScheduler.getPendingJob(jobId2.clientJobId);\n                    break;\n                }\n            }\n        }\n        return jobInfo;\n    }\n\n    @TargetApi(26)\n    public int enqueue(JobInfo job, Parcelable workItem) {\n        if (!(workItem instanceof JobWorkItem)) {\n            Log.d(\"Q_M\",\"!(workItem instanceof JobWorkItem)\");\n            return -1;\n        }\n        Log.d(\"Q_M\",\"(workItem instanceof JobWorkItem)\");\n        int callingUid = VBinder.getCallingUid();\n        int id = job.getId();\n        ComponentName service = job.getService();\n        JobId jobId = new JobId(callingUid, service.getPackageName(), id);\n        JobConfig jobConfig = (JobConfig) this.mJobStore.get(jobId);\n        if (jobConfig == null) {\n            int i = this.mGlobalJobId;\n            this.mGlobalJobId = i + 1;\n            jobConfig = new JobConfig(i, service.getClassName(), job.getExtras());\n            this.mJobStore.put(jobId, jobConfig);\n        } else {\n            jobConfig.serviceName = service.getClassName();\n            jobConfig.extras = job.getExtras();\n        }\n        saveJobs();\n        mirror.android.app.job.JobInfo.jobId.set(job, jobConfig.virtualJobId);\n        mirror.android.app.job.JobInfo.service.set(job, this.mJobProxyComponent);\n        return this.mScheduler.enqueue(job, (JobWorkItem) workItem);\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/location/VirtualLocationService.java",
    "content": "package com.lody.virtual.server.location;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.os.RemoteException;\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.vloc.VCell;\nimport com.lody.virtual.remote.vloc.VLocation;\nimport com.lody.virtual.server.interfaces.IVirtualLocationManager;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author Lody\n */\n\npublic class VirtualLocationService implements IVirtualLocationManager {\n\n    private static final VirtualLocationService sInstance = new VirtualLocationService();\n    private final SparseArray<Map<String, VLocConfig>> mLocConfigs = new SparseArray<>();\n    private final VLocConfig mGlobalConfig = new VLocConfig();\n\n    private static final int MODE_CLOSE = 0;\n    private static final int MODE_USE_GLOBAL = 1;\n    private static final int MODE_USE_SELF = 2;\n\n    private static class VLocConfig implements Parcelable {\n        int mode;\n        VCell cell;\n        List<VCell> allCell;\n        List<VCell> neighboringCell;\n        VLocation location;\n\n        public void set(VLocConfig other) {\n            this.mode = other.mode;\n            this.cell = other.cell;\n            this.allCell = other.allCell;\n            this.neighboringCell = other.neighboringCell;\n            this.location = other.location;\n        }\n\n        VLocConfig() {\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.writeParcelable(this.cell, flags);\n            dest.writeTypedList(this.allCell);\n            dest.writeTypedList(this.neighboringCell);\n            dest.writeParcelable(this.location, flags);\n        }\n\n        VLocConfig(Parcel in) {\n            this.mode = in.readInt();\n            this.cell = in.readParcelable(VCell.class.getClassLoader());\n            this.allCell = in.createTypedArrayList(VCell.CREATOR);\n            this.neighboringCell = in.createTypedArrayList(VCell.CREATOR);\n            this.location = in.readParcelable(VLocation.class.getClassLoader());\n        }\n\n        public static final Creator<VLocConfig> CREATOR = new Creator<VLocConfig>() {\n            @Override\n            public VLocConfig createFromParcel(Parcel source) {\n                return new VLocConfig(source);\n            }\n\n            @Override\n            public VLocConfig[] newArray(int size) {\n                return new VLocConfig[size];\n            }\n        };\n    }\n\n    private final PersistenceLayer mPersistenceLayer = new PersistenceLayer(VEnvironment.getVirtualLocationFile()) {\n        @Override\n        public int getCurrentVersion() {\n            return 1;\n        }\n\n        @Override\n        public void writePersistenceData(Parcel p) {\n            mGlobalConfig.writeToParcel(p, 0);\n            p.writeInt(mLocConfigs.size());\n            for (int i = 0; i < mLocConfigs.size(); i++) {\n                int userId = mLocConfigs.keyAt(i);\n                Map<String, VLocConfig> pkgs = mLocConfigs.valueAt(i);\n                p.writeInt(userId);\n                p.writeMap(pkgs);\n            }\n        }\n\n        @Override\n        public void readPersistenceData(Parcel p) {\n            mGlobalConfig.set(new VLocConfig(p));\n            mLocConfigs.clear();\n            int size = p.readInt();\n            while (size-- > 0) {\n                int userId = p.readInt();\n                //noinspection unchecked\n                Map<String, VLocConfig> pkgs = p.readHashMap(getClass().getClassLoader());\n                mLocConfigs.put(userId, pkgs);\n            }\n        }\n    };\n\n    public static VirtualLocationService get() {\n        return sInstance;\n    }\n\n    private VirtualLocationService() {\n        mPersistenceLayer.read();\n    }\n\n    @Override\n    public int getMode(int userId, String pkg) {\n        synchronized (mLocConfigs) {\n            VLocConfig config = getOrCreateConfig(userId, pkg);\n            mPersistenceLayer.save();\n            return config.mode;\n        }\n    }\n\n    @Override\n    public void setMode(int userId, String pkg, int mode) {\n        synchronized (mLocConfigs) {\n            getOrCreateConfig(userId, pkg).mode = mode;\n            mPersistenceLayer.save();\n        }\n    }\n\n    private VLocConfig getOrCreateConfig(int userId, String pkg) {\n        Map<String, VLocConfig> pkgs = mLocConfigs.get(userId);\n        if (pkgs == null) {\n            pkgs = new HashMap<>();\n            mLocConfigs.put(userId, pkgs);\n        }\n        VLocConfig config = pkgs.get(pkg);\n        if (config == null) {\n            config = new VLocConfig();\n            config.mode = MODE_CLOSE;\n            pkgs.put(pkg, config);\n        }\n        return config;\n    }\n\n    @Override\n    public void setCell(int userId, String pkg, VCell cell) {\n        getOrCreateConfig(userId, pkg).cell = cell;\n        mPersistenceLayer.save();\n    }\n\n    @Override\n    public void setAllCell(int userId, String pkg, List<VCell> cell) {\n        getOrCreateConfig(userId, pkg).allCell = cell;\n        mPersistenceLayer.save();\n    }\n\n    @Override\n    public void setNeighboringCell(int userId, String pkg, List<VCell> cell) {\n        getOrCreateConfig(userId, pkg).neighboringCell = cell;\n        mPersistenceLayer.save();\n    }\n\n    @Override\n    public void setGlobalCell(VCell cell) {\n        mGlobalConfig.cell = cell;\n        mPersistenceLayer.save();\n    }\n\n    @Override\n    public void setGlobalAllCell(List<VCell> cell) {\n        mGlobalConfig.allCell = cell;\n        mPersistenceLayer.save();\n    }\n\n    @Override\n    public void setGlobalNeighboringCell(List<VCell> cell) {\n        mGlobalConfig.neighboringCell = cell;\n        mPersistenceLayer.save();\n    }\n\n    @Override\n    public VCell getCell(int userId, String pkg) {\n        VLocConfig config = getOrCreateConfig(userId, pkg);\n        mPersistenceLayer.save();\n        switch (config.mode) {\n            case MODE_USE_SELF:\n                return config.cell;\n            case MODE_USE_GLOBAL:\n                return mGlobalConfig.cell;\n            case MODE_CLOSE:\n            default:\n                return null;\n        }\n    }\n\n    @Override\n    public List<VCell> getAllCell(int userId, String pkg) {\n        VLocConfig config = getOrCreateConfig(userId, pkg);\n        mPersistenceLayer.save();\n        switch (config.mode) {\n            case MODE_USE_SELF:\n                return config.allCell;\n            case MODE_USE_GLOBAL:\n                return mGlobalConfig.allCell;\n            case MODE_CLOSE:\n            default:\n                return null;\n        }\n    }\n\n    @Override\n    public List<VCell> getNeighboringCell(int userId, String pkg) {\n        VLocConfig config = getOrCreateConfig(userId, pkg);\n        mPersistenceLayer.save();\n        switch (config.mode) {\n            case MODE_USE_SELF:\n                return config.neighboringCell;\n            case MODE_USE_GLOBAL:\n                return mGlobalConfig.neighboringCell;\n            case MODE_CLOSE:\n            default:\n                return null;\n        }\n    }\n\n    @Override\n    public void setLocation(int userId, String pkg, VLocation loc) {\n        getOrCreateConfig(userId, pkg).location = loc;\n        mPersistenceLayer.save();\n    }\n\n    @Override\n    public VLocation getLocation(int userId, String pkg) {\n        VLocConfig config = getOrCreateConfig(userId, pkg);\n        mPersistenceLayer.save();\n        switch (config.mode) {\n            case MODE_USE_SELF:\n                return config.location;\n            case MODE_USE_GLOBAL:\n                return mGlobalConfig.location;\n            case MODE_CLOSE:\n            default:\n                return null;\n        }\n    }\n\n    @Override\n    public void setGlobalLocation(VLocation loc) {\n        mGlobalConfig.location = loc;\n    }\n\n    @Override\n    public VLocation getGlobalLocation() {\n        return mGlobalConfig.location;\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MappedMemoryRegion.java",
    "content": "package com.lody.virtual.server.memory;\n\n/**\n * @author Lody\n */\npublic class MappedMemoryRegion {\n\n    public static class FileMapping {\n        public final long offset;\n        public final long majorDeviceNumber;\n        public final long minorDeviceNumber;\n        public final long inode;\n\n        public FileMapping(long off, long major, long minor, long inode) {\n            this.offset = off;\n            this.majorDeviceNumber = major;\n            this.minorDeviceNumber = minor;\n            this.inode = inode;\n        }\n    }\n\n    public final long startAddress;\n    public final long endAddress;\n    public final boolean isReadable;\n    public final boolean isWritable;\n    public final boolean isExecutable;\n    public final boolean isShared;\n\n    public final FileMapping fileMapInfo;\n\n    public final String description;\n\n    public MappedMemoryRegion(long start, long end, boolean read, boolean write, boolean exec, boolean shared, long off, long majorDevNum, long minorDevNum, long inode, String desc) {\n        this.startAddress = start;\n        this.endAddress = end;\n        this.isReadable = read;\n        this.isWritable = write;\n        this.isExecutable = exec;\n        this.isShared = shared;\n        this.fileMapInfo = (inode == 0) ? null : new FileMapping(off, majorDevNum, minorDevNum, inode);\n        this.description = desc;\n    }\n\n    public boolean isMappedFromFile() {\n        return this.fileMapInfo != null;\n    }\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MemoryRegionParser.java",
    "content": "package com.lody.virtual.server.memory;\n\nimport java.io.BufferedReader;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * @author Lody\n */\npublic class MemoryRegionParser {\n    /**\n     * Regular Expression for /proc/self/maps line is\n     * <p>\n     * ([0-9a-f]+)  -   ([0-9a-f]+)  \\s  ([r-]) ([w-]) ([x-]) ([sp])   \\s    ([0-9a-f]+)   \\s  ([0-9a-f]+) :   ([0-9a-f]+)     \\s  (\\d+)   \\s?  (.*)\n     * StartAddress        EndAddress    Read   Write  Execute Shared        Filemap Offset   Major devnum    Minor devnum         Inode       Description\n     */\n    public static final String PATTERN = \"([0-9a-f]+)-([0-9a-f]+)\\\\s([r-])([w-])([x-])([sp])\\\\s([0-9a-f]+)\\\\s([0-9a-f]+):([0-9a-f]+)\\\\s(\\\\d+)\\\\s?(.*)\";\n    public final static Pattern MAPS_LINE_PATTERN = Pattern.compile(PATTERN, Pattern.CASE_INSENSITIVE);\n\n    private static long parseHex(String s) {\n        return Long.parseLong(s, 16);\n    }\n\n    private static MappedMemoryRegion parseMapLine(String line) {\n        line = line.trim();\n        Matcher m = MAPS_LINE_PATTERN.matcher(line);\n        if (!m.matches()) {\n            throw new IllegalArgumentException(String.format(\"The provided line does not match the pattern for /proc/$pid/maps lines. Given: %s\", line));\n        }\n\n        if (m.groupCount() != 11) // group(0) not included in this.\n        {\n            throw new InternalError(String.format(Locale.ENGLISH, \"Invalid group count: Found %d, but expected %d\", m.groupCount(), 12));\n        }\n\n        long start = parseHex(m.group(1));\n        long end = parseHex(m.group(2));\n        boolean read = m.group(3).equals(\"r\");\n        boolean write = m.group(4).equals(\"w\");\n        boolean exec = m.group(5).equals(\"x\");\n        boolean shared = m.group(6).equals(\"s\");\n        long fileOffset = parseHex(m.group(7));\n        long majorDevNum = parseHex(m.group(8));\n        long minorDevNum = parseHex(m.group(9));\n        long inode = parseHex(m.group(10));\n        String desc = m.group(11);\n\n        return new MappedMemoryRegion(start, end, read, write, exec, shared, fileOffset, majorDevNum, minorDevNum, inode, desc);\n    }\n\n\n    public static List<MappedMemoryRegion> getMemoryRegions(int pid) throws IOException {\n        List<MappedMemoryRegion> list = new LinkedList<>();\n        BufferedReader reader = new BufferedReader(new FileReader(String.format(Locale.ENGLISH, \"/proc/%d/maps\", pid)));\n        String line;\n        while ((line = reader.readLine()) != null) {\n            MappedMemoryRegion region = parseMapLine(line);\n            if (region.isReadable && region.isWritable && !region.description.endsWith(\"(deleted)\")) {\n                list.add(region);\n            }\n        }\n        return list;\n    }\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MemoryScanEngine.java",
    "content": "package com.lody.virtual.server.memory;\n\nimport com.lody.virtual.helper.utils.VLog;\n\nimport java.io.IOException;\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * @author Lody\n */\npublic class MemoryScanEngine {\n\n    private List<MappedMemoryRegion> regions;\n\n    private int pid;\n    private ProcessMemory memory;\n    private static final int PAGE = 4096;\n    private List<Match> matches;\n\n    public MemoryScanEngine(int pid) throws IOException {\n        this.pid = pid;\n        this.memory = new ProcessMemory(pid);\n        updateMemoryLayout();\n    }\n\n    public void updateMemoryLayout() {\n        try {\n            regions = MemoryRegionParser.getMemoryRegions(pid);\n        } catch (IOException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n    public List<Match> getMatches() {\n        return matches;\n    }\n\n    public void search(MemoryValue value) throws IOException {\n        matches = new LinkedList<>();\n        byte[] bytes = new byte[PAGE];\n        byte[] valueBytes = value.toBytes();\n        for (MappedMemoryRegion region : regions) {\n            long start = region.startAddress;\n            long end = region.endAddress;\n            try {\n                while (start < end) {\n                    int read = Math.min(bytes.length, (int) (end - start));\n                    read = memory.read(start, bytes, read);\n                    matches.addAll(matchBytes(region, start, bytes, read, valueBytes));\n                    start += PAGE;\n                }\n            } catch (IOException e) {\n                VLog.e(getClass().getSimpleName(), \"Unable to read region : \" + region.description);\n            }\n        }\n    }\n\n    public void modify(Match match, MemoryValue value) throws IOException {\n        memory.write(match.address, value.toBytes());\n    }\n\n    public void modifyAll(MemoryValue value) throws IOException {\n        for (Match match : matches) {\n            modify(match, value);\n        }\n    }\n\n    public class Match {\n        MappedMemoryRegion region;\n        long address;\n        int len;\n\n        public Match(MappedMemoryRegion region, long address, int len) {\n            this.region = region;\n            this.address = address;\n            this.len = len;\n        }\n    }\n\n\n    private List<Match> matchBytes(MappedMemoryRegion region, long startAddress, byte[] page, int read, byte[] value) {\n        List<Match> matches = new LinkedList<>();\n        int start = 0;\n        int len = value.length;\n        int step = 2;\n        while (start < read) {\n            boolean match = true;\n            for (int i = 0; i < len && i + start < read; i++) {\n                if (page[start + i] != value[i]) {\n                    match = false;\n                    break;\n                }\n            }\n            if (match) {\n                matches.add(new Match(region, startAddress + start, len));\n            }\n            start += step;\n        }\n        return matches;\n    }\n\n\n    public void close() {\n        try {\n            memory.close();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/MemoryValue.java",
    "content": "package com.lody.virtual.server.memory;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\n\n/**\n * @author Lody\n */\npublic abstract class MemoryValue {\n    \n    private static final ByteOrder BYTE_ORDER = ByteOrder.BIG_ENDIAN;\n\n    public enum ValueType {\n        INT2, // short\n        INT4, // int\n        INT8, // long\n    }\n\n    public abstract byte[] toBytes();\n\n\n    public static class INT2 extends MemoryValue {\n\n        private short val;\n\n        public INT2(short val) {\n            this.val = val;\n        }\n\n        @Override\n        public byte[] toBytes() {\n            ByteBuffer buffer = ByteBuffer.allocate(2);\n            return buffer.putShort(val).order(BYTE_ORDER).array();\n        }\n    }\n\n    public static class INT4 extends MemoryValue {\n\n        private int val;\n\n        public INT4(int val) {\n            this.val = val;\n        }\n\n        @Override\n        public byte[] toBytes() {\n            ByteBuffer buffer = ByteBuffer.allocate(4);\n            return buffer.order(BYTE_ORDER).putInt(val).array();\n        }\n    }\n\n    public static class INT8 extends MemoryValue {\n\n        private long val;\n\n        public INT8(long val) {\n            this.val = val;\n        }\n\n        @Override\n        public byte[] toBytes() {\n            ByteBuffer buffer = ByteBuffer.allocate(8);\n            return buffer.order(BYTE_ORDER).putLong(val).array();\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/com/lody/virtual/server/memory/ProcessMemory.java",
    "content": "package com.lody.virtual.server.memory;\n\nimport java.io.IOException;\nimport java.io.RandomAccessFile;\nimport java.util.Locale;\n\n/**\n * @author Lody\n */\npublic class ProcessMemory {\n\n    private int pid;\n    private RandomAccessFile memFile;\n\n    public ProcessMemory(int pid) throws IOException {\n        this.pid = pid;\n        this.memFile = new RandomAccessFile(String.format(Locale.ENGLISH, \"/proc/%d/mem\", pid), \"rw\");\n    }\n\n    public void write(long offset, byte[] bytes) throws IOException {\n        memFile.seek(offset);\n        memFile.write(bytes);\n    }\n\n    public int read(long offset, byte[] bytes, int len) throws IOException {\n        memFile.seek(offset);\n        return memFile.read(bytes, 0, len);\n    }\n\n    public void close() throws IOException {\n        memFile.close();\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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            if(notification.icon != 0) {\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            }\n            return true;\n        }\n        if (notification.tickerView != null) {\n\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        if(notification.icon != 0) {\n            notification.icon = getHostContext().getApplicationInfo().icon;\n        }\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": "VirtualApp/lib/src/main/java/com/lody/virtual/server/notification/NotificationCompatCompatV21.java",
    "content": "package com.lody.virtual.server.notification;\r\n\r\nimport android.annotation.TargetApi;\r\nimport android.app.Notification;\r\nimport android.content.Context;\r\nimport android.content.pm.ApplicationInfo;\r\nimport android.content.pm.PackageInfo;\r\nimport android.os.Build;\r\nimport android.os.Bundle;\r\nimport android.text.TextUtils;\r\nimport android.widget.RemoteViews;\r\n\r\nimport com.lody.virtual.helper.utils.Reflect;\r\n\r\nimport static com.lody.virtual.os.VEnvironment.getPackageResourcePath;\r\n\r\n/**\r\n * @author 247321543\r\n */\r\n@TargetApi(Build.VERSION_CODES.LOLLIPOP)\r\n/* package */ class NotificationCompatCompatV21 extends NotificationCompatCompatV14 {\r\n\r\n    private static final String TAG = NotificationCompatCompatV21.class.getSimpleName();\r\n\r\n    NotificationCompatCompatV21() {\r\n        super();\r\n    }\r\n\r\n    @Override\r\n    public boolean dealNotification(int id, Notification notification, String packageName) {\r\n        Context appContext = getAppContext(packageName);\r\n        return resolveRemoteViews(appContext, packageName, notification)\r\n                || resolveRemoteViews(appContext, packageName, notification.publicVersion);\r\n    }\r\n\r\n    private boolean resolveRemoteViews(Context appContext, String packageName, Notification notification) {\r\n        if (notification == null) {\r\n            return false;\r\n        }\r\n        String sourcePath = null;\r\n        PackageInfo packageInfo = getPackageInfo(packageName);\r\n        ApplicationInfo host = getHostContext().getApplicationInfo();\r\n        if (packageInfo != null) {\r\n            sourcePath = packageInfo.applicationInfo.sourceDir;\r\n        }\r\n        if (TextUtils.isEmpty(sourcePath)) {\r\n            sourcePath = getPackageResourcePath(packageName).getAbsolutePath();\r\n        }\r\n\r\n        //Fix RemoteViews\r\n        getNotificationFixer().fixNotificationRemoteViews(appContext, notification);\r\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\r\n            getNotificationFixer().fixIcon(notification.getSmallIcon(), appContext, packageInfo != null);\r\n            getNotificationFixer().fixIcon(notification.getLargeIcon(), appContext, packageInfo != null);\r\n        } else {\r\n            getNotificationFixer().fixIconImage(appContext.getResources(), notification.contentView, false, notification);\r\n        }\r\n        notification.icon = host.icon;\r\n\r\n        ApplicationInfo proxyApplicationInfo = new ApplicationInfo(host);\r\n\r\n        proxyApplicationInfo.packageName = packageName;\r\n        proxyApplicationInfo.publicSourceDir = sourcePath;\r\n        proxyApplicationInfo.sourceDir = sourcePath;\r\n\r\n        fixApplicationInfo(notification.tickerView, proxyApplicationInfo);\r\n        fixApplicationInfo(notification.contentView, proxyApplicationInfo);\r\n        fixApplicationInfo(notification.bigContentView, proxyApplicationInfo);\r\n        fixApplicationInfo(notification.headsUpContentView, proxyApplicationInfo);\r\n        Bundle bundle = Reflect.on(notification).get(\"extras\");\r\n        if (bundle != null) {\r\n            bundle.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, proxyApplicationInfo);\r\n        }\r\n        return true;\r\n    }\r\n\r\n    private ApplicationInfo getApplicationInfo(Notification notification) {\r\n        ApplicationInfo ai = getApplicationInfo(notification.tickerView);\r\n        if (ai != null) {\r\n            return ai;\r\n        }\r\n        ai = getApplicationInfo(notification.contentView);\r\n        if (ai != null) {\r\n            return ai;\r\n        }\r\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\r\n            ai = getApplicationInfo(notification.bigContentView);\r\n            if (ai != null) {\r\n                return ai;\r\n            }\r\n        }\r\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\r\n            ai = getApplicationInfo(notification.headsUpContentView);\r\n            if (ai != null) {\r\n                return ai;\r\n            }\r\n        }\r\n        return null;\r\n    }\r\n\r\n    private ApplicationInfo getApplicationInfo(RemoteViews remoteViews) {\r\n        if (remoteViews != null) {\r\n            return mirror.android.widget.RemoteViews.mApplication.get(remoteViews);\r\n        }\r\n        return null;\r\n    }\r\n\r\n    private void fixApplicationInfo(RemoteViews remoteViews, ApplicationInfo ai) {\r\n        if (remoteViews != null) {\r\n            mirror.android.widget.RemoteViews.mApplication.set(remoteViews, ai);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "VirtualApp/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.OSUtils;\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    @TargetApi(Build.VERSION_CODES.M)\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                        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 || notification.icon == 0) 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                //emui\n                if(OSUtils.getInstance().isEmui()) {\n                    if (notification.largeIcon == null) {\n                        notification.largeIcon = bitmap;\n                    }\n                }\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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/server/notification/ReflectionActionCompat.java",
    "content": "package com.lody.virtual.server.notification;\r\n\r\nimport android.widget.RemoteViews;\r\n\r\n\r\n/* package */ class ReflectionActionCompat {\r\n    private static Class ReflectionActionClass;\r\n    private static final String ReflectionAction = \"ReflectionAction\";\r\n\r\n    static final int TAG = 2;\r\n\r\n    static final int BOOLEAN = 1;\r\n    static final int BYTE = 2;\r\n    static final int SHORT = 3;\r\n    static final int INT = 4;\r\n    static final int LONG = 5;\r\n    static final int FLOAT = 6;\r\n    static final int DOUBLE = 7;\r\n    static final int CHAR = 8;\r\n    static final int STRING = 9;\r\n    static final int CHAR_SEQUENCE = 10;\r\n    static final int URI = 11;\r\n    // BITMAP actions are never stored in the list of actions. They are only used locally\r\n    // to implement BitmapReflectionAction, which eliminates duplicates using BitmapCache.\r\n    static final int BITMAP = 12;\r\n    static final int BUNDLE = 13;\r\n    static final int INTENT = 14;\r\n    static final int COLOR_STATE_LIST = 15;\r\n    static final int ICON = 16;\r\n\r\n    static {\r\n        try {\r\n            ReflectionActionClass = Class.forName(RemoteViews.class.getName() + \"$\" + ReflectionAction);\r\n        } catch (ClassNotFoundException e) {\r\n        }\r\n    }\r\n\r\n    static boolean isInstance(Object object) {\r\n        return ReflectionActionClass != null && ReflectionActionClass.isInstance(object);\r\n    }\r\n}\r\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/server/notification/VNotificationManagerService.java",
    "content": "package com.lody.virtual.server.notification;\r\n\r\nimport android.app.NotificationManager;\r\nimport android.content.Context;\r\nimport android.text.TextUtils;\r\n\r\nimport com.lody.virtual.helper.utils.VLog;\r\nimport com.lody.virtual.server.interfaces.INotificationManager;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.HashMap;\r\nimport java.util.List;\r\nimport java.util.concurrent.atomic.AtomicReference;\r\n\r\npublic class VNotificationManagerService implements INotificationManager {\r\n    private static final AtomicReference<VNotificationManagerService> gService = new AtomicReference<>();\r\n    private NotificationManager mNotificationManager;\r\n    static final String TAG = NotificationCompat.class.getSimpleName();\r\n    private final List<String> mDisables = new ArrayList<>();\r\n    //VApp's Notifications\r\n    private final HashMap<String, List<NotificationInfo>> mNotifications = new HashMap<>();\r\n    private Context mContext;\r\n\r\n    private VNotificationManagerService(Context context) {\r\n        mContext = context;\r\n        mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);\r\n    }\r\n\r\n    public static void systemReady(Context context) {\r\n        VNotificationManagerService instance = new VNotificationManagerService(context);\r\n        gService.set(instance);\r\n    }\r\n\r\n    public static VNotificationManagerService get() {\r\n        return gService.get();\r\n    }\r\n\r\n    /***\r\n     * fake notification's id\r\n     *\r\n     * @param id          notification's id\r\n     * @param packageName notification's package\r\n     * @param userId      user\r\n     * @return\r\n     */\r\n    @Override\r\n    public int dealNotificationId(int id, String packageName, String tag, int userId) {\r\n        return id;\r\n    }\r\n\r\n    /***\r\n     * fake notification's tag\r\n     *\r\n     * @param id          notification's id\r\n     * @param packageName notification's package\r\n     * @param tag         notification's tag\r\n     * @param userId      user\r\n     * @return\r\n     */\r\n    @Override\r\n    public String dealNotificationTag(int id, String packageName, String tag, int userId) {\r\n        if (TextUtils.equals(mContext.getPackageName(), packageName)) {\r\n            return tag;\r\n        }\r\n        if (tag == null) {\r\n            return packageName + \"@\" + userId;\r\n        }\r\n        return packageName + \":\" + tag + \"@\" + userId;\r\n    }\r\n\r\n    @Override\r\n    public boolean areNotificationsEnabledForPackage(String packageName, int userId) {\r\n        return !mDisables.contains(packageName + \":\" + userId);\r\n    }\r\n\r\n    @Override\r\n    public void setNotificationsEnabledForPackage(String packageName, boolean enable, int userId) {\r\n        String key = packageName + \":\" + userId;\r\n        if (enable) {\r\n            if (mDisables.contains(key)) {\r\n                mDisables.remove(key);\r\n            }\r\n        } else {\r\n            if (!mDisables.contains(key)) {\r\n                mDisables.add(key);\r\n            }\r\n        }\r\n        //TODO: save mDisables ?\r\n    }\r\n\r\n    @Override\r\n    public void addNotification(int id, String tag, String packageName, int userId) {\r\n        NotificationInfo notificationInfo = new NotificationInfo(id, tag, packageName, userId);\r\n        synchronized (mNotifications) {\r\n            List<NotificationInfo> list = mNotifications.get(packageName);\r\n            if (list == null) {\r\n                list = new ArrayList<>();\r\n                mNotifications.put(packageName, list);\r\n            }\r\n            if (!list.contains(notificationInfo)) {\r\n                list.add(notificationInfo);\r\n            }\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void cancelAllNotification(String packageName, int userId) {\r\n        List<NotificationInfo> infos = new ArrayList<>();\r\n        synchronized (mNotifications) {\r\n            List<NotificationInfo> list = mNotifications.get(packageName);\r\n            if (list != null) {\r\n                int count = list.size();\r\n                for (int i = count - 1; i >= 0; i--) {\r\n                    NotificationInfo info = list.get(i);\r\n                    if (info.userId == userId) {\r\n                        infos.add(info);\r\n                        list.remove(i);\r\n                    }\r\n                }\r\n            }\r\n        }\r\n        for (NotificationInfo info : infos) {\r\n            VLog.d(TAG, \"cancel \" + info.tag + \" \" + info.id);\r\n            mNotificationManager.cancel(info.tag, info.id);\r\n        }\r\n    }\r\n\r\n    private static class NotificationInfo {\r\n        int id;\r\n        String tag;\r\n        String packageName;\r\n        int userId;\r\n\r\n        NotificationInfo(int id, String tag, String packageName, int userId) {\r\n            this.id = id;\r\n            this.tag = tag;\r\n            this.packageName = packageName;\r\n            this.userId = userId;\r\n        }\r\n\r\n        @Override\r\n        public boolean equals(Object obj) {\r\n            if (obj instanceof NotificationInfo) {\r\n                NotificationInfo that = (NotificationInfo) obj;\r\n                return that.id == id && TextUtils.equals(that.tag, tag)\r\n                        && TextUtils.equals(packageName, that.packageName)\r\n                        && that.userId == userId;\r\n            }\r\n            return super.equals(obj);\r\n        }\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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\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": "VirtualApp/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    @Deprecated\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": "VirtualApp/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": "VirtualApp/lib/src/main/java/com/lody/virtual/server/pm/PrivilegeAppOptimizer.java",
    "content": "package com.lody.virtual.server.pm;\n\nimport android.content.Intent;\nimport android.util.Log;\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": "VirtualApp/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": "VirtualApp/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.IBinder;\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.client.env.VirtualRuntime;\nimport com.lody.virtual.helper.ArtDexOptimizer;\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.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.IAppManager;\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\nimport dalvik.system.DexFile;\n\n/**\n * @author Lody\n */\npublic class VAppManagerService implements IAppManager {\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\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            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        chmodPackageDictionary(cacheFile);\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    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        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            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        File appDir = VEnvironment.getDataAppPackageDirectory(pkg.packageName);\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        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\n        NativeLibraryHelperCompat.copyNativeBinaries(new File(path), libDir);\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        chmodPackageDictionary(packageFile);\n        PackageSetting ps;\n        if (existSetting != null) {\n            ps = existSetting;\n        } else {\n            ps = new PackageSetting();\n        }\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        PackageParserEx.savePackageCache(pkg);\n        PackageCacheManager.put(pkg, ps);\n        mPersistenceLayer.save();\n        if (!dependSystem) {\n            boolean runDexOpt = false;\n            if (VirtualRuntime.isArt()) {\n                try {\n                    ArtDexOptimizer.interpretDex2Oat(ps.apkPath, VEnvironment.getOdexFile(ps.packageName).getPath());\n                } catch (IOException e) {\n                    e.printStackTrace();\n                    runDexOpt = true;\n                }\n            } else {\n                runDexOpt = true;\n            }\n            if (runDexOpt) {\n                try {\n                    DexFile.loadDex(ps.apkPath, VEnvironment.getOdexFile(ps.packageName).getPath(), 0).close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        BroadcastSystem.get().startApp(pkg);\n        if (notify) {\n            notifyAppInstalled(ps, -1);\n        }\n        res.isSuccess = true;\n        return res;\n    }\n\n\n    @Override\n    public synchronized boolean installPackageAsUser(int userId, String packageName) {\n        if (VUserManagerService.get().exists(userId)) {\n            PackageSetting ps = PackageCacheManager.getSetting(packageName);\n            if (ps != null) {\n                if (!ps.isInstalled(userId)) {\n                    ps.setInstalled(userId, true);\n                    notifyAppInstalled(ps, userId);\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 IBinder.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": "VirtualApp/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.IBinder;\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.interfaces.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.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\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\n\n/**\n * @author Lody\n */\npublic class VPackageManagerService implements IPackageManager {\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 | PackageManager.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                //从候选列表中查找一个最合适的，如果候选列表没有最合适的返回null\n                //然后从系统中查找合适的打开intent\n                ResolveInfo ri = findPreferredActivity(intent, resolvedType,\n                        flags, query, r0.priority);\n                //noinspection ConstantConditions\n                if (ri != null) {\n                    return ri;\n                }\n\n                return null;\n            }\n        }\n        return null;\n    }\n\n    private ResolveInfo findPreferredActivity(Intent intent, String resolvedType, int flags, List<ResolveInfo> query, int priority) {\n\n        try {\n            Class clazz = Class.forName(\"com.virtual.helper.VALibHelper\");\n            Method method = clazz.getDeclaredMethod(\"findPreferredActivity\", Intent.class, String.class, int.class, List.class, int.class);\n            return (ResolveInfo) method.invoke(null, intent, resolvedType, flags, query, priority);\n        } catch (Exception e) {\n            e.printStackTrace();\n            return query.get(0);\n        }\n\n//        return null;\n    }\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 IBinder 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": "VirtualApp/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.interfaces.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 implements IUserManager {\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     *\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     *\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\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     *\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     *\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}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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    public static VPackage parsePackage(File packageFile) throws Throwable {\n        PackageParser parser = PackageParserCompat.createParser(packageFile);\n        PackageParser.Package p = PackageParserCompat.parsePackage(parser, packageFile, 0);\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            PackageParserCompat.collectCertificates(parser, p, PackageParser.PARSE_IS_SYSTEM);\n        }\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 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        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            if(Build.VERSION.SDK_INT < 26) {\n                ApplicationInfoN.deviceEncryptedDataDir.set(ai, ai.dataDir);\n                ApplicationInfoN.credentialEncryptedDataDir.set(ai, ai.dataDir);\n            }\n            ApplicationInfoN.deviceProtectedDataDir.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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.interfaces.IVirtualStorageService;\nimport com.lody.virtual.server.pm.VUserManagerService;\n\nimport java.util.HashMap;\n\n/**\n * @author Lody\n */\n\npublic class VirtualStorageService implements IVirtualStorageService {\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) {\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) {\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) {\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) {\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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        Field[] fields = mappingClass.getDeclaredFields();\n        for (Field field : fields) {\n            try {\n                if (Modifier.isStatic(field.getModifiers())) {\n                    Constructor<?> constructor = REF_TYPES.get(field.getType());\n                    if (constructor != null) {\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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@SuppressWarnings(\"unchecked\")\npublic class RefMethod<T> {\n    private Method method;\n\n    public RefMethod(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            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": "VirtualApp/lib/src/main/java/mirror/RefObject.java",
    "content": "package mirror;\n\nimport java.lang.reflect.Field;\n\n@SuppressWarnings(\"unchecked\")\npublic class RefObject<T> {\n    private Field field;\n\n    public RefObject(Class<?> cls, Field field) throws NoSuchFieldException {\n        this.field = cls.getDeclaredField(field.getName());\n        this.field.setAccessible(true);\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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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    public static RefObject<Object> mDisplayAdjustments;\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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.MethodReflectParams;\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.RefStaticMethod;\n\n/**\n * @author Lody\n */\n\npublic class IApplicationThreadOreo {\n\n    public static Class<?> TYPE = RefClass.load(IApplicationThreadOreo.class, \"android.app.IApplicationThread\");\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    @MethodReflectParams({\"android.os.IBinder\", \"android.content.pm.ParceledListSlice\"})\n    public static RefMethod<Void> scheduleServiceArgs;\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/lib/src/main/java/mirror/android/app/IServiceConnectionO.java",
    "content": "package mirror.android.app;\n\nimport android.content.ComponentName;\nimport android.os.IBinder;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefMethod;\n\npublic class IServiceConnectionO {\n    public static Class<?> TYPE = RefClass.load(IServiceConnectionO.class, \"android.app.IServiceConnection\");\n\n    @MethodParams({ComponentName.class, IBinder.class, boolean.class})\n    public static RefMethod<Void> connected;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/mirror/android/app/IUsageStatsManager.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 * Created by caokai on 2017/9/8.\n */\n\npublic class IUsageStatsManager {\n    public static Class<?> TYPE = RefClass.load(IUsageStatsManager.class, \"android.app.usage.IUsageStatsManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(IUsageStatsManager.Stub.class, \"android.app.usage.IUsageStatsManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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.IServiceConnection;\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.Handler;\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    @MethodParams({ServiceConnection.class, Context.class, Handler.class, int.class})\n    public static RefMethod<IServiceConnection> getServiceDispatcher;\n    @MethodParams({Context.class, ServiceConnection.class})\n    public static RefMethod<IServiceConnection> forgetServiceDispatcher;\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": "VirtualApp/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": "VirtualApp/lib/src/main/java/mirror/android/app/LoadedApkICS.java",
    "content": "package mirror.android.app;\n\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class LoadedApkICS {\n    public static Class<?> Class = RefClass.load(LoadedApkICS.class, \"android.app.LoadedApk\");\n    public static RefObject<Object> mCompatibilityInfo;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/java/mirror/android/app/LoadedApkKitkat.java",
    "content": "package mirror.android.app;\n\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\npublic class LoadedApkKitkat {\n    public static Class<?> Class = RefClass.load(LoadedApkKitkat.class, \"android.app.LoadedApk\");\n    public static RefObject<Object> mDisplayAdjustments;\n}"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/mirror/android/app/ServiceStartArgs.java",
    "content": "package mirror.android.app;\n\nimport android.content.Intent;\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 */\npublic class ServiceStartArgs {\n    public static Class<?> TYPE = RefClass.load(ServiceStartArgs.class, \"android.app.ServiceStartArgs\");\n    @MethodParams({boolean.class, int.class, int.class, Intent.class})\n    public static RefConstructor<Object> ctor;\n    public static RefBoolean taskRemoved;\n    public static RefInt startId;\n    public static RefInt flags;\n    public static RefObject<Intent> args;\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/mirror/android/app/admin/IDevicePolicyManager.java",
    "content": "package mirror.android.app.admin;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\n/**\n * Created by wy on 2017/10/20.\n */\n\npublic class IDevicePolicyManager {\n    public static Class<?> TYPE = RefClass.load(IDevicePolicyManager.class, \"android.app.admin.IDevicePolicyManager\");\n\n    public static class  Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.app.admin.IDevicePolicyManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/mirror/android/app/job/JobWorkItem.java",
    "content": "package mirror.android.app.job;\n\nimport android.annotation.TargetApi;\nimport android.content.Intent;\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefInt;\nimport mirror.RefMethod;\nimport mirror.RefObject;\n\n@TargetApi(26)\npublic class JobWorkItem {\n    public static Class<?> TYPE = RefClass.load(JobWorkItem.class, android.app.job.JobWorkItem.class);\n    @MethodParams({Intent.class})\n    public static RefConstructor<Object> ctor;\n    public static RefMethod<Intent> getIntent;\n    public static RefInt mDeliveryCount;\n    public static RefObject<Object> mGrants;\n    public static RefInt mWorkId;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/java/mirror/android/bluetooth/IBluetooth.java",
    "content": "package mirror.android.bluetooth;\r\n\r\nimport android.os.IBinder;\r\nimport android.os.IInterface;\r\n\r\nimport mirror.MethodParams;\r\nimport mirror.RefClass;\r\nimport mirror.RefStaticMethod;\r\n\r\npublic class IBluetooth {\r\n    /**\r\n     * @see android.bluetooth.IBluetooth\r\n     * */\r\n    public static Class<?> TYPE = RefClass.load(IBluetooth.class, \"android.bluetooth.IBluetooth\");\r\n    /**\r\n     * @see android.bluetooth.IBluetooth.Stub\r\n     * */\r\n    public static class Stub {\r\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.bluetooth.IBluetooth$Stub\");\r\n        @MethodParams({IBinder.class})\r\n        public static RefStaticMethod<IInterface> asInterface;\r\n    }\r\n}\r\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/mirror/android/bluetooth/IBluetoothManager.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 IBluetoothManager {\n    /**\n     * @see android.bluetooth.IBluetoothManager\n     * */\n    public static Class<?> TYPE = RefClass.load(IBluetoothManager.class, \"android.bluetooth.IBluetoothManager\");\n    /**\n     * @see android.bluetooth.IBluetoothManager.Stub\n     * */\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.bluetooth.IBluetoothManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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<?> TYPE = 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<?> TYPE = 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<?> TYPE = 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<?> TYPE = 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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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    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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/mirror/android/hardware/fingerprint/IFingerprintService.java",
    "content": "package mirror.android.hardware.fingerprint;\n\nimport android.os.*;\n\nimport mirror.*;\n\n/**\n * Created by natsuki on 12/10/2017.\n */\n\npublic class IFingerprintService {\n\n    public static Class<?> TYPE = RefClass.load(IFingerprintService.class, \"android.hardware.fingerprint.IFingerprintService\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.hardware.fingerprint.IFingerprintService$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/mirror/android/location/LocationManager.java",
    "content": "package mirror.android.location;\n\nimport android.location.Location;\nimport android.location.LocationListener;\nimport android.os.Bundle;\n\nimport java.util.HashMap;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefMethod;\nimport mirror.RefObject;\n\npublic class LocationManager {\n    public static Class<?> TYPE = RefClass.load(LocationManager.class, \"android.location.LocationManager\");\n    public static RefObject<HashMap> mGnssNmeaListeners;\n    public static RefObject<HashMap> mGnssStatusListeners;\n    public static RefObject<HashMap> mGpsNmeaListeners;\n    public static RefObject<HashMap> mGpsStatusListeners;\n    public static RefObject<HashMap> mListeners;\n    public static RefObject<HashMap> mNmeaListeners;\n\n    public static class GnssStatusListenerTransport {\n        public static Class<?> TYPE = RefClass.load(GnssStatusListenerTransport.class, \"android.location.LocationManager$GnssStatusListenerTransport\");\n        public static RefObject<Object> mGpsListener;\n        public static RefObject<Object> mGpsNmeaListener;\n        @MethodParams({int.class})\n        public static RefMethod<Void> onFirstFix;\n        public static RefMethod<Void> onGnssStarted;\n        @MethodParams({long.class, String.class})\n        public static RefMethod<Void> onNmeaReceived;\n        @MethodParams({int.class, int[].class, float[].class, float[].class, float[].class, float[].class})\n        public static RefMethod<Void> onSvStatusChanged;\n        public static RefObject<Object> this$0;\n    }\n\n    public static class GpsStatusListenerTransport {\n        public static Class<?> TYPE = RefClass.load(GpsStatusListenerTransport.class, \"android.location.LocationManager$GpsStatusListenerTransport\");\n        public static RefObject<Object> mListener;\n        public static RefObject<Object> mNmeaListener;\n        @MethodParams({int.class})\n        public static RefMethod<Void> onFirstFix;\n        public static RefMethod<Void> onGpsStarted;\n        @MethodParams({long.class, String.class})\n        public static RefMethod<Void> onNmeaReceived;\n        @MethodParams({int.class, int[].class, float[].class, float[].class, float[].class, int.class, int.class, int.class})\n        public static RefMethod<Void> onSvStatusChanged;\n        public static RefObject<Object> this$0;\n    }\n\n    public static class GpsStatusListenerTransportOPPO_R815T {\n        public static Class<?> TYPE = RefClass.load(GpsStatusListenerTransportOPPO_R815T.class, \"android.location.LocationManager$GpsStatusListenerTransport\");\n        @MethodParams({int.class, int[].class, float[].class, float[].class, float[].class, int[].class, int[].class, int[].class, int.class})\n        public static RefMethod<Void> onSvStatusChanged;\n    }\n\n    public static class GpsStatusListenerTransportSumsungS5 {\n        public static Class<?> TYPE = RefClass.load(GpsStatusListenerTransportSumsungS5.class, \"android.location.LocationManager$GpsStatusListenerTransport\");\n        @MethodParams({int.class, int[].class, float[].class, float[].class, float[].class, int.class, int.class, int.class, int[].class})\n        public static RefMethod<Void> onSvStatusChanged;\n    }\n\n    public static class GpsStatusListenerTransportVIVO {\n        public static Class<?> TYPE = RefClass.load(GpsStatusListenerTransportVIVO.class, \"android.location.LocationManager$GpsStatusListenerTransport\");\n        @MethodParams({int.class, int[].class, float[].class, float[].class, float[].class, int.class, int.class, int.class, long[].class})\n        public static RefMethod<Void> onSvStatusChanged;\n    }\n\n    public static class ListenerTransport {\n        public static Class<?> TYPE = RefClass.load(ListenerTransport.class, \"android.location.LocationManager$ListenerTransport\");\n        public static RefObject<LocationListener> mListener;\n        @MethodParams({Location.class})\n        public static RefMethod<Void> onLocationChanged;\n        @MethodParams({String.class})\n        public static RefMethod<Void> onProviderDisabled;\n        @MethodParams({String.class})\n        public static RefMethod<Void> onProviderEnabled;\n        @MethodParams({String.class, int.class, Bundle.class})\n        public static RefMethod<Void> onStatusChanged;\n        public static RefObject<Object> this$0;\n    }\n}"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/mirror/android/os/BaseBundle.java",
    "content": "package mirror.android.os;\n\nimport android.os.Parcel;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\n\npublic class BaseBundle {\n    public static Class<?> TYPE = RefClass.load(BaseBundle.class, \"android.os.BaseBundle\");\n    public static RefObject<Parcel> mParcelledData;\n}"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/mirror/android/os/BundleICS.java",
    "content": "package mirror.android.os;\n\nimport android.os.Parcel;\n\nimport mirror.RefClass;\nimport mirror.RefObject;\n\n\npublic class BundleICS {\n    public static Class<?> TYPE = RefClass.load(BundleICS.class, \"android.os.Bundle\");\n    public static RefObject<Parcel> mParcelledData;\n}"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/mirror/android/os/storage/IStorageManager.java",
    "content": "package mirror.android.os.storage;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\npublic class IStorageManager {\n    public static Class<?> Class = RefClass.load(IStorageManager.class, \"android.os.storage.IStorageManager\");\n\n    public static class Stub {\n        public static Class<?> Class = RefClass.load(Stub.class, \"android.os.storage.IStorageManager$Stub\");\n        @MethodParams({IBinder.class})\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n}"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/java/mirror/android/telephony/CellIdentityCdma.java",
    "content": "package mirror.android.telephony;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefInt;\n\n/**\n * @author Lody\n */\n\n@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\npublic class CellIdentityCdma {\n    public static Class<?> TYPE = RefClass.load(CellIdentityCdma.class, android.telephony.CellIdentityCdma.class);\n    public static RefConstructor<android.telephony.CellIdentityCdma> ctor;\n    public static RefInt mNetworkId;\n    public static RefInt mSystemId;\n    public static RefInt mBasestationId;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/mirror/android/telephony/CellIdentityGsm.java",
    "content": "package mirror.android.telephony;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefInt;\n\n/**\n * @author Lody\n */\n\n@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\npublic class CellIdentityGsm {\n    public static Class<?> TYPE = RefClass.load(CellIdentityGsm.class, android.telephony.CellIdentityGsm.class);\n    public static RefConstructor<android.telephony.CellIdentityGsm> ctor;\n    public static RefInt mMcc;\n    public static RefInt mMnc;\n    public static RefInt mLac;\n    public static RefInt mCid;\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/mirror/android/telephony/CellInfoCdma.java",
    "content": "package mirror.android.telephony;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.telephony.CellSignalStrengthCdma;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefObject;\n\n/**\n * @author Lody\n */\n\n@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\npublic class CellInfoCdma {\n    public static Class<?> TYPE = RefClass.load(CellInfoCdma.class, android.telephony.CellInfoCdma.class);\n    public static RefConstructor<android.telephony.CellInfoCdma> ctor;\n    public static RefObject<android.telephony.CellIdentityCdma> mCellIdentityCdma;\n    public static RefObject<CellSignalStrengthCdma> mCellSignalStrengthCdma;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/mirror/android/telephony/CellInfoGsm.java",
    "content": "package mirror.android.telephony;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefObject;\n\n/**\n * @author Lody\n */\n\n@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\npublic class CellInfoGsm {\n    public static Class<?> TYPE = RefClass.load(CellInfoGsm.class, android.telephony.CellInfoGsm.class);\n    public static RefConstructor<android.telephony.CellInfoGsm> ctor;\n    public static RefObject<android.telephony.CellIdentityGsm> mCellIdentityGsm;\n    public static RefObject<android.telephony.CellSignalStrengthGsm> mCellSignalStrengthGsm;\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/mirror/android/telephony/CellSignalStrengthCdma.java",
    "content": "package mirror.android.telephony;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefInt;\n\n/**\n * @author Lody\n */\n\n@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\npublic class CellSignalStrengthCdma {\n    public static Class<?> TYPE = RefClass.load(CellSignalStrengthCdma.class, android.telephony.CellSignalStrengthCdma.class);\n    public static RefConstructor<android.telephony.CellSignalStrengthCdma> ctor;\n    public static RefInt mCdmaDbm;\n    public static RefInt mCdmaEcio;\n    public static RefInt mEvdoDbm;\n    public static RefInt mEvdoEcio;\n    public static RefInt mEvdoSnr;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/mirror/android/telephony/CellSignalStrengthGsm.java",
    "content": "package mirror.android.telephony;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\n\nimport mirror.RefClass;\nimport mirror.RefConstructor;\nimport mirror.RefInt;\n\n/**\n * @author Lody\n */\n\n@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\npublic class CellSignalStrengthGsm {\n    public static Class<?> TYPE = RefClass.load(CellSignalStrengthGsm.class, android.telephony.CellSignalStrengthGsm.class);\n    public static RefConstructor<android.telephony.CellSignalStrengthGsm> ctor;\n    public static RefInt mSignalStrength;\n    public static RefInt mBitErrorRate;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/java/mirror/android/telephony/NeighboringCellInfo.java",
    "content": "package mirror.android.telephony;\n\nimport mirror.RefClass;\nimport mirror.RefInt;\n\n/**\n * @author Lody\n */\n\npublic class NeighboringCellInfo {\n    public static Class<?> TYPE = RefClass.load(NeighboringCellInfo.class, android.telephony.NeighboringCellInfo.class);\n    public static RefInt mLac;\n    public static RefInt mCid;\n    public static RefInt mRssi;\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/lib/src/main/java/mirror/android/view/CompatibilityInfoHolder.java",
    "content": "package mirror.android.view;\n\n\nimport mirror.MethodReflectParams;\nimport mirror.RefClass;\nimport mirror.RefMethod;\n\npublic class CompatibilityInfoHolder {\n    public static Class<?> Class = RefClass.load(CompatibilityInfoHolder.class, \"android.view.CompatibilityInfoHolder\");\n    @MethodReflectParams({\"android.content.res.CompatibilityInfo\"})\n    public static RefMethod<Void> set;\n}"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/lib/src/main/java/mirror/android/view/DisplayAdjustments.java",
    "content": "package mirror.android.view;\n\n\nimport mirror.MethodReflectParams;\nimport mirror.RefClass;\nimport mirror.RefMethod;\n\npublic class DisplayAdjustments {\n    public static Class<?> Class = RefClass.load(DisplayAdjustments.class, \"android.view.DisplayAdjustments\");\n    @MethodReflectParams({\"android.content.res.CompatibilityInfo\"})\n    public static RefMethod<Void> setCompatibilityInfo;\n}"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/lib/src/main/java/mirror/android/view/IAutoFillManager.java",
    "content": "package mirror.android.view;\n\nimport android.os.IBinder;\nimport android.os.IInterface;\n\nimport mirror.MethodParams;\nimport mirror.RefClass;\nimport mirror.RefStaticMethod;\n\n/**\n * @author 陈磊.\n */\n\npublic class IAutoFillManager {\n\n    public static Class<?> TYPE = RefClass.load(IAutoFillManager.class, \"android.view.autofill.IAutoFillManager\");\n\n    public static class Stub {\n        public static Class<?> TYPE = RefClass.load(Stub.class, \"android.view.autofill.IAutoFillManager$Stub\");\n        @MethodParams(IBinder.class)\n        public static RefStaticMethod<IInterface> asInterface;\n    }\n\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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        public static RefStaticMethod<String> getCurrentInstructionSet;\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/jni/Android.mk",
    "content": "LOCAL_PATH := $(call my-dir)\nMAIN_LOCAL_PATH := $(call my-dir)\ninclude $(CLEAR_VARS)\nLOCAL_MODULE := va++\n\nLOCAL_CFLAGS := -Wno-error=format-security -fpermissive -DLOG_TAG=\\\"VA++\\\"\nLOCAL_CFLAGS += -fno-rtti -fno-exceptions\n\nLOCAL_C_INCLUDES += $(MAIN_LOCAL_PATH)\nLOCAL_C_INCLUDES += $(MAIN_LOCAL_PATH)/Foundation\nLOCAL_C_INCLUDES += $(MAIN_LOCAL_PATH)/Jni\n\nLOCAL_SRC_FILES := Jni/VAJni.cpp \\\n\t\t\t\t   Foundation/IOUniformer.cpp \\\n\t\t\t\t   Foundation/VMPatch.cpp \\\n\t\t\t\t   Foundation/SymbolFinder.cpp \\\n\t\t\t\t   Foundation/Path.cpp \\\n\t\t\t\t   Foundation/SandboxFs.cpp \\\n\t\t\t\t   Substrate/hde64.c \\\n                   Substrate/SubstrateDebug.cpp \\\n                   Substrate/SubstrateHook.cpp \\\n                   Substrate/SubstratePosixMemory.cpp \\\n\nLOCAL_LDLIBS := -llog -latomic\nLOCAL_STATIC_LIBRARIES := fb\n\ninclude $(BUILD_SHARED_LIBRARY)\ninclude $(MAIN_LOCAL_PATH)/fb/Android.mk"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Application.mk",
    "content": "APP_ABI := armeabi-v7a x86\nAPP_PLATFORM := android-14\nAPP_STL := gnustl_static\nAPP_OPTIM := release\nVA_ROOT          := $(call my-dir)\nNDK_MODULE_PATH  := $(NDK_MODULE_PATH):$(VA_ROOT)"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Foundation/IOUniformer.cpp",
    "content": "//\n// VirtualApp Native Project\n//\n#include <unistd.h>\n#include <stdlib.h>\n#include <fb/include/fb/ALog.h>\n#include <Substrate/CydiaSubstrate.h>\n\n#include \"IOUniformer.h\"\n#include \"SandboxFs.h\"\n#include \"Path.h\"\n#include \"SymbolFinder.h\"\n\nbool iu_loaded = false;\n\nvoid IOUniformer::init_env_before_all() {\n    if (iu_loaded)\n        return;\n    char *api_level_chars = getenv(\"V_API_LEVEL\");\n    char *preview_api_level_chars = getenv(\"V_PREVIEW_API_LEVEL\");\n    if (api_level_chars) {\n        ALOGE(\"Enter init before all.\");\n        int api_level = atoi(api_level_chars);\n        int preview_api_level;\n        preview_api_level = atoi(preview_api_level_chars);\n        char keep_env_name[25];\n        char forbid_env_name[25];\n        char replace_src_env_name[25];\n        char replace_dst_env_name[25];\n        int i = 0;\n        while (true) {\n            sprintf(keep_env_name, \"V_KEEP_ITEM_%d\", i);\n            char *item = getenv(keep_env_name);\n            if (!item) {\n                break;\n            }\n            add_keep_item(item);\n            i++;\n        }\n        i = 0;\n        while (true) {\n            sprintf(forbid_env_name, \"V_FORBID_ITEM_%d\", i);\n            char *item = getenv(forbid_env_name);\n            if (!item) {\n                break;\n            }\n            add_forbidden_item(item);\n            i++;\n        }\n        i = 0;\n        while (true) {\n            sprintf(replace_src_env_name, \"V_REPLACE_ITEM_SRC_%d\", i);\n            char *item_src = getenv(replace_src_env_name);\n            if (!item_src) {\n                break;\n            }\n            sprintf(replace_dst_env_name, \"V_REPLACE_ITEM_DST_%d\", i);\n            char *item_dst = getenv(replace_dst_env_name);\n            add_replace_item(item_src, item_dst);\n            i++;\n        }\n        startUniformer(getenv(\"V_SO_PATH\"),api_level, preview_api_level);\n        iu_loaded = true;\n    }\n}\n\n\nstatic inline void\nhook_function(void *handle, const char *symbol, void *new_func, void **old_func) {\n    void *addr = dlsym(handle, symbol);\n    if (addr == NULL) {\n        return;\n    }\n    MSHookFunction(addr, new_func, old_func);\n}\n\n\nvoid onSoLoaded(const char *name, void *handle);\n\nvoid IOUniformer::redirect(const char *orig_path, const char *new_path) {\n    add_replace_item(orig_path, new_path);\n}\n\nconst char *IOUniformer::query(const char *orig_path) {\n    int res;\n    return relocate_path(orig_path, &res);\n}\n\nvoid IOUniformer::whitelist(const char *_path) {\n    add_keep_item(_path);\n}\n\nvoid IOUniformer::forbid(const char *_path) {\n    add_forbidden_item(_path);\n}\n\n\nconst char *IOUniformer::reverse(const char *_path) {\n    return reverse_relocate_path(_path);\n}\n\n\n__BEGIN_DECLS\n\n#define FREE(ptr, org_ptr) { if ((void*) ptr != NULL && (void*) ptr != (void*) org_ptr) { free((void*) ptr); } }\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\n    int ret = syscall(__NR_fstatat64, dirfd, redirect_path, buf, flags);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n// int fstatat64(int dirfd, const char *pathname, struct stat *buf, int flags);\nHOOK_DEF(int, fstatat64, int dirfd, const char *pathname, struct stat *buf, int flags) {\n    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\n    int ret = syscall(__NR_fstatat64, dirfd, redirect_path, buf, flags);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\n// int fstat(const char *pathname, struct stat *buf, int flags);\nHOOK_DEF(int, fstat, const char *pathname, struct stat *buf) {\n    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res_old;\n    int res_new;\n    const char *redirect_path_old = relocate_path(oldpath, &res_old);\n    const char *redirect_path_new = relocate_path(newpath, &res_new);\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    int res_old;\n    int res_new;\n    const char *redirect_path_old = relocate_path(oldpath, &res_old);\n    const char *redirect_path_new = relocate_path(newpath, &res_new);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res_old;\n    int res_new;\n    const char *redirect_path_old = relocate_path(oldpath, &res_old);\n    const char *redirect_path_new = relocate_path(newpath, &res_new);\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    int res_old;\n    int res_new;\n    const char *redirect_path_old = relocate_path(oldpath, &res_old);\n    const char *redirect_path_new = relocate_path(newpath, &res_new);\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    int res_old;\n    int res_new;\n    const char *redirect_path_old = relocate_path(oldpath, &res_old);\n    const char *redirect_path_new = relocate_path(newpath, &res_new);\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    int res_old;\n    int res_new;\n    const char *redirect_path_old = relocate_path(oldpath, &res_old);\n    const char *redirect_path_new = relocate_path(newpath, &res_new);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\n    ssize_t ret = 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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\n    int ret = syscall(__NR_truncate, redirect_path, length);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n#define RETURN_IF_FORBID if(res == FORBID) return -1;\n\n// int truncate64(const char *pathname, off_t length);\nHOOK_DEF(int, truncate64, const char *pathname, off_t length) {\n    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\n    RETURN_IF_FORBID\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\n    RETURN_IF_FORBID\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    if (!ret) {\n\n    }\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\n    int ret = syscall(__NR_open, redirect_path, flags, mode);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n// int __statfs (__const char *__file, struct statfs *__buf);\nHOOK_DEF(int, __statfs, __const char *__file, struct statfs *__buf) {\n    int res;\n    const char *redirect_path = relocate_path(__file, &res);\n    int ret = syscall(__NR_statfs, redirect_path, __buf);\n    FREE(redirect_path, __file);\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    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\n    int ret = syscall(__NR_lchown, redirect_path, owner, group);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\nint inline getArrayItemCount(char *const array[]) {\n    int i;\n    for (i = 0; array[i]; ++i);\n    return i;\n}\n\n\nchar **build_new_env(char *const envp[]) {\n    char *provided_ld_preload = NULL;\n    int provided_ld_preload_index = -1;\n    int orig_envp_count = getArrayItemCount(envp);\n\n    for (int i = 0; i < orig_envp_count; i++) {\n        if (strstr(envp[i], \"LD_PRELOAD\")) {\n            provided_ld_preload = envp[i];\n            provided_ld_preload_index = i;\n        }\n    }\n    char ld_preload[200];\n    char *so_path = getenv(\"V_SO_PATH\");\n    if (provided_ld_preload) {\n        sprintf(ld_preload, \"LD_PRELOAD=%s:%s\", so_path, provided_ld_preload + 11);\n    } else {\n        sprintf(ld_preload, \"LD_PRELOAD=%s\", so_path);\n    }\n    int new_envp_count = orig_envp_count\n                         + get_keep_item_count()\n                         + get_forbidden_item_count()\n                         + get_replace_item_count() * 2 + 1;\n    if (provided_ld_preload) {\n        new_envp_count--;\n    }\n    char **new_envp = (char **) malloc(new_envp_count * sizeof(char *));\n    int cur = 0;\n    new_envp[cur++] = ld_preload;\n    for (int i = 0; i < orig_envp_count; ++i) {\n        if (i != provided_ld_preload_index) {\n            new_envp[cur++] = envp[i];\n        }\n    }\n    for (int i = 0; environ[i]; ++i) {\n        if (environ[i][0] == 'V' && environ[i][1] == '_') {\n            new_envp[cur++] = environ[i];\n        }\n    }\n    new_envp[cur] = NULL;\n    return new_envp;\n}\n\n// int (*origin_execve)(const char *pathname, char *const argv[], char *const envp[]);\nHOOK_DEF(int, execve, const char *pathname, char *argv[], char *const envp[]) {\n    /**\n     * CANNOT LINK EXECUTABLE \"/system/bin/cat\": \"/data/app/io.virtualapp-1/lib/arm/libva-native.so\" is 32-bit instead of 64-bit.\n     *\n     * We will support 64Bit to adopt it.\n     */\n    // ALOGE(\"execve : %s\", pathname); // any output can break exec. See bug: https://issuetracker.google.com/issues/109448553\n    int res;\n    const char *redirect_path = relocate_path(pathname, &res);\n    char *ld = getenv(\"LD_PRELOAD\");\n    if (ld) {\n        if (strstr(ld, \"libNimsWrap.so\") || strstr(ld, \"stamina.so\")) {\n            int ret = syscall(__NR_execve, redirect_path, argv, envp);\n            FREE(redirect_path, pathname);\n            return ret;\n        }\n    }\n    if (strstr(pathname, \"dex2oat\")) {\n        char **new_envp = build_new_env(envp);\n        int ret = syscall(__NR_execve, redirect_path, argv, new_envp);\n        FREE(redirect_path, pathname);\n        free(new_envp);\n        return ret;\n    }\n    int ret = syscall(__NR_execve, redirect_path, argv, envp);\n    FREE(redirect_path, pathname);\n    return ret;\n}\n\n\nHOOK_DEF(void*, dlopen, const char *filename, int flag) {\n    int res;\n    const char *redirect_path = relocate_path(filename, &res);\n    void *ret = orig_dlopen(redirect_path, flag);\n    onSoLoaded(filename, ret);\n    ALOGD(\"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    int res;\n    const char *redirect_path = relocate_path(filename, &res);\n    void *ret = orig_do_dlopen_V19(redirect_path, flag, extinfo);\n    onSoLoaded(filename, ret);\n    ALOGD(\"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    int res;\n    const char *redirect_path = relocate_path(name, &res);\n    void *ret = orig_do_dlopen_V24(redirect_path, flags, extinfo, caller_addr);\n    onSoLoaded(name, ret);\n    ALOGD(\"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    ALOGD(\"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    ALOGD(\">>>>> kill >>> pid: %d, sig: %d.\", 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\nint findSymbol(const char *name, const char *libn,\n               unsigned long *addr) {\n    return find_name(getpid(), name, libn, addr);\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            MSHookFunction(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            MSHookFunction(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            MSHookFunction(symbol, (void *) new_dlopen, (void **) &orig_dlopen);\n        }\n    }\n}\n\n\nvoid IOUniformer::startUniformer(const char *so_path, int api_level, int preview_api_level) {\n    char api_level_chars[5];\n    setenv(\"V_SO_PATH\", so_path, 1);\n    sprintf(api_level_chars, \"%i\", api_level);\n    setenv(\"V_API_LEVEL\", api_level_chars, 1);\n    sprintf(api_level_chars, \"%i\", preview_api_level);\n    setenv(\"V_PREVIEW_API_LEVEL\", api_level_chars, 1);\n\n    void *handle = dlopen(\"libc.so\", RTLD_NOW);\n    if (handle) {\n        HOOK_SYMBOL(handle, faccessat);\n        HOOK_SYMBOL(handle, __openat);\n        HOOK_SYMBOL(handle, fchmodat);\n        HOOK_SYMBOL(handle, fchownat);\n        HOOK_SYMBOL(handle, renameat);\n        HOOK_SYMBOL(handle, fstatat64);\n        HOOK_SYMBOL(handle, __statfs);\n        HOOK_SYMBOL(handle, __statfs64);\n        HOOK_SYMBOL(handle, mkdirat);\n        HOOK_SYMBOL(handle, mknodat);\n        HOOK_SYMBOL(handle, truncate);\n        HOOK_SYMBOL(handle, linkat);\n        HOOK_SYMBOL(handle, readlinkat);\n        HOOK_SYMBOL(handle, unlinkat);\n        HOOK_SYMBOL(handle, symlinkat);\n        HOOK_SYMBOL(handle, utimensat);\n        HOOK_SYMBOL(handle, __getcwd);\n//        HOOK_SYMBOL(handle, __getdents64);\n        HOOK_SYMBOL(handle, chdir);\n        HOOK_SYMBOL(handle, execve);\n        if (api_level <= 20) {\n            HOOK_SYMBOL(handle, access);\n            HOOK_SYMBOL(handle, __open);\n            HOOK_SYMBOL(handle, stat);\n            HOOK_SYMBOL(handle, lstat);\n            HOOK_SYMBOL(handle, fstatat);\n            HOOK_SYMBOL(handle, chmod);\n            HOOK_SYMBOL(handle, chown);\n            HOOK_SYMBOL(handle, rename);\n            HOOK_SYMBOL(handle, rmdir);\n            HOOK_SYMBOL(handle, mkdir);\n            HOOK_SYMBOL(handle, mknod);\n            HOOK_SYMBOL(handle, link);\n            HOOK_SYMBOL(handle, unlink);\n            HOOK_SYMBOL(handle, readlink);\n            HOOK_SYMBOL(handle, symlink);\n//            HOOK_SYMBOL(handle, getdents);\n//            HOOK_SYMBOL(handle, execv);\n        }\n        dlclose(handle);\n    }\n    hook_dlopen(api_level);\n}\n"
  },
  {
    "path": "VirtualApp/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 \"Jni/Helper.h\"\n\n\n#define HOOK_SYMBOL(handle, func) hook_function(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_env_before_all();\n\n    void startUniformer(const char *so_path, int api_level, int preview_api_level);\n\n    void redirect(const char *orig_path, const char *new_path);\n\n    void whitelist(const char *path);\n\n    const char *query(const char *orig_path);\n\n    const char *reverse(const char *redirected_path);\n\n    void forbid(const char *path);\n}\n\n#endif //NDK_HOOK_H\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Foundation/Path.cpp",
    "content": "#include \"Path.h\"\n#define MAX_PATH_SIZE 4096\n\n/* returns last slash position in @s or -1 if there is no one */\nint get_last_slash_pos(char *s) {\n    int last_slash = -1;\n    char *slash = strrchr(s, '/');\n    if (slash)\n        last_slash = slash - s;\n    return last_slash;\n}\n\nchar *canonicalize_filename(const char *str) {\n    int prev_last_slash = -1;\n    int last_slash = -1;\n    int i = 0;\n    int j = 0;\n    char c;\n    char cprev = '\\0';\n    char result[MAX_PATH_SIZE] = {0};\n\n    if (!str)\n        return NULL;\n\n    for (; i < MAX_PATH_SIZE && str[i]; ++i) {\n        c = str[i];\n\n        switch (c) {\n            case '/':\n                if (cprev == '/'/** || j == 0*/) {\n                    // eat repeating and leading slashes\n                    ;\n                } else {\n                    result[j++] = c;\n                    prev_last_slash = last_slash;\n                    last_slash = j - 1;\n                }\n                break;\n            case '.':\n                if (cprev == '.') {\n                    int start_position = 0;\n                    if (prev_last_slash > 0) {\n                        start_position = prev_last_slash;\n                        // handle following duplicate slash on next iteration, if any\n                        cprev = '/';\n                    }\n                    while (j > start_position)\n                        result[j--] = '\\0';\n                    result[j] = '\\0';\n\n                    // we lost last slash positions, calculate them\n                    prev_last_slash = -1;\n                    last_slash = get_last_slash_pos(result);\n                    if (last_slash != -1) {\n                        // trying to find previous last slash position\n                        result[last_slash] = ' ';\n                        prev_last_slash = get_last_slash_pos(result);\n                        result[last_slash] = '/';\n                    }\n                } else {\n                    // assume it is a valid to have dot in names\n                    result[j++] = c;\n                }\n                break;\n            default:\n                result[j++] = c;\n                break;\n        }\n        cprev = c;\n    }\n    return strndup(result, MAX_PATH_SIZE - 1);\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Foundation/Path.h",
    "content": "//\n// VirtualApp Native Project\n//\n\n#ifndef FOUNDATION_PATH\n#define FOUNDATION_PATH\n\n#include <string.h>\n\nint get_last_slash_pos(char *s);\n\nchar* canonicalize_filename(const char *str);\n\n\n#endif //FOUNDATION_PATH\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Foundation/SandboxFs.cpp",
    "content": "#include <stdlib.h>\n#include \"SandboxFs.h\"\n#include \"Path.h\"\n\nPathItem *keep_items;\nPathItem *forbidden_items;\nReplaceItem *replace_items;\nint keep_item_count;\nint forbidden_item_count;\nint replace_item_count;\n\nint add_keep_item(const char *path) {\n    char keep_env_name[25];\n    sprintf(keep_env_name, \"V_KEEP_ITEM_%d\", keep_item_count);\n    setenv(keep_env_name, path, 1);\n    keep_items = (PathItem *) realloc(keep_items,\n                                      keep_item_count * sizeof(PathItem) + sizeof(PathItem));\n    PathItem &item = keep_items[keep_item_count];\n    item.path = strdup(path);\n    item.size = strlen(path);\n    return ++keep_item_count;\n}\n\nint add_forbidden_item(const char *path) {\n    char forbidden_env_name[25];\n    sprintf(forbidden_env_name, \"V_FORBID_ITEM_%d\", forbidden_item_count);\n    setenv(forbidden_env_name, path, 1);\n    forbidden_items = (PathItem *) realloc(forbidden_items,\n                                           forbidden_item_count * sizeof(PathItem) +\n                                           sizeof(PathItem));\n    PathItem &item = forbidden_items[forbidden_item_count];\n    item.path = strdup(path);\n    item.size = strlen(path);\n    item.is_folder = (path[strlen(path) - 1] == '/');\n    return ++forbidden_item_count;\n}\n\nint add_replace_item(const char *orig_path, const char *new_path) {\n    char src_env_name[25];\n    char dst_env_name[25];\n    sprintf(src_env_name, \"V_REPLACE_ITEM_SRC_%d\", replace_item_count);\n    sprintf(dst_env_name, \"V_REPLACE_ITEM_DST_%d\", replace_item_count);\n    setenv(src_env_name, orig_path, 1);\n    setenv(dst_env_name, new_path, 1);\n\n    replace_items = (ReplaceItem *) realloc(replace_items,\n                                            replace_item_count * sizeof(ReplaceItem) +\n                                            sizeof(ReplaceItem));\n    ReplaceItem &item = replace_items[replace_item_count];\n    item.orig_path = strdup(orig_path);\n    item.orig_size = strlen(orig_path);\n    item.new_path = strdup(new_path);\n    item.new_size = strlen(new_path);\n    item.is_folder = (orig_path[strlen(orig_path) - 1] == '/');\n    return ++replace_item_count;\n}\n\n\nPathItem *get_keep_items() {\n    return keep_items;\n}\n\nPathItem *get_forbidden_item() {\n    return forbidden_items;\n}\n\nReplaceItem *get_replace_items() {\n    return replace_items;\n}\n\nint get_keep_item_count() {\n    return keep_item_count;\n}\n\nint get_forbidden_item_count() {\n    return forbidden_item_count;\n}\n\nint get_replace_item_count() {\n    return replace_item_count;\n}\n\ninline bool match_path(bool is_folder, size_t size, const char *item_path, const char *path) {\n    if (is_folder) {\n        if (strlen(path) < size) {\n            // ignore the last '/'\n            return strncmp(item_path, path, size - 1) == 0;\n        }\n    }\n    return strncmp(item_path, path, size) == 0;\n}\n\n\nconst char *relocate_path(const char *path, int *result) {\n    if (path == NULL) {\n        *result = NOT_MATCH;\n        return NULL;\n    }\n    for (int i = 0; i < keep_item_count; ++i) {\n        PathItem &item = keep_items[i];\n        if (strcmp(item.path, path) == 0) {\n            *result = KEEP;\n            return path;\n        }\n    }\n    for (int i = 0; i < forbidden_item_count; ++i) {\n        PathItem &item = forbidden_items[i];\n        if (match_path(item.is_folder, item.size, item.path, path)) {\n            *result = FORBID;\n            // Permission denied\n            errno = 13;\n            return NULL;\n        }\n    }\n    for (int i = 0; i < replace_item_count; ++i) {\n        ReplaceItem &item = replace_items[i];\n        if (match_path(item.is_folder, item.orig_size, item.orig_path, path)) {\n            *result = MATCH;\n            int len = strlen(path);\n            if (len < item.orig_size) {\n                //remove last /\n                std::string redirect_path(item.new_path, 0, item.new_size - 1);\n                return strdup(redirect_path.c_str());\n            } else {\n                std::string redirect_path(item.new_path);\n                redirect_path += path + item.orig_size;\n                return strdup(redirect_path.c_str());\n            }\n        }\n    }\n    *result = NOT_MATCH;\n    return path;\n}\n\n\nint relocate_path_inplace(char *_path, size_t size, int *result) {\n    const char *redirect_path = relocate_path(_path, result);\n    if (redirect_path && redirect_path != _path) {\n        if (strlen(redirect_path) <= size) {\n            strcpy(_path, redirect_path);\n        } else {\n            return -1;\n        }\n        free((void *) redirect_path);\n    }\n    return 0;\n}\n\n\nconst char *reverse_relocate_path(const char *_path) {\n    if (_path == NULL) {\n        return NULL;\n    }\n    char *path = canonicalize_filename(_path);\n    for (int i = 0; i < keep_item_count; ++i) {\n        PathItem &item = keep_items[i];\n        if (strcmp(item.path, path) == 0) {\n            free(path);\n            return _path;\n        }\n    }\n    for (int i = 0; i < replace_item_count; ++i) {\n        ReplaceItem &item = replace_items[i];\n        if (match_path(item.is_folder, item.new_size, item.new_path, path)) {\n            int len = strlen(path);\n            if (len < item.new_size) {\n                //remove last /\n                std::string reverse_path(item.orig_path, 0, item.orig_size - 1);\n                free(path);\n                return strdup(reverse_path.c_str());\n            } else {\n                std::string reverse_path(item.orig_path);\n                reverse_path += path + item.new_size;\n                free(path);\n                return strdup(reverse_path.c_str());\n            }\n        }\n    }\n    return _path;\n}\n\n\nint reverse_relocate_path_inplace(char *_path, size_t size) {\n    const char *redirect_path = reverse_relocate_path(_path);\n    if (redirect_path && redirect_path != _path) {\n        if (strlen(redirect_path) <= size) {\n            strcpy(_path, redirect_path);\n        } else {\n            return -1;\n        }\n        free((void *) redirect_path);\n    }\n    return 0;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Foundation/SandboxFs.h",
    "content": "#ifndef SANDBOX_FS_H\n#define SANDBOX_FS_H\n\n#include <string>\n#include <errno.h>\n\ntypedef struct PathItem {\n    char *path;\n    bool is_folder;\n    size_t size;\n} PathItem;\n\ntypedef struct ReplaceItem {\n    char *orig_path;\n    size_t orig_size;\n    char *new_path;\n    size_t new_size;\n    bool is_folder;\n} ReplaceItem;\n\nenum RelocateResult {\n    MATCH,\n    NOT_MATCH,\n    FORBID,\n    KEEP\n};\n\n\nconst char *relocate_path(const char *_path, int *result);\n\nint relocate_path_inplace(char *_path, size_t size, int *result);\n\nconst char *reverse_relocate_path(const char *_path);\n\nint reverse_relocate_path_inplace(char *_path, size_t size);\n\nint add_keep_item(const char *path);\n\nint add_forbidden_item(const char *path);\n\nint add_replace_item(const char *orig_path, const char *new_path);\n\nPathItem *get_keep_items();\n\nPathItem *get_forbidden_item();\n\nReplaceItem *get_replace_items();\n\nint get_keep_item_count();\n\nint get_forbidden_item_count();\n\nint get_replace_item_count();\n\n\n#endif //SANDBOX_FS_H\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Foundation/SymbolFinder.cpp",
    "content": "#include <stdio.h>\n#include <elf.h>\n#include <Jni/Helper.h>\n#include <malloc.h>\n#include <stdlib.h>\n#include <fcntl.h>\n#include <sys/mman.h>\n#include <fb/include/fb/ALog.h>\n#include \"SymbolFinder.h\"\n\n/* memory map for libraries */\n#define MAX_NAME_LEN 256\n#define MEMORY_ONLY  \"[memory]\"\nstruct mm {\n    char name[MAX_NAME_LEN];\n    unsigned long start, end;\n};\n\ntypedef struct symtab *symtab_t;\nstruct symlist {\n    Elf32_Sym *sym; /* symbols */\n    char *str; /* symbol strings */\n    unsigned num; /* number of symbols */\n};\nstruct symtab {\n    struct symlist *st; /* \"static\" symbols */\n    struct symlist *dyn; /* dynamic symbols */\n};\n\nstatic void* xmalloc(size_t size) {\n    void *p;\n    p = malloc(size);\n    if (!p) {\n        printf(\"Out of memory\\n\");\n        exit(1);\n    }\n    return p;\n}\n\nstatic int my_pread(int fd, void *buf, size_t count, off_t offset) {\n    lseek(fd, offset, SEEK_SET);\n    return read(fd, buf, count);\n}\n\nstatic struct symlist* get_syms(int fd, Elf32_Shdr *symh, Elf32_Shdr *strh) {\n    struct symlist *sl, *ret;\n    int rv;\n\n    ret = NULL;\n    sl = (struct symlist *) xmalloc(sizeof(struct symlist));\n    sl->str = NULL;\n    sl->sym = NULL;\n\n    /* sanity */\n    if (symh->sh_size % sizeof(Elf32_Sym)) {\n        //printf(\"elf_error\\n\");\n        goto out;\n    }\n\n    /* symbol table */\n    sl->num = symh->sh_size / sizeof(Elf32_Sym);\n    sl->sym = (Elf32_Sym *) xmalloc(symh->sh_size);\n    rv = my_pread(fd, sl->sym, symh->sh_size, symh->sh_offset);\n    if (0 > rv) {\n        //perror(\"read\");\n        goto out;\n    }\n    if (rv != symh->sh_size) {\n        //printf(\"elf error\\n\");\n        goto out;\n    }\n\n    /* string table */\n    sl->str = (char *) xmalloc(strh->sh_size);\n    rv = my_pread(fd, sl->str, strh->sh_size, strh->sh_offset);\n    if (0 > rv) {\n        //perror(\"read\");\n        goto out;\n    }\n    if (rv != strh->sh_size) {\n        //printf(\"elf error\");\n        goto out;\n    }\n\n    ret = sl;\n    out: return ret;\n}\n\nstatic int do_load(int fd, symtab_t symtab) {\n    int rv;\n    size_t size;\n    Elf32_Ehdr ehdr;\n    Elf32_Shdr *shdr = NULL, *p;\n    Elf32_Shdr *dynsymh, *dynstrh;\n    Elf32_Shdr *symh, *strh;\n    char *shstrtab = NULL;\n    int i;\n    int ret = -1;\n\n    /* elf header */\n    rv = read(fd, &ehdr, sizeof(ehdr));\n    if (0 > rv) {\n        ALOGD(\"read\\n\");\n        goto out;\n    }\n    if (rv != sizeof(ehdr)) {\n        ALOGD(\"elf error 1\\n\");\n        goto out;\n    }\n    if (strncmp((const char *) ELFMAG, (const char *) ehdr.e_ident, SELFMAG)) { /* sanity */\n        ALOGD(\"not an elf\\n\");\n        goto out;\n    }\n    if (sizeof(Elf32_Shdr) != ehdr.e_shentsize) { /* sanity */\n        ALOGD(\"elf error 2\\n\");\n        goto out;\n    }\n\n    /* section header table */\n    size = ehdr.e_shentsize * ehdr.e_shnum;\n    shdr = (Elf32_Shdr *) xmalloc(size);\n    rv = my_pread(fd, shdr, size, ehdr.e_shoff);\n    if (0 > rv) {\n        ALOGD(\"read\\n\");\n        goto out;\n    }\n    if (rv != size) {\n        ALOGD(\"elf error 3 %d %d\\n\", rv, size);\n        goto out;\n    }\n\n    /* section header string table */\n    size = shdr[ehdr.e_shstrndx].sh_size;\n    shstrtab = (char *) xmalloc(size);\n    rv = my_pread(fd, shstrtab, size, shdr[ehdr.e_shstrndx].sh_offset);\n    if (0 > rv) {\n        ALOGD(\"read\\n\");\n        goto out;\n    }\n    if (rv != size) {\n        ALOGD(\"elf error 4 %d %d\\n\", rv, size);\n        goto out;\n    }\n\n    /* symbol table headers */\n    symh = dynsymh = NULL;\n    strh = dynstrh = NULL;\n    for (i = 0, p = shdr; i < ehdr.e_shnum; i++, p++)\n        if (SHT_SYMTAB == p->sh_type) {\n            if (symh) {\n                ALOGD(\"too many symbol tables\\n\");\n                goto out;\n            }\n            symh = p;\n        } else if (SHT_DYNSYM == p->sh_type) {\n            if (dynsymh) {\n                ALOGD(\"too many symbol tables\\n\");\n                goto out;\n            }\n            dynsymh = p;\n        } else if (SHT_STRTAB == p->sh_type\n                   && !strncmp(shstrtab + p->sh_name, \".strtab\", 7)) {\n            if (strh) {\n                ALOGD(\"too many string tables\\n\");\n                goto out;\n            }\n            strh = p;\n        } else if (SHT_STRTAB == p->sh_type\n                   && !strncmp(shstrtab + p->sh_name, \".dynstr\", 7)) {\n            if (dynstrh) {\n                ALOGD(\"too many string tables\\n\");\n                goto out;\n            }\n            dynstrh = p;\n        }\n    /* sanity checks */\n    if ((!dynsymh && dynstrh) || (dynsymh && !dynstrh)) {\n        ALOGD(\"bad dynamic symbol table\\n\");\n        goto out;\n    }\n    if ((!symh && strh) || (symh && !strh)) {\n        ALOGD(\"bad symbol table\\n\");\n        goto out;\n    }\n    if (!dynsymh && !symh) {\n        ALOGD(\"no symbol table\\n\");\n        goto out;\n    }\n\n    /* symbol tables */\n    if (dynsymh)\n        symtab->dyn = get_syms(fd, dynsymh, dynstrh);\n    if (symh)\n        symtab->st = get_syms(fd, symh, strh);\n    ret = 0;\n    out: free(shstrtab);\n    free(shdr);\n    return ret;\n}\n\nstatic symtab_t load_symtab(char *filename) {\n    int fd;\n    symtab_t symtab;\n\n    symtab = (symtab_t) xmalloc(sizeof(*symtab));\n    memset(symtab, 0, sizeof(*symtab));\n\n    fd = open(filename, O_RDONLY);\n    if (0 > fd) {\n        ALOGE(\"%s open\\n\", __func__);\n        return NULL;\n    }\n    if (0 > do_load(fd, symtab)) {\n        ALOGE(\"Error ELF parsing %s\\n\", filename);\n        free(symtab);\n        symtab = NULL;\n    }\n    close(fd);\n    return symtab;\n}\n\n\nstatic int load_memmap(pid_t pid, struct mm *mm, int *nmmp) {\n    size_t buf_size = 0x40000;\n    char *p_buf = (char *) malloc(buf_size); // increase this if needed for larger \"maps\"\n    char name[MAX_NAME_LEN] = { 0 };\n    char *p;\n    unsigned long start, end;\n    struct mm *m;\n    int nmm = 0;\n    int fd, rv;\n    int i;\n\n    sprintf(p_buf, \"/proc/%d/maps\", pid);\n    fd = open(p_buf, O_RDONLY);\n    if (0 > fd) {\n        ALOGE(\"Can't open %s for reading\\n\", p_buf);\n        free(p_buf);\n        return -1;\n    }\n\n    /* Zero to ensure data is null terminated */\n    memset(p_buf, 0, buf_size);\n\n    p = p_buf;\n    while (1) {\n        rv = read(fd, p, buf_size - (p - p_buf));\n        if (0 > rv) {\n            ALOGE(\"%s read\", __FUNCTION__);\n            free(p_buf);\n            return -1;\n        }\n        if (0 == rv)\n            break;\n        p += rv;\n        if (p - p_buf >= buf_size) {\n            ALOGE(\"Too many memory mapping\\n\");\n            free(p_buf);\n            return -1;\n        }\n    }\n    close(fd);\n\n    p = strtok(p_buf, \"\\n\");\n    m = mm;\n    while (p) {\n        /* parse current map line */\n        rv = sscanf(p, \"%08lx-%08lx %*s %*s %*s %*s %s\\n\", &start, &end, name);\n\n        p = strtok(NULL, \"\\n\");\n\n        if (rv == 2) {\n            m = &mm[nmm++];\n            m->start = start;\n            m->end = end;\n            memcpy(m->name, MEMORY_ONLY, sizeof(MEMORY_ONLY));\n            continue;\n        }\n\n        /* search backward for other mapping with same name */\n        for (i = nmm - 1; i >= 0; i--) {\n            m = &mm[i];\n            if (!strcmp(m->name, name))\n                break;\n        }\n\n        if (i >= 0) {\n            if (start < m->start)\n                m->start = start;\n            if (end > m->end)\n                m->end = end;\n        } else {\n            /* new entry */\n            m = &mm[nmm++];\n            m->start = start;\n            m->end = end;\n            memcpy(m->name, name, strlen(name));\n        }\n    }\n\n    *nmmp = nmm;\n    free(p_buf);\n    return 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                        struct mm *mm, int nmm) {\n    int i;\n    struct mm *m;\n    char *p;\n    for (i = 0, m = mm; i < nmm; i++, m++) {\n        if (!strcmp(m->name, MEMORY_ONLY))\n            continue;\n        p = strrchr(m->name, '/');\n        if (!p)\n            continue;\n        p++;\n        if (strncmp(libn, p, strlen(libn)))\n            continue;\n        p += strlen(libn);\n\n        /* here comes our crude test -> 'libc.so' or 'libc-[0-9]' */\n        if (!strncmp(\"so\", p, 2) || 1) // || (p[0] == '-' && isdigit(p[1])))\n            break;\n    }\n    if (i >= nmm)\n        /* not found */\n        return -1;\n\n    *start = m->start;\n    strncpy(name, m->name, len);\n    if (strlen(m->name) >= len)\n        name[len - 1] = '\\0';\n\n    mprotect((void*) m->start, m->end - m->start,\n             PROT_READ | PROT_WRITE | PROT_EXEC);\n    return 0;\n}\n\nstatic int lookup2(struct symlist *sl, unsigned char type, char *name,\n                   unsigned long *val) {\n    Elf32_Sym *p;\n    int len;\n    int i;\n\n    len = strlen(name);\n    for (i = 0, p = sl->sym; i < sl->num; i++, p++) {\n        //ALOGD(\"name: %s %x\\n\", sl->str+p->st_name, p->st_value)\n        if (!strncmp(sl->str + p->st_name, name, len)\n            && *(sl->str + p->st_name + len) == 0\n            && ELF32_ST_TYPE(p->st_info) == type) {\n            //if (p->st_value != 0) {\n            *val = p->st_value;\n            return 0;\n            //}\n        }\n    }\n    return -1;\n}\n\nstatic int lookup_sym(symtab_t s, unsigned char type, char *name,\n                      unsigned long *val) {\n    if (s->dyn && !lookup2(s->dyn, type, name, val))\n        return 0;\n    if (s->st && !lookup2(s->st, type, name, val))\n        return 0;\n    return -1;\n}\n\nstatic int lookup_func_sym(symtab_t s, char *name, unsigned long *val) {\n    return lookup_sym(s, STT_FUNC, name, val);\n}\n\nint find_name(pid_t pid, const char *name, const char *libn,\n              unsigned long *addr) {\n    struct mm mm[1000] = { 0 };\n    unsigned long libcaddr;\n    int nmm;\n    char libc[1024] = { 0 };\n    symtab_t s;\n\n    if (0 > load_memmap(pid, mm, &nmm)) {\n        ALOGD(\"cannot read memory map\\n\");\n        return -1;\n    }\n    if (0\n        > find_libname((char *) libn, (char *) libc, sizeof(libc),\n                       &libcaddr, mm, nmm)) {\n        ALOGD(\"cannot find lib: %s\\n\", libn);\n        return -1;\n    }\n    //ALOGD(\"lib: >%s<\\n\", libc)\n    s = load_symtab(libc);\n    if (!s) {\n        ALOGD(\"cannot read symbol table\\n\");\n        return -1;\n    }\n    if (0 > lookup_func_sym(s, (char *) name, addr)) {\n        ALOGD(\"cannot find function: %s\\n\", name);\n        return -1;\n    }\n    *addr += libcaddr;\n    return 0;\n}\n\nint find_libbase(pid_t pid, const char *libn, unsigned long *addr) {\n    struct mm mm[1000] = { 0 };\n    unsigned long libcaddr;\n    int nmm;\n    char libc[1024] = { 0 };\n    symtab_t s;\n\n    if (0 > load_memmap(pid, mm, &nmm)) {\n        ALOGD(\"cannot read memory map\\n\");\n        return -1;\n    }\n    if (0 > find_libname(libn, libc, sizeof(libc), &libcaddr, mm, nmm)) {\n        ALOGD(\"cannot find lib\\n\");\n        return -1;\n    }\n    *addr = libcaddr;\n    return 0;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Foundation/SymbolFinder.h",
    "content": "#ifndef SYMBOL_FINDER\n#define SYMBOL_FINDER\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": "VirtualApp/lib/src/main/jni/Foundation/VMPatch.cpp",
    "content": "//\n// VirtualApp Native Project\n//\n#include <Jni/VAJni.h>\n#include \"VMPatch.h\"\n\nnamespace FunctionDef {\n    typedef void (*Function_DalvikBridgeFunc)(const void **, void *, const void *, void *);\n\n    typedef jobject (*Function_openDexNativeFunc)(JNIEnv *, jclass, jstring, jstring, jint);\n\n    typedef jobject (*Native_openDexNativeFunc_N)(JNIEnv *, jclass, jstring, jstring, jint, jobject,\n                                                  jobject);\n\n\n    typedef jint (*Function_cameraNativeSetupFunc_T1)(JNIEnv *, jobject, jobject, jint, jstring);\n\n    typedef jint (*Function_cameraNativeSetupFunc_T2)(JNIEnv *, jobject, jobject, jint, jint,\n                                                      jstring);\n\n    typedef jint (*Function_cameraNativeSetupFunc_T3)(JNIEnv *, jobject, jobject, jint, jint,\n                                                      jstring,\n                                                      jboolean);\n\n    typedef jint (*Function_cameraNativeSetupFunc_T4)(JNIEnv *, jobject, jobject, jint, jstring,\n                                                      jboolean);\n\n    typedef jint (*Function_getCallingUid)(JNIEnv *, jclass);\n\n    typedef jint (*Function_audioRecordNativeCheckPermission)(JNIEnv *, jobject, jstring);\n}\n\nusing namespace FunctionDef;\n\n\nstatic struct {\n\n    bool is_art;\n    int native_offset;\n    char *host_packageName;\n    jint api_level;\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    int (*native_getCallingUid)(int);\n\n    int (*IPCThreadState_self)(void);\n\n    Function_getCallingUid jni_orig_getCallingUid;\n    Function_DalvikBridgeFunc orig_cameraNativeSetup_dvm;\n\n    int cameraMethodType;\n    union {\n        Function_cameraNativeSetupFunc_T1 t1;\n        Function_cameraNativeSetupFunc_T2 t2;\n        Function_cameraNativeSetupFunc_T3 t3;\n        Function_cameraNativeSetupFunc_T4 t4;\n    } orig_native_cameraNativeSetupFunc;\n\n    Function_DalvikBridgeFunc orig_openDexFile_dvm;\n    union {\n        Function_openDexNativeFunc beforeN;\n        Native_openDexNativeFunc_N afterN;\n    } orig_openDexNativeFunc_art;\n\n    Function_audioRecordNativeCheckPermission orig_audioRecordNativeCheckPermission;\n\n} patchEnv;\n\n\njint getCallingUid(alias_ref<jclass> clazz) {\n    jint uid;\n    if (patchEnv.is_art) {\n        uid = patchEnv.jni_orig_getCallingUid(Environment::ensureCurrentThreadIsAttached(),\n                                              clazz.get());\n    } else {\n        uid = patchEnv.native_getCallingUid(patchEnv.IPCThreadState_self());\n    }\n    uid = Environment::ensureCurrentThreadIsAttached()->CallStaticIntMethod(nativeEngineClass.get(),\n                                                                            patchEnv.method_onGetCallingUid,\n                                                                            uid);\n    return uid;\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(nativeEngineClass.get(), patchEnv.method_onOpenDexFileNative, array);\n\n    jstring newSource = (jstring) env->GetObjectArrayElement(array, 0);\n    jstring newOutput = (jstring) env->GetObjectArrayElement(array, 1);\n\n    return patchEnv.orig_openDexNativeFunc_art.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(nativeEngineClass.get(), patchEnv.method_onOpenDexFileNative, array);\n\n    jstring newSource = (jstring) env->GetObjectArrayElement(array, 0);\n    jstring newOutput = (jstring) env->GetObjectArrayElement(array, 1);\n\n    return patchEnv.orig_openDexNativeFunc_art.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\n    JNIEnv *env = Environment::ensureCurrentThreadIsAttached();\n\n    const char *source = args[0] == NULL ? NULL : patchEnv.GetCstrFromString((void *) args[0]);\n    const char *output = args[1] == NULL ? NULL : patchEnv.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    env->CallStaticVoidMethod(nativeEngineClass.get(), patchEnv.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 : patchEnv.GetStringFromCstr(_newSource);\n    args[1] = _newOutput == NULL ? NULL : patchEnv.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    patchEnv.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(patchEnv.host_packageName);\n\n    return patchEnv.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(patchEnv.host_packageName);\n\n    return patchEnv.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(patchEnv.host_packageName);\n\n    return patchEnv.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(patchEnv.host_packageName);\n\n    return patchEnv.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(patchEnv.host_packageName);\n    return patchEnv.orig_audioRecordNativeCheckPermission(env, thiz, host);\n}\n\n\nstatic void\nnew_bridge_cameraNativeSetupFunc(const void **args, void *pResult, const void *method, void *self) {\n    // args[0] = this\n    switch (patchEnv.cameraMethodType) {\n        case 1:\n            args[4] = patchEnv.GetStringFromCstr(patchEnv.host_packageName);\n            break;\n        case 2:\n            args[5] = patchEnv.GetStringFromCstr(patchEnv.host_packageName);\n            break;\n        case 3:\n            args[5] = patchEnv.GetStringFromCstr(patchEnv.host_packageName);\n            break;\n        case 4:\n            args[4] = patchEnv.GetStringFromCstr(patchEnv.host_packageName);\n            break;\n    }\n    patchEnv.orig_cameraNativeSetup_dvm(args, pResult, method, self);\n}\n\nvoid mark() {\n    // Do nothing\n};\n\n\nvoid measureNativeOffset(bool isArt) {\n\n    jmethodID markMethod = nativeEngineClass->getStaticMethod<void(void)>(\"nativeMark\").getId();\n\n    size_t startAddress = (size_t) markMethod;\n    size_t targetAddress = (size_t) mark;\n    if (isArt && patchEnv.art_work_around_app_jni_bugs) {\n        targetAddress = (size_t) patchEnv.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            ALOGE(\"Error: Unable to find the jni function.\");\n            break;\n        }\n    }\n    if (found) {\n        patchEnv.native_offset = offset;\n        if (!isArt) {\n            patchEnv.native_offset += (sizeof(int) + sizeof(void *));\n        }\n    }\n}\n\n\ninline void replaceGetCallingUid(jboolean isArt) {\n    auto binderClass = findClassLocal(\"android/os/Binder\");\n    if (isArt) {\n        size_t mtd_getCallingUid = (size_t) binderClass->getStaticMethod<jint(void)>(\n                \"getCallingUid\").getId();\n        int nativeFuncOffset = patchEnv.native_offset;\n        void **jniFuncPtr = (void **) (mtd_getCallingUid + nativeFuncOffset);\n        patchEnv.jni_orig_getCallingUid = (Function_getCallingUid) (*jniFuncPtr);\n        *jniFuncPtr = (void *) getCallingUid;\n    } else {\n        binderClass->registerNatives({makeNativeMethod(\"getCallingUid\", getCallingUid)});\n    }\n}\n\ninline void\nreplaceOpenDexFileMethod(jobject javaMethod, jboolean isArt, int apiLevel) {\n\n    size_t mtd_openDexNative = (size_t) Environment::current()->FromReflectedMethod(javaMethod);\n    int nativeFuncOffset = patchEnv.native_offset;\n    void **jniFuncPtr = (void **) (mtd_openDexNative + nativeFuncOffset);\n\n    if (!isArt) {\n        patchEnv.orig_openDexFile_dvm = (Function_DalvikBridgeFunc) (*jniFuncPtr);\n        *jniFuncPtr = (void *) new_bridge_openDexNativeFunc;\n    } else {\n        if (apiLevel < 24) {\n            patchEnv.orig_openDexNativeFunc_art.beforeN = (Function_openDexNativeFunc) (*jniFuncPtr);\n            *jniFuncPtr = (void *) new_native_openDexNativeFunc;\n        } else {\n            patchEnv.orig_openDexNativeFunc_art.afterN = (Native_openDexNativeFunc_N) (*jniFuncPtr);\n            *jniFuncPtr = (void *) new_native_openDexNativeFunc_N;\n        }\n    }\n\n}\n\n\ninline void\nreplaceCameraNativeSetupMethod(jobject javaMethod, jboolean isArt, int apiLevel) {\n\n    if (!javaMethod) {\n        return;\n    }\n    size_t mtd_cameraNativeSetup = (size_t) Environment::current()->FromReflectedMethod(javaMethod);\n    int nativeFuncOffset = patchEnv.native_offset;\n    void **jniFuncPtr = (void **) (mtd_cameraNativeSetup + nativeFuncOffset);\n\n    if (!isArt) {\n        patchEnv.orig_cameraNativeSetup_dvm = (Function_DalvikBridgeFunc) (*jniFuncPtr);\n        *jniFuncPtr = (void *) new_bridge_cameraNativeSetupFunc;\n    } else {\n        switch (patchEnv.cameraMethodType) {\n            case 1:\n                patchEnv.orig_native_cameraNativeSetupFunc.t1 = (Function_cameraNativeSetupFunc_T1) (*jniFuncPtr);\n                *jniFuncPtr = (void *) new_native_cameraNativeSetupFunc_T1;\n                break;\n            case 2:\n                patchEnv.orig_native_cameraNativeSetupFunc.t2 = (Function_cameraNativeSetupFunc_T2) (*jniFuncPtr);\n                *jniFuncPtr = (void *) new_native_cameraNativeSetupFunc_T2;\n                break;\n            case 3:\n                patchEnv.orig_native_cameraNativeSetupFunc.t3 = (Function_cameraNativeSetupFunc_T3) (*jniFuncPtr);\n                *jniFuncPtr = (void *) new_native_cameraNativeSetupFunc_T3;\n                break;\n            case 4:\n                patchEnv.orig_native_cameraNativeSetupFunc.t4 = (Function_cameraNativeSetupFunc_T4) (*jniFuncPtr);\n                *jniFuncPtr = (void *) new_native_cameraNativeSetupFunc_T4;\n                break;\n        }\n    }\n\n}\n\n\nvoid\nreplaceAudioRecordNativeCheckPermission(jobject javaMethod, jboolean isArt, int api) {\n    if (!javaMethod || !isArt) {\n        return;\n    }\n    jmethodID methodStruct = Environment::current()->FromReflectedMethod(javaMethod);\n    void **funPtr = (void **) (reinterpret_cast<size_t>(methodStruct) + patchEnv.native_offset);\n    patchEnv.orig_audioRecordNativeCheckPermission = (Function_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 hookAndroidVM(JArrayClass<jobject> javaMethods,\n                   jstring packageName, jboolean isArt, jint apiLevel,\n                   jint cameraMethodType) {\n\n    JNIEnv *env = Environment::current();\n\n    JNINativeMethod methods[] = {\n            NATIVE_METHOD((void *) mark, \"nativeMark\", \"()V\"),\n    };\n    if (env->RegisterNatives(nativeEngineClass.get(), methods, 1) < 0) {\n        return;\n    }\n    patchEnv.is_art = isArt;\n    patchEnv.cameraMethodType = cameraMethodType;\n    patchEnv.host_packageName = (char *) env->GetStringUTFChars(packageName,\n                                                                NULL);\n    patchEnv.api_level = apiLevel;\n    void *soInfo = getDvmOrArtSOHandle();\n    patchEnv.method_onGetCallingUid = nativeEngineClass->getStaticMethod<jint(jint)>(\n            \"onGetCallingUid\").getId();\n    patchEnv.method_onOpenDexFileNative = env->GetStaticMethodID(nativeEngineClass.get(),\n                                                                 \"onOpenDexFileNative\",\n                                                                 \"([Ljava/lang/String;)V\");\n\n    if (isArt) {\n        patchEnv.art_work_around_app_jni_bugs = dlsym(soInfo, \"art_work_around_app_jni_bugs\");\n    } else {\n        // workaround for dlsym returns null when system has libhoudini\n        void *h = dlopen(\"/system/lib/libandroid_runtime.so\", RTLD_LAZY);\n        {\n            patchEnv.IPCThreadState_self = (int (*)(void)) dlsym(RTLD_DEFAULT,\n                                                                 \"_ZN7android14IPCThreadState4selfEv\");\n            patchEnv.native_getCallingUid = (int (*)(int)) dlsym(RTLD_DEFAULT,\n                                                                 \"_ZNK7android14IPCThreadState13getCallingUidEv\");\n            if (patchEnv.IPCThreadState_self == NULL) {\n                patchEnv.IPCThreadState_self = (int (*)(void)) dlsym(RTLD_DEFAULT,\n                                                                     \"_ZN7android14IPCThreadState13getCallingUidEv\");\n            }\n        }\n        if (h != NULL) {\n            dlclose(h);\n        }\n\n        patchEnv.GetCstrFromString = (char *(*)(void *)) dlsym(soInfo,\n                                                               \"_Z23dvmCreateCstrFromStringPK12StringObject\");\n        if (!patchEnv.GetCstrFromString) {\n            patchEnv.GetCstrFromString = (char *(*)(void *)) dlsym(soInfo,\n                                                                   \"dvmCreateCstrFromString\");\n        }\n        patchEnv.GetStringFromCstr = (void *(*)(const char *)) dlsym(soInfo,\n                                                                     \"_Z23dvmCreateStringFromCstrPKc\");\n        if (!patchEnv.GetStringFromCstr) {\n            patchEnv.GetStringFromCstr = (void *(*)(const char *)) dlsym(soInfo,\n                                                                         \"dvmCreateStringFromCstr\");\n        }\n    }\n    measureNativeOffset(isArt);\n    replaceGetCallingUid(isArt);\n    replaceOpenDexFileMethod(javaMethods.getElement(OPEN_DEX).get(), isArt,\n                             apiLevel);\n    replaceCameraNativeSetupMethod(javaMethods.getElement(CAMERA_SETUP).get(),\n                                   isArt, apiLevel);\n    replaceAudioRecordNativeCheckPermission(javaMethods.getElement(\n            AUDIO_NATIVE_CHECK_PERMISSION).get(),\n                                            isArt, apiLevel);\n}\n\nvoid *getDvmOrArtSOHandle() {\n    char so_name[25] = {0};\n    __system_property_get(\"persist.sys.dalvik.vm.lib.2\", so_name);\n    if (strlen(so_name) == 0) {\n        __system_property_get(\"persist.sys.dalvik.vm.lib\", so_name);\n    }\n    void *soInfo = dlopen(so_name, 0);\n    if (!soInfo) {\n        soInfo = RTLD_DEFAULT;\n    }\n    return soInfo;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Foundation/VMPatch.h",
    "content": "//\n// VirtualApp Native Project\n//\n\n#ifndef FOUNDATION_PATH\n#define FOUNDATION_PATH\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#include <fb/include/fb/ALog.h>\n#include <fb/include/fb/fbjni.h>\n#include \"Jni/Helper.h\"\n\nusing namespace facebook::jni;\n\nenum METHODS {\n    OPEN_DEX = 0, CAMERA_SETUP, AUDIO_NATIVE_CHECK_PERMISSION\n};\n\nvoid hookAndroidVM(JArrayClass<jobject> javaMethods,\n                   jstring packageName, jboolean isArt, jint apiLevel, jint cameraMethodType);\n\nvoid *getDvmOrArtSOHandle();\n\n\n#endif //NDK_HOOK_NATIVE_H\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/.gitignore",
    "content": ".DS_Store\nbuild/\ndarwin.ios.release.mk\n.idea/\n.vscode/\ncmake-build-debug/\nMachoParser.xcworkspace/\n\n# Prerequisites\n*.d\n\n# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Linker output\n*.ilk\n*.map\n*.exp\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Libraries\n*.lib\n*.a\n*.la\n*.lo\n\n# Shared objects (inc. Windows DLLs)\n*.dll\n*.so\n*.so.*\n*.dylib\n\n# Executables\n*.exe\n*.out\n*.app\n*.i*86\n*.x86_64\n*.hex\n\n# Debug files\n*.dSYM/\n*.su\n*.idb\n*.pdb\n\n# Kernel Module Compile Results\n*.mod*\n*.cmd\n.tmp_versions/\nmodules.order\nModule.symvers\nMkfile.old\ndkms.conf\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/.gitmodules",
    "content": "[submodule \"tools/deps/MachoParser\"]\n\tpath = tools/deps/MachoParser\n\turl = https://github.com/jmpews/MachoParser.git\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/Android.mk",
    "content": "#\n# ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./android.mk APP_ABI=armeabi(armeabi-v7a/arm64-v8a)\n#\n\nLOCAL_PATH := $(call my-dir)\n\ninclude $(CLEAR_VARS)\n\nZZ_INCLUDE := $(LOCAL_PATH)/include \\\n\t\t\t$(LOCAL_PATH)/src\n\nZZ_SRC := $(wildcard $(LOCAL_PATH)/src/*.c) \\\n\t\t\t$(wildcard $(LOCAL_PATH)/src/zzdeps/common/*.c) \\\n\t\t\t$(wildcard $(LOCAL_PATH)/src/zzdeps/linux/*.c) \\\n\t\t\t$(wildcard $(LOCAL_PATH)/src/zzdeps/posix/*.c) \\\n\t\t\t$(wildcard $(LOCAL_PATH)/src/platforms/backend-linux/*.c) \\\n\t\t\t$(wildcard $(LOCAL_PATH)/src/platforms/backend-posix/*.c)\n\nifeq ($(TARGET_ARCH), arm)\n\tZZ_SRC += $(wildcard $(LOCAL_PATH)/src/platforms/arch-arm/*.c) \\\n\t\t\t$(wildcard $(LOCAL_PATH)/src/platforms/backend-arm/*.c)\nelse ifeq ($(TARGET_ARCH), arm64)\n\tZZ_SRC += $(wildcard $(LOCAL_PATH)/src/platforms/arch-arm64/*.c) \\\n\t\t\t$(wildcard $(LOCAL_PATH)/src/platforms/backend-arm64/*.c)\nelse ifeq ($(TARGET_ARCH), x86)\n\tZZ_SRC += $(wildcard $(LOCAL_PATH)/src/platforms/arch-x86/*.c) \\\n\t\t\t$(wildcard $(LOCAL_PATH)/src/platforms/backend-x86/*.c)\nendif\n\nLOCAL_MODULE := hookzz\nLOCAL_C_INCLUDES := $(ZZ_INCLUDE)\nLOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include\nLOCAL_SRC_FILES := \t$(ZZ_SRC)\n\ninclude $(BUILD_STATIC_LIBRARY)"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\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."
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/Makefile",
    "content": "NO_COLOR=\\x1b[0m\nOK_COLOR=\\x1b[32;01m\nERROR_COLOR=\\x1b[31;01m\nWARN_COLOR=\\x1b[33;01m\n\nHOOKZZ_NAME := hookzz\nHOOKZZ_DIR := $(abspath .)\nLOCAL_PATH := $(abspath .)\nOUTPUT_DIR := $(abspath build)\n\nCFLAGS ?= -O0 -g\nCXXFLAGS = $(CFLAGS) -stdlib=libc++ -std=c++11 -gmodules\nLDFLAGS ?=\nLIBS_CFLAGS ?= -fPIC\n\nHOST ?= $(shell uname -s)\nHOST_ARCH ?= $(shell uname -m)\n\nifeq ($(HOST), Darwin)\n\nendif\n\nZZ_SRCS_PATH := $(abspath $(LOCAL_PATH)/src)\nZZ_DEPS_PATH := $(abspath $(LOCAL_PATH)/src/zzdeps)\nZZ_CAPSTONE_DEPS_PATH := $(abspath $(LOCAL_PATH)/deps/capstone)\n\nZZ_COMMON_SRCS := $(wildcard $(ZZ_SRCS_PATH)/*.c)\nZZ_SRCS := $(ZZ_COMMON_SRCS) \\\n\t\t\t$(wildcard $(ZZ_SRCS_PATH)/platforms/backend-posix/*.c)\n\n# zzdeps\nZZ_DEPS_SRCS := $(wildcard $(ZZ_DEPS_PATH)/common/*.c) \\\n\t\t$(wildcard $(ZZ_DEPS_PATH)/posix/*.c) \n\nBACKEND ?= ios\nARCH ?= arm64\n\nifeq ($(BACKEND), ios)\n\tifeq ($(ARCH), arm)\n\t\tZZ_ARCH := armv7\n\telse ifeq ($(ARCH), arm64)\n\t\tZZ_ARCH := arm64\n\tendif\n\n\tZZ_BACKEND := ios\n\tZZ_GXX_BIN := $(shell xcrun --sdk iphoneos --find clang++)\n\tZZ_GCC_BIN := $(shell xcrun --sdk iphoneos --find clang)\n\tZZ_SDK_ROOT := $(shell xcrun --sdk iphoneos --show-sdk-path)\n\tZZ_AR_BIN := $(shell which ar)\n\tZZ_RANLIB_BIN := $(shell which ranlib)\n\n\tZZ_DEPS_SRCS += $(wildcard $(ZZ_DEPS_PATH)/darwin/*.c)\n\tZZ_SRCS += $(wildcard $(ZZ_SRCS_PATH)/platforms/backend-darwin/*.c)\n\t\n\tZZ_CFLAGS := -g -fPIC -shared -dynamiclib\n\tZZ_DLL := lib$(HOOKZZ_NAME).dylib\n\n\tCFLAGS += -arch $(ZZ_ARCH)\n\t\n\tZZ_GCC_SOURCE := $(ZZ_GCC_BIN) -isysroot $(ZZ_SDK_ROOT)\n\tZZ_GXX_SOURCE := $(ZZ_GXX_BIN) -isysroot $(ZZ_SDK_ROOT)\n\tZZ_GCC_TEST := $(ZZ_GCC_BIN) -isysroot $(ZZ_SDK_ROOT)\n\tZZ_GXX_TEST := $(ZZ_GXX_BIN) -isysroot $(ZZ_SDK_ROOT)\nelse ifeq ($(BACKEND), macos)\n\tifeq ($(ARCH), x86)\n\t\tZZ_ARCH := i386\n\telse ifeq ($(ARCH), x86_64)\n\t\tZZ_ARCH := x86_64\n\tendif\n\n\tZZ_BACKEND := macos\n\tZZ_GXX_BIN := $(shell xcrun --sdk macosx --find clang++)\n\tZZ_GCC_BIN := $(shell xcrun --sdk macosx --find clang)\n\tZZ_SDK_ROOT := $(shell xcrun --sdk macosx --show-sdk-path)\n\tZZ_AR_BIN := $(shell which ar)\n\tZZ_RANLIB_BIN := $(shell which ranlib)\n\n\tZZ_DEPS_SRCS += $(wildcard $(ZZ_DEPS_PATH)/darwin/*.c)\n\tZZ_SRCS += $(wildcard $(ZZ_SRCS_PATH)/platforms/backend-darwin/*.c)\n\t\n\tZZ_CFLAGS := -g -fPIC -shared -dynamiclib\n\tZZ_DLL := lib$(HOOKZZ_NAME).dylib\n\n\tCFLAGS += -arch $(ZZ_ARCH)\n\t\n\tZZ_GCC_SOURCE := $(ZZ_GCC_BIN) -isysroot $(ZZ_SDK_ROOT)\n\tZZ_GXX_SOURCE := $(ZZ_GXX_BIN) -isysroot $(ZZ_SDK_ROOT)\n\tZZ_GCC_TEST := $(ZZ_GCC_BIN) -isysroot $(ZZ_SDK_ROOT)\n\tZZ_GXX_TEST := $(ZZ_GXX_BIN) -isysroot $(ZZ_SDK_ROOT)\nelse ifeq ($(BACKEND), android)\n\tZZ_BACKEND := android\n\n\tifeq ($(ARCH), arm)\n\t\tZZ_ARCH := armv7\n\t\tZZ_API_LEVEL := android-19\n\t\tZZ_CROSS_PREFIX := arm-linux-androideabi-\n\t\tZZ_BIN_CROSS_PREFIX := arm-linux-androideabi-\n\telse ifeq ($(ARCH), arm64)\n\t\tZZ_ARCH := arm64\n\t\tZZ_API_LEVEL := android-21\n\t\tZZ_CROSS_PREFIX := aarch64-linux-android-\n\t\tZZ_BIN_CROSS_PREFIX := aarch64-linux-android-\n\telse ifeq ($(ARCH), x86)\n\t\tZZ_ARCH := x86\n\t\tZZ_API_LEVEL := android-21\n\t\tZZ_CROSS_PREFIX := x86-\n\t\tZZ_BIN_CROSS_PREFIX := i686-linux-android-\n\n\tendif\n\n\tHOST_DIR := $(shell echo $(HOST) | tr A-Z a-z)-$(HOST_ARCH)\n\tZZ_NDK_HOME := $(shell dirname `which ndk-build`)\n\tZZ_SDK_ROOT := $(ZZ_NDK_HOME)/platforms/$(ZZ_API_LEVEL)/arch-$(ARCH)\n\tZZ_GCC_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_BIN_CROSS_PREFIX)gcc\n\tZZ_GXX_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_BIN_CROSS_PREFIX)g++\n\tZZ_AR_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_BIN_CROSS_PREFIX)ar\n\tZZ_RANLIB_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_BIN_CROSS_PREFIX)ranlib\n\n\tZZ_DEPS_SRCS += $(wildcard $(ZZ_DEPS_PATH)/linux/*.c)\n\tZZ_SRCS += $(wildcard $(ZZ_SRCS_PATH)/platforms/backend-linux/*.c)\n\n\tZZ_CFLAGS := -g -fPIC -shared\n\tZZ_DLL := lib$(HOOKZZ_NAME).so\n\n\tZZ_GCC_SOURCE := $(ZZ_GCC_BIN) --sysroot=$(ZZ_SDK_ROOT)\n\tZZ_GXX_SOURCE := $(ZZ_GXX_BIN) --sysroot=$(ZZ_SDK_ROOT)\n\tZZ_GCC_TEST := $(ZZ_GCC_BIN) --sysroot=$(ZZ_SDK_ROOT)\n\tZZ_GXX_TEST := $(ZZ_GXX_BIN) --sysroot=$(ZZ_SDK_ROOT)\nendif\n\nZZ_SRCS += $(wildcard $(ZZ_SRCS_PATH)/platforms/arch-$(ARCH)/*.c) \\\n\t\t$(wildcard $(ZZ_SRCS_PATH)/platforms/backend-$(ARCH)/*.c)\n\nifeq ($(ARCH), arm64)\nZZ_SS += $(wildcard $(ZZ_SRCS_PATH)/platforms/backend-$(ARCH)/*.s)\nendif\n\t\nZZ_EXPORT_INCLUDE := -I$(LOCAL_PATH)/include\n\nZZ_SRCS_INCLUDE := $(ZZ_EXPORT_INCLUDE) \\\n\t\t-I$(ZZ_SRCS_PATH)\n\nZZ_DEPS_OBJS := $(ZZ_DEPS_SRCS:.c=.o)\n\nOUTPUT_DIR := $(OUTPUT_DIR)/$(ZZ_BACKEND)-$(ZZ_ARCH)\n\nZZ_GCC_SOURCE += $(ZZ_SRCS_INCLUDE)\nZZ_GCC_TEST += $(ZZ_INCLUDE)\n\n\nLDFLAGS += $(ZZ_LIB)\n\nZZ_SS_OBJS := $(ZZ_SS:.s=.o)\nZZ_SRCS_OBJS := $(ZZ_SRCS:.c=.o)\nZZ_OBJS := $(ZZ_SRCS_OBJS) $(ZZ_DEPS_OBJS) $(ZZ_SS_OBJS)\n\n# ATTENTION !!!\n# 1. simple `ar` can't make a 'static library', need `ar -x` to extract `libcapstone.ios.arm64.a` and then `ar rcs` to pack as `.a`\n# 2. must `rm -rf  $(OUTPUT_DIR)/libhookzz.static.a`, very important!!!\n$(HOOKZZ_NAME) : $(ZZ_OBJS)\n\t@mkdir -p $(OUTPUT_DIR)\n\t@rm -rf $(OUTPUT_DIR)/*\n\n\t@$(ZZ_GCC_SOURCE) $(ZZ_CFLAGS) $(CFLAGS) $(LDFLAGS) $(ZZ_OBJS) -o $(OUTPUT_DIR)/$(ZZ_DLL)\n\t@$(ZZ_AR_BIN) -rcs $(OUTPUT_DIR)/lib$(HOOKZZ_NAME).static.a $(ZZ_OBJS)\n\n\t@echo \"$(OK_COLOR)build success for $(ARCH)-$(BACKEND)-hookzz! $(NO_COLOR)\"\n\n$(ZZ_SRCS_OBJS): %.o : %.c\n\t@$(ZZ_GCC_SOURCE) $(CFLAGS) -c $< -o $@\n\t@echo \"$(OK_COLOR)generate [$@]! $(NO_COLOR)\"\n\n$(ZZ_DEPS_OBJS): %.o : %.c\n\t@$(ZZ_GCC_SOURCE) $(CFLAGS) -c $< -o $@\n\t@echo \"$(OK_COLOR)generate [$@]! $(NO_COLOR)\"\n\n$(ZZ_SS_OBJS): %.o : %.s\n\t@$(ZZ_GCC_SOURCE) $(CFLAGS) -c $< -o $@\n\t@echo \"$(OK_COLOR)generate [$@]! $(NO_COLOR)\"\n\nclean:\n\t@rm -rf $(shell find ./src -name \"*\\.o\" | xargs echo)\n\t@rm -rf $(OUTPUT_DIR)\n\t@echo \"$(OK_COLOR)clean all *.o success!$(NO_COLOR)\""
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/README.md",
    "content": "# What is HookZz ?\n\n**a hook framwork for arm/arm64/ios/android**\n\nref to: [frida-gum](https://github.com/frida/frida-gum) and [minhook](https://github.com/TsudaKageyu/minhook) and [substrate](https://github.com/jevinskie/substrate).\n\n**special thanks to [frida-gum](https://github.com/frida/frida-gum) perfect code and modular architecture, frida is aircraft carrier, HookZz is boat, but still with some tricks**\n\n**thanks for @lauos with contributing android code**\n\n# Features\n\n- **solidify inlinehook without Jailbreak [new-90%]**\n\n- **GOT hook with HookZz(i.e. change fishhook to inlinehook), better for APM [new-0%]**\n\n- [HookZz-Modules help you to hook.](https://github.com/jmpews/HookZzModules)\n\n- the power to access registers directly\n\n- hook function with `replace_call`\n\n- hook function with `pre_call` and `post_call`\n\n- hook **address(a piece of code)** with `pre_call` and `half_call`\n\n- (almost)only **one instruction** to hook(i.e. hook **short funciton, even only one instruction**) [arm/thumb/arm64]\n\n- runtime code patch, without codesign limit\n\n- it's cute\n\n# Getting Started\n\n[Move to HookZz Getting Started](https://jmpews.github.io/zzpp/getting-started/) **[need update]**\n\n# How it works ?\n\n[Move to HookFrameworkDesign.md](https://github.com/jmpews/HookZz/blob/master/docs/HookFrameworkDesign.md) **[need update]**\n\n# Why I do this?\n\n1. for arsenal - [zzdeps](https://github.com/jmpews/zzdeps)\n2. for low-level control\n\n# Who use this?\n\n**[VirtualApp](https://github.com/asLody/VirtualApp) An open source implementation of MultiAccount.(Support 4.0 - 8.0)**\n\n**[AppleTrace](https://github.com/everettjf/AppleTrace) Trace tool for iOS/macOS (similar to systrace for Android)**\n\n# Docs\n\n[Move to HookZz docs](https://jmpews.github.io/zzpp/hookzz-docs/) **[need update]**\n\n# Example\n\n[Move to HookZz example](https://jmpews.github.io/zzpp/hookzz-example/) **[need update]**\n\n# Modules\n\n[Move to HookZzModules](https://github.com/jmpews/HookZzModules) **[need update]**\n\n# Compile\n\n## build for arm64-ios\n\n#### 1. build `libhookzz.dylib` and `libhookzz.static.a`\n\n```\njmpews at jmpewsdeMBP in ~/Desktop/SpiderZz/project/HookZz\nλ : >>> make clean; make BACKEND=ios ARCH=arm64\nclean all *.o success!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/allocator.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/interceptor.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/memory.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/stack.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/trampoline.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/backend-posix/thread-posix.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/backend-darwin/memory-darwin.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/arch-arm64/instructions.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/arch-arm64/reader-arm64.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/arch-arm64/regs-arm64.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/arch-arm64/relocator-arm64.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/arch-arm64/writer-arm64.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/backend-arm64/thunker-arm64.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/backend-arm64/trampoline-arm64.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/zzdeps/common/memory-utils-common.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/zzdeps/posix/memory-utils-posix.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/zzdeps/posix/thread-utils-posix.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/zzdeps/darwin/macho-utils-darwin.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/zzdeps/darwin/memory-utils-darwin.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/backend-arm64/interceptor-template-arm64.o]!\nbuild success for arm64-ios-hookzz!\n```\n\ncheck `build/ios-arm64/*`.\n\n#### 2. build tests dylib\n\n```\njmpews at jmpewsdeMBP in ~/Desktop/SpiderZz/project/HookZz/tests/arm64-ios\nλ : >>> make clean; make\nclean all *.o success!\nbuild [test_hook_oc.dylib] success for arm64-ios!\nbuild [test_hook_address.dylib] success for arm64-ios!\nbuild [test_hook_printf.dylib] success for arm64-ios!\nbuild [test] success for arm64-ios-hookzz!\n```\n\ncheck `build/ios-arm64/*`.\n\n## build for arm-ios\n\nignore...\n\n## build for arm-android\n\n#### 1. build `libhookzz.so` and `libhookzz.static.a`\n\n```\njmpews at jmpewsdeMBP in ~/Desktop/SpiderZz/project/HookZz\nλ : >>> make clean; make BACKEND=android ARCH=arm\nclean all *.o success!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/allocator.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/interceptor.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/memory.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/stack.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/trampoline.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/backend-posix/thread-posix.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/backend-linux/memory-linux.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/arch-arm/instructions.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/arch-arm/reader-arm.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/arch-arm/reader-thumb.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/arch-arm/regs-arm.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/arch-arm/relocator-arm.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/arch-arm/relocator-thumb.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/arch-arm/writer-arm.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/arch-arm/writer-thumb.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/backend-arm/thunker-arm.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/platforms/backend-arm/trampoline-arm.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/zzdeps/common/memory-utils-common.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/zzdeps/posix/memory-utils-posix.o]!\ngenerate [/Users/jmpews/Desktop/SpiderZz/project/HookZz/src/zzdeps/posix/thread-utils-posix.o]!\nbuild success for arm-android-hookzz!\n```\n\nand check `build/android-armv7`\n\n#### build tests ELF\n\n```\njmpews at jmpewsdeMBP in ~/Desktop/SpiderZz/project/HookZz/tests/arm-android\nλ : >>> make clean; make\nclean all *.o success!\nbuild [test_hook_open_arm.dylib] success for armv7-ios!\nbuild [test_hook_address_thumb.dylib] success for armv7-ios!\nbuild [test_hook_printf.dylib] success for armv7-ios!\nbuild [test] success for armv7-android-hookzz!\n```\n\nand check `build/android-armv7/*`\n\n# Quick Example\n\n#### `test_hook_printf.c` output for arm64-ios\n\ntest hook `printf` with `try_near_jump` option , and `ZzEnableDebugMode()` with `replace_call`, `pre_call`, `post_call`.\n\n```\nZzThunkerBuildThunk:\nLogInfo: enter_thunk at 0x100162c20, use enter_thunk_template.\n\nZzThunkerBuildThunk:\nLogInfo: leave_thunk at 0x1001500f4, length: 240.\n\nZzBuildEnterTrampoline:\nLogInfo: on_enter_trampoline at 0x1001502d8, length: 44. hook-entry: 0x145e0c720. and will jump to enter_thunk(0x100162c20).\n\nZzBuildEnterTransferTrampoline:\nLogInfo: on_enter_transfer_trampoline at 0x180f1f414, length: 20. and will jump to on_enter_trampoline(0x1001502d8).\n\nZzBuildInvokeTrampoline:\nLogInfo: on_invoke_trampoline at 0x100150304, length: 24. and will jump to rest code(0x181402a60).\nArmInstructionFix: origin instruction at 0x181402a5c, relocator end at 0x181402a60, relocator instruction nums 1\norigin_prologue: 0xf4 0x4f 0xbe 0xa9 \n\nZzBuildLeaveTrampoline:\nLogInfo: on_leave_trampoline at 0x10015031c, length: 44. and will jump to leave_thunk(0x1001500f4).\n\nHookZzzzzzz, %d, %p, %d, %d, %d, %d, %d, %d, %d\n\nprintf-pre-call\ncall printf\nHookZzzzzzz, 1, 0x2, 3, 4, 5, 6, 7, 8, 9\nHookZzzzzzz, %d, %p, %d, %d, %d, %d, %d, %d, %d\n\nprintf-post-call\n```\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/docs/HookFrameworkDesign.md",
    "content": "# HookZz\n\n画了一半的流程, 其实另一半还有 invoke trampoline, leave trampoline, 大致相同.\n\n```\n                 enter            enter\nfunction         trampoline       thunk\n+------------+   +------------+   +------------+\n| hook patch +--->            +--->            |\n+------------+   |            |   | storeReg   |\n|            |   |            |   |            |\n|            |   +------------+   | prep args  |\n|            |                    |            |\n|            |                    | call func  <---+begin_invocation_func()\n|            |                    |            |\n+------------+                    | restoreReg |\n                                  |            |\n                                  +------------+\n\n```\n\n# HookFramework 架构设计 2.0\n\n一般来说可以分为以下几个模块\n\n1. 内存分配 模块\n2. 指令写 模块\n3. 指令读 模块\n4. 指令修复 模块\n5. 跳板 模块\n6. 调度器 模块\n7. 栈 模块\n\n## 1. 内存分配 模块\n\n这里主要关注三个点:\n\n1. 内存的分配\n2. 内存属性修改\n3. 内存布局获取\n\n#### 1.1 内存的分配\n\n**设计方面:** 1. 提供一个 allocator 去管理/分配内存 2. 需要封装成架构无关的 API.\n\n通常使用 posix 标准的 `mmap`, darwin 下的 mach kernel 分配内存使用 `mmap` 实际使用的是 `mach_vm_allocate`. [move to detail]( https://github.com/bminor/glibc/blob/master/sysdeps/mach/hurd/mmap.c)\n\n在入口点的 patch, 通常会使用绝对地址跳到 `trampoline`, 如果使用绝对地址跳, 将会占用 4 条指令, 如下的形式.\n\n```\nldr x17, #0x8\nb #0xc\n.long 0x0\n.long 0x0\nbr x17\n```\n\n但是如果可以使用 `B #0x?`, 实现相对地址跳(near jump), 将是最好的, 在 armv8 中的可以想在 `+-128MB` 范围内进行 `near jump`, 具体可以参考 `ARM Architecture Reference Manual ARMv8, for ARMv8-A architecture profile Page: C6-550`. 所以问题转换为找到一块 `rx-` 的内存写入 enter trampline. \n\n大概有以下几种方法可以获取到 `rx-` 内存块.\n\n1. 尝试使用 mmap 的 fixed flag 分配相近内存\n\n2. 当时获取进程内的所有动态库列表, 之后搜索每一个动态库的 `__TEXT`, 查找是否存在 code cave.(尝试搜索内存空洞(`code cave`), 搜索 `__text` 这个 `section` 其实更准确来说是搜索 `__TEXT` 这个 `segment`. 由于内存页对齐的原因以及其他原因很容易出现 `code cave`. 所以只需要搜索这个区间内的 `00` 即可, `00` 本身就是无效指令, 所以可以判断该位置无指令使用.)\n\n3. 获取当前进程的内存布局, 对所有 `rx-` 属性内存页搜索 code cave. (内存布局的获取会在1.3详细提到)\n\n```\n__asm__ {\n\t// 第一次绝对地址跳, 跳转到修复模块, 执行正常流程\n\t\"ldr x17, #0x8\\n\"\n\t\"b #0xc\\n\"\n\t\".long\\n\"\n\t\".long\\n\"\n\t\"br x17\"\n\n\t// double jump, 跳转到 on_enter_trampoline\n\t\"ldr x17, #0x8\\n\"\n\t\"b #0xc\\n\"\n\t\".long\\n\"\n\t\".long\\n\"\n\t\"br x17\"\n}\n```\n\n#### 1.2 内存属性修改\n\n通常使用 posix 标准的 `mprotect`, darwin 下的 mach kernel 修改内存属性使用的是 `mach_vm_protect`, 注意: ios 不允许引用 `#include <mach_vm.h>`, 可以用单独拷贝一份该头文件到项目下.\n\n这一部分与具体的操作系统有关. 比如 .\n\n在 lldb 中可以通过 `memory region address` 查看地址的内存属性.\n\n当然这里也存在一个巨大的坑, ios 下无法分配 `rwx` 属性的内存页. 这导致 inlinehook 无法在非越狱系统上使用, 并且只有 `MobileSafari` 才有 `VM_FLAGS_MAP_JIT` 权限. 具体解释请参下方 **[坑 - rwx 与 codesigning]**.\n\n#### 1.3 内存布局获取\n\nlinux 下可以通过 `/proc/pid/maps` 获取当前进程的内存, 而且也仅有此方法.\n\ndarwin 下有 `vmmap` 这个命令可以在 `macOS` 使用, darwin 下 pid 与 `task_t`, 所以具体的内存页管理都在 `task_t` 这个内核结构体里. 但是可以通过 `vm_region` 函数遍历获取当前进程的所有属性的内存页. 通常用来爆破 dyld 的地址\n\n\n#### 2. 指令写 模块\n\n先说坑,  非越狱状态下不允许设置 `rw-` 为 `r-x`, 或者  设置 `r-x` 为 `rx-`. 具体解释请参考下方坑 **[坑-rwx 与 codesigning]**.\n\n其实这里的指令写有种简单的方法, 就是在本地生成指令的16进制串, 之后直接写即可. 但这种应该是属于 hardcode.\n\n这里使用 `frida-gum` 和 `CydiaSubstrace` 都用的方法, 把需要用到的指令都写成一个小函数.\n\n例如:\n\n```\n// frida-gum/gum/arch-arm64/gumarm64writer.c\nvoid\ngum_arm64_zz_arm64_writer_put_ldr_reg_address (GumArm64Writer * self,\n                                      ZzARM64Reg reg,\n                                      GumAddress address)\n{\n  gum_arm64_writer_put_ldr_reg_u64 (self, reg, (zuint64) address);\n}\n\nvoid\ngum_arm64_writer_put_ldr_reg_u64 (GumArm64Writer * self,\n                                  ZzARM64Reg reg,\n                                  zuint64 val)\n{\n  GumArm64RegInfo ri;\n\n  gum_arm64_zz_arm64_writer_describe_reg (self, reg, &ri);\n\n  g_assert_cmpuint (ri.width, ==, 64);\n\n  gum_arm64_writer_add_literal_reference_here (self, val);\n  gum_arm64_zz_arm64_writer_put_instruction (self,\n      (ri.is_integer ? 0x58000000 : 0x5c000000) | ri.index);\n}\n\n```\n\n其实有另外一个小思路,  有一点小不足, 就是确定指令片段的长度, 但其实也有解决方法, **可以放几条特殊指令作为结尾标记**.\n\n先使用内联汇编写一个函数.\n\n```\n\n__attribute__((__naked__)) static void ctx_save() {\n  __asm__ volatile(\n\n      /* reserve space for next_hop */\n      \"sub sp, sp, #(2*8)\\n\"\n\n      /* save {q0-q7} */\n      \"sub sp, sp, #(8*16)\\n\"\n      \"stp q6, q7, [sp, #(6*16)]\\n\"\n      \"stp q4, q5, [sp, #(4*16)]\\n\"\n      \"stp q2, q3, [sp, #(2*16)]\\n\"\n      \"stp q0, q1, [sp, #(0*16)]\\n\"\n\n      /* save {x1-x30} */\n      \"sub sp, sp, #(30*8)\\n\"\n      \"stp fp, lr, [sp, #(28*8)]\\n\"\n      \"stp x27, x28, [sp, #(26*8)]\\n\"\n      \"stp x25, x26, [sp, #(24*8)]\\n\"\n      \"stp x23, x24, [sp, #(22*8)]\\n\"\n      \"stp x21, x22, [sp, #(20*8)]\\n\"\n      \"stp x19, x20, [sp, #(18*8)]\\n\"\n      \"stp x17, x18, [sp, #(16*8)]\\n\"\n      \"stp x15, x16, [sp, #(14*8)]\\n\"\n      \"stp x13, x14, [sp, #(12*8)]\\n\"\n      \"stp x11, x12, [sp, #(10*8)]\\n\"\n      \"stp x9, x10, [sp, #(8*8)]\\n\"\n      \"stp x7, x8, [sp, #(6*8)]\\n\"\n      \"stp x5, x6, [sp, #(4*8)]\\n\"\n      \"stp x3, x4, [sp, #(2*8)]\\n\"\n      \"stp x1, x2, [sp, #(0*8)]\\n\"\n\n      /* save sp, x0 */\n      \"sub sp, sp, #(2*8)\\n\"\n      \"add x1, sp, #(2*8 + 8*16 + 30*8 + 2*8)\\n\"\n      \"stp x1, x0, [sp, #(0*8)]\\n\"\n\n      /* alignment padding + dummy PC */\n      \"sub sp, sp, #(2*8)\\n\");\n}\n\n```\n\n之后直接复制这块函数内存数据即可, 这一般适合那种指令片段堆.\n\n```\nvoid ZzThunkerBuildEnterThunk(ZzWriter *writer)\n{\n\n    // pop x17\n    zz_arm64_writer_put_ldr_reg_reg_offset(writer, ZZ_ARM64_REG_X17, ZZ_ARM64_REG_SP, 0);\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 16);\n\n    zz_arm64_writer_put_bytes(writer, (void *)ctx_save, 26 * 4);\n\n    // call `function_context_begin_invocation`\n    zz_arm64_writer_put_bytes(writer, (void *)pass_enter_func_args, 4 * 4);\n    zz_arm64_writer_put_ldr_reg_address(\n        writer, ZZ_ARM64_REG_X17,\n        (zaddr)(zpointer)function_context_begin_invocation);\n    zz_arm64_writer_put_blr_reg(writer, ZZ_ARM64_REG_X17);\n\n    zz_arm64_writer_put_bytes(writer, (void *)ctx_restore, 23 * 4);\n}\n```\n\n#### 3. 指令读 模块\n\n这一部分实际上就是 `disassembler`, 这一部分可以直接使用 `capstone`, 这里需要把 `capstone` 编译成多种架构.\n\n#### 4. 指令修复 模块\n\n这里的指令修复主要是发生在 hook 函数头几条指令, 由于备份指令到另一个地址, 这就需要对所有 `PC(IP)` 相关指令进行修复. 对于确定的哪些指令需要修复可以参考 [Move to <解析ARM和x86_x64指令格式>](http://jmpews.github.io/2017/05/17/pwn/%E8%A7%A3%E6%9E%90ARM%E5%92%8Cx86_x64%E6%8C%87%E4%BB%A4%E6%A0%BC%E5%BC%8F/).\n\n大致的思路就是: 判断 `capstone` 读取到的指令 ID, 针对特定指令写一个小函数进行修复.\n\n例如在 `frida-gum` 中:\n\n```\nfrida-gum/gum/arch-arm64/gumarm64relocator.c\nstatic gboolean\ngum_arm64_relocator_rewrite_b (GumArm64Relocator * self,\n                               GumCodeGenCtx * ctx)\n{\n  const cs_arm64_op * target = &ctx->detail->operands[0];\n\n  (void) self;\n\n  gum_arm64_zz_arm64_writer_put_ldr_reg_address (ctx->output, ZZ_ARM64_REG_X16,\n      target->imm);\n  gum_arm64_zz_arm64_writer_put_br_reg (ctx->output, ZZ_ARM64_REG_X16);\n\n  return TRUE;\n}\n```\n\n#### 5. 跳板 模块\n\n跳板模块的设计是希望各个模块的实现更浅的耦合, 跳板函数主要作用就是进行跳转, 并准备 `跳转目标` 需要的参数. 举个例子, 被 hook 的函数经过入口跳板(`enter_trampoline`), 跳转到调度函数(`enter_chunk`), 需要被 hook 的函数相关信息等, 这个就需要在构造跳板时完成.\n\n#### 6. 调度 模块\n\n可以理解为所有被 hook 的函数都必须经过的函数, 类似于 `objc_msgSend`, 在这里通过栈返回值来控制函数(`replace_call`, `pre_call`, `half_call`, `post_call`)调用顺序.\n\n本质有些类似于 `objc_msgSend` 所有的被 hook 的函数都在经过 `enter_trampoline` 跳板后, 跳转到 `enter_thunk`, 在此进行下一步的跳转判断决定, 并不是直接跳转到 `replace_call`.\n\n#### 7. 栈模块\n\n如果希望在 `pre_call` 和 `post_call`  使用同一个局部变量, 就想在同一个函数内一样. 在 `frida-js` 中也就是 `this` 这个关键字. 这就需要自建函数栈, 模拟栈的行为. 同时还要避免线程冲突, 所以需要使用 `thread local variable`, 为每一个线程中的每一个 `hook-entry` 添加线程栈, 同时为每一次调用添加函数栈. 所以这里存在两种栈. 1. 线程栈(保存了该 hook-entry 的所有当前函数调用栈) 2. 函数调用栈(本次函数调用时的栈)\n\n# 坑\n\n## `ldr` 指令\n\n在进行指令修复时, 需要需要将 PC 相关的地址转换为绝对地址, 其中涉及到保存地址到寄存器. 一般来说是使用指令 `ldr`. 也就是说如何完成该函数 `zz_arm64_writer_put_ldr_reg_address(relocate_writer, ZZ_ARM64_REG_X17, target_addr);`\n\n`frida-gum` 的实现原理是, 有一个相对地址表, 在整体一段写完后进行修复.\n\n```\nvoid\ngum_arm64_writer_put_ldr_reg_u64 (GumArm64Writer * self,\n                                  ZzARM64Reg reg,\n                                  zuint64 val)\n{\n  GumArm64RegInfo ri;\n\n  gum_arm64_zz_arm64_writer_describe_reg (self, reg, &ri);\n\n  g_assert_cmpuint (ri.width, ==, 64);\n\n  gum_arm64_writer_add_literal_reference_here (self, val);\n  gum_arm64_zz_arm64_writer_put_instruction (self,\n      (ri.is_integer ? 0x58000000 : 0x5c000000) | ri.index);\n}\n```\n\n在 HookZz 中的实现, 直接将地址写在指令后, 之后使用 `b` 到正常的下一条指令, 从而实现将地址保存到寄存器.\n\n```\nvoid zz_arm64_writer_put_ldr_reg_address(ZzWriter *self, ZzARM64Reg reg, zaddr address)\n{\n    zz_arm64_writer_put_ldr_reg_imm(self, reg, (zuint)0x8);\n    zz_arm64_writer_put_b_imm(self, (zaddr)0xc);\n    zz_arm64_writer_put_bytes(self, (zpointer)&address, sizeof(address));\n}\n```\n\n也就是下面的样子.\n\n```\n__asm__ {\n\t\"ldr x17, #0x8\\n\"\n\t\"b #0xc\\n\"\n\t\".long\\n\"\n\t\".long\\n\"\n\t\"br x17\"\n}\n```\n\n## 寄存器污染\n\n在进行 inlinehook 需要进行各种跳转, 通常会以以下模板进行跳转.\n\n```\n0:  ldr x16, 8;\n4:  br x16;\n8:  0x12345678\n12: 0x00000000\n```\n\n问题在于这会造成 x16 寄存器被污染(\barm64 中 `svc #0x80` 使用 x16 传递系统调用号) 所以这里有两种思路解决这个问题.\n\n思路一:\n\n在使用寄存器之前进行 `push`, 跳转后 `pop`, 这里存在一个问题就是在原地址的几条指令进行 `patch code` 时一定会污染一个寄存器(也不能说一定, 如果这时进行压栈, 在之后的 `invoke_trampline` 会导致函数栈发生改变, 此时有个解决方法可以 `pop` 出来, 由 hook-entry 或者其他变量暂时保存, 但这时需要处理锁的问题. )\n\n思路二:\n\n挑选合适的寄存器, 不考虑污染问题. 这时可以参考, 下面的资料, 选择 x16 or x17, 或者自己做一个实验 `otool -tv ~/Downloads/DiSpecialDriver64 > ~/Downloads/DiSpecialDriver64.txt` 通过 dump 一个 arm64 程序的指令, 来判断哪个寄存器用的最少, 但是不要使用 `x18` 寄存器, 你对该寄存器的修改是无效的.\n\nTips: 之前还想过为对每一个寄存器都做适配, 用户可以选择当前的 `hook-entry` 选择哪一个寄存器作为临时寄存器.\n\n参考资料:\n\n```\nPAGE: 9-3\nProgrammer’s Guide for ARMv8-A\n9.1 Register use in the AArch64 Procedure Call Standard \n9.1.1 Parameters in general-purpose registers\n```\n\n这里也有一个问题,  这也是 `frida-gum` 中遇到一个问题, 就是对于 `svc #0x80` 类系统调用, 系统调用号(syscall number)的传递是利用 `x16` 寄存器进行传递的, 所以本框架使用 `x17` 寄存器, 并且在传递参数时使用 `push` & `pop`, 在跳转后恢复 `x17`, 避免了一个寄存器的使用.\n\n## `rwx` 与 `codesigning`\n\n对于非越狱, 不能分配可执行内存, 不能进行 `code patch`.\n\n两篇原理讲解 codesign 的原理\n\n```\nhttps://papers.put.as/papers/ios/2011/syscan11_breaking_ios_code_signing.pdf\nhttp://www.newosxbook.com/articles/CodeSigning.pdf\n```\n\n以及源码分析如下:\n\ncrash 异常如下, 其中 `0x0000000100714000` 是 mmap 分配的页.\n\n```\nException Type:  EXC_BAD_ACCESS (SIGKILL - CODESIGNING)\nException Subtype: unknown at 0x0000000100714000\nTermination Reason: Namespace CODESIGNING, Code 0x2\nTriggered by Thread:  0\n```\n\n寻找对应的错误码\n\n```\nxnu-3789.41.3/bsd/sys/reason.h\n/*\n * codesigning exit reasons\n */\n#define CODESIGNING_EXIT_REASON_TASKGATED_INVALID_SIG 1\n#define CODESIGNING_EXIT_REASON_INVALID_PAGE          2\n#define CODESIGNING_EXIT_REASON_TASK_ACCESS_PORT      3\n```\n\n找到对应处理函数, 请仔细阅读注释里内容, 不做解释了.\n\n```\n# xnu-3789.41.3/osfmk/vm/vm_fault.c:2632\n\n\t/* If the map is switched, and is switch-protected, we must protect\n\t * some pages from being write-faulted: immutable pages because by \n\t * definition they may not be written, and executable pages because that\n\t * would provide a way to inject unsigned code.\n\t * If the page is immutable, we can simply return. However, we can't\n\t * immediately determine whether a page is executable anywhere. But,\n\t * we can disconnect it everywhere and remove the executable protection\n\t * from the current map. We do that below right before we do the \n\t * PMAP_ENTER.\n\t */\n\tcs_enforcement_enabled = cs_enforcement(NULL);\n\n\tif(cs_enforcement_enabled && map_is_switched && \n\t   map_is_switch_protected && page_immutable(m, prot) && \n\t   (prot & VM_PROT_WRITE))\n\t{\n\t\treturn KERN_CODESIGN_ERROR;\n\t}\n\n\tif (cs_enforcement_enabled && page_nx(m) && (prot & VM_PROT_EXECUTE)) {\n\t\tif (cs_debug)\n\t\t\tprintf(\"page marked to be NX, not letting it be mapped EXEC\\n\");\n\t\treturn KERN_CODESIGN_ERROR;\n\t}\n\n\tif (cs_enforcement_enabled &&\n\t    !m->cs_validated &&\n\t    (prot & VM_PROT_EXECUTE) &&\n\t    !(caller_prot & VM_PROT_EXECUTE)) {\n\t\t/*\n\t\t * FOURK PAGER:\n\t\t * This page has not been validated and will not be\n\t\t * allowed to be mapped for \"execute\".\n\t\t * But the caller did not request \"execute\" access for this\n\t\t * fault, so we should not raise a code-signing violation\n\t\t * (and possibly kill the process) below.\n\t\t * Instead, let's just remove the \"execute\" access request.\n\t\t * \n\t\t * This can happen on devices with a 4K page size if a 16K\n\t\t * page contains a mix of signed&executable and\n\t\t * unsigned&non-executable 4K pages, making the whole 16K\n\t\t * mapping \"executable\".\n\t\t */\n\t\tprot &= ~VM_PROT_EXECUTE;\n\t}\n\n\t/* A page could be tainted, or pose a risk of being tainted later.\n\t * Check whether the receiving process wants it, and make it feel\n\t * the consequences (that hapens in cs_invalid_page()).\n\t * For CS Enforcement, two other conditions will \n\t * cause that page to be tainted as well: \n\t * - pmapping an unsigned page executable - this means unsigned code;\n\t * - writeable mapping of a validated page - the content of that page\n\t *   can be changed without the kernel noticing, therefore unsigned\n\t *   code can be created\n\t */\n\tif (!cs_bypass &&\n\t    (m->cs_tainted ||\n\t     (cs_enforcement_enabled &&\n\t      (/* The page is unsigned and wants to be executable */\n\t       (!m->cs_validated && (prot & VM_PROT_EXECUTE))  ||\n\t       /* The page should be immutable, but is in danger of being modified\n\t\t* This is the case where we want policy from the code directory -\n\t\t* is the page immutable or not? For now we have to assume that \n\t\t* code pages will be immutable, data pages not.\n\t\t* We'll assume a page is a code page if it has a code directory \n\t\t* and we fault for execution.\n\t\t* That is good enough since if we faulted the code page for\n\t\t* writing in another map before, it is wpmapped; if we fault\n\t\t* it for writing in this map later it will also be faulted for executing \n\t\t* at the same time; and if we fault for writing in another map\n\t\t* later, we will disconnect it from this pmap so we'll notice\n\t\t* the change.\n\t\t*/\n\t      (page_immutable(m, prot) && ((prot & VM_PROT_WRITE) || m->wpmapped))\n\t      ))\n\t\t    )) \n\t{\n```\n\n#### 其他文章:\n\nhttp://ddeville.me/2014/04/dynamic-linking\n\n> Later on, whenever a page fault occurs the vm_fault function in `vm_fault.c` is called. During the page fault the signature is validated if necessary. The signature will need to be validated if the page is mapped in user space, if the page belongs to a code-signed object, if the page will be writable or simply if it has not previously been validated. Validation happens in the `vm_page_validate_cs` function inside vm_fault.c (the validation process and how it is enforced continually and not only at load time is interesting, see Charlie Miller’s book for more details).\n\n> If for some reason the page cannot be validated, the kernel checks whether the `CS_KILL` flag has been set and kills the process if necessary. There is a major distinction between iOS and OS X regarding this flag. All iOS processes have this flag set whereas on OS X, although code signing is checked it is not set and thus not enforced.\n\n> In our case we can safely assume that the (missing) code signature couldn’t be verified leading to the kernel killing the process.\n\n---"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/docs/hookzz-docs.md",
    "content": "# HookZz docs\n\n> [Move to HookZz Getting Started](https://jmpews.github.io/zzpp/getting-started/)\n\n> [Move to HookZz Example](https://jmpews.github.io/zzpp/hookzz-example/)\n\n> [Move to HookZz docs](https://jmpews.github.io/zzpp/hookzz-docs/)\n\n> [Move to HookZzModules](https://github.com/jmpews/HookZzModules)\n\n> [Move to HookZzWebsite](https://jmpews.github.io/zzpp/)\n\n\n## Export 4 core function\n\n#### 1. `ZzBuildHook`\n\nbuild hook with `replace_call`, `pre_call`, `post_call`, but not enable. the definition of `PRECALL` and `POSTCALL` type is below.\n\n```\n@target_ptr: hook function address\n@replace_ptr: replace function address\n@origin_ptr: origin function address work with @replace_tpr\n@pre_call_ptr: pre function call address\n@post_call_ptr: post funciton call address\n\nZZSTATUS ZzBuildHook(zpointer target_ptr, zpointer replace_ptr, zpointer *origin_ptr, PRECALL pre_call_ptr, POSTCALL post_call_ptr);\n```\n\n#### 2. `ZzBuildHookAddress`\n\nbuild hook address(a piece of code) with `pre_call`, `half_call`. the definition of `PRECALL` and `POSTCALL` type is below.\n\n```\n@target_start_ptr: hook instruction start address\n@target_end_ptr: hook instruction end address\n@pre_call_ptr: pre function call address\n@half_call_ptr: half function call address\n\nZZSTATUS ZzBuildHookAddress(zpointer target_start_ptr, zpointer target_end_ptr, PRECALL pre_call_ptr, HALFCALL half_call_ptr);\n```\n\n#### 3. `ZzEnableHook`\n\nenable hook with `code patch` at target_ptr.\n\n```\n@target_ptr: target address\n\nZZSTATUS ZzEnableHook(zpointer target_ptr);\n```\n\n#### 4. `ZzRuntimeCodePatch`\n\nruntime code patch without codesign limit, and will work better with [MachoParser](https://github.com/jmpews/MachoParser).\n\n```\n@address: patch address\n@codedata: code patch data\n@codedata: code ptach data size\n\nZZSTATUS ZzRuntimeCodePatch(zaddr address, zpointer codedata, zuint codedata_size);\n```\n\n**[Move to AntiDebugBypass Example](https://github.com/jmpews/HookZzModules/blob/master/AntiDebugBypass/AntiDebugBypass.mm#L270)**\n\n## Export 3 core type:\n\n#### 1. `PRECALL`, `POSTCALL`, `HALFCALL`\n\n**For `RegState`:**\n\nwithout the explicit argument, use `RegState` to replace, you can access all the registers. \n\n**For `ThreadStack`:**\n\nContains all of the current `CallStack`.\n\n\n**For `CallStack`:**\n\nif you want use variable in `pre_call` and `post_call`(`half_call`), just like the trick variable `self`, you need `CallStack *stack`.\n\n```\ntypedef struct _CallStack\n{\n    long call_id;\n} CallStack;\n\ntypedef struct _ThreadStack\n{\n    long thread_id;\n    zsize size;\n} ThreadStack;\n\n\ntypedef void (*PRECALL)(RegState *rs, ThreadStack *threadstack, CallStack *callstack);\ntypedef void (*POSTCALL)(RegState *rs, ThreadStack *threadstack, CallStack *callstack);\ntypedef void (*HALFCALL)(RegState *rs, ThreadStack *threadstack, CallStack *callstack);\n\n```\n\n#### 2. `RegState`\n\ncurrent all cpu register state.\n\n```\ntypedef union FPReg_ {\n    __int128_t q;\n    struct {\n        double d1; // Holds the double (LSB).\n        double d2;\n    } d;\n    struct {\n        float f1; // Holds the float (LSB).\n        float f2;\n        float f3;\n        float f4;\n    } f;\n} FPReg;\n\ntypedef struct _RegState {\n    uint64_t pc;\n    uint64_t sp;\n\n    union {\n        uint64_t x[29];\n        struct {\n            uint64_t x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20,x21,x22,x23,x24,x25,x26,x27,x28;\n        } regs;\n    } general;\n\n    uint64_t fp;\n    uint64_t lr;\n\n    union {\n        FPReg q[8];\n        FPReg q0,q1,q2,q3,q4,q5,q6,q7;\n    } floating;\n} RegState;\n```\n\n#### 3. `CallStack`\n\nexport 2 method user to get/set `callstack`\n\n```\n// get value with the key\nzpointer ZzGetCallStackData(CallStack *callstack_ptr, char *key);\n\n// set value with key.\nzbool ZzSetCallStackData(CallStack *callstack_ptr, char *key, zpointer value_ptr, zsize value_size);\n```\n\nbut for convenience, the macro is better.\n\n```\n#define STACK_CHECK_KEY(callstack, key) (bool)ZzGetCallStackData(callstack, key)\n#define STACK_GET(callstack, key, type) *(type *)ZzGetCallStackData(callstack, key)\n#define STACK_SET(callstack, key, value, type) ZzSetCallStackData(callstack, key, &(value), sizeof(type))\n```\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/docs/hookzz-example.md",
    "content": "# HookZz example\n\n> [Move to HookZz Getting Started](https://jmpews.github.io/zzpp/getting-started/)\n\n> [Move to HookZz Example](https://jmpews.github.io/zzpp/hookzz-example/)\n\n> [Move to HookZz docs](https://jmpews.github.io/zzpp/hookzz-docs/)\n\n> [Move to HookZzModules](https://github.com/jmpews/HookZzModules)\n\n> [Move to HookZzWebsite](https://jmpews.github.io/zzpp/)\n\n\n## Simple Example\n\n#### 1. `ZzBuildHookAddress` & `ZzRuntimeCodePatch`\n\nhook at address, and specify the hook length. (i.e. hook a piece of code.)\n\n```c\n#include \"hookzz.h\"\n#include <stdio.h>\n#include <unistd.h>\n\nstatic void hack_this_function()\n{\n#ifdef __arm64__\n    __asm__(\"mov X0, #0\\n\"\n            \"mov w16, #20\\n\"\n            \"svc #0x80\");\n#endif\n}\n\nstatic void sorry_to_exit()\n{\n#ifdef __arm64__\n    __asm__(\"mov X0, #0\\n\"\n            \"mov w16, #1\\n\"\n            \"svc #0x80\");\n#endif\n}\n\nvoid getpid_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    unsigned long request = *(unsigned long *)(&rs->general.regs.x16);\n    printf(\"request(x16) is: %ld\\n\", request);\n    printf(\"x0 is: %ld\\n\", (long)rs->general.regs.x0);\n}\n\nvoid getpid_half_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    pid_t x0 = (pid_t)(rs->general.regs.x0);\n    printf(\"getpid() return at x0 is: %d\\n\", x0);\n}\n\n__attribute__((constructor)) void test_hook_address()\n{\n    void *hack_this_function_ptr = (void *)hack_this_function;\n    ZzBuildHookAddress(hack_this_function_ptr + 8, hack_this_function_ptr + 12, getpid_pre_call, getpid_half_call);\n    ZzEnableHook((void *)hack_this_function_ptr + 8);\n\n\n    void *sorry_to_exit_ptr = (void *)sorry_to_exit;\n    unsigned long nop_bytes = 0xD503201F;\n    ZzRuntimeCodePatch((unsigned long)sorry_to_exit_ptr + 8, (zpointer)&nop_bytes, 4);\n\n    hack_this_function();\n    sorry_to_exit();\n\n    printf(\"hack success -.0\\n\");\n}\n```\n\nbreakpoint with lldb.\n\n```\n(lldb) disass -n hack_this_function\ntest_hook_address.dylib`hack_this_function:\n    0x1000b0280 <+0>:  mov    x0, #0x0\n    0x1000b0284 <+4>:  mov    w16, #0x14\n    0x1000b0288 <+8>:  svc    #0x80\n    0x1000b028c <+12>: ret    \n\n(lldb) disass -n sorry_to_exit\ntest_hook_address.dylib`sorry_to_exit:\n    0x1000b0290 <+0>:  mov    x0, #0x0\n    0x1000b0294 <+4>:  mov    w16, #0x1\n    0x1000b0298 <+8>:  svc    #0x80\n    0x1000b029c <+12>: ret    \n\n(lldb) c\nProcess 41414 resuming\nrequest(x16) is: 20\nx0 is: 0\ngetpid() return at x0 is: 41414\nhack success -.0\n(lldb) disass -n hack_this_function\ntest_hook_address.dylib`hack_this_function:\n    0x1000b0280 <+0>:  mov    x0, #0x0\n    0x1000b0284 <+4>:  mov    w16, #0x14\n    0x1000b0288 <+8>:  b      0x1001202cc\n    0x1000b028c <+12>: ret    \n\n(lldb) disass -n sorry_to_exit\ntest_hook_address.dylib`sorry_to_exit:\n    0x1000b0290 <+0>:  mov    x0, #0x0\n    0x1000b0294 <+4>:  mov    w16, #0x1\n    0x1000b0298 <+8>:  nop    \n    0x1000b029c <+12>: ret  \n```\n\n\n#### 2. use `ZzBuildHook` to hook `OC-Method`\n\n```c\n#include \"hookzz.h\"\n#import <Foundation/Foundation.h>\n#import <objc/runtime.h>\n#import <mach-o/dyld.h>\n#import <dlfcn.h>\n\n@interface HookZz : NSObject\n\n@end\n\n@implementation HookZz\n\n+ (void)load {\n  [self zzMethodSwizzlingHook];\n}\n\nvoid objcMethod_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n  zpointer t = 0x1234; \n  STACK_SET(callstack ,\"key_x\", t, void *);\n  STACK_SET(callstack ,\"key_y\", t, zpointer);\n  NSLog(@\"hookzz OC-Method: -[UIViewController %s]\",\n        (zpointer)(rs->general.regs.x1));\n}\n\nvoid objcMethod_post_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n  zpointer x = STACK_GET(callstack, \"key_x\", void *);\n  zpointer y = STACK_GET(callstack, \"key_y\", zpointer);\n  NSLog(@\"function over, and get 'key_x' is: %p\", x);\n  NSLog(@\"function over, and get 'key_y' is: %p\", y);\n}\n\n+ (void)zzMethodSwizzlingHook {\n  Class hookClass = objc_getClass(\"UIViewController\");\n  SEL oriSEL = @selector(viewWillAppear:);\n  Method oriMethod = class_getInstanceMethod(hookClass, oriSEL);\n  IMP oriImp = method_getImplementation(oriMethod);\n\n  ZzBuildHook((void *)oriImp, NULL, NULL, objcMethod_pre_call, objcMethod_post_call);\n  ZzEnableHook((void *)oriImp);\n\n}\n\n@end\n```\n\nbreakpoint with lldb.\n\n```\n(lldb) disass -n \"-[UIViewController viewWillAppear:]\" -c 3\nUIKit`-[UIViewController viewWillAppear:]:\n    0x18881c10c <+0>: adrp   x8, 126868\n    0x18881c110 <+4>: ldrsw  x8, [x8, #0x280]\n    0x18881c114 <+8>: ldr    x9, [x0, x8]\n\n(lldb) c\nProcess 41637 resuming\n(lldb) c\nProcess 41637 resuming\n(lldb) c\nProcess 41637 resuming\n2017-08-30 02:01:58.954875+0800 T007[41637:10198806] hookzz OC-Method: -[UIViewController viewWillAppear:]\n2017-08-30 02:01:58.956558+0800 T007[41637:10198806] function over, and get 'key_x' is: 0x1234\n2017-08-30 02:01:58.956654+0800 T007[41637:10198806] function over, and get 'key_y' is: 0x1234\n(lldb) disass -n \"-[UIViewController viewWillAppear:]\" -c 3\nUIKit`-[UIViewController viewWillAppear:]:\n    0x18881c10c <+0>: b      0x1810b0b4c\n    0x18881c110 <+4>: ldrsw  x8, [x8, #0x280]\n    0x18881c114 <+8>: ldr    x9, [x0, x8]\n```\n\n#### 3. hook variadic function\n\n```c\n#include \"hookzz.h\"\n#include <string.h>\n#include <stdarg.h>\n#include <stdio.h>\n\nint (*orig_printf)(const char * restrict format, ...);\nint fake_printf(const char * restrict format, ...) {\n    puts(\"call printf\");\n\n    char *stack[16];\n    va_list args;\n    va_start(args, format);\n    memcpy(stack, args, 8 * 16);\n    va_end(args);\n\n    // how to hook variadic function? fake a original copy stack.\n    // [move to detail-1](http://jmpews.github.io/2017/08/29/pwn/%E7%9F%AD%E5%87%BD%E6%95%B0%E5%92%8C%E4%B8%8D%E5%AE%9A%E5%8F%82%E6%95%B0%E7%9A%84hook/)\n    // [move to detail-2](https://github.com/jmpews/HookZzModules/tree/master/AntiDebugBypass)\n    int x = orig_printf(format, stack[0], stack[1], stack[2], stack[3], stack[4], stack[5], stack[6], stack[7], stack[8], stack[9], stack[10], stack[11], stack[12], stack[13], stack[14], stack[15]);\n    return x;\n}\n\nvoid printf_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    puts((char *)rs->general.regs.x0);\n    STACK_SET(callstack, \"format\", rs->general.regs.x0, char *);\n    puts(\"printf-pre-call\");\n}\n\nvoid printf_post_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    if(STACK_CHECK_KEY(callstack, \"format\")) {\n        char *format = STACK_GET(callstack, \"format\", char *);\n        puts(format);\n    }\n    puts(\"printf-post-call\");\n}\n\n__attribute__((constructor)) void test_hook_printf()\n{\n    void *printf_ptr = (void *)printf;\n\n    ZzBuildHook((void *)printf_ptr, (void *)fake_printf, (void **)&orig_printf, printf_pre_call, printf_post_call);\n    ZzEnableHook((void *)printf_ptr);\n    printf(\"HookZzzzzzz, %d, %p, %d, %d, %d, %d, %d, %d, %d\\n\",1, (void *)2, 3, (char)4, (char)5, (char)6 , 7, 8 , 9);\n}\n```\n\nbreakpoint with lldb.\n\n```\n(lldb) disass -s 0x1815f61d8 -c 3\nlibsystem_c.dylib`printf:\n    0x1815f61d8 <+0>: sub    sp, sp, #0x30             ; =0x30 \n    0x1815f61dc <+4>: stp    x20, x19, [sp, #0x10]\n    0x1815f61e0 <+8>: stp    x29, x30, [sp, #0x20]\n(lldb) c\nProcess 41408 resuming\nHookZzzzzzz, %d, %p, %d, %d, %d, %d, %d, %d, %d\n\nprintf-pre-call\ncall printf\nHookZzzzzzz, 1, 0x2, 3, 4, 5, 6, 7, 8, 9\nHookZzzzzzz, %d, %p, %d, %d, %d, %d, %d, %d, %d\n\nprintf-post-call\n(lldb) disass -s 0x1815f61d8 -c 3\nlibsystem_c.dylib`printf:\n    0x1815f61d8 <+0>: b      0x1795f61d8\n    0x1815f61dc <+4>: stp    x20, x19, [sp, #0x10]\n    0x1815f61e0 <+8>: stp    x29, x30, [sp, #0x20]\n\n```\n\n## Advanced Example\n\n#### 1. AntiDebugBypass\n\n**[Move to AntiDebugBypass Detail](https://github.com/jmpews/HookZzModules/tree/master/AntiDebugBypass)**\n\n#### 5. hook `objc_msgSend`\n\n**[Move to hook_objc_msgSend Detail](https://github.com/jmpews/HookZzModules/tree/master/hook_objc_msgSend)**\n\n#### 6. hook `MGCopyAnswer`\n\n**[Move to hook_MGCopyAnswer Detail](https://github.com/jmpews/HookZzModules/tree/master/hook_MGCopyAnswer)**"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/docs/hookzz-getting-started.md",
    "content": "# HookZz Getting Started\n\n> [Move to HookZz Getting Started](https://jmpews.github.io/zzpp/getting-started/)\n\n> [Move to HookZz Example](https://jmpews.github.io/zzpp/hookzz-example/)\n\n> [Move to HookZz docs](https://jmpews.github.io/zzpp/hookzz-docs/)\n\n> [Move to HookZzModules](https://github.com/jmpews/HookZzModules)\n\n> [Move to HookZzWebsite](https://jmpews.github.io/zzpp/)\n\n## 1. build hookzz dylib\n\nclone the repo `git clone https://github.com/jmpews/HookZz` and build for `darwin.ios`. btw, you can set the log infomation in `hookzz.h`.\n\n```\nλ : >>> make -f darwin.ios.mk darwin.ios\ngenerate [src/allocator.o]!\ngenerate [src/interceptor.o]!\ngenerate [src/memory.o]!\ngenerate [src/stack.o]!\ngenerate [src/thread.o]!\ngenerate [src/trampoline.o]!\ngenerate [src/platforms/posix/thread-posix.o]!\ngenerate [src/platforms/darwin/memory-darwin.o]!\ngenerate [src/platforms/arm64/reader.o]!\ngenerate [src/platforms/arm64/relocator-arm64.o]!\ngenerate [src/platforms/arm64/thunker-arm64.o]!\ngenerate [src/platforms/arm64/writer-arm64.o]!\ngenerate [src/zzdeps/darwin/macho-utils-darwin.o]!\ngenerate [src/zzdeps/darwin/memory-utils-darwin.o]!\ngenerate [src/zzdeps/common/memory-utils-common.o]!\ngenerate [src/zzdeps/posix/memory-utils-posix.o]!\ngenerate [src/zzdeps/posix/thread-utils-posix.o]!\nbuild success for arm64(IOS)!\n```\n\ncheck the dylibs in `build` directory. `libhookzz.dylib` is shared library, and `libhookzz.static.a` is static library.\n\n```\nλ : >>> ls build\nlibhookzz.dylib    libhookzz.static.a\n```\n\n## 2. build the test demo dylib\n\na demo dylib to hook `[UIViewController viewWillAppear]`\n\nbefore build demo dylib, specify the hookzz library path(shared or static).\n\n1. build with commandline.\n\n```\n`xcrun --sdk iphoneos --find clang` -isysroot `xcrun --sdk iphoneos --show-sdk-path` -g -gmodules -I/path/HookZz/include  -L/path/HookZz/build -lhookzz.static -framework Foundation -dynamiclib -arch arm64 test_hook_oc.m -o test_hook_oc.dylib\n```\n\n2. build with `make -f darwin.ios.mk test`\n\n```\nλ : >>> make -f darwin.ios.mk test\nbuild success for arm64(IOS)!\nbuild [test_hook_oc.dylib] success for arm64(ios)!\nbuild [test_hook_address.dylib] success for arm64(ios)!\nbuild [test_hook_printf.dylib] success for arm64(ios)!\nbuild [test] success for arm64(IOS)!\n```\n\n```\nλ : >>> ls build\nlibhookzz.dylib         test_hook_address.dylib test_hook_printf.dylib\nlibhookzz.static.a      test_hook_oc.dylib\n```\n\n## 3. test your demo dylib\n\nbuild new ios app project. and then `Build Phases -> New Run Script Phase` add a run script.\n\n```\ncd ${BUILT_PRODUCTS_DIR}\ncd ${FULL_PRODUCT_NAME}\n\ncp /path/HookZz/build/test_hook_oc.dylib ./\n/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --timestamp=none test_hook_oc.dylib\n/Users/jmpews/Desktop/SpiderZz/Pwntools/Darwin/bin/optool install -c load -p \"@executable_path/test_hook_oc.dylib\" -t ${EXECUTABLE_NAME}\n```\n\nlast thing, run the app ,you will get the such output(with open `GLOBAL_DEBUG`, `GLOBAL_INFO`)\n\n```\ntarget 0x188525804 near jump to 0x183c9141c\ntarget 0x188525804 call begin-invocation\n2017-08-20 16:41:26.155 T007[409:129805] hookzz OC-Method: -[ViewController viewWillAppear:]\n0x188525804 call end-invocation\n2017-08-20 16:41:26.157 T007[409:129805] function over, and get 'key_x' is: 0x1234\n2017-08-20 16:41:26.157 T007[409:129805] function over, and get 'key_y' is: 0x1234\n```\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/include/hookzz.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef hook_zz_h\n#define hook_zz_h\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#define DEBUG_MODE 0\n\n#ifndef zz_type\n#define zz_type\n\ntypedef void *zpointer;\ntypedef unsigned long zsize;\ntypedef unsigned long zaddr;\n\ntypedef uint64_t zuint64;\ntypedef uint32_t zuint32;\ntypedef uint16_t zuint16;\ntypedef uint8_t zuint8;\n\ntypedef int32_t zint32;\ntypedef int16_t zint16;\ntypedef int8_t zint8;\n\ntypedef unsigned long zuint;\ntypedef long zint;\ntypedef unsigned char zbyte;\ntypedef bool zbool;\n\n#endif\n\n#if defined(FALSE)\n#else\n#define FALSE 0\n#define TRUE 1\n#endif\n\n#ifndef zz_register_type\n#define zz_register_type\n#if defined(__arm64__) || defined(__aarch64__)\ntypedef union FPReg_ {\n    __int128_t q;\n    struct {\n        double d1;\n        double d2;\n    } d;\n    struct {\n        float f1;\n        float f2;\n        float f3;\n        float f4;\n    } f;\n} FPReg;\n\ntypedef struct _RegState {\n    uint64_t sp;\n\n    union {\n        uint64_t x[29];\n        struct {\n            uint64_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21,\n                x22, x23, x24, x25, x26, x27, x28;\n        } regs;\n    } general;\n\n    uint64_t fp;\n    uint64_t lr;\n\n    union {\n        FPReg q[8];\n        FPReg q0, q1, q2, q3, q4, q5, q6, q7;\n    } floating;\n} RegState;\n#elif defined(__arm__)\ntypedef struct _RegState {\n    zuint32 sp;\n\n    union {\n        zuint32 r[13];\n        struct {\n            zuint32 r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12;\n        } regs;\n    } general;\n\n    zuint32 lr;\n} RegState;\n#elif defined(__i386__)\ntypedef struct _RegState {\n} RegState;\n#elif defined(__x86_64__)\ntypedef struct _RegState {\n} RegState;\n#endif\n#endif\n\ntypedef enum _ZZSTATUS {\n    ZZ_UNKOWN = -1,\n    ZZ_DONE = 0,\n    ZZ_SUCCESS,\n    ZZ_FAILED,\n    ZZ_DONE_HOOK,\n    ZZ_DONE_INIT,\n    ZZ_DONE_ENABLE,\n    ZZ_ALREADY_HOOK,\n    ZZ_ALREADY_INIT,\n    ZZ_ALREADY_ENABLED,\n    ZZ_NEED_INIT,\n    ZZ_NO_BUILD_HOOK\n} ZZSTATUS;\n\ntypedef struct _CallStack {\n    zsize call_id;\n    struct _ThreadStack *threadstack;\n} CallStack;\n\ntypedef struct _ThreadStack {\n    zsize thread_id;\n    zsize size;\n} ThreadStack;\n\ntypedef void (*PRECALL)(RegState *rs, ThreadStack *threadstack, CallStack *callstack);\ntypedef void (*POSTCALL)(RegState *rs, ThreadStack *threadstack, CallStack *callstack);\ntypedef void (*HALFCALL)(RegState *rs, ThreadStack *threadstack, CallStack *callstack);\n\n// ------- export API -------\n\nzpointer ZzGetCallStackData(CallStack *callstack_ptr, char *key);\nzbool ZzSetCallStackData(CallStack *callstack_ptr, char *key, zpointer value_ptr, zsize value_size);\n\n#define STACK_CHECK_KEY(callstack, key) (bool)ZzGetCallStackData(callstack, key)\n#define STACK_GET(callstack, key, type) *(type *)ZzGetCallStackData(callstack, key)\n#define STACK_SET(callstack, key, value, type) ZzSetCallStackData(callstack, key, &(value), sizeof(type))\n\nZZSTATUS ZzBuildHook(zpointer target_ptr, zpointer replace_call_ptr, zpointer *origin_ptr, PRECALL pre_call_ptr,\n                     POSTCALL post_call_ptr, zbool try_near_jump);\nZZSTATUS ZzBuildHookAddress(zpointer target_start_ptr, zpointer target_end_ptr, PRECALL pre_call_ptr,\n                            HALFCALL half_call_ptr, zbool try_near_jump);\nZZSTATUS ZzEnableHook(zpointer target_ptr);\n\nZZSTATUS ZzHook(zpointer target_ptr, zpointer replace_ptr, zpointer *origin_ptr, PRECALL pre_call_ptr,\n                POSTCALL post_call_ptr, zbool try_near_jump);\nZZSTATUS ZzHookPrePost(zpointer target_ptr, PRECALL pre_call_ptr, POSTCALL post_call_ptr);\nZZSTATUS ZzHookReplace(zpointer target_ptr, zpointer replace_ptr, zpointer *origin_ptr);\nZZSTATUS ZzHookAddress(zpointer target_start_ptr, zpointer target_end_ptr, PRECALL pre_call_ptr,\n                       HALFCALL half_call_ptr);\n\nvoid ZzEnableDebugMode(void);\n\nZZSTATUS ZzRuntimeCodePatch(zaddr address, zpointer codedata, zsize codedata_size);\n\n// ------- export API -------\n\n#if defined(__arm64__) || defined(__aarch64__)\n#if defined(__APPLE__) && defined(__MACH__)\n#include <TargetConditionals.h>\n#if TARGET_OS_IPHONE\n#define TARGET_IS_IOS 1\n#endif\n#endif\n#endif\n#ifdef TARGET_IS_IOS\nZZSTATUS ZzSolidifyHook(zpointer target_fileoff, zpointer replace_call_ptr, zpointer *origin_ptr, PRECALL pre_call_ptr,\n                        POSTCALL post_call_ptr);\n#endif\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/allocator.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"allocator.h\"\n#include \"zzdeps/zz.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define DEFAULT_ALLOCATOR_CAPACITY 4\n\nZzAllocator *ZzNewAllocator() {\n\n    if (!ZzMemoryIsSupportAllocateRXPage())\n        return NULL;\n\n    ZzAllocator *allocator;\n    allocator = (ZzAllocator *)malloc(sizeof(ZzAllocator));\n    allocator->memory_pages = (ZzMemoryPage **)malloc(sizeof(ZzMemoryPage *) * DEFAULT_ALLOCATOR_CAPACITY);\n    if (!allocator->memory_pages)\n        return NULL;\n    allocator->size = 0;\n    allocator->capacity = DEFAULT_ALLOCATOR_CAPACITY;\n    return allocator;\n}\n\nZzMemoryPage *ZzNewMemoryPage() {\n    zsize page_size = ZzMemoryGetPageSzie();\n    zpointer page_ptr = NULL;\n    zpointer cave_ptr = NULL;\n    ZzMemoryPage *page = NULL;\n\n    page_ptr = ZzMemoryAllocatePages(1);\n    if (!page_ptr) {\n        return NULL;\n    }\n    if (!ZzMemoryProtectAsExecutable((zaddr)page_ptr, page_size)) {\n        Xerror(\"ZzMemoryProtectAsExecutable error at %p\", page_ptr);\n#if defined(DEBUG_MODE)\n        debug_break();\n#endif\n        exit(1);\n    }\n\n    page = (ZzMemoryPage *)malloc(sizeof(ZzMemoryPage));\n    page->base = page_ptr;\n    page->curr_pos = page_ptr;\n    page->size = page_size;\n    page->used_size = 0;\n    return page;\n}\n\nZzMemoryPage *ZzNewNearMemoryPage(zaddr address, zsize redirect_range_size) {\n    zsize page_size = ZzMemoryGetPageSzie();\n    zpointer page_ptr = NULL;\n    zpointer cave_ptr = NULL;\n    ZzMemoryPage *page = NULL;\n\n    page_ptr = ZzMemoryAllocateNearPages(address, redirect_range_size, 1);\n    if (!page_ptr) {\n        return NULL;\n    }\n\n    if (!ZzMemoryProtectAsExecutable((zaddr)page_ptr, page_size)) {\n        Xerror(\"ZzMemoryProtectAsExecutable error at %p\", page_ptr);\n#if defined(DEBUG_MODE)\n        debug_break();\n#endif\n        exit(1);\n    }\n\n    page = (ZzMemoryPage *)malloc(sizeof(ZzMemoryPage));\n\n    page->base = page_ptr;\n\n    if ((zaddr)page_ptr > address && ((zaddr)page_ptr + page_size) > (address + redirect_range_size)) {\n        page->size = (address + redirect_range_size) - (zaddr)page_ptr;\n        page->used_size = 0;\n        page->curr_pos = page_ptr;\n    } else if ((zaddr)page_ptr < address && (zaddr)page_ptr < (address - redirect_range_size)) {\n        page->size = page_size;\n        page->used_size = (address - redirect_range_size) - (zaddr)page_ptr;\n        page->curr_pos = (zpointer)(address - redirect_range_size);\n    } else {\n        page->size = page_size;\n        page->used_size = 0;\n        page->curr_pos = page_ptr;\n    }\n    page->isCodeCave = FALSE;\n    return page;\n}\n\nZzMemoryPage *ZzNewNearCodeCave(zaddr address, zsize redirect_range_size, zsize code_slice_size) {\n    zsize page_size = ZzMemoryGetPageSzie();\n    zpointer cave_ptr = NULL;\n    ZzMemoryPage *page = NULL;\n    zsize cave_size = code_slice_size;\n\n    cave_ptr = ZzMemorySearchCodeCave(address, redirect_range_size, cave_size);\n\n    if (!cave_ptr)\n        return NULL;\n\n    page = (ZzMemoryPage *)malloc(sizeof(ZzMemoryPage));\n    page->base = cave_ptr;\n    page->curr_pos = cave_ptr;\n    page->size = cave_size;\n    page->used_size = 0;\n    page->isCodeCave = TRUE;\n    return page;\n}\n\nZZSTATUS ZzAddMemoryPage(ZzAllocator *allocator, ZzMemoryPage *page) {\n    if (!allocator)\n        return ZZ_FAILED;\n    if (allocator->size >= allocator->capacity) {\n        ZzMemoryPage **pages = realloc(allocator->memory_pages, sizeof(ZzMemoryPage) * (allocator->capacity) * 2);\n        if (!pages) {\n            return ZZ_FAILED;\n        }\n        allocator->capacity = allocator->capacity * 2;\n        allocator->memory_pages = pages;\n    }\n    allocator->memory_pages[allocator->size++] = page;\n    return ZZ_SUCCESS;\n}\n\n//  1. try allocate from the history pages\n//  2. try allocate a new page\n//  3. add it to the page manager\nZzCodeSlice *ZzNewCodeSlice(ZzAllocator *allocator, zsize code_slice_size) {\n    ZzCodeSlice *code_slice = NULL;\n    ZzMemoryPage *page = NULL;\n    int i;\n\n    for (i = 0; i < allocator->size; i++) {\n        page = allocator->memory_pages[i];\n        // 1. page is initialized\n        // 2. can't be codecave\n        // 3. the rest memory of this page is enough for code_slice_size\n        // 4. the page address is near\n\n        if ((zaddr)page->curr_pos % 4) {\n            int t = 4 - (zaddr)page->curr_pos % 4;\n            page->used_size += t;\n            page->curr_pos += t;\n        }\n\n        if (page->base && !page->isCodeCave && (page->size - page->used_size) > code_slice_size) {\n            code_slice = (ZzCodeSlice *)malloc(sizeof(ZzCodeSlice));\n            code_slice->data = page->curr_pos;\n            code_slice->size = code_slice_size;\n\n            page->curr_pos += code_slice_size;\n            page->used_size += code_slice_size;\n            return code_slice;\n        }\n    }\n\n    page = ZzNewMemoryPage();\n    ZzAddMemoryPage(allocator, page);\n\n    if ((zaddr)page->curr_pos % 4) {\n        int t = 4 - (zaddr)page->curr_pos % 4;\n        page->used_size += t;\n        page->curr_pos += t;\n    }\n\n    code_slice = (ZzCodeSlice *)malloc(sizeof(ZzCodeSlice));\n    code_slice->data = page->curr_pos;\n    code_slice->size = code_slice_size;\n\n    page->curr_pos += code_slice_size;\n    page->used_size += code_slice_size;\n\n    return code_slice;\n}\n\n//  1. try allocate from the history pages\n//  2. try allocate a new near page\n//  3. add it to the page manager\nZzCodeSlice *ZzNewNearCodeSlice(ZzAllocator *allocator, zaddr address, zsize redirect_range_size,\n                                zsize code_slice_size) {\n    ZzCodeSlice *code_slice = NULL;\n    ZzMemoryPage *page = NULL;\n    int i;\n    for (i = 0; i < allocator->size; i++) {\n        page = allocator->memory_pages[i];\n        // 1. page is initialized\n        // 2. can't be codecave\n        // 3. the rest memory of this page is enough for code_slice_size\n        // 4. the page address is near\n        if (page->base && !page->isCodeCave) {\n            int flag = 0;\n            zaddr split_addr = 0;\n\n            if ((zaddr)page->curr_pos < address) {\n                if (address - redirect_range_size < (zaddr)page->curr_pos) {\n                    // enough for code_slice_size\n                    if ((page->size - page->used_size) < code_slice_size)\n                        continue;\n                    flag = 1;\n                } else if (address - redirect_range_size > (zaddr)page->curr_pos &&\n                           (address - redirect_range_size) < ((zaddr)page->base + page->size)) {\n                    // enough for code_slice_size\n                    if (((zaddr)page->base + page->size) - (address - redirect_range_size) < code_slice_size)\n                        continue;\n                    split_addr = address - redirect_range_size;\n                    flag = 2;\n                }\n            } else {\n                if (address + redirect_range_size > ((zaddr)page->base + page->size)) {\n                    // enough for code_slice_size\n                    if ((page->size - page->used_size) < code_slice_size)\n                        continue;\n                    flag = 1;\n                } else if ((address + redirect_range_size) > (zaddr)page->curr_pos &&\n                           (address + redirect_range_size) < ((zaddr)page->base + page->size)) {\n                    if ((address + redirect_range_size) - (zaddr)page->curr_pos > code_slice_size)\n                        continue;\n                    flag = 1;\n                }\n            }\n\n            if (1 == flag) {\n                code_slice = (ZzCodeSlice *)malloc(sizeof(ZzCodeSlice));\n                code_slice->isCodeCave = page->isCodeCave;\n                code_slice->data = page->curr_pos;\n                code_slice->size = code_slice_size;\n\n                page->curr_pos += code_slice_size;\n                page->used_size += code_slice_size;\n                return code_slice;\n            } else if (2 == flag) {\n\n                // new page\n                ZzMemoryPage *new_page = (ZzMemoryPage *)malloc(sizeof(ZzMemoryPage));\n                new_page->base = (zpointer)split_addr;\n                new_page->size = ((zaddr)page->base + page->size) - split_addr;\n                new_page->used_size = 0;\n                new_page->curr_pos = (zpointer)split_addr;\n                ZzAddMemoryPage(allocator, new_page);\n\n                // origin page\n                page->size = split_addr - (zaddr)page->base;\n\n                code_slice = (ZzCodeSlice *)malloc(sizeof(ZzCodeSlice));\n                code_slice->isCodeCave = FALSE;\n                code_slice->data = new_page->curr_pos;\n                code_slice->size = code_slice_size;\n\n                new_page->curr_pos += code_slice_size;\n                new_page->used_size += code_slice_size;\n                return code_slice;\n            }\n        }\n    }\n\n#if 0\n    page = ZzNewNearMemoryPage(address, redirect_range_size);\n    // try allocate again, avoid the boundary page\n    if (page && (page->size - page->used_size) < code_slice_size) {\n        page = ZzNewNearMemoryPage(address, redirect_range_size);\n    }\n#endif\n    page = NULL;\n\n    if (!page) {\n        page = ZzNewNearCodeCave(address, redirect_range_size, code_slice_size);\n        if (!page)\n            return NULL;\n    }\n    if (!page)\n        return NULL;\n\n    code_slice = (ZzCodeSlice *)malloc(sizeof(ZzCodeSlice));\n    code_slice->isCodeCave = page->isCodeCave;\n    code_slice->data = page->curr_pos;\n    code_slice->size = code_slice_size;\n\n    page->curr_pos += code_slice_size;\n    page->used_size += code_slice_size;\n\n    return code_slice;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/allocator.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef allocator_h\n#define allocator_h\n\n#include <stdint.h>\n\n// platforms\n\n// hookzz\n#include \"hookzz.h\"\n#include \"memory.h\"\n\n// zzdeps\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n\ntypedef struct _codeslice {\n    zpointer data;\n    zsize size;\n    zbool is_used;\n    zbool isCodeCave;\n} ZzCodeSlice;\n\ntypedef struct _ZzMemoryPage {\n    zpointer base;\n    zpointer curr_pos;\n    zsize size;\n    zsize used_size;\n    zbool isCodeCave;\n} ZzMemoryPage;\n\ntypedef struct _allocator {\n    ZzMemoryPage **memory_pages;\n    zsize size;\n    zsize capacity;\n} ZzAllocator;\n\nZzCodeSlice *ZzNewNearCodeSlice(ZzAllocator *allocator, zaddr address, zsize redirect_range_size,\n                                zsize codeslice_size);\n\nZzCodeSlice *ZzNewCodeSlice(ZzAllocator *allocator, zsize codeslice_size);\n\nZzAllocator *ZzNewAllocator();\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/interceptor.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include <stdlib.h>\n#include <string.h>\n\n#include \"interceptor.h\"\n#include \"trampoline.h\"\n#include \"zzinfo.h\"\n\n#define ZZHOOKENTRIES_DEFAULT 100\nZzInterceptor *g_interceptor = NULL;\n\nZZSTATUS ZzInitializeInterceptor(void) {\n    ZzInterceptor *interceptor = g_interceptor;\n    ZzHookFunctionEntrySet *hook_function_entry_set;\n\n    if (NULL == interceptor) {\n        interceptor = (ZzInterceptor *)malloc(sizeof(ZzInterceptor));\n        memset(interceptor, 0, sizeof(ZzInterceptor));\n\n        hook_function_entry_set = &(interceptor->hook_function_entry_set);\n        hook_function_entry_set->capacity = ZZHOOKENTRIES_DEFAULT;\n        hook_function_entry_set->entries =\n            (ZzHookFunctionEntry **)malloc(sizeof(ZzHookFunctionEntry *) * hook_function_entry_set->capacity);\n        memset(hook_function_entry_set->entries, 0, sizeof(ZzHookFunctionEntry *) * hook_function_entry_set->capacity);\n\n        if (!hook_function_entry_set->entries) {\n            return ZZ_FAILED;\n        }\n        hook_function_entry_set->size = 0;\n        g_interceptor = interceptor;\n        interceptor->is_support_rx_page = ZzMemoryIsSupportAllocateRXPage();\n        if (interceptor->is_support_rx_page) {\n            interceptor->allocator = ZzNewAllocator();\n            interceptor->backend = ZzBuildInteceptorBackend(interceptor->allocator);\n        } else {\n            interceptor->allocator = NULL;\n            interceptor->backend = NULL;\n        }\n        return ZZ_DONE_INIT;\n    }\n    return ZZ_ALREADY_INIT;\n}\n\nZzHookFunctionEntry *ZzFindHookFunctionEntry(zpointer target_ptr) {\n    ZzInterceptor *interceptor = g_interceptor;\n    if (!interceptor)\n        return NULL;\n\n    ZzHookFunctionEntrySet *hook_function_entry_set = &(interceptor->hook_function_entry_set);\n\n    int i;\n    for (i = 0; i < hook_function_entry_set->size; ++i) {\n        if ((hook_function_entry_set->entries)[i] && target_ptr == (hook_function_entry_set->entries)[i]->target_ptr) {\n            return (hook_function_entry_set->entries)[i];\n        }\n    }\n    return NULL;\n}\n\nZZSTATUS ZzAddHookFunctionEntry(ZzHookFunctionEntry *entry) {\n    ZzInterceptor *interceptor = g_interceptor;\n    if (!interceptor)\n        return ZZ_FAILED;\n\n    ZzHookFunctionEntrySet *hook_function_entry_set = &(interceptor->hook_function_entry_set);\n\n    if (hook_function_entry_set->size >= hook_function_entry_set->capacity) {\n        ZzHookFunctionEntry **entries = (ZzHookFunctionEntry **)realloc(\n            hook_function_entry_set->entries, sizeof(ZzHookFunctionEntry *) * hook_function_entry_set->capacity * 2);\n        if (!entries)\n            return ZZ_FAILED;\n\n        hook_function_entry_set->capacity = hook_function_entry_set->capacity * 2;\n        hook_function_entry_set->entries = entries;\n    }\n    hook_function_entry_set->entries[hook_function_entry_set->size++] = entry;\n    return ZZ_SUCCESS;\n}\n\nvoid ZzInitializeHookFunctionEntry(ZzHookFunctionEntry *entry, int hook_type, zpointer target_ptr,\n                                   zpointer target_end_ptr, zpointer replace_call, PRECALL pre_call, HALFCALL half_call,\n                                   POSTCALL post_call, zbool try_near_jump) {\n    ZzInterceptor *interceptor = g_interceptor;\n    ZzHookFunctionEntrySet *hook_function_entry_set = &(interceptor->hook_function_entry_set);\n\n    memset(entry, 0, sizeof(ZzHookFunctionEntry));\n\n    entry->hook_type = hook_type;\n    entry->id = hook_function_entry_set->size;\n    entry->isEnabled = 0;\n    entry->try_near_jump = try_near_jump;\n    entry->interceptor = interceptor;\n    entry->target_ptr = target_ptr;\n    entry->target_end_ptr = target_end_ptr;\n    entry->replace_call = replace_call;\n    entry->pre_call = (zpointer)pre_call;\n    entry->half_call = (zpointer)half_call;\n    entry->post_call = (zpointer)post_call;\n    entry->on_enter_trampoline = NULL;\n    entry->on_invoke_trampoline = NULL;\n    entry->on_half_trampoline = NULL;\n    entry->on_leave_trampoline = NULL;\n    entry->origin_prologue.address = target_ptr;\n    entry->thread_local_key = ZzThreadNewThreadLocalKeyPtr();\n\n    /* key function */\n    ZzBuildTrampoline(interceptor->backend, entry);\n    ZzAddHookFunctionEntry(entry);\n}\n\nZZSTATUS ZzBuildHook(zpointer target_ptr, zpointer replace_call_ptr, zpointer *origin_ptr, PRECALL pre_call_ptr,\n                     POSTCALL post_call_ptr, zbool try_near_jump) {\n#if defined(__i386__) || defined(__x86_64__)\n    ZzInfoLog(\"%s\", \"x86 & x86_64 arch not support\");\n    return ZZ_FAILED;\n#endif\n\n    ZZSTATUS status = ZZ_DONE_HOOK;\n    ZzInterceptor *interceptor = g_interceptor;\n    ZzHookFunctionEntrySet *hook_function_entry_set = NULL;\n    ZzHookFunctionEntry *entry;\n\n    if (!interceptor) {\n        ZzInitializeInterceptor();\n        if (!g_interceptor)\n            return ZZ_FAILED;\n        if (!g_interceptor->is_support_rx_page) {\n            return ZZ_FAILED;\n        }\n    }\n\n    interceptor = g_interceptor;\n    hook_function_entry_set = &(interceptor->hook_function_entry_set);\n\n    do {\n        // check is already hooked\n        if (ZzFindHookFunctionEntry(target_ptr)) {\n            status = ZZ_ALREADY_HOOK;\n            break;\n        }\n\n        entry = (ZzHookFunctionEntry *)malloc(sizeof(ZzHookFunctionEntry));\n        memset(entry, 0, sizeof(ZzHookFunctionEntry));\n\n        ZzInitializeHookFunctionEntry(entry, HOOK_FUNCTION_TYPE, target_ptr, 0, replace_call_ptr, pre_call_ptr, NULL,\n                                      post_call_ptr, try_near_jump);\n\n        if (origin_ptr)\n            *origin_ptr = entry->on_invoke_trampoline;\n\n    } while (0);\n    return status;\n}\n\nZZSTATUS ZzBuildHookAddress(zpointer target_start_ptr, zpointer target_end_ptr, PRECALL pre_call_ptr,\n                            HALFCALL half_call_ptr, zbool try_near_jump) {\n#if defined(__i386__) || defined(__x86_64__)\n    ZzInfoLog(\"%s\", \"x86 & x86_64 arch not support\");\n    return ZZ_FAILED;\n#endif\n    ZZSTATUS status = ZZ_DONE_HOOK;\n    ZzInterceptor *interceptor = g_interceptor;\n    ZzHookFunctionEntrySet *hook_function_entry_set = NULL;\n    ZzHookFunctionEntry *entry = NULL;\n\n    if (!interceptor) {\n        ZzInitializeInterceptor();\n        if (!g_interceptor)\n            return ZZ_FAILED;\n        if (!g_interceptor->is_support_rx_page) {\n            return ZZ_FAILED;\n        }\n    }\n\n    interceptor = g_interceptor;\n    hook_function_entry_set = &(interceptor->hook_function_entry_set);\n\n    do {\n        // check is already hooked\n        if (ZzFindHookFunctionEntry(target_start_ptr)) {\n            status = ZZ_ALREADY_HOOK;\n            break;\n        }\n\n        entry = (ZzHookFunctionEntry *)malloc(sizeof(ZzHookFunctionEntry));\n        memset(entry, 0, sizeof(ZzHookFunctionEntry));\n\n        ZzInitializeHookFunctionEntry(entry, HOOK_ADDRESS_TYPE, target_start_ptr, target_end_ptr, NULL, pre_call_ptr,\n                                      half_call_ptr, NULL, try_near_jump);\n\n    } while (0);\n    return status;\n}\n\nZZSTATUS ZzEnableHook(zpointer target_ptr) {\n    ZZSTATUS status = ZZ_DONE_ENABLE;\n    ZzInterceptor *interceptor = g_interceptor;\n    ZzHookFunctionEntry *entry = ZzFindHookFunctionEntry(target_ptr);\n\n    if (!entry) {\n        status = ZZ_NO_BUILD_HOOK;\n        Xinfo(\" %p not build HookFunctionEntry!\", target_ptr);\n        return status;\n    }\n\n    if (entry->isEnabled) {\n        status = ZZ_ALREADY_ENABLED;\n        Xinfo(\"HookFunctionEntry %p already enable!\", target_ptr);\n        return status;\n    }\n\n    return ZzActivateTrampoline(interceptor->backend, entry);\n}\n\nZZSTATUS ZzHook(zpointer target_ptr, zpointer replace_ptr, zpointer *origin_ptr, PRECALL pre_call_ptr,\n                POSTCALL post_call_ptr, zbool try_near_jump) {\n    ZzBuildHook(target_ptr, replace_ptr, origin_ptr, pre_call_ptr, post_call_ptr, try_near_jump);\n    ZzEnableHook(target_ptr);\n    return ZZ_SUCCESS;\n}\n\nZZSTATUS ZzHookPrePost(zpointer target_ptr, PRECALL pre_call_ptr, POSTCALL post_call_ptr) {\n    ZzBuildHook(target_ptr, NULL, NULL, pre_call_ptr, post_call_ptr, FALSE);\n    ZzEnableHook(target_ptr);\n    return ZZ_SUCCESS;\n}\n\nZZSTATUS ZzHookReplace(zpointer target_ptr, zpointer replace_ptr, zpointer *origin_ptr) {\n    ZzBuildHook(target_ptr, replace_ptr, origin_ptr, NULL, NULL, FALSE);\n    ZzEnableHook(target_ptr);\n    return ZZ_SUCCESS;\n}\n\nZZSTATUS ZzHookAddress(zpointer target_start_ptr, zpointer target_end_ptr, PRECALL pre_call_ptr,\n                       HALFCALL half_call_ptr) {\n    ZzBuildHookAddress(target_start_ptr, target_end_ptr, pre_call_ptr, half_call_ptr, FALSE);\n    ZzEnableHook(target_start_ptr);\n    return ZZ_SUCCESS;\n}\n\n#ifdef TARGET_IS_IOS\n\nZZSTATUS ZzSolidifyHook(zpointer target_fileoff, zpointer replace_call_ptr, zpointer *origin_ptr, PRECALL pre_call_ptr,\n                        POSTCALL post_call_ptr) {\n    ZZSTATUS status = ZZ_DONE_HOOK;\n    ZzInterceptor *interceptor = g_interceptor;\n    ZzHookFunctionEntrySet *hook_function_entry_set = NULL;\n    ZzHookFunctionEntry *entry = NULL;\n\n    if (!interceptor) {\n        ZzInitializeInterceptor();\n        if (!g_interceptor)\n            return ZZ_FAILED;\n    }\n\n    interceptor = g_interceptor;\n\n    entry = (ZzHookFunctionEntry *)malloc(sizeof(ZzHookFunctionEntry));\n    entry->target_ptr = target_fileoff;\n    entry->replace_call = replace_call_ptr;\n    entry->pre_call = (zpointer)pre_call_ptr;\n    entry->post_call = (zpointer)post_call_ptr;\n\n    ZzActivateSolidifyTrampoline(entry, (zaddr)target_fileoff);\n\n    if (origin_ptr)\n        *origin_ptr = entry->on_invoke_trampoline;\n    return status;\n}\n#endif\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/interceptor.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef interceptor_h\n#define interceptor_h\n\n// platforms\n\n// hookzz\n#include \"allocator.h\"\n#include \"hookzz.h\"\n#include \"stack.h\"\n#include \"thread.h\"\n#include \"thunker.h\"\n#include \"writer.h\"\n\n// zzdeps\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef struct _FunctionBackup {\n    zpointer address;\n    zsize size;\n    zbyte data[32];\n} FunctionBackup;\n\nstruct _ZzInterceptor;\n\n/*\n * hook entry\n */\n\n#define HOOK_FUNCTION_TYPE 1\n#define HOOK_ADDRESS_TYPE 2\n\nstruct _ZzHookFunctionEntryBackend;\ntypedef struct _ZzHookFunctionEntry {\n    int hook_type;\n    unsigned long id;\n    zbool isEnabled;\n    zbool try_near_jump;\n\n    zpointer thread_local_key;\n    struct _ZzHookFunctionEntryBackend *backend;\n\n    zpointer target_ptr;\n    zpointer target_end_ptr;\n    zpointer target_half_ret_addr;\n\n    zpointer pre_call;\n    zpointer half_call;\n    zpointer post_call;\n    zpointer replace_call;\n\n    FunctionBackup origin_prologue;\n\n    zpointer on_enter_transfer_trampoline;\n    zpointer on_enter_trampoline;\n    zpointer on_half_trampoline;\n    zpointer on_invoke_trampoline;\n    zpointer on_leave_trampoline;\n\n    struct _ZzInterceptor *interceptor;\n} ZzHookFunctionEntry;\n\ntypedef struct {\n    ZzHookFunctionEntry **entries;\n    zsize size;\n    zsize capacity;\n} ZzHookFunctionEntrySet;\n\nstruct _ZzInterceptorBackend;\n\ntypedef struct _ZzInterceptor {\n    zbool is_support_rx_page;\n    ZzHookFunctionEntrySet hook_function_entry_set;\n    struct _ZzInterceptorBackend *backend;\n    ZzAllocator *allocator;\n} ZzInterceptor;\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/memory.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"memory.h\"\n\nZZSTATUS ZzRuntimeCodePatch(zaddr address, zpointer codedata, zuint codedata_size) {\n    zaddr address_fixed = address & ~(zaddr)1;\n    if (!ZzMemoryPatchCode(address_fixed, codedata, codedata_size))\n        return ZZ_FAILED;\n    return ZZ_SUCCESS;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/memory.h",
    "content": "//    Copyright 2017 jmpews\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#ifndef memory_h\n#define memory_h\n\n// platforms\n\n// hookzz\n#include \"hookzz.h\"\n\n// zzdeps\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\n// #include \"platforms/darwin/memory-darwin.h\"\n// #include \"zzdeps/darwin/memory-utils-darwin.h\"\n\nzsize ZzMemoryGetPageSzie();\n\nzpointer ZzMemoryAllocatePages(zsize n_pages);\nzpointer ZzMemoryAllocateNearPages(zaddr address, zsize redirect_range_size, zsize n_pages);\nzpointer ZzMemoryAllocate(zsize size);\nzbool ZzMemoryPatchCode(const zaddr address, const zpointer codedata, zuint codedata_size);\nzbool ZzMemoryProtectAsExecutable(const zaddr address, zsize size);\nzbool ZzMemoryProtectAsWritable(const zaddr address, zsize size);\nzbool ZzMemoryIsSupportAllocateRXPage();\nzpointer ZzMemorySearchCodeCave(zaddr address, zsize redirect_range_size, zsize size);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/instructions.c",
    "content": "#include \"instructions.h\"\n#include <string.h>\n\nzuint32 get_insn_sub(zuint32 insn, int start, int length) { return (insn >> start) & ((1 << length) - 1); }\n\nzbool insn_equal(zuint32 insn, char *opstr) {\n    zuint32 mask = 0, value = 0;\n    zsize length = strlen(opstr);\n    int i, j;\n    for (i = length - 1, j = 0; i >= 0 && j < length; i--, j++) {\n        if (opstr[i] == 'x') {\n            mask = mask | (0 << j);\n        } else if (opstr[i] == '0') {\n            mask = mask | (1 << j);\n        } else if (opstr[i] == '1') {\n            value = value | (1 << j);\n            mask = mask | (1 << j);\n        }\n    }\n    return (insn & mask) == value;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/instructions.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_arm_instructions_h\n#define platforms_arch_arm_instructions_h\n\n#include \"hookzz.h\"\n\ntypedef enum _INSN_TYPE { ARM_INSN, THUMB_INSN, THUMB2_INSN } InsnType;\n\ntypedef struct _Instruction {\n    InsnType type;\n    zaddr pc;\n    zaddr address;\n    zuint8 size;\n    union {\n        zuint32 trick_insn;\n        struct {\n            zuint16 trick_insn1;\n            zuint16 trick_insn2;\n        };\n    };\n\n    zuint32 insn;\n    zuint16 insn1;\n    zuint16 insn2;\n} ZzInstruction;\n\ntypedef struct _ZzRelocateInstruction {\n    const ZzInstruction *insn_ctx;\n    zaddr relocated_offset;\n    zsize relocated_length;\n} ZzRelocateInstruction;\n\nzuint32 get_insn_sub(zuint32 insn, int start, int length);\nzbool insn_equal(zuint32 insn, char *opstr);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-arm.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"reader-arm.h\"\n\nzpointer zz_arm_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address) {\n    insn_ctx->type = ARM_INSN;\n    insn_ctx->address = (zaddr)address;\n    insn_ctx->pc = (zaddr)address + 8;\n    insn_ctx->insn = *(zuint32 *)address;\n    insn_ctx->size = 4;\n    return (zpointer)insn_ctx->pc;\n}\n\n// ARM Manual\n// A5 ARM Instruction Set Encoding\n// A5.3 Load/store word and unsigned byte\nARMInsnType GetARMInsnType(zuint32 insn) {\n\n    if (insn_equal(insn, \"xxxx0000100xxxxxxxxxxxxxxxx0xxxx\") && (get_insn_sub(insn, 28, 4) != 0xF)) {\n        return ARM_INS_ADD_register_A1;\n    }\n\n    if (insn_equal(insn, \"xxxx0101x0011111xxxxxxxxxxxxxxxx\") && (get_insn_sub(insn, 28, 4) != 0xF)) {\n        return ARM_INS_LDR_literal_A1;\n    }\n\n    if (insn_equal(insn, \"xxxx001010001111xxxxxxxxxxxxxxxx\") && (get_insn_sub(insn, 28, 4) != 0xF)) {\n        return ARM_INS_ADR_A1;\n    }\n    if (insn_equal(insn, \"xxxx001001001111xxxxxxxxxxxxxxxx\") && (get_insn_sub(insn, 28, 4) != 0xF)) {\n        return ARM_INS_ADR_A2;\n    }\n    if (insn_equal(insn, \"xxxx1010xxxxxxxxxxxxxxxxxxxxxxxx\") && (get_insn_sub(insn, 28, 4) != 0xF)) {\n        return ARM_INS_B_A1;\n    }\n    if (insn_equal(insn, \"xxxx1011xxxxxxxxxxxxxxxxxxxxxxxx\") && (get_insn_sub(insn, 28, 4) != 0xF)) {\n        return ARM_INS_BLBLX_immediate_A1;\n    }\n    if (insn_equal(insn, \"1111101xxxxxxxxxxxxxxxxxxxxxxxxx\")) {\n        return ARM_INS_BLBLX_immediate_A2;\n    }\n\n    return ARM_UNDEF;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-arm.h",
    "content": "//    Copyright 2017 jmpews\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#ifndef platforms_arch_arm_reader_arm_h\n#define platforms_arch_arm_reader_arm_h\n\n// platforms\n#include \"instructions.h\"\n\n// hookzz\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef enum _ARMInsnType {\n    ARM_INS_ADD_register_A1,\n    ARM_INS_LDR_literal_A1,\n    ARM_INS_ADR_A1,\n    ARM_INS_ADR_A2,\n    ARM_INS_B_A1,\n    ARM_INS_BLBLX_immediate_A1,\n    ARM_INS_BLBLX_immediate_A2,\n    ARM_UNDEF\n} ARMInsnType;\n\nARMInsnType GetARMInsnType(zuint32 insn);\nzpointer zz_arm_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-thumb.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"reader-thumb.h\"\n\nzbool insn_is_thumb2(zuint32 insn) {\n    // PAGE: A6-221\n    // PAGE: A6-230\n\n    if (insn_equal(insn & 0x0000FFFF, \"11101xxxxxxxxxxx\") || insn_equal(insn & 0x0000FFFF, \"11111xxxxxxxxxxx\") ||\n        insn_equal(insn & 0x0000FFFF, \"11110xxxxxxxxxxx\")) {\n        return TRUE;\n    } else {\n        return FALSE;\n    }\n}\n\nzpointer zz_thumb_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address) {\n    // ZzInstruction *insn_ctx = (ZzInstruction *)malloc(sizeof(ZzInstruction));\n    insn_ctx->pc = (zaddr)address + 4;\n    insn_ctx->address = (zaddr)address;\n    insn_ctx->insn = *(zuint32 *)address;\n\n    // PAGE: A6-221\n    if (insn_is_thumb2(insn_ctx->insn)) {\n        insn_ctx->type = THUMB2_INSN;\n        insn_ctx->size = 4;\n        insn_ctx->insn1 = insn_ctx->insn & 0x0000FFFF;\n        insn_ctx->insn2 = (insn_ctx->insn & 0xFFFF0000) >> 16;\n    } else {\n        insn_ctx->type = THUMB_INSN;\n        insn_ctx->size = 2;\n        insn_ctx->insn1 = insn_ctx->insn & 0x0000FFFF;\n        insn_ctx->insn2 = 0;\n    }\n    return (zpointer)insn_ctx->pc;\n}\n\n// ARM Manual\n// A5 ARM Instruction Set Encoding\n// A5.3 Load/store word and unsigned byte\nTHUMBInsnType GetTHUMBInsnType(zuint16 insn1, zuint16 insn2) {\n\n    if (!insn_is_thumb2(insn1) && insn_equal(insn1, \"1011x0x1xxxxxxxx\")) {\n        return THUMB_INS_CBNZ_CBZ;\n    }\n\n    if (!insn_is_thumb2(insn1) && insn_equal(insn1, \"01000100xxxxxxxx\")) {\n        return THUMB_INS_ADD_register_T2;\n    }\n\n    if (!insn_is_thumb2(insn1) && insn_equal(insn1, \"01001xxxxxxxxxxx\")) {\n        return THUMB_INS_LDR_literal_T1;\n    }\n\n    if (insn_is_thumb2(insn1) && insn_equal(insn1, \"11111000x1011111\") && insn_equal(insn2, \"xxxxxxxxxxxxxxxx\")) {\n        return THUMB_INS_LDR_literal_T2;\n    }\n\n    if (!insn_is_thumb2(insn1) && insn_equal(insn1, \"10100xxxxxxxxxxx\")) {\n        return THUMB_INS_ADR_T1;\n    }\n\n    if (insn_is_thumb2(insn1) && insn_equal(insn1, \"11110x1010101111\") && insn_equal(insn2, \"0xxxxxxxxxxxxxxx\")) {\n        return THUMB_INS_ADR_T2;\n    }\n\n    if (insn_is_thumb2(insn1) && insn_equal(insn1, \"11110x1000001111\") && insn_equal(insn2, \"0xxxxxxxxxxxxxxx\")) {\n        return THUMB_INS_ADR_T3;\n    }\n\n    if (!insn_is_thumb2(insn1) && insn_equal(insn1, \"1101xxxxxxxxxxxx\")) {\n        return THUMB_INS_B_T1;\n    }\n\n    if (!insn_is_thumb2(insn1) && insn_equal(insn1, \"11100xxxxxxxxxxx\")) {\n        return THUMB_INS_B_T2;\n    }\n\n    if (insn_is_thumb2(insn1) && insn_equal(insn1, \"11110xxxxxxxxxxx\") && insn_equal(insn2, \"10x0xxxxxxxxxxxx\")) {\n        return THUMB_INS_B_T3;\n    }\n\n    if (insn_is_thumb2(insn1) && insn_equal(insn1, \"11110xxxxxxxxxxx\") && insn_equal(insn2, \"10x1xxxxxxxxxxxx\")) {\n        return THUMB_INS_B_T4;\n    }\n\n    if (insn_is_thumb2(insn1) && insn_equal(insn1, \"11110xxxxxxxxxxx\") && insn_equal(insn2, \"11x1xxxxxxxxxxxx\")) {\n        return THUMB_INS_BLBLX_immediate_T1;\n    }\n\n    if (insn_is_thumb2(insn1) && insn_equal(insn1, \"11110xxxxxxxxxxx\") && insn_equal(insn2, \"11x0xxxxxxxxxxxx\")) {\n        return THUMB_INS_BLBLX_immediate_T2;\n    }\n\n    return THUMB_UNDEF;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/reader-thumb.h",
    "content": "//    Copyright 2017 jmpews\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#ifndef platforms_arch_arm_reader_thumb_h\n#define platforms_arch_arm_reader_thumb_h\n\n// platforms\n#include \"instructions.h\"\n\n// hookzz\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef enum _THUMBInsnType {\n    THUMB_INS_CBNZ_CBZ,\n    THUMB_INS_ADD_register_T2,\n    THUMB_INS_LDR_literal_T1,\n    THUMB_INS_LDR_literal_T2,\n    THUMB_INS_ADR_T1,\n    THUMB_INS_ADR_T2,\n    THUMB_INS_ADR_T3,\n    THUMB_INS_B_T1,\n    THUMB_INS_B_T2,\n    THUMB_INS_B_T3,\n    THUMB_INS_B_T4,\n    THUMB_INS_BLBLX_immediate_T1,\n    THUMB_INS_BLBLX_immediate_T2,\n    THUMB_UNDEF\n} THUMBInsnType;\n\nTHUMBInsnType GetTHUMBInsnType(zuint16 insn1, zuint16 insn2);\nzpointer zz_thumb_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/regs-arm.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"regs-arm.h\"\n\nvoid zz_arm_register_describe(ZzARMReg reg, ZzArmRegInfo *ri) {\n    if (reg >= ZZ_ARM_REG_R0 && reg <= ZZ_ARM_REG_R12) {\n        ri->width = 32;\n        ri->meta = reg;\n    } else if (reg == ZZ_ARM_REG_SP) {\n        ri->width = 32;\n        ri->meta = reg;\n    } else if (reg == ZZ_ARM_REG_LR) {\n        ri->width = 32;\n        ri->meta = reg;\n    } else if (reg == ZZ_ARM_REG_PC) {\n        ri->width = 32;\n        ri->meta = reg;\n    } else {\n        Serror(\"zz_arm64_register_describe error.\");\n#if defined(DEBUG_MODE)\n        debug_break();\n#endif\n        ri->index = 0;\n    }\n    ri->index = reg - ZZ_ARM_REG_R0;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/regs-arm.h",
    "content": "//    Copyright 2017 jmpews\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#ifndef platforms_arch_arm_regs_h\n#define platforms_arch_arm_regs_h\n\n// platforms\n#include \"instructions.h\"\n\n// hookzz\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\n// REF:\n// ARM Architecture Reference Manual\n// A2.4 Registers\n\ntypedef enum _ZzReg {\n    ZZ_ARM_REG_R0 = 0,\n    ZZ_ARM_REG_R1,\n    ZZ_ARM_REG_R2,\n    ZZ_ARM_REG_R3,\n    ZZ_ARM_REG_R4,\n    ZZ_ARM_REG_R5,\n    ZZ_ARM_REG_R6,\n    ZZ_ARM_REG_R7,\n    ZZ_ARM_REG_R8,\n    ZZ_ARM_REG_R9,\n    ZZ_ARM_REG_R10,\n    ZZ_ARM_REG_R11,\n    ZZ_ARM_REG_R12,\n    ZZ_ARM_REG_R13,\n    ZZ_ARM_REG_R14,\n    ZZ_ARM_REG_R15,\n    ZZ_ARM_REG_SP = ZZ_ARM_REG_R13,\n    ZZ_ARM_REG_LR = ZZ_ARM_REG_R14,\n    ZZ_ARM_REG_PC = ZZ_ARM_REG_R15\n} ZzARMReg;\n\ntypedef struct _ZzArmRegInfo {\n    zuint index;\n    zuint meta;\n    zuint width;\n} ZzArmRegInfo;\n\nvoid zz_arm_register_describe(ZzARMReg reg, ZzArmRegInfo *ri);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-arm.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"relocator-arm.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\n#define MAX_RELOCATOR_INSTRUCIONS_SIZE 64\n\nvoid zz_arm_relocator_init(ZzArmRelocator *relocator, zpointer input_code, ZzArmWriter *output) {\n    relocator->inpos = 0;\n    relocator->outpos = 0;\n    relocator->input_start = input_code;\n    relocator->input_cur = input_code;\n    relocator->input_pc = (zaddr)input_code;\n    relocator->output = output;\n    relocator->relocate_literal_insns_size = 0;\n    relocator->try_relocated_length = 0;\n\n    relocator->input_insns = (ZzInstruction *)malloc(MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));\n    memset(relocator->input_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));\n    relocator->output_insns =\n        (ZzRelocateInstruction *)malloc(MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));\n    memset(relocator->output_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));\n    relocator->relocate_literal_insns =\n        (ZzLiteralInstruction **)malloc(MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));\n    memset(relocator->relocate_literal_insns, 0, MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));\n}\n\nvoid zz_arm_relocator_reset(ZzArmRelocator *self, zpointer input_code, ZzArmWriter *output) {\n    self->input_cur = input_code;\n    self->input_start = input_code;\n    self->input_pc = (zaddr)input_code;\n    self->inpos = 0;\n    self->outpos = 0;\n    self->output = output;\n    self->relocate_literal_insns_size = 0;\n    self->try_relocated_length = 0;\n\n    memset(self->input_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));\n    memset(self->output_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));\n    memset(self->relocate_literal_insns, 0, MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));\n}\n\nzsize zz_arm_relocator_read_one(ZzArmRelocator *self, ZzInstruction *instruction) {\n    ZzInstruction *insn_ctx = &self->input_insns[self->inpos];\n    ZzRelocateInstruction *re_insn_ctx = &self->output_insns[self->inpos];\n\n    re_insn_ctx->insn_ctx = insn_ctx;\n    zz_arm_reader_read_one_instruction(insn_ctx, self->input_cur);\n\n    // switch (1) {}\n\n    self->inpos++;\n\n    if (instruction != NULL)\n        *instruction = *insn_ctx;\n\n    self->input_cur += insn_ctx->size;\n    self->input_pc += insn_ctx->size;\n\n    return self->input_cur - self->input_start;\n}\nvoid zz_arm_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes) {\n    int tmp_size = 0;\n    zpointer target_addr;\n    ZzInstruction insn_ctx;\n    zbool early_end = FALSE;\n    target_addr = (zpointer)address;\n\n    do {\n        zz_arm_reader_read_one_instruction(&insn_ctx, target_addr);\n        switch (GetARMInsnType(insn_ctx.insn)) {\n        case ARM_INS_B_A1: {\n            zuint32 cond = get_insn_sub(insn_ctx.insn, 28, 4);\n            if (cond == 0xE)\n                early_end = TRUE;\n        }; break;\n        default:;\n        }\n        tmp_size += insn_ctx.size;\n        target_addr = target_addr + insn_ctx.size;\n    } while (tmp_size < min_bytes);\n\n    if (early_end) {\n        *max_bytes = tmp_size;\n    }\n    return;\n}\n\nzaddr zz_arm_relocator_get_insn_relocated_offset(ZzArmRelocator *self, zaddr address) {\n    const ZzInstruction *insn_ctx;\n    const ZzRelocateInstruction *re_insn_ctx;\n    int i;\n    for (i = 0; i < self->inpos; i++) {\n        re_insn_ctx = &self->output_insns[i];\n        insn_ctx = re_insn_ctx->insn_ctx;\n        if (insn_ctx->address == address && re_insn_ctx->relocated_offset) {\n            return re_insn_ctx->relocated_offset;\n        }\n    }\n    return 0;\n}\n\nvoid zz_arm_relocator_relocate_writer(ZzArmRelocator *relocator, zaddr code_address) {\n    ZzArmWriter *arm_writer;\n    arm_writer = relocator->output;\n    if (relocator->relocate_literal_insns_size) {\n        int i;\n        zaddr literal_address, relocated_offset, relocated_address, *literal_address_ptr;\n        for (i = 0; i < relocator->relocate_literal_insns_size; i++) {\n            literal_address_ptr = (zaddr *)relocator->relocate_literal_insns[i]->literal_address_ptr;\n            literal_address = *literal_address_ptr;\n            relocated_offset = zz_arm_relocator_get_insn_relocated_offset(relocator, literal_address);\n            if (relocated_offset) {\n                relocated_address = code_address + relocated_offset;\n                *literal_address_ptr = relocated_address;\n            }\n        }\n    }\n}\n\nvoid zz_arm_relocator_write_all(ZzArmRelocator *self) {\n    zuint count = 0;\n    zuint outpos = self->outpos;\n    ZzArmWriter arm_writer = *self->output;\n\n    while (zz_arm_relocator_write_one(self))\n        count++;\n}\n\n// PAGE: A8-312\nstatic zbool zz_arm_relocator_rewrite_ADD_register_A1(ZzArmRelocator *self, const ZzInstruction *insn_ctx,\n                                                      ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn = insn_ctx->insn;\n\n    zuint32 Rn_ndx, Rd_ndx, Rm_ndx;\n    Rn_ndx = get_insn_sub(insn, 16, 4);\n    Rd_ndx = get_insn_sub(insn, 12, 4);\n    Rm_ndx = get_insn_sub(insn, 0, 4);\n\n    if (Rn_ndx != ZZ_ARM_REG_PC) {\n        return FALSE;\n    }\n    // push R7\n    zz_arm_writer_put_push_reg(self->output, ZZ_ARM_REG_R7);\n    zz_arm_writer_put_ldr_b_reg_address(self->output, ZZ_ARM_REG_R7, insn_ctx->pc);\n    zz_arm_writer_put_instruction(self->output, (insn & 0xFFF0FFFF) | ZZ_ARM_REG_R7 << 16);\n    // pop R7\n    zz_arm_writer_put_pop_reg(self->output, ZZ_ARM_REG_R7);\n    return TRUE;\n}\n\n// PAGE: A8-410\nstatic zbool zz_arm_relocator_rewrite_LDR_literal_A1(ZzArmRelocator *self, const ZzInstruction *insn_ctx,\n                                                     ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn = insn_ctx->insn;\n    zuint32 imm12 = get_insn_sub(insn, 0, 12);\n    zuint32 imm32 = imm12;\n    zbool add = get_insn_sub(insn, 7 + 16, 1) == 1;\n    zaddr target_address;\n    if (add)\n        target_address = insn_ctx->pc + imm32;\n    else\n        target_address = insn_ctx->pc - imm32;\n    int Rt_ndx = get_insn_sub(insn, 12, 4);\n\n    zz_arm_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);\n    zz_arm_writer_put_ldr_reg_reg_imm(self->output, Rt_ndx, Rt_ndx, 0);\n\n    return TRUE;\n}\n\n// PAGE: A8-322\nstatic zbool zz_arm_relocator_rewrite_ADR_A1(ZzArmRelocator *self, const ZzInstruction *insn_ctx,\n                                             ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn = insn_ctx->insn;\n    zuint32 imm12 = get_insn_sub(insn, 0, 12);\n    zuint32 imm32 = imm12;\n    zaddr target_address;\n    target_address = insn_ctx->pc + imm32;\n    int Rt_ndx = get_insn_sub(insn, 12, 4);\n\n    zz_arm_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);\n\n    return TRUE;\n}\n\n// PAGE: A8-322\nstatic zbool zz_arm_relocator_rewrite_ADR_A2(ZzArmRelocator *self, const ZzInstruction *insn_ctx,\n                                             ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn = insn_ctx->insn;\n    zuint32 imm12 = get_insn_sub(insn, 0, 12);\n    zuint32 imm32 = imm12;\n    zaddr target_address;\n    target_address = insn_ctx->pc - imm32;\n    int Rt_ndx = get_insn_sub(insn, 12, 4);\n\n    zz_arm_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);\n\n    return TRUE;\n}\n\n// 0x000 : b.cond 0x0;\n// 0x004 : b 0x4\n// 0x008 : ldr pc, [pc, #0]\n// 0x00c : .long 0x0\n// 0x010 : remain code\n\n// PAGE: A8-334\nstatic zbool zz_arm_relocator_rewrite_B_A1(ZzArmRelocator *self, const ZzInstruction *insn_ctx,\n                                           ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn = insn_ctx->insn;\n    zuint32 imm24 = get_insn_sub(insn, 0, 24);\n    zuint32 imm32 = imm24 << 2;\n    zaddr target_address;\n    target_address = insn_ctx->pc + imm32;\n\n    zz_arm_writer_put_instruction(self->output, (insn & 0xFF000000) | 0);\n    zz_arm_writer_put_b_imm(self->output, 0x4);\n    zz_arm_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address);\n\n    return TRUE;\n}\n\n// 0x000 : bl.cond 0x0;\n\n// 0x004 : b 0x10\n\n// 0x008 : ldr lr, [pc, #0]\n// 0x00c : b 0x0\n// 0x010 : .long 0x0\n\n// 0x014 : ldr pc, [pc, #0]\n// 0x018 : .long 0x0\n\n// 0x01c : remain code\n\n// PAGE: A8-348\nstatic zbool zz_arm_relocator_rewrite_BLBLX_immediate_A1(ZzArmRelocator *self, const ZzInstruction *insn_ctx,\n                                                         ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn = insn_ctx->insn;\n    zuint32 imm24 = get_insn_sub(insn, 0, 24);\n    zuint32 imm32 = imm24 << 2;\n    zaddr target_address;\n    target_address = ALIGN_4(insn_ctx->pc) + imm32;\n\n    // CurrentInstrSet = thumb\n    // targetInstrSet = arm\n\n    // convert 'bl' to 'b', but save 'cond'\n    zz_arm_writer_put_instruction(self->output, (insn & 0xF0000000) | 0b1010 << 24 | 0);\n\n    ZzArmWriter ouput_bak = *self->output;\n\n    zz_arm_writer_put_b_imm(self->output, 0);\n    ZzLiteralInstruction **literal_insn_ptr = &(self->relocate_literal_insns[self->relocate_literal_insns_size++]);\n    zz_arm_writer_put_ldr_b_reg_relocate_address(self->output, ZZ_ARM_REG_LR, insn_ctx->pc - 4, literal_insn_ptr);\n    zz_arm_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address);\n\n    // overwrite `zz_arm_writer_put_b_imm`\n    zz_arm_writer_put_b_imm(&ouput_bak, self->output->pc - ouput_bak.pc - 8);\n    return TRUE;\n}\n\n// PAGE: A8-348\nstatic zbool zz_arm_relocator_rewrite_BLBLX_immediate_A2(ZzArmRelocator *self, const ZzInstruction *insn_ctx,\n                                                         ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn = insn_ctx->insn;\n    zuint32 H = get_insn_sub(insn, 24, 1);\n    zuint32 imm24 = get_insn_sub(insn, 0, 24);\n    zuint32 imm32 = (imm24 << 2) | (H << 1);\n    zaddr target_address;\n    target_address = insn_ctx->pc + imm32;\n\n    ZzLiteralInstruction **literal_insn_ptr = &(self->relocate_literal_insns[self->relocate_literal_insns_size++]);\n    zz_arm_writer_put_ldr_b_reg_relocate_address(self->output, ZZ_ARM_REG_LR, insn_ctx->pc - 4, literal_insn_ptr);\n    zz_arm_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address);\n\n    return TRUE;\n}\n\nzbool zz_arm_relocator_write_one(ZzArmRelocator *self) {\n    const ZzInstruction *insn_ctx;\n    ZzRelocateInstruction *re_insn_ctx;\n    zbool rewritten = FALSE;\n\n    if (self->inpos != self->outpos) {\n        insn_ctx = &self->input_insns[self->outpos];\n        re_insn_ctx = &self->output_insns[self->outpos];\n\n        self->outpos++;\n    } else\n        return FALSE;\n\n    re_insn_ctx->relocated_offset = (zaddr)self->output->pc - (zaddr)self->output->base;\n\n    switch (GetARMInsnType(insn_ctx->insn)) {\n    case ARM_INS_ADD_register_A1:\n        rewritten = zz_arm_relocator_rewrite_ADD_register_A1(self, insn_ctx, re_insn_ctx);\n        break;\n    case ARM_INS_LDR_literal_A1:\n        rewritten = zz_arm_relocator_rewrite_LDR_literal_A1(self, insn_ctx, re_insn_ctx);\n        break;\n    case ARM_INS_ADR_A1:\n        rewritten = zz_arm_relocator_rewrite_ADR_A1(self, insn_ctx, re_insn_ctx);\n        break;\n    case ARM_INS_ADR_A2:\n        rewritten = zz_arm_relocator_rewrite_ADR_A2(self, insn_ctx, re_insn_ctx);\n        break;\n    case ARM_INS_B_A1:\n        rewritten = zz_arm_relocator_rewrite_B_A1(self, insn_ctx, re_insn_ctx);\n        break;\n    case ARM_INS_BLBLX_immediate_A1:\n        rewritten = zz_arm_relocator_rewrite_BLBLX_immediate_A1(self, insn_ctx, re_insn_ctx);\n        break;\n    case ARM_INS_BLBLX_immediate_A2:\n        rewritten = zz_arm_relocator_rewrite_BLBLX_immediate_A2(self, insn_ctx, re_insn_ctx);\n        break;\n    case ARM_UNDEF:\n        rewritten = FALSE;\n        break;\n    }\n    if (!rewritten)\n        zz_arm_writer_put_bytes(self->output, (zbyte *)&insn_ctx->insn, insn_ctx->size);\n\n    re_insn_ctx->relocated_length =\n        (zaddr)self->output->pc - (zaddr)self->output->base - (zaddr)re_insn_ctx->relocated_offset;\n    return TRUE;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-arm.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_arm_relocator_arm_h\n#define platforms_arch_arm_relocator_arm_h\n\n// platforms\n#include \"instructions.h\"\n#include \"reader-arm.h\"\n#include \"regs-arm.h\"\n#include \"writer-arm.h\"\n\n// hookzz\n#include \"writer.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef struct _ZzArmRelocator {\n    zbool try_relocated_again;\n    zsize try_relocated_length;\n    zpointer input_start;\n    zpointer input_cur;\n    zaddr input_pc;\n    zuint inpos;\n    zuint outpos;\n    ZzInstruction *input_insns;\n    ZzRelocateInstruction *output_insns;\n    ZzLiteralInstruction **relocate_literal_insns;\n    zsize relocate_literal_insns_size;\n    ZzArmWriter *output;\n} ZzArmRelocator;\n\nvoid zz_arm_relocator_init(ZzArmRelocator *relocator, zpointer input_code, ZzArmWriter *output);\nvoid zz_arm_relocator_reset(ZzArmRelocator *self, zpointer input_code, ZzArmWriter *output);\nvoid zz_arm_relocator_write_all(ZzArmRelocator *self);\nzsize zz_arm_relocator_read_one(ZzArmRelocator *self, ZzInstruction *instruction);\nvoid zz_arm_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes);\nzbool zz_arm_relocator_write_one(ZzArmRelocator *self);\nvoid zz_arm_relocator_relocate_writer(ZzArmRelocator *relocator, zaddr code_address);\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-thumb.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include <stdlib.h>\n#include <string.h>\n\n#include \"relocator-thumb.h\"\n\n#define MAX_RELOCATOR_INSTRUCIONS_SIZE 64\n\nvoid zz_thumb_relocator_init(ZzThumbRelocator *relocator, zpointer input_code, ZzThumbWriter *output) {\n\n    memset(relocator, 0, sizeof(ZzThumbRelocator));\n\n    relocator->inpos = 0;\n    relocator->outpos = 0;\n    relocator->input_start = input_code;\n    relocator->input_cur = input_code;\n    relocator->input_pc = (zaddr)input_code;\n    relocator->output = output;\n    relocator->relocate_literal_insns_size = 0;\n    relocator->try_relocated_length = 0;\n\n    relocator->input_insns = (ZzInstruction *)malloc(MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));\n    memset(relocator->input_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));\n    relocator->output_insns =\n        (ZzRelocateInstruction *)malloc(MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));\n    memset(relocator->output_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));\n    relocator->relocate_literal_insns =\n        (ZzLiteralInstruction **)malloc(MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));\n    memset(relocator->relocate_literal_insns, 0, MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));\n}\n\nvoid zz_thumb_relocator_reset(ZzThumbRelocator *self, zpointer input_code, ZzThumbWriter *output) {\n    self->input_cur = input_code;\n    self->input_start = input_code;\n    self->input_pc = (zaddr)input_code;\n    self->inpos = 0;\n    self->outpos = 0;\n    self->output = output;\n    self->relocate_literal_insns_size = 0;\n    self->try_relocated_length = 0;\n\n    memset(self->input_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));\n    memset(self->output_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));\n    memset(self->relocate_literal_insns, 0, MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));\n}\n\nzsize zz_thumb_relocator_read_one(ZzThumbRelocator *self, ZzInstruction *instruction) {\n    ZzInstruction *insn_ctx = &self->input_insns[self->inpos];\n    ZzRelocateInstruction *re_insn_ctx = &self->output_insns[self->inpos];\n\n    re_insn_ctx->insn_ctx = insn_ctx;\n    zz_thumb_reader_read_one_instruction(insn_ctx, self->input_cur);\n\n    // switch (1) {}\n\n    self->inpos++;\n\n    if (instruction != NULL)\n        *instruction = *insn_ctx;\n\n    self->input_cur += insn_ctx->size;\n    self->input_pc += insn_ctx->size;\n\n    return self->input_cur - self->input_start;\n}\n\nvoid zz_thumb_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes) {\n    int tmp_size = 0;\n    zbool is_thumb;\n    zpointer target_addr;\n    ZzInstruction insn_ctx;\n    zbool early_end = FALSE;\n    is_thumb = INSTRUCTION_IS_THUMB((zaddr)address);\n    target_addr = (zpointer)address;\n\n    do {\n        zz_thumb_reader_read_one_instruction(&insn_ctx, target_addr);\n        switch (GetTHUMBInsnType(insn_ctx.insn1, insn_ctx.insn2)) {\n        case THUMB_INS_B_T2:\n            early_end = TRUE;\n            break;\n        case THUMB_INS_B_T4:\n            early_end = TRUE;\n            break;\n        default:;\n        }\n        tmp_size += insn_ctx.size;\n        target_addr = target_addr + insn_ctx.size;\n    } while (tmp_size < min_bytes);\n\n    if (early_end) {\n        *max_bytes = tmp_size;\n    }\n    return;\n}\n\nzaddr zz_thumb_relocator_get_insn_relocated_offset(ZzThumbRelocator *self, zaddr address) {\n    const ZzInstruction *insn_ctx;\n    const ZzRelocateInstruction *re_insn_ctx;\n    int i;\n\n    for (i = 0; i < self->inpos; i++) {\n        re_insn_ctx = &self->output_insns[i];\n        insn_ctx = re_insn_ctx->insn_ctx;\n        if (insn_ctx->address == address && re_insn_ctx->relocated_offset) {\n            return re_insn_ctx->relocated_offset;\n        }\n    }\n    return 0;\n}\n\nvoid zz_thumb_relocator_relocate_writer(ZzThumbRelocator *relocator, zaddr code_address) {\n    ZzThumbWriter *thumb_writer;\n    thumb_writer = relocator->output;\n    if (relocator->relocate_literal_insns_size) {\n        int i;\n        zaddr literal_address, relocated_offset, relocated_address, *literal_address_ptr;\n        for (i = 0; i < relocator->relocate_literal_insns_size; i++) {\n            literal_address_ptr = (zaddr *)relocator->relocate_literal_insns[i]->literal_address_ptr;\n            literal_address = *literal_address_ptr;\n            relocated_offset = zz_thumb_relocator_get_insn_relocated_offset(relocator, literal_address & ~(zaddr)1);\n            if (relocated_offset) {\n                relocated_address = code_address + relocated_offset + 1;\n                *literal_address_ptr = relocated_address;\n            }\n        }\n    }\n}\n\nvoid zz_thumb_relocator_write_all(ZzThumbRelocator *self) {\n    zuint count = 0;\n    zuint outpos = self->outpos;\n    ZzThumbWriter thumb_writer = *self->output;\n    while (zz_thumb_relocator_write_one(self))\n        count++;\n}\n\n// A8-357\n// 0: cbz #0\n// 2: b #6\n// 4: ldr pc, #0\n// 8: .long ?\n// c: next insn\nstatic zbool zz_thumb_relocator_rewrite_CBNZ_CBZ(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,\n                                                 ZzRelocateInstruction *re_insn_ctx) {\n\n    zuint32 insn1 = insn_ctx->insn1;\n    zuint16 op, i, imm5, Rn_ndx;\n    zuint32 imm32, nonzero;\n\n    op = get_insn_sub(insn1, 11, 1);\n    i = get_insn_sub(insn1, 9, 1);\n    imm5 = get_insn_sub(insn1, 3, 5);\n    Rn_ndx = get_insn_sub(insn1, 0, 3);\n\n    imm32 = imm5 << 1 | i << (5 + 1);\n    nonzero = (op == 1);\n\n    zaddr target_address = insn_ctx->pc + imm32;\n\n    /* for align , simple solution, maybe the correct solution is get `ldr_reg_address` length and adjust the immediate\n     * of `b_imm`. */\n    if ((zaddr)self->output->pc % 4) {\n        zz_thumb_writer_put_nop(self->output);\n    }\n    zz_thumb_writer_put_instruction(self->output, (insn1 & 0b1111110100000111) | 0);\n\n    zz_thumb_writer_put_b_imm(self->output, 0x6);\n    ZzLiteralInstruction **literal_insn_ptr = &(self->relocate_literal_insns[self->relocate_literal_insns_size++]);\n    zz_thumb_writer_put_ldr_reg_relocate_address(self->output, ZZ_ARM_REG_PC, target_address + 1, literal_insn_ptr);\n\n    // zz_thumb_writer_put_b_imm(self->output, 0x10);\n    // zz_thumb_writer_put_push_reg(self->output, ZZ_ARM_REG_R0);\n    // zz_thumb_writer_put_push_reg(self->output, ZZ_ARM_REG_R0);\n    // zz_thumb_writer_put_ldr_b_reg_address(self->output, ZZ_ARM_REG_R0, target_address + 1);\n    // zz_thumb_writer_put_str_reg_reg_offset(self->output, ZZ_ARM_REG_R0, ZZ_ARM_REG_SP, 4);\n    // zz_thumb_writer_put_pop_reg(self->output, ZZ_ARM_REG_R0);\n    // zz_thumb_writer_put_pop_reg(self->output, ZZ_ARM_REG_PC);\n    return TRUE;\n}\n\n// PAGE: A8-310\nstatic zbool zz_thumb_relocator_rewrite_ADD_register_T2(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,\n                                                        ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn1 = insn_ctx->insn1;\n\n    zuint16 Rm_ndx, Rdn_ndx, DN, Rd_ndx;\n    Rm_ndx = get_insn_sub(insn1, 3, 4);\n    Rdn_ndx = get_insn_sub(insn1, 0, 3);\n    DN = get_insn_sub(insn1, 7, 1);\n    Rd_ndx = (DN << 3) | Rdn_ndx;\n\n    if (Rm_ndx != ZZ_ARM_REG_PC) {\n        return FALSE;\n    }\n\n    zz_thumb_writer_put_push_reg(self->output, ZZ_ARM_REG_R7);\n    zz_thumb_writer_put_ldr_b_reg_address(self->output, ZZ_ARM_REG_R7, insn_ctx->pc);\n    zz_thumb_writer_put_instruction(self->output, (insn1 & 0b1111111110000111) | ZZ_ARM_REG_R7 << 3);\n    zz_thumb_writer_put_pop_reg(self->output, ZZ_ARM_REG_R7);\n\n    return TRUE;\n}\n\n// PAGE: A8-410\nzbool zz_thumb_relocator_rewrite_LDR_literal_T1(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,\n                                                ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn1 = insn_ctx->insn1;\n    zuint32 imm8 = get_insn_sub(insn1, 0, 8);\n    zuint32 imm32 = imm8 << 2;\n    zaddr target_address = ALIGN_4(insn_ctx->pc) + imm32;\n    int Rt_ndx = get_insn_sub(insn1, 8, 3);\n\n    zz_thumb_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);\n    zz_thumb_writer_put_ldr_reg_reg_offset(self->output, Rt_ndx, Rt_ndx, 0);\n\n    return TRUE;\n}\n\n// PAGE: A8-410\nzbool zz_thumb_relocator_rewrite_LDR_literal_T2(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,\n                                                ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn1 = insn_ctx->insn1;\n    zuint32 insn2 = insn_ctx->insn2;\n\n    zuint32 imm12 = get_insn_sub(insn2, 0, 12);\n    zuint32 imm32 = imm12;\n\n    zbool add = get_insn_sub(insn_ctx->insn1, 7, 1) == 1;\n    zaddr target_address;\n    if (add)\n        target_address = ALIGN_4(insn_ctx->pc) + imm32;\n    else\n        target_address = ALIGN_4(insn_ctx->pc) - imm32;\n    int Rt_ndx = get_insn_sub(insn_ctx->insn2, 12, 4);\n\n    zz_thumb_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);\n    zz_thumb_writer_put_ldr_reg_reg_offset(self->output, Rt_ndx, Rt_ndx, 0);\n\n    return TRUE;\n}\n\n// PAGE: A8-322\nzbool zz_thumb_relocator_rewrite_ADR_T1(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,\n                                        ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn1 = insn_ctx->insn1;\n\n    zuint32 imm8 = get_insn_sub(insn1, 0, 8);\n    zuint32 imm32 = imm8 << 2;\n    zaddr target_address = insn_ctx->pc + imm32;\n    int Rt_ndx = get_insn_sub(insn1, 8, 3);\n\n    zz_thumb_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);\n    return TRUE;\n}\n\n// PAGE: A8-322\nzbool zz_thumb_relocator_rewrite_ADR_T2(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,\n                                        ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn1 = insn_ctx->insn1;\n    zuint32 insn2 = insn_ctx->insn2;\n\n    zuint32 imm32 =\n        get_insn_sub(insn2, 0, 8) | (get_insn_sub(insn2, 12, 3) << 8) | ((get_insn_sub(insn1, 10, 1) << (3 + 8)));\n\n    zaddr target_address;\n    target_address = insn_ctx->pc - imm32;\n    int Rt_ndx = get_insn_sub(insn_ctx->insn2, 8, 4);\n    zz_thumb_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);\n    return TRUE;\n}\n\n// PAGE: A8-322\nzbool zz_thumb_relocator_rewrite_ADR_T3(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,\n                                        ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn1 = insn_ctx->insn1;\n    zuint32 insn2 = insn_ctx->insn2;\n\n    zuint32 imm32 =\n        get_insn_sub(insn2, 0, 8) | (get_insn_sub(insn2, 12, 3) << 8) | ((get_insn_sub(insn1, 10, 1) << (3 + 8)));\n\n    zaddr target_address;\n    target_address = insn_ctx->pc + imm32;\n    int Rt_ndx = get_insn_sub(insn_ctx->insn2, 8, 4);\n\n    zz_thumb_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);\n    return TRUE;\n}\n\n// 0x000 : b.cond 0x0;\n// 0x002 : b 0x6\n// 0x004 : ldr pc, [pc, #0]\n// 0x008 : .long 0x0\n// 0x00c : remain code\n\n// PAGE: A8-334\nzbool zz_thumb_relocator_rewrite_B_T1(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,\n                                      ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn1 = insn_ctx->insn1;\n    // zuint32 insn2 = insn_ctx->insn2;\n\n    zuint32 imm8 = get_insn_sub(insn1, 0, 8);\n    zuint32 imm32 = imm8 << 1;\n    zaddr target_address = insn_ctx->pc + imm32;\n\n    /* for align , simple solution, maybe the correct solution is get `ldr_reg_address` length and adjust the immediate\n     * of `b_imm`. */\n    if ((zaddr)self->output->pc % 4) {\n        zz_thumb_writer_put_nop(self->output);\n    }\n    zz_thumb_writer_put_instruction(self->output, (insn1 & 0xFF00) | 0);\n    zz_thumb_writer_put_b_imm(self->output, 0x6);\n    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address + 1);\n    return TRUE;\n}\n\n// PAGE: A8-334\nzbool zz_thumb_relocator_rewrite_B_T2(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,\n                                      ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn1 = insn_ctx->insn1;\n\n    zuint32 imm11 = get_insn_sub(insn1, 0, 11);\n    zuint32 imm32 = imm11 << 1;\n    zaddr target_address = insn_ctx->pc + imm32;\n\n    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address + 1);\n    return TRUE;\n}\n\n// 0x002 : b.cond.W 0x2;\n// 0x006 : b 0x6\n// 0x008 : ldr pc, [pc, #0]\n// 0x00c : .long 0x0\n// 0x010 : remain code\n\n// PAGE: A8-334\nzbool zz_thumb_relocator_rewrite_B_T3(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,\n                                      ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn1 = insn_ctx->insn1;\n    zuint32 insn2 = insn_ctx->insn2;\n\n    int S = get_insn_sub(insn_ctx->insn1, 10, 1);\n    int J2 = get_insn_sub(insn_ctx->insn2, 11, 1);\n    int J1 = get_insn_sub(insn_ctx->insn2, 13, 1);\n    int imm6 = get_insn_sub(insn_ctx->insn1, 0, 6);\n    int imm11 = get_insn_sub(insn_ctx->insn2, 0, 11);\n    zuint32 imm32 =\n        imm11 << 1 | imm6 << (1 + 11) | J1 << (1 + 11 + 6) | J2 << (1 + 11 + 6 + 1) | S << (1 + 11 + 6 + 1 + 1);\n    zaddr target_address;\n    target_address = insn_ctx->pc + imm32;\n\n    /* for align , simple solution, maybe the correct solution is get `ldr_reg_address` length and adjust the immediate\n     * of `b_imm`. */\n    if ((zaddr)self->output->pc % 4 == 0) {\n        zz_thumb_writer_put_nop(self->output);\n    }\n    zz_thumb_writer_put_instruction(self->output, insn_ctx->insn1 & 0b1111101111000000);\n    zz_thumb_writer_put_instruction(self->output, (insn_ctx->insn2 & 0b1101000000000000) | 0b1);\n    zz_thumb_writer_put_b_imm(self->output, 0x6);\n    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address + 1);\n    return TRUE;\n}\n\n// PAGE: A8-334\nzbool zz_thumb_relocator_rewrite_B_T4(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,\n                                      ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn1 = insn_ctx->insn1;\n    zuint32 insn2 = insn_ctx->insn2;\n\n    zuint32 S = get_insn_sub(insn_ctx->insn1, 10 + 16, 1);\n    zuint32 J2 = get_insn_sub(insn_ctx->insn2, 11, 1);\n    zuint32 J1 = get_insn_sub(insn_ctx->insn2, 13, 1);\n    zuint32 imm10 = get_insn_sub(insn_ctx->insn1, 0, 10);\n    zuint32 imm11 = get_insn_sub(insn_ctx->insn2, 0, 11);\n    zuint32 I1 = (~(J1 ^ S)) & 0x1;\n    zuint32 I2 = (~(J2 ^ S)) & 0x1;\n    zuint32 imm32 =\n        imm11 << 1 | imm10 << (1 + 11) | I1 << (1 + 11 + 6) | I2 << (1 + 11 + 6 + 1) | S << (1 + 11 + 6 + 1 + 1);\n    zaddr target_address;\n    target_address = insn_ctx->pc + imm32;\n\n    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address + 1);\n    return TRUE;\n}\n\n// PAGE: A8-348\nzbool zz_thumb_relocator_rewrite_BLBLX_immediate_T1(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,\n                                                    ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn1 = insn_ctx->insn1;\n    zuint32 insn2 = insn_ctx->insn2;\n\n    zuint32 S = get_insn_sub(insn_ctx->insn1, 10, 1);\n    zuint32 J2 = get_insn_sub(insn_ctx->insn2, 11, 1);\n    zuint32 J1 = get_insn_sub(insn_ctx->insn2, 13, 1);\n    zuint32 imm10 = get_insn_sub(insn_ctx->insn1, 0, 10);\n    zuint32 imm11 = get_insn_sub(insn_ctx->insn2, 0, 11);\n    zuint32 I1 = (~(J1 ^ S)) & 0x1;\n    zuint32 I2 = (~(J2 ^ S)) & 0x1;\n    zuint32 imm32 =\n        imm11 << 1 | imm10 << (1 + 11) | I1 << (1 + 11 + 6) | I2 << (1 + 11 + 6 + 1) | S << (1 + 11 + 6 + 1 + 1);\n    zaddr target_address;\n\n    // CurrentInstrSet = thumb\n    // targetInstrSet = arm\n    target_address = insn_ctx->pc + imm32;\n\n    ZzLiteralInstruction **literal_insn_ptr = &(self->relocate_literal_insns[self->relocate_literal_insns_size++]);\n    zz_thumb_writer_put_ldr_b_reg_relocate_address(self->output, ZZ_ARM_REG_LR, insn_ctx->pc + 1, literal_insn_ptr);\n    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address + 1);\n    return TRUE;\n}\n\n// PAGE: A8-348\nzbool zz_thumb_relocator_rewrite_BLBLX_T2(ZzThumbRelocator *self, const ZzInstruction *insn_ctx,\n                                          ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn1 = insn_ctx->insn1;\n    zuint32 insn2 = insn_ctx->insn2;\n\n    zuint32 S = get_insn_sub(insn_ctx->insn1, 10, 1);\n    zuint32 J2 = get_insn_sub(insn_ctx->insn2, 11, 1);\n    zuint32 J1 = get_insn_sub(insn_ctx->insn2, 13, 1);\n    zuint32 imm10_1 = get_insn_sub(insn_ctx->insn1, 0, 10);\n    zuint32 imm10_2 = get_insn_sub(insn_ctx->insn2, 1, 10);\n    zuint32 I1 = (~(J1 ^ S)) & 0x1;\n    zuint32 I2 = (~(J2 ^ S)) & 0x1;\n    ;\n    zuint32 H = get_insn_sub(insn_ctx->insn2, 0, 1);\n    zuint32 imm32 =\n        imm10_2 << 2 | imm10_1 << (2 + 10) | I1 << (2 + 10 + 6) | I2 << (2 + 10 + 6 + 1) | S << (2 + 10 + 6 + 1 + 1);\n    zaddr target_address;\n\n    // CurrentInstrSet = thumb\n    // targetInstrSet = arm\n    target_address = ALIGN_4(insn_ctx->pc) + imm32;\n\n    ZzLiteralInstruction **literal_insn_ptr = &(self->relocate_literal_insns[self->relocate_literal_insns_size++]);\n    zz_thumb_writer_put_ldr_b_reg_relocate_address(self->output, ZZ_ARM_REG_LR, insn_ctx->pc + 1, literal_insn_ptr);\n    zz_thumb_writer_put_ldr_reg_address(self->output, ZZ_ARM_REG_PC, target_address);\n    return TRUE;\n}\n\nzbool zz_thumb_relocator_write_one(ZzThumbRelocator *self) {\n    const ZzInstruction *insn_ctx;\n    ZzRelocateInstruction *re_insn_ctx;\n    zbool rewritten = FALSE;\n\n    if (self->inpos != self->outpos) {\n        insn_ctx = &self->input_insns[self->outpos];\n        re_insn_ctx = &self->output_insns[self->outpos];\n        self->outpos++;\n    } else\n        return FALSE;\n\n    re_insn_ctx->relocated_offset = (zaddr)self->output->pc - (zaddr)self->output->base;\n\n    switch (GetTHUMBInsnType(insn_ctx->insn1, insn_ctx->insn2)) {\n    case THUMB_INS_CBNZ_CBZ:\n        rewritten = zz_thumb_relocator_rewrite_CBNZ_CBZ(self, insn_ctx, re_insn_ctx);\n        break;\n    case THUMB_INS_ADD_register_T2:\n        rewritten = zz_thumb_relocator_rewrite_ADD_register_T2(self, insn_ctx, re_insn_ctx);\n        break;\n    case THUMB_INS_LDR_literal_T1:\n        rewritten = zz_thumb_relocator_rewrite_LDR_literal_T1(self, insn_ctx, re_insn_ctx);\n        break;\n    case THUMB_INS_LDR_literal_T2:\n        rewritten = zz_thumb_relocator_rewrite_LDR_literal_T2(self, insn_ctx, re_insn_ctx);\n        break;\n    case THUMB_INS_ADR_T1:\n        rewritten = zz_thumb_relocator_rewrite_ADR_T1(self, insn_ctx, re_insn_ctx);\n        break;\n    case THUMB_INS_ADR_T2:\n        rewritten = zz_thumb_relocator_rewrite_ADR_T2(self, insn_ctx, re_insn_ctx);\n        break;\n    case THUMB_INS_ADR_T3:\n        rewritten = zz_thumb_relocator_rewrite_ADR_T3(self, insn_ctx, re_insn_ctx);\n        break;\n    case THUMB_INS_B_T1:\n        rewritten = zz_thumb_relocator_rewrite_B_T1(self, insn_ctx, re_insn_ctx);\n        break;\n    case THUMB_INS_B_T2:\n        rewritten = zz_thumb_relocator_rewrite_B_T2(self, insn_ctx, re_insn_ctx);\n        break;\n    case THUMB_INS_B_T3:\n        rewritten = zz_thumb_relocator_rewrite_B_T3(self, insn_ctx, re_insn_ctx);\n        break;\n    case THUMB_INS_B_T4:\n        rewritten = zz_thumb_relocator_rewrite_B_T4(self, insn_ctx, re_insn_ctx);\n        break;\n    case THUMB_INS_BLBLX_immediate_T1:\n        rewritten = zz_thumb_relocator_rewrite_BLBLX_immediate_T1(self, insn_ctx, re_insn_ctx);\n        break;\n    case THUMB_INS_BLBLX_immediate_T2:\n        rewritten = zz_thumb_relocator_rewrite_BLBLX_T2(self, insn_ctx, re_insn_ctx);\n        break;\n    case THUMB_UNDEF:\n        rewritten = FALSE;\n        break;\n    }\n    if (!rewritten)\n        zz_thumb_writer_put_bytes(self->output, (zbyte *)&insn_ctx->insn, insn_ctx->size);\n\n    re_insn_ctx->relocated_length =\n        (zaddr)self->output->pc - (zaddr)self->output->base - (zaddr)re_insn_ctx->relocated_offset;\n\n    return TRUE;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/relocator-thumb.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_arm_relocator_thumb_h\n#define platforms_arch_arm_relocator_thumb_h\n\n// platforms\n#include \"instructions.h\"\n#include \"reader-thumb.h\"\n#include \"regs-arm.h\"\n#include \"writer-thumb.h\"\n\n// hookzz\n#include \"writer.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef struct _ZzThumbRelocator {\n    zbool try_relocated_again;\n    zsize try_relocated_length;\n    zpointer input_start;\n    zpointer input_cur;\n    zaddr input_pc;\n    ZzInstruction *input_insns;\n    ZzRelocateInstruction *output_insns;\n    ZzLiteralInstruction **relocate_literal_insns;\n    zsize relocate_literal_insns_size;\n    ZzThumbWriter *output;\n    zuint inpos;\n    zuint outpos;\n} ZzThumbRelocator;\n\nvoid zz_thumb_relocator_init(ZzThumbRelocator *relocator, zpointer input_code, ZzThumbWriter *writer);\nvoid zz_thumb_relocator_reset(ZzThumbRelocator *self, zpointer input_code, ZzThumbWriter *output);\nzsize zz_thumb_relocator_read_one(ZzThumbRelocator *self, ZzInstruction *instruction);\nzbool zz_thumb_relocator_write_one(ZzThumbRelocator *self);\nvoid zz_thumb_relocator_relocate_writer(ZzThumbRelocator *relocator, zaddr code_address);\nvoid zz_thumb_relocator_write_all(ZzThumbRelocator *self);\nvoid zz_thumb_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-arm.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"writer-arm.h\"\n\n#include <stdlib.h>\n\n// ATTENTION !!!:\n// 写 writer 部分, 需要参考, `Instrcution Set Encoding` 部分\n// `writer` REF: `ZzInstruction Set Encoding`\n\nZzArmWriter *zz_arm_writer_new(zpointer data_ptr) {\n    ZzArmWriter *writer = (ZzArmWriter *)malloc(sizeof(ZzArmWriter));\n    memset(writer, 0, sizeof(ZzArmWriter));\n\n    zaddr align_address = (zaddr)data_ptr & ~(zaddr)3;\n    writer->codedata = (zpointer)align_address;\n    writer->base = (zpointer)align_address;\n    writer->pc = align_address;\n    writer->size = 0;\n\n    writer->literal_insn_size = 0;\n    memset(writer->literal_insns, 0, sizeof(ZzLiteralInstruction) * MAX_LITERAL_INSN_SIZE);\n\n    return writer;\n}\n\nvoid zz_arm_writer_init(ZzArmWriter *self, zpointer data_ptr) { zz_arm_writer_reset(self, data_ptr); }\n\nvoid zz_arm_writer_reset(ZzArmWriter *self, zpointer data_ptr) {\n\n    zaddr align_address = (zaddr)data_ptr & ~(zaddr)3;\n    self->codedata = (zpointer)align_address;\n    self->base = (zpointer)align_address;\n    self->pc = align_address;\n\n    self->literal_insn_size = 0;\n    memset(self->literal_insns, 0, sizeof(ZzLiteralInstruction) * MAX_LITERAL_INSN_SIZE);\n\n    self->size = 0;\n}\n\nzsize zz_arm_writer_near_jump_range_size() { return ((1 << 23) << 2); }\n\n// ------- relocator -------\n\nZzLiteralInstruction *zz_arm_writer_put_ldr_b_reg_relocate_address(ZzArmWriter *self, ZzARMReg reg, zaddr address,\n                                                                   ZzLiteralInstruction **literal_insn_ptr) {\n    zz_arm_writer_put_ldr_b_reg_address(self, reg, address);\n    ZzLiteralInstruction *literal_insn = &(self->literal_insns[self->literal_insn_size - 1]);\n    *literal_insn_ptr = literal_insn;\n    return literal_insn;\n}\n\nZzLiteralInstruction *zz_arm_writer_put_ldr_reg_relocate_address(ZzArmWriter *self, ZzARMReg reg, zaddr address,\n                                                                 ZzLiteralInstruction **literal_insn_ptr) {\n    zz_arm_writer_put_ldr_reg_address(self, reg, address);\n    ZzLiteralInstruction *literal_insn = &(self->literal_insns[self->literal_insn_size - 1]);\n    *literal_insn_ptr = literal_insn;\n    return literal_insn;\n}\n\n// ------- user custom -------\n\nvoid zz_arm_writer_put_ldr_b_reg_address(ZzArmWriter *self, ZzARMReg reg, zaddr address) {\n    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;\n    zz_arm_writer_put_ldr_reg_reg_imm(self, reg, ZZ_ARM_REG_PC, 0);\n    zz_arm_writer_put_b_imm(self, 0x0);\n    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;\n    zz_arm_writer_put_bytes(self, (zpointer)&address, sizeof(zpointer));\n}\n\nvoid zz_arm_writer_put_bx_to_thumb(ZzArmWriter *self) {\n    zz_arm_writer_put_sub_reg_reg_imm(self, ZZ_ARM_REG_SP, ZZ_ARM_REG_SP, 0x8);\n    zz_arm_writer_put_str_reg_reg_imm(self, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0);\n    zz_arm_writer_put_add_reg_reg_imm(self, ZZ_ARM_REG_R1, ZZ_ARM_REG_PC, 9);\n    zz_arm_writer_put_str_reg_reg_imm(self, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);\n    zz_arm_writer_put_ldr_reg_reg_imm_index(self, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 4, 0);\n    zz_arm_writer_put_ldr_reg_reg_imm_index(self, ZZ_ARM_REG_PC, ZZ_ARM_REG_SP, 4, 0);\n}\n// ------- architecture default -------\nvoid zz_arm_writer_put_bytes(ZzArmWriter *self, zbyte *data, zuint data_size) {\n    memcpy(self->codedata, data, data_size);\n    self->codedata = (zpointer)self->codedata + data_size;\n    self->pc += data_size;\n    self->size += data_size;\n}\n\nvoid zz_arm_writer_put_instruction(ZzArmWriter *self, zuint32 insn) {\n    *(zuint32 *)(self->codedata) = insn;\n    self->codedata = (zpointer)self->codedata + sizeof(zuint32);\n    self->pc += 4;\n    self->size += 4;\n}\n\nvoid zz_arm_writer_put_b_imm(ZzArmWriter *self, zuint32 imm) {\n    zz_arm_writer_put_instruction(self, 0xea000000 | ((imm / 4) & 0xffffff));\n}\n\nvoid zz_arm_writer_put_ldr_reg_reg_imm(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zint32 imm) {\n    ZzArmRegInfo rd, rs;\n\n    zz_arm_register_describe(dst_reg, &rd);\n    zz_arm_register_describe(src_reg, &rs);\n\n    if (rs.meta == ZZ_ARM_REG_PC) {\n        zz_arm_writer_put_ldr_reg_imm_literal(self, dst_reg, imm);\n    } else {\n        zbool P = 1;\n        zbool U = 0;\n        zbool W = 0;\n        if (imm >= 0)\n            U = 1;\n\n        zz_arm_writer_put_ldr_reg_reg_imm_A1(self, dst_reg, src_reg, ABS(imm), P, U, W);\n    }\n}\n\nvoid zz_arm_writer_put_ldr_reg_reg_imm_index(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zint32 imm,\n                                             zbool index) {\n    ZzArmRegInfo rd, rs;\n\n    zz_arm_register_describe(dst_reg, &rd);\n    zz_arm_register_describe(src_reg, &rs);\n\n    zbool P = index;\n    zbool U = 0;\n    zbool W = 1;\n    if (P == 0)\n        W = 0;\n    if (imm >= 0)\n        U = 1;\n\n    zz_arm_writer_put_ldr_reg_reg_imm_A1(self, dst_reg, src_reg, ABS(imm), P, U, W);\n}\nvoid zz_arm_writer_put_ldr_reg_reg_imm_A1(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zuint32 imm, zbool P,\n                                          zbool U, zbool W) {\n    ZzArmRegInfo rd, rs;\n\n    zz_arm_register_describe(dst_reg, &rd);\n    zz_arm_register_describe(src_reg, &rs);\n\n    zz_arm_writer_put_instruction(self, 0xe4100000 | rd.index << 12 | rs.index << 16 | P << 24 | U << 23 | W << 21 |\n                                            (imm & ZZ_INT12_MASK));\n}\nvoid zz_arm_writer_put_ldr_reg_imm_literal(ZzArmWriter *self, ZzARMReg dst_reg, zint32 imm) {\n    ZzArmRegInfo rd;\n\n    zz_arm_register_describe(dst_reg, &rd);\n    zbool U = 0;\n    if (imm >= 0)\n        U = 1;\n    zz_arm_writer_put_instruction(self, 0xe51f0000 | U << 23 | rd.index << 12 | (ABS(imm) & ZZ_INT12_MASK));\n}\n\nvoid zz_arm_writer_put_str_reg_reg_imm(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zint32 imm) {\n    ZzArmRegInfo rd, rs;\n\n    zz_arm_register_describe(dst_reg, &rd);\n    zz_arm_register_describe(src_reg, &rs);\n\n    zbool P = 1;\n    zbool U = 0;\n    zbool W = 0;\n    if (imm >= 0)\n        U = 1;\n    zz_arm_writer_put_instruction(self, 0xe4000000 | rd.index << 12 | rs.index << 16 | P << 24 | U << 23 | W << 21 |\n                                            (imm & ZZ_INT12_MASK));\n}\n\nvoid zz_arm_writer_put_ldr_reg_address(ZzArmWriter *self, ZzARMReg reg, zaddr address) {\n    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;\n    zz_arm_writer_put_ldr_reg_reg_imm(self, reg, ZZ_ARM_REG_PC, -4);\n    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;\n    zz_arm_writer_put_bytes(self, (zpointer)&address, sizeof(zpointer));\n}\n\nvoid zz_arm_writer_put_add_reg_reg_imm(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zuint32 imm) {\n    ZzArmRegInfo rd, rs;\n\n    zz_arm_register_describe(dst_reg, &rd);\n    zz_arm_register_describe(src_reg, &rs);\n\n    zz_arm_writer_put_instruction(self, 0xe2800000 | rd.index << 12 | rs.index << 16 | (imm & ZZ_INT12_MASK));\n}\n\nvoid zz_arm_writer_put_sub_reg_reg_imm(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zuint32 imm) {\n    ZzArmRegInfo rd, rs;\n\n    zz_arm_register_describe(dst_reg, &rd);\n    zz_arm_register_describe(src_reg, &rs);\n\n    zz_arm_writer_put_instruction(self, 0xe2400000 | rd.index << 12 | rs.index << 16 | (imm & ZZ_INT12_MASK));\n}\n\nvoid zz_arm_writer_put_bx_reg(ZzArmWriter *self, ZzARMReg reg) {\n    ZzArmRegInfo rs;\n    zz_arm_register_describe(reg, &rs);\n    zz_arm_writer_put_instruction(self, 0xe12fff10 | rs.index);\n}\n\nvoid zz_arm_writer_put_nop(ZzArmWriter *self) { zz_arm_writer_put_instruction(self, 0xe320f000); }\n\nvoid zz_arm_writer_put_push_reg(ZzArmWriter *self, ZzARMReg reg) {\n    ZzArmRegInfo ri;\n    zz_arm_register_describe(reg, &ri);\n    zz_arm_writer_put_instruction(self, 0b11100101001011010000000000000100 | ri.index << 12);\n    return;\n}\n\nvoid zz_arm_writer_put_pop_reg(ZzArmWriter *self, ZzARMReg reg) {\n    ZzArmRegInfo ri;\n    zz_arm_register_describe(reg, &ri);\n\n    zz_arm_writer_put_instruction(self, 0b11100100100111010000000000000100 | ri.index << 12);\n    return;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-arm.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_arm_writer_arm_h\n#define platforms_arch_arm_writer_arm_h\n\n#include <string.h>\n\n// platforms\n#include \"instructions.h\"\n#include \"reader-arm.h\"\n#include \"regs-arm.h\"\n#include \"writer-arm.h\"\n\n// hookzz\n#include \"writer.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef ZzWriter ZzArmWriter;\nZzArmWriter *zz_arm_writer_new(zpointer data_ptr);\nvoid zz_arm_writer_init(ZzArmWriter *self, zpointer data_ptr);\nvoid zz_arm_writer_reset(ZzArmWriter *self, zpointer data_ptr);\nzsize zz_arm_writer_near_jump_range_size();\n\n// ------- user custom -------\n\nvoid zz_arm_writer_put_ldr_b_reg_address(ZzArmWriter *self, ZzARMReg reg, zaddr address);\nvoid zz_arm_writer_put_bx_to_thumb(ZzArmWriter *self);\n\n// ------- architecture default -------\n\nvoid zz_arm_writer_put_bytes(ZzArmWriter *self, zbyte *data, zuint data_size);\nvoid zz_arm_writer_put_instruction(ZzArmWriter *self, zuint32 insn);\nvoid zz_arm_writer_put_b_imm(ZzArmWriter *self, zuint32 imm);\nvoid zz_arm_writer_put_bx_reg(ZzArmWriter *self, ZzARMReg reg);\nvoid zz_arm_writer_put_nop(ZzArmWriter *self);\nvoid zz_arm_writer_put_ldr_reg_reg_imm(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zint32 imm);\nvoid zz_arm_writer_put_str_reg_reg_imm(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zint32 imm);\nvoid zz_arm_writer_put_ldr_reg_imm_literal(ZzArmWriter *self, ZzARMReg dst_reg, zint32 imm);\nvoid zz_arm_writer_put_ldr_reg_reg_imm_index(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zint32 imm,\n                                             zbool index);\nvoid zz_arm_writer_put_ldr_reg_reg_imm_A1(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zuint32 imm, zbool P,\n                                          zbool U, zbool W);\nvoid zz_arm_writer_put_ldr_reg_address(ZzArmWriter *self, ZzARMReg reg, zaddr address);\nvoid zz_arm_writer_put_add_reg_reg_imm(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zuint32 imm);\nvoid zz_arm_writer_put_sub_reg_reg_imm(ZzArmWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zuint32 imm);\nvoid zz_arm_writer_put_push_reg(ZzArmWriter *self, ZzARMReg reg);\nvoid zz_arm_writer_put_pop_reg(ZzArmWriter *self, ZzARMReg reg);\nZzLiteralInstruction *zz_arm_writer_put_ldr_b_reg_relocate_address(ZzArmWriter *self, ZzARMReg reg, zaddr address,\n                                                                   ZzLiteralInstruction **literal_insn_ptr);\nZzLiteralInstruction *zz_arm_writer_put_ldr_reg_relocate_address(ZzArmWriter *self, ZzARMReg reg, zaddr address,\n                                                                 ZzLiteralInstruction **literal_insn_ptr);\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-thumb.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"writer-thumb.h\"\n\n#include <stdlib.h>\n\n// ATTENTION !!!:\n// 写 writer 部分, 需要参考, `Instrcution Set Encoding` 部分\n// `witer` REF: `ZzInstruction Set Encoding`\n\nZzThumbWriter *zz_thumb_writer_new(zpointer data_ptr) {\n    ZzThumbWriter *writer = (ZzThumbWriter *)malloc(sizeof(ZzThumbWriter));\n    memset(writer, 0, sizeof(ZzThumbWriter));\n\n    zaddr align_address = (zaddr)data_ptr & ~(zaddr)3;\n    writer->codedata = (zpointer)align_address;\n    writer->base = (zpointer)align_address;\n    writer->pc = align_address;\n    writer->size = 0;\n\n    writer->literal_insn_size = 0;\n    memset(writer->literal_insns, 0, sizeof(ZzLiteralInstruction) * MAX_LITERAL_INSN_SIZE);\n\n    return writer;\n}\n\nvoid zz_thumb_writer_init(ZzThumbWriter *self, zpointer data_ptr) { zz_thumb_writer_reset(self, data_ptr); }\n\nvoid zz_thumb_writer_reset(ZzThumbWriter *self, zpointer data_ptr) {\n    zaddr align_address = (zaddr)data_ptr & ~(zaddr)3;\n\n    self->codedata = (zpointer)align_address;\n    self->base = (zpointer)align_address;\n    self->pc = align_address;\n    self->size = 0;\n\n    self->literal_insn_size = 0;\n    memset(self->literal_insns, 0, sizeof(ZzLiteralInstruction) * MAX_LITERAL_INSN_SIZE);\n}\n\nzsize zz_thumb_writer_near_jump_range_size() { return ((1 << 23) << 1); }\n\n// ------- relocator -------\n\nZzLiteralInstruction *zz_thumb_writer_put_ldr_b_reg_relocate_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address,\n                                                                     ZzLiteralInstruction **literal_insn_ptr) {\n    zz_thumb_writer_put_ldr_b_reg_address(self, reg, address);\n    ZzLiteralInstruction *literal_insn = &(self->literal_insns[self->literal_insn_size - 1]);\n    *literal_insn_ptr = literal_insn;\n    return literal_insn;\n}\n\nZzLiteralInstruction *zz_thumb_writer_put_ldr_reg_relocate_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address,\n                                                                   ZzLiteralInstruction **literal_insn_ptr) {\n    zz_thumb_writer_put_ldr_reg_address(self, reg, address);\n    ZzLiteralInstruction *literal_insn = &(self->literal_insns[self->literal_insn_size - 1]);\n    *literal_insn_ptr = literal_insn;\n    return literal_insn;\n}\n\n// ------- custom -------\n\nvoid zz_thumb_writer_put_ldr_b_reg_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address) {\n    ZzArmRegInfo ri;\n    zz_arm_register_describe(reg, &ri);\n    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;\n\n    if ((((zaddr)self->pc) % 4)) {\n        if (ri.meta <= ZZ_ARM_REG_R7) {\n            zz_thumb_writer_put_ldr_reg_imm(self, reg, 0x4);\n            zz_thumb_writer_put_nop(self);\n        } else {\n            zz_thumb_writer_put_ldr_reg_imm(self, reg, 0x4);\n        }\n    } else {\n        if (ri.meta <= ZZ_ARM_REG_R7) {\n            zz_thumb_writer_put_ldr_reg_imm(self, reg, 0x0);\n        } else {\n            zz_thumb_writer_put_ldr_reg_imm(self, reg, 0x4);\n            zz_thumb_writer_put_nop(self);\n        }\n    }\n\n    zz_thumb_writer_put_b_imm(self, 0x2);\n    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;\n    zz_thumb_writer_put_bytes(self, (zpointer)&address, sizeof(zpointer));\n    return;\n}\n\nvoid zz_thumb_writer_put_ldr_reg_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address) {\n    ZzArmRegInfo ri;\n    zz_arm_register_describe(reg, &ri);\n\n    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;\n\n    if ((((zaddr)self->pc) % 4)) {\n        if (ri.meta <= ZZ_ARM_REG_R7) {\n            zz_thumb_writer_put_ldr_reg_imm(self, reg, 0x0);\n        } else {\n            zz_thumb_writer_put_ldr_reg_imm(self, reg, 0x4);\n            zz_thumb_writer_put_nop(self);\n        }\n    } else {\n        zz_thumb_writer_put_ldr_reg_imm(self, reg, 0x0);\n        if (ri.meta <= ZZ_ARM_REG_R7)\n            zz_thumb_writer_put_nop(self);\n    }\n\n    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;\n    zz_thumb_writer_put_bytes(self, (zpointer)&address, sizeof(zpointer));\n    return;\n}\n\n// ------- architecture default -------\nvoid zz_thumb_writer_put_nop(ZzThumbWriter *self) {\n    zz_thumb_writer_put_instruction(self, 0x46c0);\n    return;\n}\n\nvoid zz_thumb_writer_put_bytes(ZzThumbWriter *self, zbyte *data, zuint data_size) {\n    memcpy(self->codedata, data, data_size);\n    self->codedata = (zpointer)self->codedata + data_size;\n    self->pc += data_size;\n    self->size += data_size;\n    return;\n}\n\nvoid zz_thumb_writer_put_instruction(ZzThumbWriter *self, uint16_t insn) {\n    *(uint16_t *)(self->codedata) = insn;\n    self->codedata = (zpointer)self->codedata + sizeof(uint16_t);\n    self->pc += 2;\n    self->size += 2;\n    return;\n}\n\nvoid zz_thumb_writer_put_b_imm(ZzThumbWriter *self, zuint32 imm) {\n\n    zz_thumb_writer_put_instruction(self, 0xe000 | ((imm / 2) & ZZ_INT11_MASK));\n    return;\n}\n\nvoid zz_thumb_writer_put_bx_reg(ZzThumbWriter *self, ZzARMReg reg) {\n    ZzArmRegInfo ri;\n\n    zz_arm_register_describe(reg, &ri);\n\n    if ((((zaddr)self->pc) % 4)) {\n        zz_thumb_writer_put_nop(self);\n    }\n\n    zz_thumb_writer_put_instruction(self, 0x4700 | (ri.index << 3));\n    zz_thumb_writer_put_nop(self);\n    return;\n}\n\nvoid zz_thumb_writer_put_blx_reg(ZzThumbWriter *self, ZzARMReg reg) {\n    ZzArmRegInfo ri;\n\n    zz_arm_register_describe(reg, &ri);\n\n    zz_thumb_writer_put_instruction(self, 0x4780 | (ri.index << 3));\n    return;\n}\n\n// A8.8.18\nvoid zz_thumb_writer_put_branch_imm(ZzThumbWriter *self, zuint32 imm, zbool link, zbool thumb) {\n    union {\n        zint32 i;\n        zuint32 u;\n    } distance;\n    zuint16 s, j1, j2, imm10, imm11;\n\n    distance.i = (zint32)(imm) / 2;\n\n    s = (distance.u >> 31) & 1;\n    j1 = (~((distance.u >> 22) ^ s)) & 1;\n    j2 = (~((distance.u >> 21) ^ s)) & 1;\n\n    imm10 = (distance.u >> 11) & ZZ_INT10_MASK;\n    imm11 = distance.u & ZZ_INT11_MASK;\n\n    zz_thumb_writer_put_instruction(self, 0xf000 | (s << 10) | imm10);\n    zz_thumb_writer_put_instruction(self, 0x8000 | (link << 14) | (j1 << 13) | (thumb << 12) | (j2 << 11) | imm11);\n    return;\n}\n\nvoid zz_thumb_writer_put_bl_imm(ZzThumbWriter *self, zuint32 imm) {\n    zz_thumb_writer_put_branch_imm(self, imm, TRUE, TRUE);\n    return;\n}\n\nvoid zz_thumb_writer_put_blx_imm(ZzThumbWriter *self, zuint32 imm) {\n    zz_thumb_writer_put_branch_imm(self, imm, TRUE, FALSE);\n    return;\n}\n\nvoid zz_thumb_writer_put_b_imm32(ZzThumbWriter *self, zuint32 imm) {\n    zz_thumb_writer_put_branch_imm(self, imm, FALSE, TRUE);\n    return;\n}\n\n// PAGE: A8-410\n// A8.8.64 LDR (literal)\nvoid zz_thumb_writer_put_ldr_reg_imm(ZzThumbWriter *self, ZzARMReg reg, zint32 imm) {\n    ZzArmRegInfo ri;\n\n    zz_arm_register_describe(reg, &ri);\n\n    if (ri.meta <= ZZ_ARM_REG_R7 && imm >= 0 && imm < ((1 << 8) << 2)) {\n\n        zz_thumb_writer_put_instruction(self, 0x4800 | (ri.index << 8) | ((imm / 4) & ZZ_INT8_MASK));\n    } else if (imm < (1 << 12)) {\n        zbool add = 0;\n        if (imm >= 0)\n            add = 1;\n        zz_thumb_writer_put_instruction(self, 0xf85f | (add << 7));\n        zz_thumb_writer_put_instruction(self, (ri.index << 12) | ABS(imm));\n    }\n    return;\n}\n\nzbool zz_thumb_writer_put_transfer_reg_reg_offset_T1(ZzThumbWriter *self, ZzThumbMemoryOperation operation,\n                                                     ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset) {\n    ZzArmRegInfo lr, rr;\n\n    zz_arm_register_describe(left_reg, &lr);\n    zz_arm_register_describe(right_reg, &rr);\n\n    zuint16 insn;\n\n    if (right_offset < 0)\n        return FALSE;\n\n    if (lr.meta <= ZZ_ARM_REG_R7 && rr.meta <= ZZ_ARM_REG_R7 && right_offset < ((1 << 5) << 2)) {\n        insn = 0x6000 | (right_offset / 4) << 6 | (rr.index << 3) | lr.index;\n        if (operation == ZZ_THUMB_MEMORY_LOAD)\n            insn |= 0x0800;\n        zz_thumb_writer_put_instruction(self, insn);\n        return TRUE;\n    }\n    return FALSE;\n}\n\nzbool zz_thumb_writer_put_transfer_reg_reg_offset_T2(ZzThumbWriter *self, ZzThumbMemoryOperation operation,\n                                                     ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset) {\n    ZzArmRegInfo lr, rr;\n\n    zz_arm_register_describe(left_reg, &lr);\n    zz_arm_register_describe(right_reg, &rr);\n\n    zuint16 insn;\n\n    if (right_offset < 0)\n        return FALSE;\n\n    if (rr.meta == ZZ_ARM_REG_SP && lr.meta <= ZZ_ARM_REG_R7 && right_offset < ((1 << 8) << 2)) {\n        insn = 0x9000 | (lr.index << 8) | (right_offset / 4);\n        if (operation == ZZ_THUMB_MEMORY_LOAD)\n            insn |= 0x0800;\n        zz_thumb_writer_put_instruction(self, insn);\n        return TRUE;\n    }\n    return FALSE;\n}\n\nzbool zz_thumb_writer_put_transfer_reg_reg_offset_T3(ZzThumbWriter *self, ZzThumbMemoryOperation operation,\n                                                     ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset) {\n    ZzArmRegInfo lr, rr;\n\n    zz_arm_register_describe(left_reg, &lr);\n    zz_arm_register_describe(right_reg, &rr);\n\n    zuint16 insn;\n\n    if (right_offset < 0)\n        return FALSE;\n\n    if (right_offset < (1 << 12)) {\n        if (rr.meta == ZZ_ARM_REG_PC) {\n            zz_thumb_writer_put_ldr_reg_imm(self, left_reg, right_offset);\n        }\n        zz_thumb_writer_put_instruction(self,\n                                        0xf8c0 | ((operation == ZZ_THUMB_MEMORY_LOAD) ? 0x0010 : 0x0000) | rr.index);\n        zz_thumb_writer_put_instruction(self, (lr.index << 12) | right_offset);\n\n        return TRUE;\n    }\n    return FALSE;\n}\n\nzbool zz_thumb_writer_put_transfer_reg_reg_offset_T4(ZzThumbWriter *self, ZzThumbMemoryOperation operation,\n                                                     ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset,\n                                                     zbool index, zbool wback) {\n    ZzArmRegInfo lr, rr;\n\n    zz_arm_register_describe(left_reg, &lr);\n    zz_arm_register_describe(right_reg, &rr);\n\n    zuint16 insn;\n\n    if (ABS(right_offset) < (1 << 8)) {\n        if (rr.meta == ZZ_ARM_REG_PC) {\n            zz_thumb_writer_put_ldr_reg_imm(self, left_reg, right_offset);\n        } else {\n            zbool add = 0;\n            if (right_offset > 0)\n                add = 1;\n            zz_thumb_writer_put_instruction(self, 0xf840 | ((operation == ZZ_THUMB_MEMORY_LOAD) ? 0x0010 : 0x0000) |\n                                                      rr.index);\n            zz_thumb_writer_put_instruction(self, 0x0800 | (lr.index << 12) | (index << 10) | (add << 9) |\n                                                      (wback << 8) | (ABS(right_offset)));\n            return TRUE;\n        }\n    }\n    return FALSE;\n}\n\n// PAGE: A8-406\n// PAGE: A8.8.203 STR (immediate, Thumb)\nstatic void zz_thumb_writer_put_transfer_reg_reg_offset(ZzThumbWriter *self, ZzThumbMemoryOperation operation,\n                                                        ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset) {\n    if (zz_thumb_writer_put_transfer_reg_reg_offset_T1(self, operation, left_reg, right_reg, right_offset))\n        return;\n\n    if (zz_thumb_writer_put_transfer_reg_reg_offset_T2(self, operation, left_reg, right_reg, right_offset))\n        return;\n\n    if (zz_thumb_writer_put_transfer_reg_reg_offset_T3(self, operation, left_reg, right_reg, right_offset))\n        return;\n    if (zz_thumb_writer_put_transfer_reg_reg_offset_T4(self, operation, left_reg, right_reg, right_offset, 1, 0))\n        return;\n    return;\n}\n\nvoid zz_thumb_writer_put_ldr_reg_reg_offset(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg,\n                                            zint32 src_offset) {\n    zz_thumb_writer_put_transfer_reg_reg_offset(self, ZZ_THUMB_MEMORY_LOAD, dst_reg, src_reg, src_offset);\n    return;\n}\n\nvoid zz_thumb_writer_put_str_reg_reg_offset(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg,\n                                            zint32 dst_offset) {\n    zz_thumb_writer_put_transfer_reg_reg_offset(self, ZZ_THUMB_MEMORY_STORE, src_reg, dst_reg, dst_offset);\n    return;\n}\n\nvoid zz_thumb_writer_put_ldr_index_reg_reg_offset(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg,\n                                                  zint32 src_offset, zbool index) {\n    zz_thumb_writer_put_transfer_reg_reg_offset_T4(self, ZZ_THUMB_MEMORY_LOAD, dst_reg, src_reg, src_offset, index, 1);\n    return;\n}\n\nvoid zz_thumb_writer_put_str_index_reg_reg_offset(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg,\n                                                  zint32 dst_offset, zbool index) {\n    zz_thumb_writer_put_transfer_reg_reg_offset_T4(self, ZZ_THUMB_MEMORY_STORE, src_reg, dst_reg, dst_offset, index, 1);\n    return;\n}\n\nvoid zz_thumb_writer_put_str_reg_reg(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg) {\n    zz_thumb_writer_put_str_reg_reg_offset(self, src_reg, dst_reg, 0);\n    return;\n}\n\nvoid zz_thumb_writer_put_ldr_reg_reg(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg) {\n    zz_thumb_writer_put_ldr_reg_reg_offset(self, dst_reg, src_reg, 0);\n    return;\n}\n\nvoid zz_thumb_writer_put_add_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, zint32 imm) {\n    ZzArmRegInfo dst;\n    zuint16 sign_mask, insn;\n\n    zz_arm_register_describe(dst_reg, &dst);\n\n    sign_mask = 0x0000;\n    if (dst.meta == ZZ_ARM_REG_SP) {\n\n        if (imm < 0)\n            sign_mask = 0x0080;\n\n        insn = 0xb000 | sign_mask | ABS(imm / 4);\n    } else {\n        if (imm < 0)\n            sign_mask = 0x0800;\n\n        insn = 0x3000 | sign_mask | (dst.index << 8) | ABS(imm);\n    }\n\n    zz_thumb_writer_put_instruction(self, insn);\n    return;\n}\n\nvoid zz_thumb_writer_put_sub_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, zint32 imm) {\n    zz_thumb_writer_put_add_reg_imm(self, dst_reg, -imm);\n    return;\n}\n\nvoid zz_thumb_writer_put_add_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg, zint32 right_value) {\n    ZzArmRegInfo dst, left;\n    zuint16 insn;\n\n    zz_arm_register_describe(dst_reg, &dst);\n    zz_arm_register_describe(left_reg, &left);\n\n    if (left.meta == dst.meta) {\n        return zz_thumb_writer_put_add_reg_imm(self, dst_reg, right_value);\n    }\n\n    if (dst.meta <= ZZ_ARM_REG_R7 && left.meta <= ZZ_ARM_REG_R7 && ABS(right_value) < (1 << 3)) {\n        zuint32 sign_mask = 0;\n\n        if (right_value < 0)\n            sign_mask = 1 << 9;\n\n        insn = 0x1c00 | sign_mask | (ABS(right_value) << 6) | (left.index << 3) | dst.index;\n        zz_thumb_writer_put_instruction(self, insn);\n    } else if ((left.meta == ZZ_ARM_REG_SP || left.meta == ZZ_ARM_REG_PC) && dst.meta <= ZZ_ARM_REG_R7 &&\n               right_value > 0 && (right_value % 4 == 0) && right_value < (1 << 8)) {\n        zuint16 base_mask;\n\n        if (left.meta == ZZ_ARM_REG_SP)\n            base_mask = 0x0800;\n        else\n            base_mask = 0x0000;\n\n        insn = 0xa000 | base_mask | (dst.index << 8) | (right_value / 4);\n        zz_thumb_writer_put_instruction(self, insn);\n    } else {\n        zuint16 insn1, insn2;\n        zuint i, imm3, imm8;\n        i = (ABS(right_value) >> (3 + 8)) & 0x1;\n        imm3 = (ABS(right_value) >> 8) & 0b111;\n        imm8 = ABS(right_value) & 0b11111111;\n\n        // A8-708, sub\n        // A8-306 add\n        if (right_value < 0)\n            zz_thumb_writer_put_instruction(self, 0b1111001010100000 | i << 10 | left.index);\n        else\n            zz_thumb_writer_put_instruction(self, 0b1111001000000000 | i << 10 | left.index);\n        zz_thumb_writer_put_instruction(self, 0b0 | imm3 << 12 | dst.index << 8 | imm8);\n    }\n\n    return;\n}\n\nvoid zz_thumb_writer_put_sub_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg, zint32 right_value) {\n    zz_thumb_writer_put_add_reg_reg_imm(self, dst_reg, left_reg, -right_value);\n    return;\n}\n\nvoid zz_thumb_writer_put_push_reg(ZzThumbWriter *self, ZzARMReg reg) {\n    ZzArmRegInfo ri;\n    zz_arm_register_describe(reg, &ri);\n\n    zuint16 M, register_list;\n    M = 0;\n\n    zz_thumb_writer_put_instruction(self, 0b1011010000000000 | M << 8 | 1 << ri.index);\n    return;\n}\n\nvoid zz_thumb_writer_put_pop_reg(ZzThumbWriter *self, ZzARMReg reg) {\n    ZzArmRegInfo ri;\n    zz_arm_register_describe(reg, &ri);\n\n    zuint16 P, register_list;\n    P = 0;\n\n    zz_thumb_writer_put_instruction(self, 0b1011110000000000 | P << 8 | 1 << ri.index);\n    return;\n}\n\nvoid zz_thumb_writer_put_add_reg_reg_reg(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg, ZzARMReg right_reg) {\n    ZzArmRegInfo dst, left, right;\n    zz_arm_register_describe(dst_reg, &dst);\n    zz_arm_register_describe(left_reg, &left);\n    zz_arm_register_describe(right_reg, &right);\n\n    zuint16 Rm_ndx, Rn_ndx, Rd_ndx;\n    Rd_ndx = dst.index;\n    Rm_ndx = right.index;\n    Rn_ndx = left.index;\n\n    zz_thumb_writer_put_instruction(self, 0b0001100000000000 | Rm_ndx << 6 | Rn_ndx << 3 | Rd_ndx);\n    return;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm/writer-thumb.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_arm_writer_thumb_h\n#define platforms_arch_arm_writer_thumb_h\n\n#include <string.h>\n\n// platforms\n#include \"instructions.h\"\n#include \"reader-thumb.h\"\n#include \"regs-arm.h\"\n#include \"writer-thumb.h\"\n\n// hookzz\n#include \"writer.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef ZzWriter ZzThumbWriter;\n\ntypedef enum _ZzThumbMemoryOperation { ZZ_THUMB_MEMORY_LOAD, ZZ_THUMB_MEMORY_STORE } ZzThumbMemoryOperation;\n\n// ------- user custom -------\n\nvoid zz_thumb_writer_put_ldr_b_reg_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address);\n\n// ------- architecture default -------\n\nZzThumbWriter *zz_thumb_writer_new(zpointer data_ptr);\nvoid zz_thumb_writer_init(ZzThumbWriter *self, zpointer data_ptr);\nvoid zz_thumb_writer_reset(ZzThumbWriter *self, zpointer data_ptr);\nzsize zz_thumb_writer_near_jump_range_size();\nvoid zz_thumb_writer_put_nop(ZzThumbWriter *self);\nvoid zz_thumb_writer_put_bytes(ZzThumbWriter *self, zbyte *data, zuint data_size);\nvoid zz_thumb_writer_put_instruction(ZzThumbWriter *self, uint16_t insn);\nvoid zz_thumb_writer_put_b_imm(ZzThumbWriter *self, zuint32 imm);\nvoid zz_thumb_writer_put_bx_reg(ZzThumbWriter *self, ZzARMReg reg);\nvoid zz_thumb_writer_put_blx_reg(ZzThumbWriter *self, ZzARMReg reg);\nvoid zz_thumb_writer_put_branch_imm(ZzThumbWriter *self, zuint32 imm, zbool link, zbool thumb);\nvoid zz_thumb_writer_put_bl_imm(ZzThumbWriter *self, zuint32 imm);\nvoid zz_thumb_writer_put_blx_imm(ZzThumbWriter *self, zuint32 imm);\nvoid zz_thumb_writer_put_b_imm32(ZzThumbWriter *self, zuint32 imm);\n\nvoid zz_thumb_writer_put_ldr_reg_imm(ZzThumbWriter *self, ZzARMReg reg, zint32 imm);\nvoid zz_thumb_writer_put_ldr_reg_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address);\n\nstatic void zz_thumb_writer_put_transfer_reg_reg_offset(ZzThumbWriter *self, ZzThumbMemoryOperation operation,\n                                                        ZzARMReg left_reg, ZzARMReg right_reg, zint32 right_offset);\nvoid zz_thumb_writer_put_ldr_reg_reg_offset(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg, zint32 src_offset);\nvoid zz_thumb_writer_put_str_reg_reg_offset(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg, zint32 dst_offset);\nvoid zz_thumb_writer_put_str_index_reg_reg_offset(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg,\n                                                  zint32 dst_offset, zbool index);\nvoid zz_thumb_writer_put_ldr_index_reg_reg_offset(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg,\n                                                  zint32 src_offset, zbool index);\nvoid zz_thumb_writer_put_str_reg_reg(ZzThumbWriter *self, ZzARMReg src_reg, ZzARMReg dst_reg);\nvoid zz_thumb_writer_put_ldr_reg_reg(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg src_reg);\nvoid zz_thumb_writer_put_add_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, zint32 imm);\nvoid zz_thumb_writer_put_sub_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, zint32 imm);\nvoid zz_thumb_writer_put_add_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg, zint32 right_value);\nvoid zz_thumb_writer_put_sub_reg_reg_imm(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg, zint32 right_value);\nvoid zz_thumb_writer_put_push_reg(ZzThumbWriter *self, ZzARMReg reg);\nvoid zz_thumb_writer_put_pop_reg(ZzThumbWriter *self, ZzARMReg reg);\nvoid zz_thumb_writer_put_add_reg_reg_reg(ZzThumbWriter *self, ZzARMReg dst_reg, ZzARMReg left_reg, ZzARMReg right_reg);\nZzLiteralInstruction *zz_thumb_writer_put_ldr_reg_relocate_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address,\n                                                                   ZzLiteralInstruction **literal_insn_ptr);\nZzLiteralInstruction *zz_thumb_writer_put_ldr_b_reg_relocate_address(ZzThumbWriter *self, ZzARMReg reg, zaddr address,\n                                                                     ZzLiteralInstruction **literal_insn_ptr);\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/instructions.c",
    "content": "#include \"instructions.h\"\n#include <string.h>\n\nzuint32 get_insn_sub(zuint32 insn, int start, int length) { return (insn >> start) & ((1 << length) - 1); }\n\nzbool insn_equal(zuint32 insn, char *opstr) {\n    zuint32 mask = 0, value = 0;\n    zsize length = strlen(opstr);\n    int i, j;\n    for (i = length - 1, j = 0; i >= 0 && j < length; i--, j++) {\n        if (opstr[i] == 'x') {\n            mask = mask | (0 << j);\n        } else if (opstr[i] == '0') {\n            mask = mask | (1 << j);\n        } else if (opstr[i] == '1') {\n            value = value | (1 << j);\n            mask = mask | (1 << j);\n        }\n    }\n    return (insn & mask) == value;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/instructions.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_arm64_instructions_h\n#define platforms_arch_arm64_instructions_h\n\n#include \"hookzz.h\"\n\ntypedef struct _ZzInstruction {\n    zaddr pc;\n    zaddr address;\n    uint8_t size;\n    zuint32 insn;\n} ZzInstruction;\n\ntypedef struct _ZzRelocateInstruction {\n    const ZzInstruction *insn_ctx;\n    zaddr relocated_offset;\n    zsize relocated_length;\n} ZzRelocateInstruction;\n\nzuint32 get_insn_sub(zuint32 insn, int start, int length);\nzbool insn_equal(zuint32 insn, char *opstr);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/reader-arm64.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"reader-arm64.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\nzpointer zz_arm64_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address) {\n    insn_ctx->address = (zaddr)address;\n    insn_ctx->size = 4;\n    insn_ctx->pc = (zaddr)address;\n    insn_ctx->insn = *(zuint32 *)address;\n    return (zpointer)insn_ctx->pc;\n}\n\nARM64InsnType GetARM64InsnType(zuint32 insn) {\n    // PAGE: C6-673\n    if (insn_equal(insn, \"01011000xxxxxxxxxxxxxxxxxxxxxxxx\")) {\n        return ARM64_INS_LDR_literal;\n    }\n\n    // PAGE: C6-535\n    if (insn_equal(insn, \"0xx10000xxxxxxxxxxxxxxxxxxxxxxxx\")) {\n        return ARM64_INS_ADR;\n    }\n\n    // PAGE: C6-536\n    if (insn_equal(insn, \"1xx10000xxxxxxxxxxxxxxxxxxxxxxxx\")) {\n        return ARM64_INS_ADRP;\n    }\n\n    // PAGE: C6-550\n    if (insn_equal(insn, \"000101xxxxxxxxxxxxxxxxxxxxxxxxxx\")) {\n        return ARM64_INS_B;\n    }\n\n    // PAGE: C6-560\n    if (insn_equal(insn, \"100101xxxxxxxxxxxxxxxxxxxxxxxxxx\")) {\n        return ARM64_INS_BL;\n    }\n\n    // PAGE: C6-549\n    if (insn_equal(insn, \"01010100xxxxxxxxxxxxxxxxxxx0xxxx\")) {\n        return ARM64_INS_B_cond;\n    }\n\n    return ARM64_UNDEF;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/reader-arm64.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_arm64_reader_h\n#define platforms_arch_arm64_reader_h\n\n// platforms\n#include \"instructions.h\"\n\n// hookzz\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef enum _ARM64InsnType {\n    ARM64_INS_LDR_literal,\n    ARM64_INS_ADR,\n    ARM64_INS_ADRP,\n    ARM64_INS_B,\n    ARM64_INS_BL,\n    ARM64_INS_B_cond,\n    ARM64_UNDEF\n} ARM64InsnType;\n\nARM64InsnType GetARM64InsnType(zuint32 insn);\nzpointer zz_arm64_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/regs-arm64.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"regs-arm64.h\"\n\nvoid zz_arm64_register_describe(ZzARM64Reg reg, ZzArm64RegInfo *ri) {\n    if (reg >= ZZ_ARM64_REG_X0 && reg <= ZZ_ARM64_REG_X28) {\n        ri->is_integer = TRUE;\n        ri->width = 64;\n        ri->meta = ZZ_ARM64_REG_X0 + (reg - ZZ_ARM64_REG_X0);\n    } else if (reg == ZZ_ARM64_REG_X29 || reg == ZZ_ARM64_REG_FP) {\n        ri->is_integer = TRUE;\n        ri->width = 64;\n        ri->meta = ZZ_ARM64_REG_X29;\n    } else if (reg == ZZ_ARM64_REG_X30 || reg == ZZ_ARM64_REG_LR) {\n        ri->is_integer = TRUE;\n        ri->width = 64;\n        ri->meta = ZZ_ARM64_REG_X30;\n    } else if (reg == ZZ_ARM64_REG_SP) {\n        ri->is_integer = TRUE;\n        ri->width = 64;\n        ri->meta = ZZ_ARM64_REG_X31;\n    } else {\n        Serror(\"zz_arm64_register_describe error.\");\n#if defined(DEBUG_MODE)\n        debug_break();\n#endif\n        ri->index = 0;\n    }\n    ri->index = ri->meta - ZZ_ARM64_REG_X0;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/regs-arm64.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_arm64_regs_h\n#define platforms_arch_arm64_regs_h\n\n// platforms\n#include \"instructions.h\"\n\n// hookzz\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef enum _ZzARM64Reg {\n    ZZ_ARM64_REG_X0 = 0,\n    ZZ_ARM64_REG_X1,\n    ZZ_ARM64_REG_X2,\n    ZZ_ARM64_REG_X3,\n    ZZ_ARM64_REG_X4,\n    ZZ_ARM64_REG_X5,\n    ZZ_ARM64_REG_X6,\n    ZZ_ARM64_REG_X7,\n    ZZ_ARM64_REG_X8,\n    ZZ_ARM64_REG_X9,\n    ZZ_ARM64_REG_X10,\n    ZZ_ARM64_REG_X11,\n    ZZ_ARM64_REG_X12,\n    ZZ_ARM64_REG_X13,\n    ZZ_ARM64_REG_X14,\n    ZZ_ARM64_REG_X15,\n    ZZ_ARM64_REG_X16,\n    ZZ_ARM64_REG_X17,\n    ZZ_ARM64_REG_X18,\n    ZZ_ARM64_REG_X19,\n    ZZ_ARM64_REG_X20,\n    ZZ_ARM64_REG_X21,\n    ZZ_ARM64_REG_X22,\n    ZZ_ARM64_REG_X23,\n    ZZ_ARM64_REG_X24,\n    ZZ_ARM64_REG_X25,\n    ZZ_ARM64_REG_X26,\n    ZZ_ARM64_REG_X27,\n    ZZ_ARM64_REG_X28,\n    ZZ_ARM64_REG_X29,\n    ZZ_ARM64_REG_X30,\n    ZZ_ARM64_REG_X31,\n    ZZ_ARM64_REG_FP = ZZ_ARM64_REG_X29,\n    ZZ_ARM64_REG_LR = ZZ_ARM64_REG_X30,\n    ZZ_ARM64_REG_SP = ZZ_ARM64_REG_X31\n} ZzARM64Reg;\n\ntypedef struct _ZzArm64RegInfo {\n    zuint index;\n    zuint meta;\n    zuint width;\n    zbool is_integer;\n} ZzArm64RegInfo;\n\nvoid zz_arm64_register_describe(ZzARM64Reg reg, ZzArm64RegInfo *ri);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/relocator-arm64.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"relocator-arm64.h\"\n#include <stdlib.h>\n#include <string.h>\n\n#define MAX_RELOCATOR_INSTRUCIONS_SIZE 64\n\nvoid zz_arm64_relocator_init(ZzArm64Relocator *relocator, zpointer input_code, ZzArm64Writer *output) {\n    relocator->inpos = 0;\n    relocator->outpos = 0;\n    relocator->output = output;\n    relocator->input_start = input_code;\n    relocator->input_cur = input_code;\n    relocator->input_pc = (zaddr)input_code;\n    relocator->relocate_literal_insns_size = 0;\n    relocator->try_relocated_length = 0;\n\n    relocator->input_insns = (ZzInstruction *)malloc(MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));\n    memset(relocator->input_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));\n    relocator->output_insns =\n        (ZzRelocateInstruction *)malloc(MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));\n    memset(relocator->output_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));\n    relocator->relocate_literal_insns =\n        (ZzLiteralInstruction **)malloc(MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));\n    memset(relocator->relocate_literal_insns, 0, MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));\n}\n\nvoid zz_arm64_relocator_reset(ZzArm64Relocator *self, zpointer input_code, ZzArm64Writer *output) {\n    self->input_cur = input_code;\n    self->input_start = input_code;\n    self->input_pc = (zaddr)input_code;\n    self->inpos = 0;\n    self->outpos = 0;\n    self->output = output;\n    self->relocate_literal_insns_size = 0;\n    self->try_relocated_length = 0;\n\n    memset(self->input_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzInstruction));\n    memset(self->output_insns, 0, MAX_RELOCATOR_INSTRUCIONS_SIZE * sizeof(ZzRelocateInstruction));\n    memset(self->relocate_literal_insns, 0, MAX_LITERAL_INSN_SIZE * sizeof(ZzLiteralInstruction *));\n}\n\nzsize zz_arm64_relocator_read_one(ZzArm64Relocator *self, ZzInstruction *instruction) {\n    ZzInstruction *insn_ctx = &self->input_insns[self->inpos];\n    ZzRelocateInstruction *re_insn_ctx = &self->output_insns[self->inpos];\n\n    re_insn_ctx->insn_ctx = insn_ctx;\n    zz_arm64_reader_read_one_instruction(insn_ctx, self->input_cur);\n\n    // switch (0) {}\n\n    self->inpos++;\n\n    if (instruction != NULL)\n        *instruction = *insn_ctx;\n\n    self->input_cur += insn_ctx->size;\n    self->input_pc += insn_ctx->size;\n\n    return self->input_cur - self->input_start;\n}\n\nzaddr zz_arm64_relocator_get_insn_relocated_offset(ZzArm64Relocator *self, zaddr address) {\n    const ZzInstruction *insn_ctx;\n    const ZzRelocateInstruction *re_insn_ctx;\n    int i;\n    for (i = 0; i < self->inpos; i++) {\n        re_insn_ctx = &self->output_insns[i];\n        insn_ctx = re_insn_ctx->insn_ctx;\n        if (insn_ctx->address == address && re_insn_ctx->relocated_offset) {\n            return re_insn_ctx->relocated_offset;\n        }\n    }\n    return 0;\n}\n\nvoid zz_arm64_relocator_relocate_writer(ZzArm64Relocator *relocator, zaddr code_address) {\n    ZzArm64Writer *arm64_writer;\n    arm64_writer = relocator->output;\n    if (relocator->relocate_literal_insns_size) {\n        int i;\n        zaddr *rebase_ptr;\n        zaddr literal_address, relocated_offset, relocated_address, *literal_address_ptr;\n        for (i = 0; i < relocator->relocate_literal_insns_size; i++) {\n            literal_address_ptr = relocator->relocate_literal_insns[i]->literal_address_ptr;\n            literal_address = *literal_address_ptr;\n            relocated_offset = zz_arm64_relocator_get_insn_relocated_offset(relocator, literal_address);\n            if (relocated_offset) {\n                relocated_address = code_address + relocated_offset;\n                *literal_address_ptr = relocated_address;\n            }\n        }\n    }\n}\n\nvoid zz_arm64_relocator_write_all(ZzArm64Relocator *self) {\n    zuint count = 0;\n    zuint outpos = self->outpos;\n    ZzArm64Writer arm64_writer = *self->output;\n\n    while (zz_arm64_relocator_write_one(self))\n        count++;\n}\n\nvoid zz_arm64_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes) {\n    int tmp_size = 0;\n    zpointer target_addr;\n    ZzInstruction insn_ctx;\n    zbool early_end = FALSE;\n    target_addr = (zpointer)address;\n\n    do {\n        zz_arm64_reader_read_one_instruction(&insn_ctx, target_addr);\n        switch (GetARM64InsnType(insn_ctx.insn)) {\n        case ARM64_INS_B:\n            early_end = TRUE;\n            break;\n        default:;\n        }\n        tmp_size += insn_ctx.size;\n        target_addr = target_addr + insn_ctx.size;\n    } while (tmp_size < min_bytes);\n\n    if (early_end) {\n        *max_bytes = tmp_size;\n    }\n    return;\n}\n\n// PAGE: C6-673\nstatic zbool zz_arm64_relocator_rewrite_LDR_literal(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,\n                                                    ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn = insn_ctx->insn;\n    // TODO: check opc == 10, with signed\n    zuint32 imm19 = get_insn_sub(insn, 5, 19);\n    zuint64 offset = imm19 << 2;\n\n    zaddr target_address;\n    target_address = insn_ctx->pc + offset;\n    int Rt_ndx = get_insn_sub(insn, 0, 4);\n\n    zz_arm64_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);\n    zz_arm64_writer_put_ldr_reg_reg_offset(self->output, Rt_ndx, Rt_ndx, 0);\n\n    return TRUE;\n}\n\n// PAGE: C6-535\nstatic zbool zz_arm64_relocator_rewrite_ADR(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,\n                                            ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn = insn_ctx->insn;\n    zuint32 immhi = get_insn_sub(insn, 5, 19);\n    zuint32 immlo = get_insn_sub(insn, 29, 2);\n    zuint64 imm = immhi << 2 | immlo;\n\n    zaddr target_address;\n    target_address = insn_ctx->pc + imm;\n    int Rt_ndx = get_insn_sub(insn, 0, 4);\n\n    zz_arm64_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);\n\n    return TRUE;\n}\n\n// PAGE: C6-536\nstatic zbool zz_arm64_relocator_rewrite_ADRP(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,\n                                             ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn = insn_ctx->insn;\n    zuint32 immhi = get_insn_sub(insn, 5, 19);\n    zuint32 immlo = get_insn_sub(insn, 29, 2);\n    // 12 is PAGE-SIZE\n    zuint64 imm = immhi << 2 << 12 | immlo << 12;\n\n    zaddr target_address;\n    target_address = (insn_ctx->pc & 0xFFFFFFFFFFFFF000) + imm;\n    int Rt_ndx = get_insn_sub(insn, 0, 4);\n\n    zz_arm64_writer_put_ldr_b_reg_address(self->output, Rt_ndx, target_address);\n\n    return TRUE;\n}\n\n// PAGE: C6-550\nstatic zbool zz_arm64_relocator_rewrite_B(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,\n                                          ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn = insn_ctx->insn;\n    zuint32 imm26 = get_insn_sub(insn, 0, 26);\n\n    zuint64 offset = imm26 << 2;\n\n    zaddr target_address;\n    target_address = insn_ctx->pc + offset;\n\n    zz_arm64_writer_put_ldr_br_reg_address(self->output, ZZ_ARM64_REG_X17, target_address);\n\n    return TRUE;\n}\n\n// PAGE: C6-560\nstatic zbool zz_arm64_relocator_rewrite_BL(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,\n                                           ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn = insn_ctx->insn;\n    zuint32 imm26 = get_insn_sub(insn, 0, 26);\n\n    zuint64 offset = imm26 << 2;\n\n    zaddr target_address;\n    target_address = insn_ctx->pc + offset;\n\n    zz_arm64_writer_put_ldr_blr_b_reg_address(self->output, ZZ_ARM64_REG_X17, target_address);\n    ZzLiteralInstruction **literal_insn_ptr = &(self->relocate_literal_insns[self->relocate_literal_insns_size++]);\n    zz_arm64_writer_put_ldr_br_reg_relocate_address(self->output, ZZ_ARM64_REG_X17, insn_ctx->pc + 4, literal_insn_ptr);\n\n    return TRUE;\n}\n\n// 0x000 : b.cond 0x8;\n\n// 0x004 : b 0x14\n\n// 0x008 : ldr x17, [pc, #4]\n// 0x00c : br x17\n// 0x010 : .long 0x0\n// 0x014 : .long 0x0\n\n// 0x018 : remain code\n\n// PAGE: C6-549\nstatic zbool zz_arm64_relocator_rewrite_B_cond(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,\n                                               ZzRelocateInstruction *re_insn_ctx) {\n    zuint32 insn = insn_ctx->insn;\n    zuint32 imm19 = get_insn_sub(insn, 5, 19);\n\n    zuint64 offset = imm19 << 2;\n\n    zaddr target_address;\n    target_address = insn_ctx->pc + offset;\n\n    zuint32 cond = get_insn_sub(insn, 0, 4);\n\n    zz_arm64_writer_put_b_cond_imm(self->output, cond, 0x8);\n    zz_arm64_writer_put_b_imm(self->output, 0x14);\n    zz_arm64_writer_put_ldr_br_reg_address(self->output, ZZ_ARM64_REG_X17, target_address);\n\n    return TRUE;\n}\n\nzbool zz_arm64_relocator_write_one(ZzArm64Relocator *self) {\n    const ZzInstruction *insn_ctx;\n    ZzRelocateInstruction *re_insn_ctx;\n\n    zbool rewritten = FALSE;\n\n    if (self->inpos != self->outpos) {\n        insn_ctx = &self->input_insns[self->outpos];\n        re_insn_ctx = &self->output_insns[self->outpos];\n        self->outpos++;\n    } else\n        return FALSE;\n\n    re_insn_ctx->relocated_offset = (zaddr)self->output->pc - (zaddr)self->output->base;\n\n    switch (GetARM64InsnType(insn_ctx->insn)) {\n    case ARM64_INS_LDR_literal:\n        rewritten = zz_arm64_relocator_rewrite_LDR_literal(self, insn_ctx, re_insn_ctx);\n        break;\n    case ARM64_INS_ADR:\n        rewritten = zz_arm64_relocator_rewrite_ADR(self, insn_ctx, re_insn_ctx);\n        break;\n    case ARM64_INS_ADRP:\n        rewritten = zz_arm64_relocator_rewrite_ADRP(self, insn_ctx, re_insn_ctx);\n        break;\n    case ARM64_INS_B:\n        rewritten = zz_arm64_relocator_rewrite_B(self, insn_ctx, re_insn_ctx);\n        break;\n    case ARM64_INS_BL:\n        rewritten = zz_arm64_relocator_rewrite_BL(self, insn_ctx, re_insn_ctx);\n        break;\n    case ARM64_INS_B_cond:\n        rewritten = zz_arm64_relocator_rewrite_B_cond(self, insn_ctx, re_insn_ctx);\n        break;\n    default:\n        rewritten = FALSE;\n        break;\n    }\n    if (!rewritten)\n        zz_arm64_writer_put_bytes(self->output, (zbyte *)&insn_ctx->insn, insn_ctx->size);\n    re_insn_ctx->relocated_length =\n        (zaddr)self->output->pc - (zaddr)self->output->base - (zaddr)re_insn_ctx->relocated_offset;\n    return TRUE;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/relocator-arm64.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_arm64_relocator_h\n#define platforms_arch_arm64_relocator_h\n\n// platforms\n#include \"instructions.h\"\n#include \"reader-arm64.h\"\n#include \"regs-arm64.h\"\n#include \"writer-arm64.h\"\n\n// hookzz\n#include \"writer.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef struct _ZzArm64Relocator {\n    zbool try_relocated_again;\n    zsize try_relocated_length;\n    zpointer input_start;\n    zpointer input_cur;\n    zaddr input_pc;\n    zuint inpos;\n    zuint outpos;\n    ZzInstruction *input_insns;\n    ZzRelocateInstruction *output_insns;\n    ZzArm64Writer *output;\n    ZzLiteralInstruction **relocate_literal_insns;\n    zsize relocate_literal_insns_size;\n} ZzArm64Relocator;\n\nvoid zz_arm64_relocator_init(ZzArm64Relocator *relocator, zpointer input_code, ZzArm64Writer *writer);\nvoid zz_arm64_relocator_reset(ZzArm64Relocator *self, zpointer input_code, ZzArm64Writer *output);\n\nzsize zz_arm64_relocator_read_one(ZzArm64Relocator *self, ZzInstruction *instruction);\nzbool zz_arm64_relocator_write_one(ZzArm64Relocator *self);\nvoid zz_arm64_relocator_write_all(ZzArm64Relocator *self);\nvoid zz_arm64_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes);\n\n/* rewrite */\nstatic zbool zz_arm64_relocator_rewrite_ldr(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,\n                                            ZzRelocateInstruction *re_insn_ctx);\nstatic zbool zz_arm64_relocator_rewrite_adr(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,\n                                            ZzRelocateInstruction *re_insn_ctx);\nstatic zbool zz_arm64_relocator_rewrite_b(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,\n                                          ZzRelocateInstruction *re_insn_ctx);\nstatic zbool zz_arm64_relocator_rewrite_b_cond(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,\n                                               ZzRelocateInstruction *re_insn_ctx);\nstatic zbool zz_arm64_relocator_rewrite_bl(ZzArm64Relocator *self, const ZzInstruction *insn_ctx,\n                                           ZzRelocateInstruction *re_insn_ctx);\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/writer-arm64.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include <string.h>\n\n#include \"writer-arm64.h\"\n#include <stdlib.h>\n\n// REF:\n// ARM Architecture Reference Manual ARMV8\n// C2.1 Understanding the A64 instruction descriptions\n// C2.1.3 The instruction encoding or encodings\n\n// ATTENTION !!!:\n// 写 writer 部分, 需要参考, `Instrcution Set Encoding` 部分\n// `witer` REF: `ZzInstruction Set Encoding`\n\nZzArm64Writer *zz_arm64_writer_new(zpointer data_ptr) {\n    ZzArm64Writer *writer = (ZzArm64Writer *)malloc(sizeof(ZzArm64Writer));\n    memset(writer, 0, sizeof(ZzArm64Writer));\n\n    zaddr align_address = (zaddr)data_ptr & ~(zaddr)3;\n    writer->codedata = (zpointer)align_address;\n    writer->base = (zpointer)align_address;\n    writer->pc = align_address;\n    writer->size = 0;\n\n    writer->literal_insn_size = 0;\n    memset(writer->literal_insns, 0, sizeof(ZzLiteralInstruction) * MAX_LITERAL_INSN_SIZE);\n\n    return writer;\n}\n\nvoid zz_arm64_writer_init(ZzArm64Writer *self, zpointer target_addr) { zz_arm64_writer_reset(self, target_addr); }\n\nvoid zz_arm64_writer_reset(ZzArm64Writer *self, zpointer data_ptr) {\n    zaddr align_address = (zaddr)data_ptr & ~(zaddr)3;\n\n    self->codedata = (zpointer)align_address;\n    self->base = (zpointer)align_address;\n    self->pc = align_address;\n    self->size = 0;\n\n    self->literal_insn_size = 0;\n    memset(self->literal_insns, 0, sizeof(ZzLiteralInstruction) * MAX_LITERAL_INSN_SIZE);\n}\n\n// ======= relocator =======\n\nZzLiteralInstruction *zz_arm64_writer_put_ldr_br_reg_relocate_address(ZzWriter *self, ZzARM64Reg reg, zaddr address,\n                                                                      ZzLiteralInstruction **literal_insn_ptr) {\n\n    zz_arm64_writer_put_ldr_br_reg_address(self, reg, address);\n    ZzLiteralInstruction *literal_insn = &(self->literal_insns[self->literal_insn_size - 1]);\n    *literal_insn_ptr = literal_insn;\n    return literal_insn;\n}\n\n// ======= user custom =======\n\nvoid zz_arm64_writer_put_ldr_br_reg_address(ZzWriter *self, ZzARM64Reg reg, zaddr address) {\n    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;\n    zz_arm64_writer_put_ldr_reg_imm(self, reg, (zuint)0x8);\n    zz_arm64_writer_put_br_reg(self, reg);\n    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;\n    zz_arm64_writer_put_bytes(self, (zpointer)&address, sizeof(zpointer));\n}\n\nvoid zz_arm64_writer_put_ldr_blr_b_reg_address(ZzWriter *self, ZzARM64Reg reg, zaddr address) {\n    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;\n    zz_arm64_writer_put_ldr_reg_imm(self, reg, (zuint)0xc);\n    zz_arm64_writer_put_blr_reg(self, reg);\n    zz_arm64_writer_put_b_imm(self, 0xc);\n    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;\n    zz_arm64_writer_put_bytes(self, (zpointer)&address, sizeof(zpointer));\n}\n\nvoid zz_arm64_writer_put_ldr_b_reg_address(ZzWriter *self, ZzARM64Reg reg, zaddr address) {\n    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;\n    zz_arm64_writer_put_ldr_reg_imm(self, reg, (zuint)0x8);\n    zz_arm64_writer_put_b_imm(self, 0xc);\n    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;\n    zz_arm64_writer_put_bytes(self, (zpointer)&address, sizeof(address));\n}\n\nzsize zz_arm64_writer_near_jump_range_size() { return ((1 << 25) << 2); }\n\nvoid zz_arm64_writer_put_ldr_br_b_reg_address(ZzWriter *self, ZzARM64Reg reg, zaddr address) {\n    self->literal_insns[self->literal_insn_size].literal_insn_ptr = self->codedata;\n    zz_arm64_writer_put_ldr_reg_imm(self, reg, (zuint)0xc);\n    zz_arm64_writer_put_br_reg(self, reg);\n    zz_arm64_writer_put_b_imm(self, 0xc);\n    self->literal_insns[self->literal_insn_size++].literal_address_ptr = self->codedata;\n    zz_arm64_writer_put_bytes(self, (zpointer)&address, sizeof(address));\n}\n\n// ======= default =======\n\nvoid zz_arm64_writer_put_ldr_reg_imm(ZzWriter *self, ZzARM64Reg reg, zuint32 offset) {\n    ZzArm64RegInfo ri;\n    zz_arm64_register_describe(reg, &ri);\n\n    zuint32 imm19, Rt_ndx;\n\n    imm19 = offset >> 2;\n    Rt_ndx = ri.index;\n\n    zz_arm64_writer_put_instruction(self, 0x58000000 | imm19 << 5 | Rt_ndx);\n    return;\n}\n\n// PAGE: C6-871\nvoid zz_arm64_writer_put_str_reg_reg_offset(ZzWriter *self, ZzARM64Reg src_reg, ZzARM64Reg dst_reg, zuint64 offset) {\n    ZzArm64RegInfo rs, rd;\n\n    zz_arm64_register_describe(src_reg, &rs);\n    zz_arm64_register_describe(dst_reg, &rd);\n\n    zuint32 size, v = 0, opc = 0, Rn_ndx, Rt_ndx;\n    Rn_ndx = rd.index;\n    Rt_ndx = rs.index;\n\n    if (rs.is_integer) {\n        size = (rs.width == 64) ? 0b11 : 0b10;\n    }\n\n    zuint32 imm12 = offset >> size;\n\n    zz_arm64_writer_put_instruction(self, 0x39000000 | size << 30 | opc << 22 | imm12 << 10 | Rn_ndx << 5 | Rt_ndx);\n    return;\n}\n\nvoid zz_arm64_writer_put_ldr_reg_reg_offset(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg src_reg, zuint64 offset) {\n    ZzArm64RegInfo rs, rd;\n\n    zz_arm64_register_describe(src_reg, &rs);\n    zz_arm64_register_describe(dst_reg, &rd);\n\n    zuint32 size, v = 0, opc = 0b01, Rn_ndx, Rt_ndx;\n    Rn_ndx = rs.index;\n    Rt_ndx = rd.index;\n\n    if (rs.is_integer) {\n        size = (rs.width == 64) ? 0b11 : 0b10;\n    }\n\n    zuint32 imm12 = offset >> size;\n\n    zz_arm64_writer_put_instruction(self, 0x39000000 | size << 30 | opc << 22 | imm12 << 10 | Rn_ndx << 5 | Rt_ndx);\n    return;\n}\n\n// C6-562\nvoid zz_arm64_writer_put_br_reg(ZzWriter *self, ZzARM64Reg reg) {\n    ZzArm64RegInfo ri;\n    zz_arm64_register_describe(reg, &ri);\n\n    zuint32 op = 0, Rn_ndx;\n    Rn_ndx = ri.index;\n    zz_arm64_writer_put_instruction(self, 0xd61f0000 | op << 21 | Rn_ndx << 5);\n    return;\n}\n\n// C6-561\nvoid zz_arm64_writer_put_blr_reg(ZzWriter *self, ZzARM64Reg reg) {\n    ZzArm64RegInfo ri;\n    zz_arm64_register_describe(reg, &ri);\n\n    zuint32 op = 0b01, Rn_ndx;\n\n    Rn_ndx = ri.index;\n\n    zz_arm64_writer_put_instruction(self, 0xd63f0000 | op << 21 | Rn_ndx << 5);\n    return;\n}\n\n// C6-550\nvoid zz_arm64_writer_put_b_imm(ZzWriter *self, zuint64 offset) {\n    zuint32 op = 0b0, imm26;\n    imm26 = (offset >> 2) & 0x03ffffff;\n    zz_arm64_writer_put_instruction(self, 0x14000000 | op << 31 | imm26);\n    return;\n}\n\n// TODO: standard form, need fix others\n// PAGE: C6-549\nvoid zz_arm64_writer_put_b_cond_imm(ZzWriter *self, zuint32 condition, zuint64 imm) {\n    zuint32 imm19, cond;\n    cond = condition;\n    imm19 = (imm >> 2) & 0x7ffff;\n    zz_arm64_writer_put_instruction(self, 0x54000000 | imm19 << 5 | cond);\n    return;\n}\n\n// C6-525\nvoid zz_arm64_writer_put_add_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg left_reg, zuint64 imm) {\n    ZzArm64RegInfo rd, rl;\n\n    zz_arm64_register_describe(dst_reg, &rd);\n    zz_arm64_register_describe(left_reg, &rl);\n\n    zuint32 sf = 1, op = 0, S = 0, shift = 0b00, imm12, Rn_ndx, Rd_ndx;\n\n    Rd_ndx = rd.index;\n    Rn_ndx = rl.index;\n    imm12 = imm & 0xFFF;\n\n    zz_arm64_writer_put_instruction(self, 0x11000000 | sf << 31 | op << 30 | S << 29 | shift << 22 | imm12 << 10 |\n                                              Rn_ndx << 5 | Rd_ndx);\n    return;\n}\n\n// C6-930\nvoid zz_arm64_writer_put_sub_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg left_reg, zuint64 imm) {\n    ZzArm64RegInfo rd, rl;\n\n    zz_arm64_register_describe(dst_reg, &rd);\n    zz_arm64_register_describe(left_reg, &rl);\n\n    zuint32 sf = 1, op = 1, S = 0, shift = 0b00, imm12, Rn_ndx, Rd_ndx;\n\n    Rd_ndx = rd.index;\n    Rn_ndx = rl.index;\n    imm12 = imm & 0xFFF;\n\n    zz_arm64_writer_put_instruction(self, 0x11000000 | sf << 31 | op << 30 | S << 29 | shift << 22 | imm12 << 10 |\n                                              Rn_ndx << 5 | Rd_ndx);\n    return;\n}\n\nvoid zz_arm64_writer_put_bytes(ZzWriter *self, zbyte *data, zsize size) {\n    memcpy(self->codedata, data, size);\n    self->codedata = (zpointer)self->codedata + size;\n    self->pc += size;\n    self->size += size;\n    return;\n}\n\nvoid zz_arm64_writer_put_instruction(ZzWriter *self, zuint32 insn) {\n    *(zuint32 *)(self->codedata) = insn;\n    self->codedata = (zpointer)self->codedata + sizeof(zuint32);\n    self->pc += 4;\n    self->size += 4;\n    return;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-arm64/writer-arm64.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_arm64_writer_h\n#define platforms_arch_arm64_writer_h\n\n// platforms\n#include \"instructions.h\"\n#include \"regs-arm64.h\"\n#include \"writer-arm64.h\"\n\n// hookzz\n#include \"writer.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef ZzWriter ZzArm64Writer;\n\nZzArm64Writer *zz_arm64_writer_new(zpointer data_ptr);\n\nvoid zz_arm64_writer_reset(ZzArm64Writer *self, zpointer data_ptr);\n\nvoid zz_arm64_writer_init(ZzArm64Writer *self, zpointer target_addr);\n\nzsize zz_arm64_writer_near_jump_range_size();\n\n// ======= user custom =======\n\nvoid zz_arm64_writer_put_ldr_br_reg_address(ZzArm64Writer *self, ZzARM64Reg reg, zaddr address);\nvoid zz_arm64_writer_put_ldr_blr_b_reg_address(ZzArm64Writer *self, ZzARM64Reg reg, zaddr address);\nvoid zz_arm64_writer_put_ldr_b_reg_address(ZzArm64Writer *self, ZzARM64Reg reg, zaddr address);\nvoid zz_arm64_writer_put_ldr_br_b_reg_address(ZzArm64Writer *self, ZzARM64Reg reg, zaddr address);\n\n// ======= default =======\n\nvoid zz_arm64_writer_put_ldr_reg_imm(ZzWriter *self, ZzARM64Reg reg, zuint32 offset);\nvoid zz_arm64_writer_put_str_reg_reg_offset(ZzWriter *self, ZzARM64Reg src_reg, ZzARM64Reg dst_reg, zuint64 offset);\nvoid zz_arm64_writer_put_ldr_reg_reg_offset(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg src_reg, zuint64 offset);\nvoid zz_arm64_writer_put_br_reg(ZzWriter *self, ZzARM64Reg reg);\nvoid zz_arm64_writer_put_blr_reg(ZzWriter *self, ZzARM64Reg reg);\nvoid zz_arm64_writer_put_b_imm(ZzWriter *self, zuint64 offset);\nvoid zz_arm64_writer_put_b_cond_imm(ZzWriter *self, zuint32 condition, zuint64 imm);\nvoid zz_arm64_writer_put_add_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg left_reg, zuint64 imm);\nvoid zz_arm64_writer_put_sub_reg_reg_imm(ZzWriter *self, ZzARM64Reg dst_reg, ZzARM64Reg left_reg, zuint64 imm);\nvoid zz_arm64_writer_put_bytes(ZzWriter *self, zbyte *data, zsize size);\nvoid zz_arm64_writer_put_instruction(ZzWriter *self, zuint32 insn);\n\n// ======= relocator =======\n\nZzLiteralInstruction *zz_arm64_writer_put_ldr_br_reg_relocate_address(ZzWriter *self, ZzARM64Reg reg, zaddr address,\n                                                                      ZzLiteralInstruction **literal_insn_ptr);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/instructions.c",
    "content": "#include \"instructions.h\"\n#include <string.h>\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/instructions.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_x86_instructions_h\n#define platforms_arch_x86_instructions_h\n\n#include \"hookzz.h\"\n\ntypedef struct _ZzInstruction {\n} ZzInstruction;\n\ntypedef struct _ZzRelocateInstruction {\n    const ZzInstruction *insn_ctx;\n    zaddr relocated_offset;\n    zsize relocated_length;\n} ZzRelocateInstruction;\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/reader-x86.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"reader-x86.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\nzpointer zz_x86_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address) { return NULL; }\n\nX86InsnType GetX86InsnType(zuint32 insn) { return X86_UNDEF; }"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/reader-x86.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_x86_reader_h\n#define platforms_arch_x86_reader_h\n\n// platforms\n#include \"instructions.h\"\n\n// hookzz\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef enum _X86InsnType { X86_UNDEF } X86InsnType;\n\nX86InsnType GetX86InsnType(zuint32 insn);\n\nzpointer zz_x86_reader_read_one_instruction(ZzInstruction *insn_ctx, zpointer address);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/regs-x86.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"regs-x86.h\"\n\nvoid zz_x86_register_describe(ZzX86Reg reg, ZzX86RegInfo *ri) {\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/regs-x86.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_x86_regs_h\n#define platforms_arch_x86_regs_h\n\n// platforms\n#include \"instructions.h\"\n\n// hookzz\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef enum _ZzX86Reg { X86_REG_UNDEF } ZzX86Reg;\n\ntypedef struct _ZzX86RegInfo {\n\n} ZzX86RegInfo;\n\nvoid zz_x86_register_describe(ZzX86Reg reg, ZzX86RegInfo *ri);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/relocator-x86.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"relocator-x86.h\"\n#include <stdlib.h>\n#include <string.h>\n\n#define MAX_RELOCATOR_INSTRUCIONS_SIZE 64\n\nvoid zz_x86_relocator_init(ZzX86Relocator *relocator, zpointer input_code, ZzX86Writer *output) {\n}\n\nvoid zz_x86_relocator_reset(ZzX86Relocator *self, zpointer input_code, ZzX86Writer *output) {\n}\n\nzsize zz_x86_relocator_read_one(ZzX86Relocator *self, ZzInstruction *instruction) {\n    return 0;\n}\n\nzaddr zz_x86_relocator_get_insn_relocated_offset(ZzX86Relocator *self, zaddr address) {\n    return 0;\n}\n\nvoid zz_x86_relocator_relocate_writer(ZzX86Relocator *relocator, zaddr code_address) {\n}\n\nvoid zz_x86_relocator_write_all(ZzX86Relocator *self) {\n}\n\nvoid zz_x86_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes) {\n}\n\nzbool zz_x86_relocator_write_one(ZzX86Relocator *self) {\n    return TRUE;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/relocator-x86.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_x86_relocator_h\n#define platforms_arch_x86_relocator_h\n\n// platforms\n#include \"instructions.h\"\n#include \"reader-x86.h\"\n#include \"regs-x86.h\"\n#include \"writer-x86.h\"\n\n// hookzz\n#include \"writer.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef struct _ZzX86Relocator {\n    zbool try_relocated_again;\n    zsize try_relocated_length;\n    zpointer input_start;\n    zpointer input_cur;\n    zaddr input_pc;\n    zuint inpos;\n    zuint outpos;\n    ZzInstruction *input_insns;\n    ZzRelocateInstruction *output_insns;\n    ZzX86Writer *output;\n    ZzLiteralInstruction **relocate_literal_insns;\n    zsize relocate_literal_insns_size;\n} ZzX86Relocator;\n\nvoid zz_x86_relocator_init(ZzX86Relocator *relocator, zpointer input_code, ZzX86Writer *writer);\nvoid zz_x86_relocator_reset(ZzX86Relocator *self, zpointer input_code, ZzX86Writer *output);\n\nzsize zz_x86_relocator_read_one(ZzX86Relocator *self, ZzInstruction *instruction);\nzbool zz_x86_relocator_write_one(ZzX86Relocator *self);\nvoid zz_x86_relocator_write_all(ZzX86Relocator *self);\nvoid zz_x86_relocator_try_relocate(zpointer address, zuint min_bytes, zuint *max_bytes);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/writer-x86.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include <string.h>\n#include <stdlib.h>\n\n#include \"writer-x86.h\"\n\nZzX86Writer *zz_x86_writer_new(zpointer data_ptr) {\n    return NULL;\n}\n\nvoid zz_x86_writer_init(ZzX86Writer *self, zpointer target_addr) { zz_x86_writer_reset(self, target_addr); }\n\nvoid zz_x86_writer_reset(ZzX86Writer *self, zpointer data_ptr) {\n}\n\nzsize zz_x86_writer_near_jump_range_size() { return 0; }\n\n\nvoid zz_x86_writer_put_bytes(ZzWriter *self, zbyte *data, zsize size) {\n\n}\n\nvoid zz_x86_writer_put_instruction(ZzWriter *self, zuint32 insn) {\n\n}\n\n\n// ======= relocator =======\n\n// ======= user custom =======\n\n// ======= default =======\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/arch-x86/writer-x86.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_arch_x86_writer_h\n#define platforms_arch_x86_writer_h\n\n// platforms\n#include \"instructions.h\"\n#include \"regs-x86.h\"\n#include \"writer-x86.h\"\n\n// hookzz\n#include \"writer.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef ZzWriter ZzX86Writer;\n\nZzX86Writer *zz_x86_writer_new(zpointer data_ptr);\n\nvoid zz_x86_writer_reset(ZzX86Writer *self, zpointer data_ptr);\n\nvoid zz_x86_writer_init(ZzX86Writer *self, zpointer target_addr);\n\nzsize zz_x86_writer_near_jump_range_size();\nvoid zz_x86_writer_put_bytes(ZzWriter *self, zbyte *data, zsize size);\nvoid zz_x86_writer_put_instruction(ZzWriter *self, zuint32 insn);\n\n// ======= user custom =======\n\n// ======= default =======\n\n// ======= relocator =======\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/interceptor-arm.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"interceptor-arm.h\"\n#include \"zzinfo.h\"\n\n#include <stdlib.h>\n\n#define ZZ_THUMB_TINY_REDIRECT_SIZE 4\n#define ZZ_THUMB_FULL_REDIRECT_SIZE 8\n#define ZZ_ARM_TINY_REDIRECT_SIZE 4\n#define ZZ_ARM_FULL_REDIRECT_SIZE 8\n\nZzInterceptorBackend *ZzBuildInteceptorBackend(ZzAllocator *allocator) {\n    if (!ZzMemoryIsSupportAllocateRXPage()) {\n        return NULL;\n    }\n    ZZSTATUS status;\n    ZzInterceptorBackend *backend = (ZzInterceptorBackend *)malloc(sizeof(ZzInterceptorBackend));\n    memset(backend, 0, sizeof(ZzInterceptorBackend));\n\n    zz_arm_writer_init(&backend->arm_writer, NULL);\n    zz_arm_relocator_init(&backend->arm_relocator, NULL, &backend->arm_writer);\n    zz_thumb_writer_init(&backend->thumb_writer, NULL);\n    zz_thumb_relocator_init(&backend->thumb_relocator, NULL, &backend->thumb_writer);\n\n    backend->allocator = allocator;\n    backend->enter_thunk = NULL;\n    backend->half_thunk = NULL;\n    backend->leave_thunk = NULL;\n\n    status = ZzThunkerBuildThunk(backend);\n\n    if (status == ZZ_FAILED) {\n        ZzInfoLog(\"%s\", \"ZzThunkerBuildThunk return ZZ_FAILED\\n\");\n        return NULL;\n    }\n\n    return backend;\n}\n\nZzCodeSlice *zz_code_patch_thumb_writer(ZzThumbWriter *thumb_writer, ZzAllocator *allocator, zaddr target_addr,\n                                        zsize range_size) {\n    ZzCodeSlice *code_slice = NULL;\n    if (range_size > 0) {\n        code_slice = ZzNewNearCodeSlice(allocator, target_addr, range_size, thumb_writer->size);\n    } else {\n        code_slice = ZzNewCodeSlice(allocator, thumb_writer->size + 4);\n    }\n    if (!code_slice)\n        return NULL;\n\n    if (!ZzMemoryPatchCode((zaddr)code_slice->data, thumb_writer->base, thumb_writer->size)) {\n\n        free(code_slice);\n        return NULL;\n    }\n    return code_slice;\n}\n\nZzCodeSlice *zz_code_patch_thumb_relocate_writer(ZzThumbRelocator *thumb_relocator, ZzThumbWriter *thumb_writer,\n                                                 ZzAllocator *allocator, zaddr target_addr, zsize range_size) {\n    ZzCodeSlice *code_slice = NULL;\n    if (range_size > 0) {\n        code_slice = ZzNewNearCodeSlice(allocator, target_addr, range_size, thumb_writer->size);\n    } else {\n        code_slice = ZzNewCodeSlice(allocator, thumb_writer->size + 4);\n    }\n    if (!code_slice)\n        return NULL;\n\n    zz_thumb_relocator_relocate_writer(thumb_relocator, (zaddr)code_slice->data);\n\n    if (!ZzMemoryPatchCode((zaddr)code_slice->data, thumb_writer->base, thumb_writer->size)) {\n\n        free(code_slice);\n        return NULL;\n    }\n    return code_slice;\n}\n\nZzCodeSlice *zz_code_patch_arm_writer(ZzArmWriter *arm_writer, ZzAllocator *allocator, zaddr target_addr,\n                                      zsize range_size) {\n    ZzCodeSlice *code_slice = NULL;\n    if (range_size > 0) {\n        code_slice = ZzNewNearCodeSlice(allocator, target_addr, range_size, arm_writer->size);\n    } else {\n        code_slice = ZzNewCodeSlice(allocator, arm_writer->size + 4);\n    }\n    if (!code_slice)\n        return NULL;\n\n    if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm_writer->base, arm_writer->size)) {\n        free(code_slice);\n        return NULL;\n    }\n    return code_slice;\n}\n\nZzCodeSlice *zz_code_patch_arm_relocate_writer(ZzArmRelocator *arm_relocator, ZzArmWriter *arm_writer,\n                                               ZzAllocator *allocator, zaddr target_addr, zsize range_size) {\n    ZzCodeSlice *code_slice = NULL;\n    if (range_size > 0) {\n        code_slice = ZzNewNearCodeSlice(allocator, target_addr, range_size, arm_writer->size);\n    } else {\n        code_slice = ZzNewCodeSlice(allocator, arm_writer->size + 4);\n    }\n    if (!code_slice)\n        return NULL;\n\n    zz_arm_relocator_relocate_writer(arm_relocator, (zaddr)code_slice->data);\n\n    if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm_writer->base, arm_writer->size)) {\n        free(code_slice);\n        return NULL;\n    }\n    return code_slice;\n}\n\nZZSTATUS ZzPrepareTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zbool is_thumb = FALSE;\n    zaddr target_addr = (zaddr)entry->target_ptr;\n    zuint redirect_limit = 0;\n\n    ZzArmHookFunctionEntryBackend *entry_backend;\n    entry_backend = (ZzArmHookFunctionEntryBackend *)malloc(sizeof(ZzArmHookFunctionEntryBackend));\n    memset(entry_backend, 0, sizeof(ZzArmHookFunctionEntryBackend));\n\n    entry->backend = (struct _ZzHookFunctionEntryBackend *)entry_backend;\n\n    is_thumb = INSTRUCTION_IS_THUMB((zaddr)entry->target_ptr);\n    if (is_thumb)\n        target_addr = (zaddr)entry->target_ptr & ~(zaddr)1;\n\n    if (is_thumb) {\n        if (entry->try_near_jump) {\n            entry_backend->redirect_code_size = ZZ_THUMB_TINY_REDIRECT_SIZE;\n        } else {\n            zz_thumb_relocator_try_relocate((zpointer)target_addr, ZZ_THUMB_FULL_REDIRECT_SIZE, &redirect_limit);\n            if (redirect_limit != 0 && redirect_limit > ZZ_THUMB_TINY_REDIRECT_SIZE &&\n                redirect_limit < ZZ_THUMB_FULL_REDIRECT_SIZE) {\n                entry->try_near_jump = TRUE;\n                entry_backend->redirect_code_size = ZZ_THUMB_TINY_REDIRECT_SIZE;\n            } else if (redirect_limit != 0 && redirect_limit < ZZ_THUMB_TINY_REDIRECT_SIZE) {\n                return ZZ_FAILED;\n            } else {\n                entry_backend->redirect_code_size = ZZ_THUMB_FULL_REDIRECT_SIZE;\n                if (target_addr % 4) {\n                    entry_backend->redirect_code_size += 2;\n                }\n            }\n        }\n        self->thumb_relocator.try_relocated_length = entry_backend->redirect_code_size;\n    } else {\n        if (entry->try_near_jump) {\n            entry_backend->redirect_code_size = ZZ_ARM_TINY_REDIRECT_SIZE;\n        } else {\n            zz_arm_relocator_try_relocate((zpointer)target_addr, ZZ_ARM_FULL_REDIRECT_SIZE, &redirect_limit);\n            if (redirect_limit != 0 && redirect_limit > ZZ_ARM_TINY_REDIRECT_SIZE &&\n                redirect_limit < ZZ_ARM_FULL_REDIRECT_SIZE) {\n                entry->try_near_jump = TRUE;\n                entry_backend->redirect_code_size = ZZ_ARM_TINY_REDIRECT_SIZE;\n            } else if (redirect_limit != 0 && redirect_limit < ZZ_ARM_TINY_REDIRECT_SIZE) {\n                return ZZ_FAILED;\n            } else {\n                entry_backend->redirect_code_size = ZZ_ARM_FULL_REDIRECT_SIZE;\n            }\n        }\n        self->arm_relocator.try_relocated_length = entry_backend->redirect_code_size;\n    }\n\n    zz_arm_relocator_init(&self->arm_relocator, (zpointer)target_addr, &self->arm_writer);\n    zz_thumb_relocator_init(&self->thumb_relocator, (zpointer)target_addr, &self->thumb_writer);\n    return ZZ_SUCCESS;\n}\n\nZZSTATUS ZzBuildEnterTransferTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zbyte temp_code_slice_data[256] = {0};\n    ZzArmWriter *arm_writer = NULL;\n    ZzArmWriter *thumb_writer = NULL;\n    ZzCodeSlice *code_slice = NULL;\n    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;\n    ZZSTATUS status = ZZ_SUCCESS;\n    zbool is_thumb = TRUE;\n    zaddr target_addr = (zaddr)entry->target_ptr;\n\n    is_thumb = INSTRUCTION_IS_THUMB((zaddr)entry->target_ptr);\n    if (is_thumb)\n        target_addr = (zaddr)entry->target_ptr & ~(zaddr)1;\n\n    if (is_thumb) {\n        thumb_writer = &self->thumb_writer;\n        zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);\n\n        /* jump to on_enter_trampoline */\n        zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_enter_trampoline);\n\n        /* code patch */\n        code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, target_addr,\n                                                zz_thumb_writer_near_jump_range_size());\n        if (code_slice)\n            entry->on_enter_transfer_trampoline = code_slice->data;\n        else\n            return ZZ_FAILED;\n    } else {\n        arm_writer = &self->arm_writer;\n        zz_arm_writer_reset(arm_writer, temp_code_slice_data);\n\n        /* jump to on_enter_trampoline */\n        zz_arm_writer_put_ldr_reg_address(arm_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_enter_trampoline);\n\n        /* code patch */\n        code_slice =\n            zz_code_patch_arm_writer(arm_writer, self->allocator, target_addr, zz_arm_writer_near_jump_range_size());\n        if (code_slice)\n            entry->on_enter_transfer_trampoline = code_slice->data;\n        else\n            return ZZ_FAILED;\n    }\n\n    if (ZzIsEnableDebugMode()) {\n        char buffer[1024] = {};\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzBuildEnterTransferTrampoline:\");\n        sprintf(buffer + strlen(buffer),\n                \"LogInfo: on_enter_transfer_trampoline at %p, length: %ld. and will jump to \"\n                \"on_enter_trampoline(%p).\\n\",\n                code_slice->data, code_slice->size, entry->on_enter_trampoline);\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    free(code_slice);\n    return status;\n}\n\nZZSTATUS ZzBuildEnterTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zbyte temp_code_slice_data[256] = {0};\n    ZzArmWriter *arm_writer = NULL;\n    ZzArmWriter *thumb_writer = NULL;\n    ZzCodeSlice *code_slice = NULL;\n    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;\n    ZZSTATUS status = ZZ_SUCCESS;\n    zbool is_thumb;\n\n    is_thumb = INSTRUCTION_IS_THUMB((zaddr)entry->target_ptr);\n\n    thumb_writer = &self->thumb_writer;\n    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);\n\n    /* prepare 2 stack space: 1. next_hop 2. entry arg */\n    zz_thumb_writer_put_sub_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0xc);\n    zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0); // push r7\n    zz_thumb_writer_put_ldr_b_reg_address(thumb_writer, ZZ_ARM_REG_R1, (zaddr)entry);\n    zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);\n    zz_thumb_writer_put_ldr_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0); // pop r7\n    zz_thumb_writer_put_add_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0x4);\n\n    /* jump to enter thunk */\n    zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)self->enter_thunk);\n\n    /* code patch */\n    code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, 0, 0);\n    if (code_slice)\n        entry->on_enter_trampoline = code_slice->data + 1;\n    else\n        return ZZ_FAILED;\n\n    /* debug log */\n    if (ZzIsEnableDebugMode()) {\n        char buffer[1024] = {};\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzBuildEnterTrampoline:\");\n        sprintf(buffer + strlen(buffer),\n                \"LogInfo: on_enter_trampoline at %p, length: %ld. hook-entry: %p. and will jump to \"\n                \"enter_thunk(%p)\\n\",\n                code_slice->data, code_slice->size, (void *)entry, (void *)self->enter_thunk);\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    if ((is_thumb && entry_backend->redirect_code_size == ZZ_THUMB_TINY_REDIRECT_SIZE) ||\n        (!is_thumb && entry_backend->redirect_code_size == ZZ_ARM_TINY_REDIRECT_SIZE)) {\n        ZzBuildEnterTransferTrampoline(self, entry);\n    }\n\n    free(code_slice);\n    return status;\n}\n\nZZSTATUS ZzBuildInvokeTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zbyte temp_code_slice_data[256] = {0};\n    ZzCodeSlice *code_slice = NULL;\n    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;\n    ZZSTATUS status = ZZ_SUCCESS;\n    zbool is_thumb = TRUE;\n    zaddr target_addr = (zaddr)entry->target_ptr;\n    zpointer target_end_addr = 0;\n    zpointer restore_target_addr;\n\n    is_thumb = INSTRUCTION_IS_THUMB((zaddr)entry->target_ptr);\n    if (is_thumb)\n        target_addr = (zaddr)entry->target_ptr & ~(zaddr)1;\n\n    if (is_thumb) {\n        target_end_addr = (zpointer)((zaddr)entry->target_end_ptr & ~(zaddr)1);\n    }\n\n    if (is_thumb) {\n        ZzThumbRelocator *thumb_relocator;\n        ZzThumbWriter *thumb_writer;\n        thumb_relocator = &self->thumb_relocator;\n        thumb_writer = &self->thumb_writer;\n\n        zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);\n        zz_thumb_relocator_reset(thumb_relocator, (zpointer)target_addr, thumb_writer);\n        zsize tmp_relocator_insn_size = 0;\n        entry->target_half_ret_addr = 0;\n\n        if (entry->hook_type == HOOK_FUNCTION_TYPE) {\n            do {\n                zz_thumb_relocator_read_one(thumb_relocator, NULL);\n                tmp_relocator_insn_size = thumb_relocator->input_cur - thumb_relocator->input_start;\n            } while (tmp_relocator_insn_size < entry_backend->redirect_code_size);\n            zz_thumb_relocator_write_all(thumb_relocator);\n        } else if (entry->hook_type == HOOK_ADDRESS_TYPE) {\n            do {\n                zz_thumb_relocator_read_one(thumb_relocator, NULL);\n                zz_thumb_relocator_write_one(thumb_relocator);\n                tmp_relocator_insn_size = thumb_relocator->input_cur - thumb_relocator->input_start;\n                if (thumb_relocator->input_cur >= target_end_addr && !entry->target_half_ret_addr) {\n                    /* jump to rest target address */\n                    zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_half_trampoline);\n                    entry->target_half_ret_addr = (zpointer)(thumb_writer->size + 1);\n                }\n            } while (tmp_relocator_insn_size < entry_backend->redirect_code_size ||\n                     thumb_relocator->input_cur < target_end_addr);\n        }\n\n        /* jump to rest target address */\n        restore_target_addr = (zpointer)((zaddr)target_addr + tmp_relocator_insn_size);\n        zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)(restore_target_addr + 1));\n\n        /* code patch */\n        code_slice = zz_code_patch_thumb_relocate_writer(thumb_relocator, thumb_writer, self->allocator, 0, 0);\n        if (code_slice)\n            entry->on_invoke_trampoline = code_slice->data + 1;\n        else\n            return ZZ_FAILED;\n    } else {\n        ZzArmRelocator *arm_relocator;\n        ZzArmWriter *arm_writer;\n        arm_relocator = &self->arm_relocator;\n        arm_writer = &self->arm_writer;\n\n        zz_arm_writer_reset(arm_writer, temp_code_slice_data);\n        zz_arm_relocator_reset(arm_relocator, (zpointer)target_addr, arm_writer);\n        entry->target_half_ret_addr = 0;\n        zsize tmp_relocator_insn_size = 0;\n\n        if (entry->hook_type == HOOK_FUNCTION_TYPE) {\n            do {\n                zz_arm_relocator_read_one(arm_relocator, NULL);\n                tmp_relocator_insn_size = arm_relocator->input_cur - arm_relocator->input_start;\n            } while (tmp_relocator_insn_size < entry_backend->redirect_code_size);\n            zz_arm_relocator_write_all(arm_relocator);\n        } else if (entry->hook_type == HOOK_ADDRESS_TYPE) {\n            do {\n                zz_arm_relocator_read_one(arm_relocator, NULL);\n                zz_arm_relocator_write_one(arm_relocator);\n                tmp_relocator_insn_size = arm_relocator->input_cur - arm_relocator->input_start;\n                if (arm_relocator->input_cur >= target_end_addr && !entry->target_half_ret_addr) {\n                    /* jump to rest target address */\n                    zz_arm_writer_put_ldr_reg_address(arm_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_half_trampoline);\n                    entry->target_half_ret_addr = (zpointer)arm_writer->size;\n                }\n            } while (tmp_relocator_insn_size < entry_backend->redirect_code_size ||\n                     arm_relocator->input_cur < target_end_addr);\n        }\n\n        /* jump to rest target address */\n        restore_target_addr = (zpointer)((zaddr)target_addr + tmp_relocator_insn_size);\n        zz_arm_writer_put_ldr_reg_address(arm_writer, ZZ_ARM_REG_PC, (zaddr)restore_target_addr);\n\n        /* code patch */\n        code_slice = zz_code_patch_arm_relocate_writer(arm_relocator, arm_writer, self->allocator, 0, 0);\n        if (code_slice)\n            entry->on_invoke_trampoline = code_slice->data;\n        else\n            return ZZ_FAILED;\n    }\n\n    /* update target_half_ret_addr */\n    if (entry->hook_type == HOOK_ADDRESS_TYPE) {\n        entry->target_half_ret_addr += (zaddr)code_slice->data;\n    }\n\n    /* debug log */\n    if (ZzIsEnableDebugMode()) {\n        char buffer[1024] = {};\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzBuildInvokeTrampoline:\");\n        sprintf(buffer + strlen(buffer),\n                \"LogInfo: on_invoke_trampoline at %p, length: %ld. and will jump to rest code(%p).\\n\", code_slice->data,\n                code_slice->size, restore_target_addr);\n        if (is_thumb) {\n            sprintf(buffer + strlen(buffer),\n                    \"ThumbInstructionFix: origin instruction at %p, end at %p, relocator instruction nums %ld\\n\",\n                    (&self->thumb_relocator)->input_start, (&self->thumb_relocator)->input_cur,\n                    (&self->thumb_relocator)->inpos);\n        } else {\n            sprintf(buffer + strlen(buffer),\n                    \"ArmInstructionFix: origin instruction at %p, end at %p, relocator instruction nums %ld\\n\",\n                    (&self->arm_relocator)->input_start, (&self->arm_relocator)->input_cur,\n                    (&self->arm_relocator)->inpos);\n        }\n\n        char origin_prologue[256] = {0};\n        int t = 0;\n        zpointer p;\n        if (is_thumb) {\n            for (p = (&self->thumb_relocator)->input_start; p < (&self->thumb_relocator)->input_cur; p++, t = t + 5) {\n                sprintf(origin_prologue + t, \"0x%.2x \", *(unsigned char *)p);\n            }\n        } else {\n            for (p = (&self->arm_relocator)->input_start; p < (&self->arm_relocator)->input_cur; p++, t = t + 5) {\n                sprintf(origin_prologue + t, \"0x%.2x \", *(unsigned char *)p);\n            }\n        }\n        sprintf(buffer + strlen(buffer), \"origin_prologue: %s\\n\", origin_prologue);\n\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    free(code_slice);\n    return status;\n}\n\nZZSTATUS ZzBuildHalfTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zbyte temp_code_slice_data[256] = {0};\n    ZzArmWriter *arm_writer = NULL;\n    ZzArmWriter *thumb_writer = NULL;\n    ZzCodeSlice *code_slice = NULL;\n    ZZSTATUS status = ZZ_SUCCESS;\n\n    thumb_writer = &self->thumb_writer;\n    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);\n\n    /* prepare 2 stack space: 1. next_hop 2. entry arg */\n    zz_thumb_writer_put_sub_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0xc);\n    zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0); // push r7\n    zz_thumb_writer_put_ldr_b_reg_address(thumb_writer, ZZ_ARM_REG_R1, (zaddr)entry);\n    zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);\n    zz_thumb_writer_put_ldr_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0); // pop r7\n    zz_thumb_writer_put_add_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0x4);\n\n    /* jump to half_thunk */\n    zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)self->half_thunk);\n\n    /* code patch */\n    code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, 0, 0);\n    if (code_slice)\n        entry->on_half_trampoline = code_slice->data + 1;\n    else\n        return ZZ_FAILED;\n\n    free(code_slice);\n    return status;\n}\n\nZZSTATUS ZzBuildLeaveTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zbyte temp_code_slice_data[256] = {0};\n    ZzCodeSlice *code_slice = NULL;\n    ZZSTATUS status = ZZ_SUCCESS;\n    zbool is_thumb = TRUE;\n    ZzArmWriter *thumb_writer;\n\n    thumb_writer = &self->thumb_writer;\n    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);\n\n    /* prepare 2 stack space: 1. next_hop 2. entry arg */\n    zz_thumb_writer_put_sub_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0xc);\n    zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0); // push r7\n    zz_thumb_writer_put_ldr_b_reg_address(thumb_writer, ZZ_ARM_REG_R1, (zaddr)entry);\n    zz_thumb_writer_put_str_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);\n    zz_thumb_writer_put_ldr_reg_reg_offset(thumb_writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x0); // pop r7\n    zz_thumb_writer_put_add_reg_imm(thumb_writer, ZZ_ARM_REG_SP, 0x4);\n\n    /* jump to leave_thunk */\n    zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)self->leave_thunk);\n\n    /* code patch */\n    code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, 0, 0);\n    if (code_slice)\n        entry->on_leave_trampoline = code_slice->data + 1;\n    else\n        return ZZ_FAILED;\n\n    /* debug log */\n    if (ZzIsEnableDebugMode()) {\n        char buffer[1024] = {};\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzBuildLeaveTrampoline:\");\n        sprintf(buffer + strlen(buffer),\n                \"LogInfo: on_leave_trampoline at %p, length: %ld. and will jump to leave_thunk(%p).\\n\",\n                code_slice->data, code_slice->size, self->leave_thunk);\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    free(code_slice);\n    return ZZ_DONE;\n}\n\nZZSTATUS ZzActivateTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zbyte temp_code_slice_data[256] = {0};\n    ZzCodeSlice *code_slice = NULL;\n    ZzArmHookFunctionEntryBackend *entry_backend = (ZzArmHookFunctionEntryBackend *)entry->backend;\n    ZZSTATUS status = ZZ_SUCCESS;\n    zbool is_thumb = TRUE;\n    zaddr target_addr = (zaddr)entry->target_ptr;\n\n    is_thumb = INSTRUCTION_IS_THUMB((zaddr)entry->target_ptr);\n    if (is_thumb)\n        target_addr = (zaddr)entry->target_ptr & ~(zaddr)1;\n\n    if (is_thumb) {\n        ZzThumbWriter *thumb_writer;\n        thumb_writer = &self->thumb_writer;\n        zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);\n        thumb_writer->pc = target_addr + 4;\n\n        if (entry_backend->redirect_code_size == ZZ_THUMB_TINY_REDIRECT_SIZE) {\n            zz_thumb_writer_put_b_imm32(thumb_writer,\n                                        (zaddr)entry->on_enter_transfer_trampoline - (zaddr)thumb_writer->pc);\n        } else {\n            zz_thumb_writer_put_ldr_reg_address(thumb_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_enter_trampoline);\n        }\n        if (!ZzMemoryPatchCode((zaddr)target_addr, thumb_writer->base, thumb_writer->size))\n            return ZZ_FAILED;\n    } else {\n        ZzArmWriter *arm_writer;\n        arm_writer = &self->arm_writer;\n        zz_arm_writer_reset(arm_writer, temp_code_slice_data);\n        arm_writer->pc = target_addr + 8;\n\n        if (entry_backend->redirect_code_size == ZZ_ARM_TINY_REDIRECT_SIZE) {\n            zz_arm_writer_put_b_imm(arm_writer, (zaddr)entry->on_enter_transfer_trampoline - (zaddr)arm_writer->pc);\n        } else {\n            zz_arm_writer_put_ldr_reg_address(arm_writer, ZZ_ARM_REG_PC, (zaddr)entry->on_enter_trampoline);\n        }\n        if (!ZzMemoryPatchCode((zaddr)target_addr, arm_writer->base, arm_writer->size))\n            return ZZ_FAILED;\n    }\n\n    return ZZ_DONE_HOOK;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/interceptor-arm.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_backend_arm_intercetor_arm\n#define platforms_backend_arm_intercetor_arm\n\n// platforms\n#include \"platforms/arch-arm/relocator-arm.h\"\n#include \"platforms/arch-arm/relocator-thumb.h\"\n#include \"platforms/arch-arm/writer-arm.h\"\n#include \"platforms/arch-arm/writer-thumb.h\"\n\n// hookzz\n#include \"allocator.h\"\n#include \"interceptor.h\"\n#include \"thunker.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\n// (next_hop + general_regs + sp)\n#define CTX_SAVE_STACK_OFFSET (4 * 14)\n\ntypedef struct _ZzInterceptorBackend {\n    ZzAllocator *allocator;\n    ZzArmRelocator arm_relocator;\n    ZzThumbRelocator thumb_relocator;\n\n    ZzArmWriter arm_writer;\n    ZzThumbWriter thumb_writer;\n\n    zpointer enter_thunk;\n    zpointer half_thunk;\n    zpointer leave_thunk;\n} ZzInterceptorBackend;\n\ntypedef struct _ZzArmHookFuntionEntryBackend {\n    zbool is_thumb;\n    zuint redirect_code_size;\n} ZzArmHookFunctionEntryBackend;\n\nZzCodeSlice *zz_code_patch_thumb_writer(ZzThumbWriter *thumb_writer, ZzAllocator *allocator, zaddr target_addr,\n                                        zsize range_size);\nZzCodeSlice *zz_code_patch_arm_writer(ZzArmWriter *arm_writer, ZzAllocator *allocator, zaddr target_addr,\n                                      zsize range_size);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/interceptor-template-arm.s",
    "content": "// .section\t__TEXT,__text,regular,pure_instructions\n// .ios_version_min 11, 0\n.align 4\n.globl _ctx_save\n.globl _ctx_restore\n.globl _enter_thunk_template\n.globl _leave_thunk_template\n.globl _on_enter_trampoline_template\n.globl _on_invoke_trampoline_template\n.globl _on_leave_trampoline_template\n\n\n\n_on_enter_trampoline_template:\n\tsub sp, #0xc\n\tstr r1, [sp, #0x0]\n\tldr r1, [pc, #0x0]\n\tb #0x2\n\t.long 0x0\n\t.long 0x0\n\tstr r1, [sp, #0x4]\n\tldr r1, [sp, #0x0]\n\tadd sp, #0x4\n\tldr.w pc, [pc, #0x2]\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/thunker-arm.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"thunker-arm.h\"\n#include \"zzinfo.h\"\n\n// 前提: arm 可以直接访问 pc 寄存器, 也就是说无需中间寄存器就可以实现 `abs\n// jump`.\n\n// frida-gum 的做法, 手动恢复 lr, 并将 `next_hop` 写在之前 store `lr` 的位置,\n// 之后利用恢复寄存器跳转\n\n// 还有一个点, 就是 hook-entry 的参数传递, 这就是为啥 frida-gum 中为啥把\n// gum_emit_prolog 分成了两部分, 把 gum_emit_push_cpu_context_high_part 放在 x7\n// 用之前.\n\n// 这个和 arm64 有不同, arm64 中借助了一个中间寄存器 x16 or x17.\n\n// 重点是在: 1. 在 restore 之前完成所有工作 2. 利用 restore 过程修改 pc.\n\n// 当然也有其他做法, 比如最后一个操作是 ldr pc, [sp, #?], 也没啥问题.\n\n// 14 = 5 + 8 + 1\n\n// 按理说应该最先进入 ctx_save, 之后才能保证即使各种操作寄存器不被污染,\n// 几个理想方案 1. 把 ctx_save 归属为 trampoline, 优点: 优先进行寄存器状态保存,\n// 缺点: ctx_save 重复多次 2. 把 ctx_save 归属为 thunk, 统一做寄存器保存,\n// trampline 入口处进行参数保存至栈. 优点: ctx_save 可以作为公用. 缺点:\n// 操作复杂, 耦合略强. 3. 把 ctx_save 进行拆分, 缺点: 模块化设计差, 耦合强\n// (frida-gum采用)\n\n__attribute__((__naked__)) void ctx_save() {\n    __asm__ volatile(\".arm\\n\"\n                     \"sub sp, sp, #(14*4)\\n\"\n\n                     \"str lr, [sp, #(13*4)]\\n\"\n\n                     \"str r12, [sp, #(12*4)]\\n\"\n                     \"str r11, [sp, #(11*4)]\\n\"\n                     \"str r10, [sp, #(10*4)]\\n\"\n                     \"str r9, [sp, #(9*4)]\\n\"\n                     \"str r8, [sp, #(8*4)]\\n\"\n\n                     \"str r7, [sp, #(7*4)]\\n\"\n                     \"str r6, [sp, #(6*4)]\\n\"\n                     \"str r5, [sp, #(5*4)]\\n\"\n                     \"str r4, [sp, #(4*4)]\\n\"\n                     \"str r3, [sp, #(3*4)]\\n\"\n                     \"str r2, [sp, #(2*4)]\\n\"\n                     \"str r1, [sp, #(1*4)]\\n\"\n                     \"str r0, [sp, #(0*4)]\\n\");\n}\n\n__attribute__((__naked__)) void ctx_restore() {\n    __asm__ volatile(\".arm\\n\"\n                     \"ldr r0, [sp], #4\\n\"\n                     \"ldr r1, [sp], #4\\n\"\n                     \"ldr r2, [sp], #4\\n\"\n                     \"ldr r3, [sp], #4\\n\"\n                     \"ldr r4, [sp], #4\\n\"\n                     \"ldr r5, [sp], #4\\n\"\n                     \"ldr r6, [sp], #4\\n\"\n                     \"ldr r7, [sp], #4\\n\"\n\n                     \"ldr r8, [sp], #4\\n\"\n                     \"ldr r9, [sp], #4\\n\"\n                     \"ldr r10, [sp], #4\\n\"\n                     \"ldr r11, [sp], #4\\n\"\n                     \"ldr r12, [sp], #4\\n\"\n\n                     \"ldr lr, [sp], #4\\n\");\n}\n\n// just like pre_call, wow!\nvoid function_context_begin_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs,\n                                       zpointer caller_ret_addr) {\n\n    Xdebug(\"target %p call begin-invocation\", entry->target_ptr);\n\n    ZzThreadStack *threadstack = ZzGetCurrentThreadStack(entry->thread_local_key);\n    if (!threadstack) {\n        threadstack = ZzNewThreadStack(entry->thread_local_key);\n    }\n    ZzCallStack *callstack = ZzNewCallStack();\n    ZzPushCallStack(threadstack, callstack);\n\n    /* call pre_call */\n    if (entry->pre_call) {\n        PRECALL pre_call;\n        pre_call = entry->pre_call;\n        (*pre_call)(rs, (ThreadStack *)threadstack, (CallStack *)callstack);\n    }\n\n    /* set next hop */\n    if (entry->replace_call) {\n        *(zpointer *)next_hop = entry->replace_call;\n    } else {\n        *(zpointer *)next_hop = entry->on_invoke_trampoline;\n    }\n\n    if (entry->hook_type == HOOK_FUNCTION_TYPE) {\n        callstack->caller_ret_addr = *(zpointer *)caller_ret_addr;\n        *(zpointer *)caller_ret_addr = entry->on_leave_trampoline;\n    }\n}\n\nvoid function_context_half_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs,\n                                      zpointer caller_ret_addr) {\n    Xdebug(\"target %p call half-invocation\", entry->target_ptr);\n\n    ZzThreadStack *threadstack = ZzGetCurrentThreadStack(entry->thread_local_key);\n    if (!threadstack) {\n#if defined(DEBUG_MODE)\n        debug_break();\n#endif\n    }\n    ZzCallStack *callstack = ZzPopCallStack(threadstack);\n\n    /* call half_call */\n    if (entry->half_call) {\n        HALFCALL half_call;\n        half_call = entry->half_call;\n        (*half_call)(rs, (ThreadStack *)threadstack, (CallStack *)callstack);\n    }\n\n    /* set next hop */\n    *(zpointer *)next_hop = (zpointer)entry->target_half_ret_addr;\n\n    ZzFreeCallStack(callstack);\n}\n\n// just like post_call, wow!\nvoid function_context_end_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs) {\n    Xdebug(\"%p call end-invocation\", entry->target_ptr);\n\n    ZzThreadStack *threadstack = ZzGetCurrentThreadStack(entry->thread_local_key);\n    if (!threadstack) {\n#if defined(DEBUG_MODE)\n        debug_break();\n#endif\n    }\n    ZzCallStack *callstack = ZzPopCallStack(threadstack);\n\n    /* call post_call */\n    if (entry->post_call) {\n        POSTCALL post_call;\n        post_call = entry->post_call;\n        (*post_call)(rs, (ThreadStack *)threadstack, (CallStack *)callstack);\n    }\n\n    /* set next hop */\n    *(zpointer *)next_hop = callstack->caller_ret_addr;\n\n    ZzFreeCallStack(callstack);\n}\n\nvoid zz_thumb_thunker_build_enter_thunk(ZzWriter *writer) {\n\n    /* save general registers and sp */\n    zz_thumb_writer_put_bx_reg(writer, ZZ_ARM_REG_PC);\n    zz_arm_writer_put_bytes(writer, THUMB_FUNCTION_ADDRESS((void *)ctx_save), 15 * 4);\n    zz_arm_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_PC, 1);\n    zz_arm_writer_put_bx_reg(writer, ZZ_ARM_REG_R1);\n\n    zz_thumb_writer_put_sub_reg_imm(writer, ZZ_ARM_REG_SP, 0x8);\n    zz_thumb_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, CTX_SAVE_STACK_OFFSET + 0x8 + 0x8);\n    zz_thumb_writer_put_str_reg_reg_offset(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);\n\n    /* pass enter func args */\n    /* entry */\n    zz_thumb_writer_put_ldr_reg_reg_offset(writer, ZZ_ARM_REG_R0, ZZ_ARM_REG_SP, CTX_SAVE_STACK_OFFSET + 0x8);\n    /* next hop*/\n    zz_thumb_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, CTX_SAVE_STACK_OFFSET + 0x8 + 0x4);\n    /* RegState */\n    zz_thumb_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R2, ZZ_ARM_REG_SP, 0x4);\n    /* caller ret address */\n    zz_thumb_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R3, ZZ_ARM_REG_SP, 0x8 + 13 * 4);\n\n    /* call function_context_begin_invocation */\n    zz_thumb_writer_put_ldr_b_reg_address(writer, ZZ_ARM_REG_LR, (zaddr)function_context_begin_invocation);\n    zz_thumb_writer_put_blx_reg(writer, ZZ_ARM_REG_LR);\n\n    /* restore general registers and sp */\n    zz_thumb_writer_put_add_reg_imm(writer, ZZ_ARM_REG_SP, 0x8);\n    zz_thumb_writer_put_bx_reg(writer, ZZ_ARM_REG_PC);\n    zz_arm_writer_put_bytes(writer, THUMB_FUNCTION_ADDRESS((void *)ctx_restore), 14 * 4);\n    zz_arm_writer_put_bx_to_thumb(writer);\n\n    /* restore arg space */\n    zz_thumb_writer_put_add_reg_imm(writer, ZZ_ARM_REG_SP, 0x4);\n\n    /* pop and jump to next hop */\n    // use Post-indexed ldr to `pop`\n    zz_thumb_writer_put_ldr_index_reg_reg_offset(writer, ZZ_ARM_REG_PC, ZZ_ARM_REG_SP, 4, 0);\n}\n\n// A4.1.10 BX\nvoid zz_thumb_thunker_build_half_thunk(ZzWriter *writer) {\n\n    /* save general registers and sp */\n    zz_thumb_writer_put_bx_reg(writer, ZZ_ARM_REG_PC);\n    zz_arm_writer_put_bytes(writer, THUMB_FUNCTION_ADDRESS((void *)ctx_save), 15 * 4);\n    zz_arm_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_PC, 1);\n    zz_arm_writer_put_bx_reg(writer, ZZ_ARM_REG_R1);\n\n    zz_thumb_writer_put_sub_reg_imm(writer, ZZ_ARM_REG_SP, 0x8);\n    zz_thumb_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, CTX_SAVE_STACK_OFFSET + 0x8 + 0x8);\n    zz_thumb_writer_put_str_reg_reg_offset(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);\n\n    /* pass enter func args */\n    /* entry */\n    zz_thumb_writer_put_ldr_reg_reg_offset(writer, ZZ_ARM_REG_R0, ZZ_ARM_REG_SP, CTX_SAVE_STACK_OFFSET + 0x8);\n    /* next hop*/\n    zz_thumb_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, CTX_SAVE_STACK_OFFSET + 0x8 + 0x4);\n    /* RegState */\n    zz_thumb_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R2, ZZ_ARM_REG_SP, 0x4);\n    /* caller ret address */\n    zz_thumb_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R3, ZZ_ARM_REG_SP, 0x8 + 13 * 4);\n\n    /* call function_context_half_invocation */\n    zz_thumb_writer_put_ldr_b_reg_address(writer, ZZ_ARM_REG_LR, (zaddr)function_context_half_invocation);\n    zz_thumb_writer_put_blx_reg(writer, ZZ_ARM_REG_LR);\n\n    /* restore general registers and sp */\n    zz_thumb_writer_put_add_reg_imm(writer, ZZ_ARM_REG_SP, 0x8);\n    zz_thumb_writer_put_bx_reg(writer, ZZ_ARM_REG_PC);\n    zz_arm_writer_put_bytes(writer, THUMB_FUNCTION_ADDRESS((void *)ctx_restore), 14 * 4);\n    zz_arm_writer_put_bx_to_thumb(writer);\n\n    /* restore arg space */\n    zz_thumb_writer_put_add_reg_imm(writer, ZZ_ARM_REG_SP, 0x4);\n\n    /* pop and jump to next hop */\n    // use Post-indexed ldr to `pop`\n    zz_thumb_writer_put_ldr_index_reg_reg_offset(writer, ZZ_ARM_REG_PC, ZZ_ARM_REG_SP, 4, 0);\n}\n\nvoid zz_thumb_thunker_build_leave_thunk(ZzWriter *writer) {\n\n    /* save general registers and sp */\n    zz_thumb_writer_put_bx_reg(writer, ZZ_ARM_REG_PC);\n    zz_arm_writer_put_bytes(writer, THUMB_FUNCTION_ADDRESS((void *)ctx_save), 15 * 4);\n    zz_arm_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_PC, 1);\n    zz_arm_writer_put_bx_reg(writer, ZZ_ARM_REG_R1);\n\n    zz_thumb_writer_put_sub_reg_imm(writer, ZZ_ARM_REG_SP, 0x8);\n    zz_thumb_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, CTX_SAVE_STACK_OFFSET + 0x8 + 0x8);\n    zz_thumb_writer_put_str_reg_reg_offset(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, 0x4);\n\n    /* pass enter func args */\n    /* entry */\n    zz_thumb_writer_put_ldr_reg_reg_offset(writer, ZZ_ARM_REG_R0, ZZ_ARM_REG_SP, CTX_SAVE_STACK_OFFSET + 0x8);\n    /* next hop*/\n    zz_thumb_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R1, ZZ_ARM_REG_SP, CTX_SAVE_STACK_OFFSET + 0x8 + 0x4);\n    /* RegState */\n    zz_thumb_writer_put_add_reg_reg_imm(writer, ZZ_ARM_REG_R2, ZZ_ARM_REG_SP, 0x4);\n\n    /* call function_context_begin_invocation */\n    zz_thumb_writer_put_ldr_b_reg_address(writer, ZZ_ARM_REG_LR, (zaddr)function_context_end_invocation);\n    zz_thumb_writer_put_blx_reg(writer, ZZ_ARM_REG_LR);\n\n    /* restore general registers and sp */\n    zz_thumb_writer_put_add_reg_imm(writer, ZZ_ARM_REG_SP, 0x8);\n    zz_thumb_writer_put_bx_reg(writer, ZZ_ARM_REG_PC);\n    zz_arm_writer_put_bytes(writer, THUMB_FUNCTION_ADDRESS((void *)ctx_restore), 14 * 4);\n    zz_arm_writer_put_bx_to_thumb(writer);\n\n    /* restore arg space */\n    zz_thumb_writer_put_add_reg_imm(writer, ZZ_ARM_REG_SP, 0x4);\n\n    /* pop and jump to next hop */\n    // use Post-indexed ldr to `pop`\n    zz_thumb_writer_put_ldr_index_reg_reg_offset(writer, ZZ_ARM_REG_PC, ZZ_ARM_REG_SP, 4, 0);\n}\n\nZZSTATUS ZzThunkerBuildThunk(ZzInterceptorBackend *self) {\n    zbyte temp_code_slice_data[512] = {0};\n    ZzThumbWriter *thumb_writer = NULL;\n    ZzCodeSlice *code_slice = NULL;\n    ZZSTATUS status = ZZ_SUCCESS;\n\n    thumb_writer = &self->thumb_writer;\n    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);\n\n    /* buid enter_thunk */\n    zz_thumb_thunker_build_enter_thunk(thumb_writer);\n\n    /* code patch */\n    code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, 0, 0);\n    if (code_slice)\n        self->enter_thunk = code_slice->data + 1;\n    else\n        return ZZ_FAILED;\n\n    /* debug log */\n    if (ZzIsEnableDebugMode()) {\n        char buffer[2048] = {};\n        char thunk_buffer[2048] = {};\n        int t = 0;\n        zpointer p;\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzThunkerBuildThunk:\");\n\n        for (p = thumb_writer->base; p < thumb_writer->base + thumb_writer->size; p++, t = t + 5) {\n            sprintf(thunk_buffer + t, \"0x%.2x \", *(unsigned char *)p);\n        }\n\n        ZzInfoLog(\"%s\", thunk_buffer);\n        // sprintf(buffer + strlen(buffer), \"enter_thunk: %s\\n\", thunk_buffer);\n\n        sprintf(buffer + strlen(buffer), \"LogInfo: enter_thunk at %p, length: %ld.\\n\", code_slice->data,\n                code_slice->size);\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);\n\n    /* build leave_thunk */\n    zz_thumb_thunker_build_leave_thunk(thumb_writer);\n\n    /* code patch */\n    code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, 0, 0);\n    if (code_slice)\n        self->leave_thunk = code_slice->data + 1;\n    else\n        return ZZ_FAILED;\n\n    /* debug log */\n    if (ZzIsEnableDebugMode()) {\n        char buffer[2048] = {};\n        char thunk_buffer[2048] = {};\n        int t = 0;\n        zpointer p;\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzThunkerBuildThunk:\");\n\n        for (p = thumb_writer->base; p < thumb_writer->base + thumb_writer->size; p++, t = t + 5) {\n            sprintf(thunk_buffer + t, \"0x%.2x \", *(unsigned char *)p);\n        }\n\n        ZzInfoLog(\"%s\", thunk_buffer);\n        // sprintf(buffer + strlen(buffer), \"enter_thunk: %s\\n\", thunk_buffer);\n\n        sprintf(buffer + strlen(buffer), \"LogInfo: leave_thunk at %p, length: %ld.\\n\", code_slice->data,\n                code_slice->size);\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);\n\n    /* build half_thunk */\n    zz_thumb_thunker_build_half_thunk(thumb_writer);\n\n    /* code patch */\n    code_slice = zz_code_patch_thumb_writer(thumb_writer, self->allocator, 0, 0);\n    if (code_slice)\n        self->half_thunk = code_slice->data + 1;\n    else\n        return ZZ_FAILED;\n\n    /* debug log */\n    if (ZzIsEnableDebugMode()) {\n        char buffer[2048] = {};\n        char thunk_buffer[2048] = {};\n        int t = 0;\n        zpointer p;\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzThunkerBuildThunk:\");\n\n        for (p = thumb_writer->base; p < thumb_writer->base + thumb_writer->size; p++, t = t + 5) {\n            sprintf(thunk_buffer + t, \"0x%.2x \", *(unsigned char *)p);\n        }\n\n        ZzInfoLog(\"%s\", thunk_buffer);\n        // sprintf(buffer + strlen(buffer), \"half_thunk: %s\\n\", thunk_buffer);\n\n        sprintf(buffer + strlen(buffer), \"LogInfo: half_thunk at %p, length: %ld.\\n\", code_slice->data,\n                code_slice->size);\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    return status;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm/thunker-arm.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_backend_arm_thunker_arm\n#define platforms_backend_arm_thunker_arm\n\n// platforms\n#include \"platforms/arch-arm/relocator-arm.h\"\n#include \"platforms/arch-arm/relocator-thumb.h\"\n#include \"platforms/arch-arm/writer-arm.h\"\n#include \"platforms/arch-arm/writer-thumb.h\"\n\n#include \"interceptor-arm.h\"\n\n// hookzz\n#include \"stack.h\"\n#include \"thunker.h\"\n#include \"zzdefs.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/interceptor-arm64.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"interceptor-arm64.h\"\n#include \"zzinfo.h\"\n#include <stdlib.h>\n#include <string.h>\n\n#define ZZ_ARM64_TINY_REDIRECT_SIZE 4\n#define ZZ_ARM64_FULL_REDIRECT_SIZE 16\n\nZzInterceptorBackend *ZzBuildInteceptorBackend(ZzAllocator *allocator) {\n    if (!ZzMemoryIsSupportAllocateRXPage()) {\n        return NULL;\n    }\n    ZZSTATUS status;\n\n    ZzInterceptorBackend *backend = (ZzInterceptorBackend *)malloc(sizeof(ZzInterceptorBackend));\n    memset(backend, 0, sizeof(ZzInterceptorBackend));\n\n    zz_arm64_writer_init(&backend->arm64_writer, NULL);\n    zz_arm64_relocator_init(&backend->arm64_relocator, NULL, &backend->arm64_writer);\n\n    backend->allocator = allocator;\n    backend->enter_thunk = NULL;\n    backend->half_thunk = NULL;\n    backend->leave_thunk = NULL;\n\n    status = ZzThunkerBuildThunk(backend);\n    if (status == ZZ_FAILED) {\n        ZzInfoLog(\"%s\", \"ZzThunkerBuildThunk return ZZ_FAILED\\n\");\n        return NULL;\n    }\n\n    return backend;\n}\n\nZzCodeSlice *zz_code_patch_arm64_writer(ZzArm64Writer *arm64_writer, ZzAllocator *allocator, zaddr target_addr,\n                                        zsize range_size) {\n    ZzCodeSlice *code_slice = NULL;\n    if (range_size > 0) {\n        code_slice = ZzNewNearCodeSlice(allocator, target_addr, range_size, arm64_writer->size);\n    } else {\n        code_slice = ZzNewCodeSlice(allocator, arm64_writer->size + 4);\n    }\n    if (!code_slice)\n        return NULL;\n\n    if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm64_writer->base, arm64_writer->size)) {\n        free(code_slice);\n        return NULL;\n    }\n    return code_slice;\n}\n\nZzCodeSlice *zz_code_patch_arm64_relocate_writer(ZzArm64Relocator *relocator, ZzArm64Writer *arm64_writer,\n                                                 ZzAllocator *allocator, zaddr target_addr, zsize range_size) {\n    ZzCodeSlice *code_slice = NULL;\n    if (range_size > 0) {\n        code_slice = ZzNewNearCodeSlice(allocator, target_addr, range_size, arm64_writer->size);\n    } else {\n        code_slice = ZzNewCodeSlice(allocator, arm64_writer->size + 4);\n    }\n    if (!code_slice)\n        return NULL;\n\n    if (!ZzMemoryPatchCode((zaddr)code_slice->data, arm64_writer->base, arm64_writer->size)) {\n        free(code_slice);\n        return NULL;\n    }\n    return code_slice;\n}\n\nZZSTATUS ZzPrepareTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zaddr target_addr = (zaddr)entry->target_ptr;\n    zuint redirect_limit = 0;\n\n    ZzArm64HookFunctionEntryBackend *entry_backend;\n    entry_backend = (ZzArm64HookFunctionEntryBackend *)malloc(sizeof(ZzArm64HookFunctionEntryBackend));\n    memset(entry_backend, 0, sizeof(ZzArm64HookFunctionEntryBackend));\n\n    entry->backend = (struct _ZzHookFunctionEntryBackend *)entry_backend;\n\n    if (entry->try_near_jump) {\n        entry_backend->redirect_code_size = ZZ_ARM64_TINY_REDIRECT_SIZE;\n    } else {\n        zz_arm64_relocator_try_relocate((zpointer)target_addr, ZZ_ARM64_FULL_REDIRECT_SIZE, &redirect_limit);\n        if (redirect_limit != 0 && redirect_limit > ZZ_ARM64_TINY_REDIRECT_SIZE &&\n            redirect_limit < ZZ_ARM64_FULL_REDIRECT_SIZE) {\n            entry->try_near_jump = TRUE;\n            entry_backend->redirect_code_size = ZZ_ARM64_TINY_REDIRECT_SIZE;\n        } else if (redirect_limit != 0 && redirect_limit < ZZ_ARM64_TINY_REDIRECT_SIZE) {\n            return ZZ_FAILED;\n        } else {\n            entry_backend->redirect_code_size = ZZ_ARM64_FULL_REDIRECT_SIZE;\n        }\n    }\n\n    self->arm64_relocator.try_relocated_length = entry_backend->redirect_code_size;\n    zz_arm64_relocator_init(&self->arm64_relocator, (zpointer)target_addr, &self->arm64_writer);\n    return ZZ_SUCCESS;\n}\n\nZZSTATUS ZzBuildEnterTransferTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zbyte temp_code_slice_data[256] = {0};\n    ZzArm64Writer *arm64_writer = NULL;\n    ZzCodeSlice *code_slice = NULL;\n    ZzArm64HookFunctionEntryBackend *entry_backend = (ZzArm64HookFunctionEntryBackend *)entry->backend;\n    ZZSTATUS status = ZZ_SUCCESS;\n    zaddr target_addr = (zaddr)entry->target_ptr;\n\n    arm64_writer = &self->arm64_writer;\n    zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);\n    zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry->on_enter_trampoline);\n    code_slice =\n        zz_code_patch_arm64_writer(arm64_writer, self->allocator, target_addr, zz_arm64_writer_near_jump_range_size());\n    if (code_slice)\n        entry->on_enter_transfer_trampoline = code_slice->data;\n    else\n        return ZZ_FAILED;\n\n    if (ZzIsEnableDebugMode()) {\n        char buffer[1024] = {};\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzBuildEnterTransferTrampoline:\");\n        sprintf(buffer + strlen(buffer),\n                \"LogInfo: on_enter_transfer_trampoline at %p, length: %ld. and will jump to on_enter_trampoline(%p).\\n\",\n                code_slice->data, code_slice->size, entry->on_enter_trampoline);\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    free(code_slice);\n    return status;\n}\nZZSTATUS ZzBuildEnterTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zbyte temp_code_slice_data[256] = {0};\n    ZzArm64Writer *arm64_writer = NULL;\n    ZzCodeSlice *code_slice = NULL;\n    ZzArm64HookFunctionEntryBackend *entry_backend = (ZzArm64HookFunctionEntryBackend *)entry->backend;\n    ZZSTATUS status = ZZ_SUCCESS;\n    zaddr target_addr = (zaddr)entry->target_ptr;\n\n    arm64_writer = &self->arm64_writer;\n    zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);\n\n    /* prepare 2 stack space: 1. next_hop 2. entry arg */\n    zz_arm64_writer_put_sub_reg_reg_imm(arm64_writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 0x8);\n    zz_arm64_writer_put_ldr_b_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry);\n    zz_arm64_writer_put_str_reg_reg_offset(arm64_writer, ZZ_ARM64_REG_X17, ZZ_ARM64_REG_SP, 0x0);\n\n    /* jump to enter thunk */\n    zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)self->enter_thunk);\n\n    /* code patch */\n    code_slice = zz_code_patch_arm64_writer(arm64_writer, self->allocator, 0, 0);\n    if (code_slice)\n        entry->on_enter_trampoline = code_slice->data;\n    else\n        return ZZ_FAILED;\n\n    /* debug log */\n    if (ZzIsEnableDebugMode()) {\n        char buffer[1024] = {};\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzBuildEnterTrampoline:\");\n        sprintf(buffer + strlen(buffer),\n                \"LogInfo: on_enter_trampoline at %p, length: %ld. hook-entry: %p. and will jump to enter_thunk(%p).\\n\",\n                code_slice->data, code_slice->size, (void *)entry, (void *)self->enter_thunk);\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    if (entry_backend->redirect_code_size == ZZ_ARM64_TINY_REDIRECT_SIZE) {\n        ZzBuildEnterTransferTrampoline(self, entry);\n    }\n\n    free(code_slice);\n    return status;\n}\n\nZZSTATUS ZzBuildInvokeTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zbyte temp_code_slice_data[256] = {0};\n    ZzCodeSlice *code_slice = NULL;\n    ZzArm64HookFunctionEntryBackend *entry_backend = (ZzArm64HookFunctionEntryBackend *)entry->backend;\n    ZZSTATUS status = ZZ_SUCCESS;\n    zaddr target_addr = (zaddr)entry->target_ptr;\n    zpointer restore_target_addr;\n\n    ZzArm64Relocator *arm64_relocator;\n    ZzArm64Writer *arm64_writer;\n    arm64_relocator = &self->arm64_relocator;\n    arm64_writer = &self->arm64_writer;\n\n    zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);\n    zz_arm64_relocator_reset(arm64_relocator, (zpointer)target_addr, arm64_writer);\n    zsize tmp_relocator_insn_size = 0;\n    entry->target_half_ret_addr = 0;\n\n    if (entry->hook_type == HOOK_FUNCTION_TYPE) {\n        do {\n            zz_arm64_relocator_read_one(arm64_relocator, NULL);\n            tmp_relocator_insn_size = arm64_relocator->input_cur - arm64_relocator->input_start;\n        } while (tmp_relocator_insn_size < entry_backend->redirect_code_size);\n        zz_arm64_relocator_write_all(arm64_relocator);\n    } else if (entry->hook_type == HOOK_ADDRESS_TYPE) {\n        do {\n            zz_arm64_relocator_read_one(arm64_relocator, NULL);\n            zz_arm64_relocator_write_one(arm64_relocator);\n            tmp_relocator_insn_size = arm64_relocator->input_cur - arm64_relocator->input_start;\n            if (arm64_relocator->input_cur >= entry->target_end_ptr && !entry->target_half_ret_addr) {\n                zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17,\n                                                       (zaddr)entry->on_half_trampoline);\n\n                entry->target_half_ret_addr = (zpointer)arm64_writer->size;\n            }\n        } while (tmp_relocator_insn_size < entry_backend->redirect_code_size ||\n                 arm64_relocator->input_cur < entry->target_end_ptr);\n    }\n\n    /* jump to rest target address */\n    restore_target_addr = (zpointer)((zaddr)target_addr + tmp_relocator_insn_size);\n    zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)restore_target_addr);\n\n    /* code patch */\n    code_slice = zz_code_patch_arm64_relocate_writer(arm64_relocator, arm64_writer, self->allocator, 0, 0);\n    if (code_slice)\n        entry->on_invoke_trampoline = code_slice->data;\n    else\n        return ZZ_FAILED;\n\n    /* update target_half_ret_addr */\n    if (entry->hook_type == HOOK_ADDRESS_TYPE) {\n        entry->target_half_ret_addr += (zaddr)code_slice->data;\n    }\n\n    /* debug log */\n    if (ZzIsEnableDebugMode()) {\n        char buffer[1024] = {0};\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzBuildInvokeTrampoline:\");\n        sprintf(buffer + strlen(buffer),\n                \"LogInfo: on_invoke_trampoline at %p, length: %ld. and will jump to rest code(%p).\\n\", code_slice->data,\n                code_slice->size, restore_target_addr);\n        sprintf(buffer + strlen(buffer),\n                \"ArmInstructionFix: origin instruction at %p, relocator end at %p, relocator instruction nums %ld\\n\",\n                (&self->arm64_relocator)->input_start, (&self->arm64_relocator)->input_cur,\n                (&self->arm64_relocator)->inpos);\n\n        char origin_prologue[256] = {0};\n        int t = 0;\n        zpointer p;\n        for (p = (&self->arm64_relocator)->input_start; p < (&self->arm64_relocator)->input_cur; p++, t = t + 5) {\n            sprintf(origin_prologue + t, \"0x%.2x \", *(unsigned char *)p);\n        }\n        sprintf(buffer + strlen(buffer), \"origin_prologue: %s\\n\", origin_prologue);\n\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    free(code_slice);\n    return status;\n}\n\nZZSTATUS ZzBuildHalfTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zbyte temp_code_slice_data[256] = {0};\n    ZzArm64Writer *arm64_writer = NULL;\n    ZzCodeSlice *code_slice = NULL;\n    ZzArm64HookFunctionEntryBackend *entry_backend = (ZzArm64HookFunctionEntryBackend *)entry->backend;\n    ZZSTATUS status = ZZ_SUCCESS;\n    zaddr target_addr = (zaddr)entry->target_ptr;\n\n    arm64_writer = &self->arm64_writer;\n    zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);\n\n    /* prepare 2 stack space: 1. next_hop 2. entry arg */\n    zz_arm64_writer_put_sub_reg_reg_imm(arm64_writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 0x8);\n    zz_arm64_writer_put_ldr_b_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry);\n    zz_arm64_writer_put_str_reg_reg_offset(arm64_writer, ZZ_ARM64_REG_X17, ZZ_ARM64_REG_SP, 0x0);\n\n    /* jump to half thunk */\n    zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)self->half_thunk);\n\n    /* code patch */\n    code_slice = zz_code_patch_arm64_writer(arm64_writer, self->allocator, 0, 0);\n    if (code_slice)\n        entry->on_half_trampoline = code_slice->data;\n    else\n        return ZZ_FAILED;\n\n    return status;\n}\n\nZZSTATUS ZzBuildLeaveTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zbyte temp_code_slice_data[256] = {0};\n    ZzCodeSlice *code_slice = NULL;\n    ZzArm64HookFunctionEntryBackend *entry_backend = (ZzArm64HookFunctionEntryBackend *)entry->backend;\n    zaddr target_addr = (zaddr)entry->target_ptr;\n    ZzArm64Writer *arm64_writer = NULL;\n\n    arm64_writer = &self->arm64_writer;\n    zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);\n\n    /* prepare 2 stack space: 1. next_hop 2. entry arg */\n    zz_arm64_writer_put_sub_reg_reg_imm(arm64_writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 0x8);\n    zz_arm64_writer_put_ldr_b_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry);\n    zz_arm64_writer_put_str_reg_reg_offset(arm64_writer, ZZ_ARM64_REG_X17, ZZ_ARM64_REG_SP, 0x0);\n\n    /* jump to leave thunk */\n    zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)self->leave_thunk);\n\n    /* code patch */\n    code_slice = zz_code_patch_arm64_writer(arm64_writer, self->allocator, 0, 0);\n    if (code_slice)\n        entry->on_leave_trampoline = code_slice->data;\n    else\n        return ZZ_FAILED;\n\n    /* debug log */\n    if (ZzIsEnableDebugMode()) {\n        char buffer[1024] = {};\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzBuildLeaveTrampoline:\");\n        sprintf(buffer + strlen(buffer),\n                \"LogInfo: on_leave_trampoline at %p, length: %ld. and will jump to leave_thunk(%p).\\n\",\n                code_slice->data, code_slice->size, self->leave_thunk);\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    free(code_slice);\n    return ZZ_DONE;\n}\n\nZZSTATUS ZzActivateTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n    zbyte temp_code_slice_data[256] = {0};\n    ZzCodeSlice *code_slice = NULL;\n    ZzArm64HookFunctionEntryBackend *entry_backend = (ZzArm64HookFunctionEntryBackend *)entry->backend;\n    ZZSTATUS status = ZZ_SUCCESS;\n    zaddr target_addr = (zaddr)entry->target_ptr;\n    ZzArm64Writer *arm64_writer;\n\n    arm64_writer = &self->arm64_writer;\n    zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);\n    arm64_writer->pc = target_addr;\n\n    if (entry_backend->redirect_code_size == ZZ_ARM64_TINY_REDIRECT_SIZE) {\n        zz_arm64_writer_put_b_imm(arm64_writer, (zaddr)entry->on_enter_transfer_trampoline - (zaddr)arm64_writer->pc);\n    } else {\n        zz_arm64_writer_put_ldr_br_reg_address(arm64_writer, ZZ_ARM64_REG_X17, (zaddr)entry->on_enter_trampoline);\n    }\n\n    if (!ZzMemoryPatchCode((zaddr)target_addr, arm64_writer->base, arm64_writer->size))\n        status = ZZ_FAILED;\n\n    return status;\n}\n\n#ifdef TARGET_IS_IOS\n\n#include <mach-o/dyld.h>\n#include <zzdeps/darwin/macho-utils-darwin.h>\n\ntypedef struct _ZzInterceptorBackendNoJB {\n    void *enter_thunk; // hardcode\n    void *leave_thunk; // hardcode\n    unsigned long num_of_entry;\n    unsigned long code_seg_offset;\n    unsigned long data_seg_offset;\n} ZzInterceptorBackendNoJB;\n\ntypedef struct _ZzHookFunctionEntryNoJB {\n    void *target_fileoff;\n    unsigned long is_near_jump;\n    void *entry_address;\n    void *on_enter_trampoline;  // HookZzData, 99% hardcode\n    void *on_invoke_trampoline; // HookZzData, fixed instructions\n    void *on_leave_trampoline;  // HookZzData, 99% hardcode\n} ZzHookFunctionEntryNoJB;\n\nZZSTATUS ZzActivateSolidifyTrampoline(ZzHookFunctionEntry *entry, zaddr target_fileoff) {\n    struct mach_header_64 *header = (struct mach_header_64 *)_dyld_get_image_header(0);\n    struct segment_command_64 *text_seg_cmd = zz_macho_get_segment_64_via_name(header, \"__TEXT\");\n    struct segment_command_64 *data_seg_cmd = zz_macho_get_segment_64_via_name(header, \"HookZzData\");\n    zaddr aslr_slide = (zaddr)header - text_seg_cmd->vmaddr;\n    ZzInterceptorBackendNoJB *nojb_backend = (ZzInterceptorBackendNoJB *)(aslr_slide + data_seg_cmd->vmaddr);\n    nojb_backend->enter_thunk = (void *)enter_thunk_template;\n    nojb_backend->leave_thunk = (void *)leave_thunk_template;\n\n    ZzHookFunctionEntryNoJB *nojb_entry =\n        (ZzHookFunctionEntryNoJB *)(data_seg_cmd->vmaddr + sizeof(ZzHookFunctionEntryNoJB) + aslr_slide);\n    unsigned long i;\n    for (i = 0; i < nojb_backend->num_of_entry; i++) {\n        nojb_entry = &nojb_entry[i];\n        if ((zaddr)nojb_entry->target_fileoff == target_fileoff) {\n            nojb_entry->entry_address = entry;\n            entry->on_enter_trampoline = (zpointer)(nojb_entry->on_enter_trampoline + aslr_slide);\n            entry->on_invoke_trampoline = nojb_entry->on_invoke_trampoline + aslr_slide;\n            entry->on_leave_trampoline = nojb_entry->on_leave_trampoline + aslr_slide;\n        }\n    }\n    return ZZ_SUCCESS;\n}\n#endif\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/interceptor-arm64.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_backend_arm64_intercetor_arm64\n#define platforms_backend_arm64_intercetor_arm64\n\n// platforms\n#include \"platforms/arch-arm64/relocator-arm64.h\"\n#include \"platforms/arch-arm64/writer-arm64.h\"\n\n// hookzz\n#include \"allocator.h\"\n#include \"interceptor.h\"\n#include \"thunker.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\n#define CTX_SAVE_STACK_OFFSET (8 + 30 * 8 + 8 * 16)\n\ntypedef struct _ZzInterceptorBackend {\n    ZzAllocator *allocator;\n    ZzArm64Relocator arm64_relocator;\n\n    ZzArm64Writer arm64_writer;\n\n    zpointer enter_thunk;\n    zpointer half_thunk;\n    zpointer leave_thunk;\n} ZzInterceptorBackend;\n\ntypedef struct _ZzArm64HookFuntionEntryBackend {\n    zbool is_thumb;\n    zuint redirect_code_size;\n} ZzArm64HookFunctionEntryBackend;\n\nvoid ctx_save();\nvoid ctx_restore();\nvoid enter_thunk_template();\nvoid leave_thunk_template();\nvoid on_enter_trampoline_template();\nvoid on_invoke_trampoline_template();\nvoid on_leave_trampoline_template();\n\nZzCodeSlice *zz_code_patch_arm64_writer(ZzArm64Writer *arm64_writer, ZzAllocator *allocator, zaddr target_addr,\n                                        zsize range_size);\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/interceptor-template-arm64.s",
    "content": "// .section\t__TEXT,__text,regular,pure_instructions\n// .ios_version_min 11, 0\n.align 4\n.globl _ctx_save\n.globl _ctx_restore\n.globl _enter_thunk_template\n.globl _leave_thunk_template\n.globl _on_enter_trampoline_template\n.globl _on_invoke_trampoline_template\n.globl _on_leave_trampoline_template\n\n_ctx_save:\n\t// save {q0-q7}\n\tsub sp, sp, #(8*16)\n\tstp q6, q7, [sp, #(6*16)]\n\tstp q4, q5, [sp, #(4*16)]\n\tstp q2, q3, [sp, #(2*16)]\n\tstp q0, q1, [sp, #(0*16)]\n\n\t// save {x1-x30}\n\tsub sp, sp, #(30*8)\n\t// stp fp, lr, [sp, #(28*8)]\n\tstp x29, x30, [sp, #(28*8)]\n\tstp x27, x28, [sp, #(26*8)]\n\tstp x25, x26, [sp, #(24*8)]\n\tstp x23, x24, [sp, #(22*8)]\n\tstp x21, x22, [sp, #(20*8)]\n\tstp x19, x20, [sp, #(18*8)]\n\tstp x17, x18, [sp, #(16*8)]\n\tstp x15, x16, [sp, #(14*8)]\n\tstp x13, x14, [sp, #(12*8)]\n\tstp x11, x12, [sp, #(10*8)]\n\tstp x9, x10, [sp, #(8*8)]\n\tstp x7, x8, [sp, #(6*8)]\n\tstp x5, x6, [sp, #(4*8)]\n\tstp x3, x4, [sp, #(2*8)]\n\tstp x1, x2, [sp, #(0*8)]\n\n\t// C6.1.3\n\t// Use of the stack pointer\n\t// save x0 (and reserve sp, but this is trick.)\n\tsub sp, sp, #(2*8)\n\tstr x0, [sp, #8]\n\n_ctx_restore:\n\t// C6.1.3\n\t// Use of the stack pointer\n\t// restore x0\n\tldr x0, [sp, #8]\n\tadd sp, sp, #(2*8)\n\n\t// restore {x1-x30}\n\tldp x1, x2, [sp], #16\n\tldp x3, x4, [sp], #16\n\tldp x5, x6, [sp], #16\n\tldp x7, x8, [sp], #16\n\tldp x9, x10, [sp], #16\n\tldp x11, x12, [sp], #16\n\tldp x13, x14, [sp], #16\n\tldp x15, x16, [sp], #16\n\tldp x17, x18, [sp], #16\n\tldp x19, x20, [sp], #16\n\tldp x21, x22, [sp], #16\n\tldp x23, x24, [sp], #16\n\tldp x25, x26, [sp], #16\n\tldp x27, x28, [sp], #16\n\t// ldp fp, lr, [sp], #16\n\tldp x29, x30, [sp], #16\n\n\t// restore {q0-q7}\n\tldp q0, q1, [sp], #32\n\tldp q2, q3, [sp], #32\n\tldp q4, q5, [sp], #32\n\tldp q6, q7, [sp], #32\n\n_enter_thunk_template:\n\t// ----------- ctx_save begin ---------------\n\t// save {q0-q7}\n\tsub sp, sp, #(8*16)\n\tstp q6, q7, [sp, #(6*16)]\n\tstp q4, q5, [sp, #(4*16)]\n\tstp q2, q3, [sp, #(2*16)]\n\tstp q0, q1, [sp, #(0*16)]\n\n\t// save {x1-x30}\n\tsub sp, sp, #(30*8)\n\t// stp fp, lr, [sp, #(28*8)]\n\tstp x29, x30, [sp, #(28*8)]\n\tstp x27, x28, [sp, #(26*8)]\n\tstp x25, x26, [sp, #(24*8)]\n\tstp x23, x24, [sp, #(22*8)]\n\tstp x21, x22, [sp, #(20*8)]\n\tstp x19, x20, [sp, #(18*8)]\n\tstp x17, x18, [sp, #(16*8)]\n\tstp x15, x16, [sp, #(14*8)]\n\tstp x13, x14, [sp, #(12*8)]\n\tstp x11, x12, [sp, #(10*8)]\n\tstp x9, x10, [sp, #(8*8)]\n\tstp x7, x8, [sp, #(6*8)]\n\tstp x5, x6, [sp, #(4*8)]\n\tstp x3, x4, [sp, #(2*8)]\n\tstp x1, x2, [sp, #(0*8)]\n\n\t// C6.1.3\n\t// Use of the stack pointer\n\t// save x0 (and reserve sp, but this is trick.)\n\tsub sp, sp, #(2*8)\n\tstr x0, [sp, #8]\n\t// ----------- ctx_save end ---------------\n\n\t// use the `ctx_save` left space, to store origin sp\n\tadd x1, sp, #0x190\n\tstr x1, [sp, #0]\n\n\t// alignment padding\n\tsub sp, sp, #0x10\n\n\t// prepare args\n\t// x0: entry address, x1: next hop, x2: RegState address, x3: caller_ret_addr\n\tldr x0, [sp, #0x190]\n\tadd x1, sp, #0x198\n\tadd x2, sp, #0x10\n\tadd x3, sp, #0x108\n\t// call function_context_begin_invocation\n\tbl _function_context_begin_invocation\n\n\t// restore alignment padding\n\tadd sp, sp, #0x10\n\n\t// ----------- ctx_restore begin ------------\n\t// C6.1.3\n\t// Use of the stack pointer\n\t// restore x0\n\tldr x0, [sp, #8]\n\tadd sp, sp, #(2*8)\n\n\t// restore {x1-x30}\n\tldp x1, x2, [sp], #16\n\tldp x3, x4, [sp], #16\n\tldp x5, x6, [sp], #16\n\tldp x7, x8, [sp], #16\n\tldp x9, x10, [sp], #16\n\tldp x11, x12, [sp], #16\n\tldp x13, x14, [sp], #16\n\tldp x15, x16, [sp], #16\n\tldp x17, x18, [sp], #16\n\tldp x19, x20, [sp], #16\n\tldp x21, x22, [sp], #16\n\tldp x23, x24, [sp], #16\n\tldp x25, x26, [sp], #16\n\tldp x27, x28, [sp], #16\n\t// ldp fp, lr, [sp], #16\n\tldp x29, x30, [sp], #16\n\n\t// restore {q0-q7}\n\tldp q0, q1, [sp], #32\n\tldp q2, q3, [sp], #32\n\tldp q4, q5, [sp], #32\n\tldp q6, q7, [sp], #32\n\t// ----------- ctx_restore end ------------\n\n\t// jump to next hop\n\t// next hop addess(store at reserve space)\n\tldr x17, [sp, #8]\n\tadd sp, sp, #0x10\n\tbr x17\n\n_leave_thunk_template:\n\t// ----------- ctx_save begin ---------------\n\t// save {q0-q7}\n\tsub sp, sp, #(8*16)\n\tstp q6, q7, [sp, #(6*16)]\n\tstp q4, q5, [sp, #(4*16)]\n\tstp q2, q3, [sp, #(2*16)]\n\tstp q0, q1, [sp, #(0*16)]\n\n\t// save {x1-x30}\n\tsub sp, sp, #(30*8)\n\t// stp fp, lr, [sp, #(28*8)]\n\tstp x29, x30, [sp, #(28*8)]\n\tstp x27, x28, [sp, #(26*8)]\n\tstp x25, x26, [sp, #(24*8)]\n\tstp x23, x24, [sp, #(22*8)]\n\tstp x21, x22, [sp, #(20*8)]\n\tstp x19, x20, [sp, #(18*8)]\n\tstp x17, x18, [sp, #(16*8)]\n\tstp x15, x16, [sp, #(14*8)]\n\tstp x13, x14, [sp, #(12*8)]\n\tstp x11, x12, [sp, #(10*8)]\n\tstp x9, x10, [sp, #(8*8)]\n\tstp x7, x8, [sp, #(6*8)]\n\tstp x5, x6, [sp, #(4*8)]\n\tstp x3, x4, [sp, #(2*8)]\n\tstp x1, x2, [sp, #(0*8)]\n\n\t// C6.1.3\n\t// Use of the stack pointer\n\t// save x0 (and reserve sp, but this is trick.)\n\tsub sp, sp, #(2*8)\n\tstr x0, [sp, #8]\n\t// ----------- ctx_save end ---------------\n\n\t// use the `ctx_save` left space, to store origin sp\n\tadd x1, sp, #0x190\n\tstr x1, [sp, #0]\n\n\t// alignment padding\n\tsub sp, sp, #0x10\n\n\t// prepare args\n\t// x0: entry address, x1: next hop, x2: RegState address\n\tldr x0, [sp, #0x190]\n\tadd x1, sp, #0x198\n\tadd x2, sp, #0x10\n\t// call function_context_end_invocation\n\tbl _function_context_end_invocation\n\n\t// restore alignment padding\n\tadd sp, sp, #0x10\n\n\t// ----------- ctx_restore begin ------------\n\t// C6.1.3\n\t// Use of the stack pointer\n\t// restore x0\n\tldr x0, [sp, #8]\n\tadd sp, sp, #(2*8)\n\n\t// restore {x1-x30}\n\tldp x1, x2, [sp], #16\n\tldp x3, x4, [sp], #16\n\tldp x5, x6, [sp], #16\n\tldp x7, x8, [sp], #16\n\tldp x9, x10, [sp], #16\n\tldp x11, x12, [sp], #16\n\tldp x13, x14, [sp], #16\n\tldp x15, x16, [sp], #16\n\tldp x17, x18, [sp], #16\n\tldp x19, x20, [sp], #16\n\tldp x21, x22, [sp], #16\n\tldp x23, x24, [sp], #16\n\tldp x25, x26, [sp], #16\n\tldp x27, x28, [sp], #16\n\t// ldp fp, lr, [sp], #16\n\tldp x29, x30, [sp], #16\n\n\t// restore {q0-q7}\n\tldp q0, q1, [sp], #32\n\tldp q2, q3, [sp], #32\n\tldp q4, q5, [sp], #32\n\tldp q6, q7, [sp], #32\n\t// ----------- ctx_restore end ------------\n\n\t// jump to next hop\n\t// next hop addess(store at reserve space)\n\tldr x17, [sp, #8]\n\tadd sp, sp, #0x10\n\tbr x17\n\n_on_enter_trampoline_template:\n\t// store entry address and reserve space for next hop\n\tsub sp, sp, 0x10\n\tldr x17, #0x8\n\tb #0xc\n\t// entry address\n\t.long 0x0\n\t.long 0x0\n\tstr x17, [sp]\n\tldr x17, #0x8\n\tbr x17\n\t// enter_thunk address\n\t.long 0x0\n\t.long 0x0\n\n_on_invoke_trampoline_template:\n\t// fix instruction\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tldr x17, #8\n\tbr x17\n\t.long 0x0\n\t.long 0x0\n\n_on_leave_trampoline_template:\n\t// store entry address and reserve space for next hop\n\tsub sp, sp, 0x10\n\tldr x17, #0x8\n\tb #0xc\n\t// entry address\n\t.long 0x0\n\t.long 0x0\n\tstr x17, [sp]\n\tldr x17, #0x8\n\tbr x17\n\t// leave_thunk address\n\t.long 0x0\n\t.long 0x0\n\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/thunker-arm64.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"thunker-arm64.h\"\n#include \"zzinfo.h\"\n#include <string.h>\n\n/*\n    Programmer’s Guide for ARMv8-A:\n        Page: (6-15)\n        Page: (6-16)\n\n    STP X9, X8, [X4]\n        Stores the doubleword in X9 to address X4 and stores the doubleword\n   in X8 to address X4 + 8. LDP X8, X2, [X0, #0x10]! Loads doubleword at\n   address X0 + 0x10 into X8 and the doubleword at address X0 + 0x10 + 8\n   into X2 and add 0x10 to X0. See Figure 6-7.\n */\n\n// 前提: 不能直接访问 pc, 也就说只有通过寄存器才能实现绝对地址跳\n\n// __attribute__((__naked__)) static void ctx_save() {\n//     __asm__ volatile(\n\n//         /* save {q0-q7} */\n//         \"sub sp, sp, #(8*16)\\n\"\n//         \"stp q6, q7, [sp, #(6*16)]\\n\"\n//         \"stp q4, q5, [sp, #(4*16)]\\n\"\n//         \"stp q2, q3, [sp, #(2*16)]\\n\"\n//         \"stp q0, q1, [sp, #(0*16)]\\n\"\n\n//         /* save {x1-x30} */\n//         \"sub sp, sp, #(30*8)\\n\"\n//         // \"stp fp, lr, [sp, #(28*8)]\\n\"\n//         \"stp x29, x30, [sp, #(28*8)]\\n\"\n//         \"stp x27, x28, [sp, #(26*8)]\\n\"\n//         \"stp x25, x26, [sp, #(24*8)]\\n\"\n//         \"stp x23, x24, [sp, #(22*8)]\\n\"\n//         \"stp x21, x22, [sp, #(20*8)]\\n\"\n//         \"stp x19, x20, [sp, #(18*8)]\\n\"\n//         \"stp x17, x18, [sp, #(16*8)]\\n\"\n//         \"stp x15, x16, [sp, #(14*8)]\\n\"\n//         \"stp x13, x14, [sp, #(12*8)]\\n\"\n//         \"stp x11, x12, [sp, #(10*8)]\\n\"\n//         \"stp x9, x10, [sp, #(8*8)]\\n\"\n//         \"stp x7, x8, [sp, #(6*8)]\\n\"\n//         \"stp x5, x6, [sp, #(4*8)]\\n\"\n//         \"stp x3, x4, [sp, #(2*8)]\\n\"\n//         \"stp x1, x2, [sp, #(0*8)]\\n\"\n\n//         // C6.1.3\n//         // Use of the stack pointer\n//         // save x0 (and reserve sp, but this is trick.)\n//         \"sub sp, sp, #(2*8)\\n\"\n//         \"str x0, [sp, #8]\\n\");\n// }\n\n// __attribute__((__naked__)) static void ctx_restore() {\n//     __asm__ volatile(\n//         // C6.1.3\n//         // Use of the stack pointer\n//         // restore x0\n//         \"ldr x0, [sp, #8]\\n\"\n//         \"add sp, sp, #(2*8)\\n\"\n\n//         /* restore {x1-x30} */\n//         \"ldp x1, x2, [sp], #16\\n\"\n//         \"ldp x3, x4, [sp], #16\\n\"\n//         \"ldp x5, x6, [sp], #16\\n\"\n//         \"ldp x7, x8, [sp], #16\\n\"\n//         \"ldp x9, x10, [sp], #16\\n\"\n//         \"ldp x11, x12, [sp], #16\\n\"\n//         \"ldp x13, x14, [sp], #16\\n\"\n//         \"ldp x15, x16, [sp], #16\\n\"\n//         \"ldp x17, x18, [sp], #16\\n\"\n//         \"ldp x19, x20, [sp], #16\\n\"\n//         \"ldp x21, x22, [sp], #16\\n\"\n//         \"ldp x23, x24, [sp], #16\\n\"\n//         \"ldp x25, x26, [sp], #16\\n\"\n//         \"ldp x27, x28, [sp], #16\\n\"\n//         // \"ldp fp, lr, [sp], #16\\n\"\n//         \"ldp x29, x30, [sp], #16\\n\"\n\n//         /* restore {q0-q7} */\n//         \"ldp q0, q1, [sp], #32\\n\"\n//         \"ldp q2, q3, [sp], #32\\n\"\n//         \"ldp q4, q5, [sp], #32\\n\"\n//         \"ldp q6, q7, [sp], #32\\n\");\n// }\n\n// just like pre_call, wow!\nvoid function_context_begin_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs,\n                                       zpointer caller_ret_addr) {\n    Xinfo(\"target %p call begin-invocation\", entry->target_ptr);\n\n    ZzThreadStack *stack = ZzGetCurrentThreadStack(entry->thread_local_key);\n    if (!stack) {\n        stack = ZzNewThreadStack(entry->thread_local_key);\n    }\n    ZzCallStack *callstack = ZzNewCallStack();\n    ZzPushCallStack(stack, callstack);\n\n    /* call pre_call */\n    if (entry->pre_call) {\n        PRECALL pre_call;\n        pre_call = entry->pre_call;\n        (*pre_call)(rs, (ThreadStack *)stack, (CallStack *)callstack);\n    }\n\n    /* set next hop */\n    if (entry->replace_call) {\n        *(zpointer *)next_hop = entry->replace_call;\n    } else {\n        *(zpointer *)next_hop = entry->on_invoke_trampoline;\n    }\n\n    if (entry->hook_type == HOOK_FUNCTION_TYPE) {\n        callstack->caller_ret_addr = *(zpointer *)caller_ret_addr;\n        *(zpointer *)caller_ret_addr = entry->on_leave_trampoline;\n    }\n}\n\n// just like post_call, wow!\nvoid function_context_half_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs,\n                                      zpointer caller_ret_addr) {\n    Xdebug(\"target %p call half-invocation\", entry->target_ptr);\n\n    ZzThreadStack *stack = ZzGetCurrentThreadStack(entry->thread_local_key);\n    if (!stack) {\n#if defined(DEBUG_MODE)\n        debug_break();\n#endif\n    }\n    ZzCallStack *callstack = ZzPopCallStack(stack);\n\n    /* call half_call */\n    if (entry->half_call) {\n        HALFCALL half_call;\n        half_call = entry->half_call;\n        (*half_call)(rs, (ThreadStack *)stack, (CallStack *)callstack);\n    }\n\n    /*  set next hop */\n    *(zpointer *)next_hop = (zpointer)entry->target_half_ret_addr;\n\n    ZzFreeCallStack(callstack);\n}\n\n// just like post_call, wow!\nvoid function_context_end_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs) {\n    Xdebug(\"%p call end-invocation\", entry->target_ptr);\n\n    ZzThreadStack *stack = ZzGetCurrentThreadStack(entry->thread_local_key);\n    if (!stack) {\n#if defined(DEBUG_MODE)\n        debug_break();\n#endif\n    }\n    ZzCallStack *callstack = ZzPopCallStack(stack);\n\n    /* call post_call */\n    if (entry->post_call) {\n        POSTCALL post_call;\n        post_call = entry->post_call;\n        (*post_call)(rs, (ThreadStack *)stack, (CallStack *)callstack);\n    }\n\n    /* set next hop */\n    *(zpointer *)next_hop = callstack->caller_ret_addr;\n    ZzFreeCallStack(callstack);\n}\n\n// __attribute__((__naked__)) void enter_thunk_template() {\n//     /* register save */\n//     __asm__ volatile(\n\n//         /* save {q0-q7} */\n//         \"sub sp, sp, #(8*16)\\n\"\n//         \"stp q6, q7, [sp, #(6*16)]\\n\"\n//         \"stp q4, q5, [sp, #(4*16)]\\n\"\n//         \"stp q2, q3, [sp, #(2*16)]\\n\"\n//         \"stp q0, q1, [sp, #(0*16)]\\n\"\n\n//         /* save {x1-x30} */\n//         \"sub sp, sp, #(30*8)\\n\"\n//         // \"stp fp, lr, [sp, #(28*8)]\\n\"\n//         \"stp x29, x30, [sp, #(28*8)]\\n\"\n//         \"stp x27, x28, [sp, #(26*8)]\\n\"\n//         \"stp x25, x26, [sp, #(24*8)]\\n\"\n//         \"stp x23, x24, [sp, #(22*8)]\\n\"\n//         \"stp x21, x22, [sp, #(20*8)]\\n\"\n//         \"stp x19, x20, [sp, #(18*8)]\\n\"\n//         \"stp x17, x18, [sp, #(16*8)]\\n\"\n//         \"stp x15, x16, [sp, #(14*8)]\\n\"\n//         \"stp x13, x14, [sp, #(12*8)]\\n\"\n//         \"stp x11, x12, [sp, #(10*8)]\\n\"\n//         \"stp x9, x10, [sp, #(8*8)]\\n\"\n//         \"stp x7, x8, [sp, #(6*8)]\\n\"\n//         \"stp x5, x6, [sp, #(4*8)]\\n\"\n//         \"stp x3, x4, [sp, #(2*8)]\\n\"\n//         \"stp x1, x2, [sp, #(0*8)]\\n\"\n\n//         // C6.1.3\n//         // Use of the stack pointer\n//         // save x0 (and reserve sp, but this is trick.)\n//         \"sub sp, sp, #(2*8)\\n\"\n//         \"str x0, [sp, #8]\\n\");\n\n//     __asm__ volatile(\n//         /* use the `ctx_save` left space, to store origin sp */\n//         \"add x1, sp, #0x190\\n\"\n//         \"str x1, [sp, #0]\\n\"\n//         /* alignment padding */\n//         \"sub sp, sp, #0x10\\n\"\n//         /* prepare args */\n//         /* x0: entry address, x1: next hop, x2: RegState address, x3: caller_ret_addr */\n//         \"ldr x0, [sp, #0x190]\\n\"\n//         \"add x1, sp, #0x198\\n\"\n//         \"add x2, sp, #0x10\\n\"\n//         \"add x3, sp, #0x108\\n\"\n//         \"ldr x17, #0xc\\n\"\n//         \"blr x17\\n\"\n//         \"b #0xc\\n\"\n//         /* function_context_begin_invocation address */\n//         /* TODO: need some trick. */\n//         // ((void (*)(void))(function_context_begin_invocation))();\n//         \".long 0x0\\n\"\n//         \".long 0x0\\n\"\n//         /* restore alignment padding */\n//         \"add sp, sp, #0x10\\n\");\n\n//     /* register restore */\n//     __asm__ volatile(\n//         // C6.1.3\n//         // Use of the stack pointer\n//         // restore x0\n//         \"ldr x0, [sp, #8]\\n\"\n//         \"add sp, sp, #(2*8)\\n\"\n\n//         /* restore {x1-x30} */\n//         \"ldp x1, x2, [sp], #16\\n\"\n//         \"ldp x3, x4, [sp], #16\\n\"\n//         \"ldp x5, x6, [sp], #16\\n\"\n//         \"ldp x7, x8, [sp], #16\\n\"\n//         \"ldp x9, x10, [sp], #16\\n\"\n//         \"ldp x11, x12, [sp], #16\\n\"\n//         \"ldp x13, x14, [sp], #16\\n\"\n//         \"ldp x15, x16, [sp], #16\\n\"\n//         \"ldp x17, x18, [sp], #16\\n\"\n//         \"ldp x19, x20, [sp], #16\\n\"\n//         \"ldp x21, x22, [sp], #16\\n\"\n//         \"ldp x23, x24, [sp], #16\\n\"\n//         \"ldp x25, x26, [sp], #16\\n\"\n//         \"ldp x27, x28, [sp], #16\\n\"\n//         // \"ldp fp, lr, [sp], #16\\n\"\n//         \"ldp x29, x30, [sp], #16\\n\"\n\n//         /* restore {q0-q7} */\n//         \"ldp q0, q1, [sp], #32\\n\"\n//         \"ldp q2, q3, [sp], #32\\n\"\n//         \"ldp q4, q5, [sp], #32\\n\"\n//         \"ldp q6, q7, [sp], #32\\n\");\n//     /* register save */\n//     __asm__ volatile(\n//         /* jump to next hop */\n//         /* next hop address(store at reserve space  */\n//         \"ldr x17, [sp, #8]\\n\"\n//         \"add sp, sp, #0x10\\n\"\n//         \"br x17\");\n// }\n\nvoid zz_arm64_thunker_build_enter_thunk(ZzWriter *writer) {\n    /* save general registers and sp */\n    zz_arm64_writer_put_bytes(writer, (void *)ctx_save, 23 * 4);\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP, 8 + CTX_SAVE_STACK_OFFSET + 2 * 8);\n\n    /* trick: use the `ctx_save` left [sp]*/\n    zz_arm64_writer_put_str_reg_reg_offset(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP, 0 * 8);\n\n    /* alignment padding + dummy PC */\n    zz_arm64_writer_put_sub_reg_reg_imm(writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 8);\n\n    /* pass enter func args */\n    /* entry */\n    zz_arm64_writer_put_ldr_reg_reg_offset(writer, ZZ_ARM64_REG_X0, ZZ_ARM64_REG_SP, 2 * 8 + 8 + CTX_SAVE_STACK_OFFSET);\n    /* next hop*/\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP,\n                                        2 * 8 + 8 + CTX_SAVE_STACK_OFFSET + 0x8);\n    /* RegState */\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X2, ZZ_ARM64_REG_SP, 2 * 8);\n    /* caller ret address */\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X3, ZZ_ARM64_REG_SP, 2 * 8 + 2 * 8 + 28 * 8 + 8);\n\n    /* call function_context_begin_invocation */\n    zz_arm64_writer_put_ldr_blr_b_reg_address(writer, ZZ_ARM64_REG_X17, (zaddr)function_context_begin_invocation);\n\n    /* alignment padding + dummy PC */\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 8);\n\n    /* restore general registers stack */\n    zz_arm64_writer_put_bytes(writer, (void *)ctx_restore, 21 * 4);\n\n    /* load next hop to x17 */\n    zz_arm64_writer_put_ldr_reg_reg_offset(writer, ZZ_ARM64_REG_X17, ZZ_ARM64_REG_SP, 0x8);\n\n    /* restore next hop and arg stack */\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 8);\n\n    /* jump to next hop */\n    zz_arm64_writer_put_br_reg(writer, ZZ_ARM64_REG_X17);\n}\n\nvoid zz_arm64_thunker_build_half_thunk(ZzWriter *writer) {\n    /* save general registers and sp */\n    zz_arm64_writer_put_bytes(writer, (void *)ctx_save, 23 * 4);\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP, 8 + CTX_SAVE_STACK_OFFSET + 2 * 8);\n\n    /* trick: use the `ctx_save` left [sp]*/\n    zz_arm64_writer_put_str_reg_reg_offset(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP, 0 * 8);\n\n    /* alignment padding + dummy PC */\n    zz_arm64_writer_put_sub_reg_reg_imm(writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 8);\n\n    /* pass enter func args */\n    /* entry */\n    zz_arm64_writer_put_ldr_reg_reg_offset(writer, ZZ_ARM64_REG_X0, ZZ_ARM64_REG_SP, 2 * 8 + 8 + CTX_SAVE_STACK_OFFSET);\n    /* next hop*/\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP,\n                                        2 * 8 + 8 + CTX_SAVE_STACK_OFFSET + 0x8);\n\n    /* RegState */\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X2, ZZ_ARM64_REG_SP, 2 * 8);\n    /* caller ret address */\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X3, ZZ_ARM64_REG_SP, 2 * 8 + 2 * 8 + 28 * 8 + 8);\n\n    /* call function_context_half_invocation */\n    zz_arm64_writer_put_ldr_blr_b_reg_address(writer, ZZ_ARM64_REG_X17, (zaddr)function_context_half_invocation);\n\n    /* alignment padding + dummy PC */\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 8);\n\n    /* restore general registers stack */\n    zz_arm64_writer_put_bytes(writer, (void *)ctx_restore, 21 * 4);\n\n    /* load next hop to x17 */\n    zz_arm64_writer_put_ldr_reg_reg_offset(writer, ZZ_ARM64_REG_X17, ZZ_ARM64_REG_SP, 0x8);\n\n    /* restore next hop and arg stack */\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 8);\n\n    /* jump to next hop */\n    zz_arm64_writer_put_br_reg(writer, ZZ_ARM64_REG_X17);\n}\n\n// __attribute__((__naked__)) void leave_thunk_template() {\n//     /* register save */\n//     __asm__ volatile(\n\n//         /* save {q0-q7} */\n//         \"sub sp, sp, #(8*16)\\n\"\n//         \"stp q6, q7, [sp, #(6*16)]\\n\"\n//         \"stp q4, q5, [sp, #(4*16)]\\n\"\n//         \"stp q2, q3, [sp, #(2*16)]\\n\"\n//         \"stp q0, q1, [sp, #(0*16)]\\n\"\n\n//         /* save {x1-x30} */\n//         \"sub sp, sp, #(30*8)\\n\"\n//         // \"stp fp, lr, [sp, #(28*8)]\\n\"\n//         \"stp x29, x30, [sp, #(28*8)]\\n\"\n//         \"stp x27, x28, [sp, #(26*8)]\\n\"\n//         \"stp x25, x26, [sp, #(24*8)]\\n\"\n//         \"stp x23, x24, [sp, #(22*8)]\\n\"\n//         \"stp x21, x22, [sp, #(20*8)]\\n\"\n//         \"stp x19, x20, [sp, #(18*8)]\\n\"\n//         \"stp x17, x18, [sp, #(16*8)]\\n\"\n//         \"stp x15, x16, [sp, #(14*8)]\\n\"\n//         \"stp x13, x14, [sp, #(12*8)]\\n\"\n//         \"stp x11, x12, [sp, #(10*8)]\\n\"\n//         \"stp x9, x10, [sp, #(8*8)]\\n\"\n//         \"stp x7, x8, [sp, #(6*8)]\\n\"\n//         \"stp x5, x6, [sp, #(4*8)]\\n\"\n//         \"stp x3, x4, [sp, #(2*8)]\\n\"\n//         \"stp x1, x2, [sp, #(0*8)]\\n\"\n\n//         // C6.1.3\n//         // Use of the stack pointer\n//         // save x0 (and reserve sp, but this is trick.)\n//         \"sub sp, sp, #(2*8)\\n\"\n//         \"str x0, [sp, #8]\\n\");\n\n//     __asm__ volatile(\n//         /* use the `ctx_save` left space, to store origin sp */\n//         \"add x1, sp, #0x190\\n\"\n//         \"str x1, [sp, #0]\\n\"\n//         /* alignment padding */\n//         \"sub sp, sp, #0x10\\n\"\n//         /* prepare args */\n//         /* x0: entry address, x1: next hop, x2: RegState address, x3: caller_ret_addr */\n//         \"ldr x0, [sp, #0x190]\\n\"\n//         \"add x1, sp, #0x198\\n\"\n//         \"add x2, sp, #0x10\\n\"\n//         \"ldr x17, #0xc\\n\"\n//         \"blr x17\\n\"\n//         \"b #0xc\\n\"\n//         /* function_context_end_invocation address */\n//         \".long 0x0\\n\"\n//         \".long 0x0\\n\"\n//         /* restore alignment padding */\n//         \"add sp, sp, #0x10\\n\");\n\n//     /* register restore */\n//     __asm__ volatile(\n//         // C6.1.3\n//         // Use of the stack pointer\n//         // restore x0\n//         \"ldr x0, [sp, #8]\\n\"\n//         \"add sp, sp, #(2*8)\\n\"\n\n//         /* restore {x1-x30} */\n//         \"ldp x1, x2, [sp], #16\\n\"\n//         \"ldp x3, x4, [sp], #16\\n\"\n//         \"ldp x5, x6, [sp], #16\\n\"\n//         \"ldp x7, x8, [sp], #16\\n\"\n//         \"ldp x9, x10, [sp], #16\\n\"\n//         \"ldp x11, x12, [sp], #16\\n\"\n//         \"ldp x13, x14, [sp], #16\\n\"\n//         \"ldp x15, x16, [sp], #16\\n\"\n//         \"ldp x17, x18, [sp], #16\\n\"\n//         \"ldp x19, x20, [sp], #16\\n\"\n//         \"ldp x21, x22, [sp], #16\\n\"\n//         \"ldp x23, x24, [sp], #16\\n\"\n//         \"ldp x25, x26, [sp], #16\\n\"\n//         \"ldp x27, x28, [sp], #16\\n\"\n//         // \"ldp fp, lr, [sp], #16\\n\"\n//         \"ldp x29, x30, [sp], #16\\n\"\n\n//         /* restore {q0-q7} */\n//         \"ldp q0, q1, [sp], #32\\n\"\n//         \"ldp q2, q3, [sp], #32\\n\"\n//         \"ldp q4, q5, [sp], #32\\n\"\n//         \"ldp q6, q7, [sp], #32\\n\");\n//     /* register save */\n//     __asm__ volatile(\n//         /* jump to next hop */\n//         /* next hop address(store at reserve space  */\n//         \"ldr x17, [sp, #8]\\n\"\n//         \"add sp, sp, #0x10\\n\"\n//         \"br x17\");\n// }\n\nvoid zz_arm64_thunker_build_leave_thunk(ZzWriter *writer) {\n    /* save general registers and sp */\n    zz_arm64_writer_put_bytes(writer, (void *)ctx_save, 23 * 4);\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP, 8 + CTX_SAVE_STACK_OFFSET + 2 * 8);\n\n    /* trick: use the `ctx_save` left [sp]*/\n    zz_arm64_writer_put_str_reg_reg_offset(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP, 0 * 8);\n\n    /* alignment padding + dummy PC */\n    zz_arm64_writer_put_sub_reg_reg_imm(writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 8);\n\n    /* pass enter func args */\n    /* entry */\n    zz_arm64_writer_put_ldr_reg_reg_offset(writer, ZZ_ARM64_REG_X0, ZZ_ARM64_REG_SP, 2 * 8 + 8 + CTX_SAVE_STACK_OFFSET);\n    /* next hop*/\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X1, ZZ_ARM64_REG_SP,\n                                        2 * 8 + 8 + CTX_SAVE_STACK_OFFSET + 0x8);\n\n    /* RegState */\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_X2, ZZ_ARM64_REG_SP, 2 * 8);\n\n    /* call function_context_end_invocation */\n    zz_arm64_writer_put_ldr_blr_b_reg_address(writer, ZZ_ARM64_REG_X17, (zaddr)function_context_end_invocation);\n\n    /* alignment padding + dummy PC */\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 8);\n\n    /* restore general registers stack */\n    zz_arm64_writer_put_bytes(writer, (void *)ctx_restore, 21 * 4);\n\n    /* load next hop to x17 */\n    zz_arm64_writer_put_ldr_reg_reg_offset(writer, ZZ_ARM64_REG_X17, ZZ_ARM64_REG_SP, 0x8);\n\n    /* restore next hop and arg stack */\n    zz_arm64_writer_put_add_reg_reg_imm(writer, ZZ_ARM64_REG_SP, ZZ_ARM64_REG_SP, 2 * 8);\n\n    /* jump to next hop */\n    zz_arm64_writer_put_br_reg(writer, ZZ_ARM64_REG_X17);\n}\n\nZZSTATUS ZzThunkerBuildThunk(ZzInterceptorBackend *self) {\n    zbyte temp_code_slice_data[512] = {0};\n    ZzArm64Writer *arm64_writer = NULL;\n    ZzCodeSlice *code_slice = NULL;\n    ZZSTATUS status = ZZ_SUCCESS;\n\n    arm64_writer = &self->arm64_writer;\n    zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);\n\n    /* build enter_thunk */\n    zz_arm64_thunker_build_enter_thunk(arm64_writer);\n\n    /* code patch */\n    code_slice = zz_code_patch_arm64_writer(arm64_writer, self->allocator, 0, 0);\n    if (code_slice)\n        self->enter_thunk = (void *)enter_thunk_template;\n    else\n        return ZZ_FAILED;\n\n    /* debug log */\n    if (ZzIsEnableDebugMode()) {\n        char buffer[1024] = {};\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzThunkerBuildThunk:\");\n        sprintf(buffer + strlen(buffer), \"LogInfo: enter_thunk at %p, use enter_thunk_template.\\n\",\n                (void *)enter_thunk_template);\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);\n\n    /* build  leave_thunk */\n    zz_arm64_thunker_build_leave_thunk(arm64_writer);\n\n    /* code patch */\n    code_slice = zz_code_patch_arm64_writer(arm64_writer, self->allocator, 0, 0);\n    if (code_slice)\n        self->leave_thunk = code_slice->data;\n    else\n        return ZZ_FAILED;\n\n    /* debug log */\n    if (ZzIsEnableDebugMode()) {\n        char buffer[1024] = {};\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzThunkerBuildThunk:\");\n        sprintf(buffer + strlen(buffer), \"LogInfo: leave_thunk at %p, length: %ld.\\n\", code_slice->data,\n                code_slice->size);\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);\n\n    /* build half_thunk */\n    zz_arm64_thunker_build_half_thunk(arm64_writer);\n\n    /* code patch */\n    code_slice = zz_code_patch_arm64_writer(arm64_writer, self->allocator, 0, 0);\n    if (code_slice)\n        self->half_thunk = code_slice->data;\n    else\n        return ZZ_FAILED;\n\n    /* debug log */\n    if (ZzIsEnableDebugMode()) {\n        char buffer[1024] = {};\n        sprintf(buffer + strlen(buffer), \"%s\\n\", \"ZzThunkerBuildThunk:\");\n        sprintf(buffer + strlen(buffer), \"LogInfo: half_thunk at %p, length: %ld.\\n\", code_slice->data,\n                code_slice->size);\n        ZzInfoLog(\"%s\", buffer);\n    }\n\n    return status;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-arm64/thunker-arm64.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_backend_arm64_thunker_arm64\n#define platforms_backend_arm64_thunker_arm64\n\n// platforms\n#include \"platforms/arch-arm64/relocator-arm64.h\"\n#include \"platforms/arch-arm64/writer-arm64.h\"\n\n#include \"interceptor-arm64.h\"\n\n// hookzz\n#include \"stack.h\"\n#include \"thunker.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-darwin/memory-darwin.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include <mach/mach.h>\n\n#include \"memory-darwin.h\"\n\nzsize ZzMemoryGetPageSzie() { return zz_posix_vm_get_page_size(); }\n\nzpointer ZzMemoryAllocatePages(zsize n_pages) { return zz_vm_allocate_pages_via_task(mach_task_self(), n_pages); }\n\nzpointer ZzMemoryAllocateNearPages(zaddr address, zsize redirect_range_size, zsize n_pages) {\n    return zz_vm_allocate_near_pages_via_task(mach_task_self(), address, redirect_range_size, n_pages);\n}\n\nzpointer ZzMemoryAllocate(zsize size) { return zz_vm_allocate_via_task(mach_task_self(), size); }\n\nzbool ZzMemoryPatchCode(const zaddr address, const zpointer codedata, zuint codedata_size) {\n    return zz_vm_patch_code_via_task(mach_task_self(), address, codedata, codedata_size);\n}\n\nzbool ZzMemoryProtectAsExecutable(const zaddr address, zsize size) {\n\n    return zz_vm_protect_as_executable_via_task(mach_task_self(), address, size);\n}\n\nzbool ZzMemoryProtectAsWritable(const zaddr address, zsize size) {\n    return zz_vm_protect_as_writable_via_task(mach_task_self(), address, size);\n}\n\nzpointer ZzMemorySearchCodeCave(zaddr address, zsize redirect_range_size, zsize size) {\n    // return zz_vm_search_text_code_cave_via_dylibs(address, redirect_range_size, size);\n    return zz_vm_search_code_cave(address, redirect_range_size, size);\n}\n\nzbool ZzMemoryIsSupportAllocateRXPage() { return zz_vm_can_allocate_rx_page(); }"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-darwin/memory-darwin.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_backend_darwin_memory_h\n#define platforms_backend_darwin_memory_h\n\n// platforms\n\n// hookzz\n#include \"hookzz.h\"\n#include \"memory.h\"\n\n// zzdeps\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/darwin/memory-utils-darwin.h\"\n#include \"zzdeps/posix/memory-utils-posix.h\"\n#include \"zzdeps/zz.h\"\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-linux/memory-linux.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"memory-linux.h\"\n\nzsize ZzMemoryGetPageSzie() { return zz_posix_vm_get_page_size(); }\n\nzpointer ZzMemoryAllocatePages(zsize n_pages) { return zz_posix_vm_allocate_pages(n_pages); }\n\nzpointer ZzMemoryAllocateNearPages(zaddr address, zsize redirect_range_size, zsize n_pages) {\n    return zz_posix_vm_allocate_near_pages(address, redirect_range_size, n_pages);\n}\n\nzpointer ZzMemoryAllocate(zsize size) { return zz_posix_vm_allocate(size); }\n\nzbool ZzMemoryPatchCode(const zaddr address, const zpointer codedata, zuint codedata_size) {\n    return zz_posix_vm_patch_code(address, codedata, codedata_size);\n}\n\nzbool ZzMemoryProtectAsExecutable(const zaddr address, zsize size) {\n\n    return zz_posix_vm_protect_as_executable(address, size);\n}\n\nzbool ZzMemoryProtectAsWritable(const zaddr address, zsize size) {\n    return zz_posxi_vm_protect_as_writable(address, size);\n}\n\nzpointer ZzMemorySearchCodeCave(zaddr address, zsize redirect_range_size, zsize size) {\n    // return zz_vm_search_text_code_cave_via_dylibs(address, redirect_range_size, size);\n    return zz_linux_vm_search_code_cave(address, redirect_range_size, size);\n}\n\nzbool ZzMemoryIsSupportAllocateRXPage() { return TRUE; }"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-linux/memory-linux.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_backend_linux_memory_h\n#define platforms_backend_linux_memory_h\n\n// platforms\n\n// hookzz\n#include \"hookzz.h\"\n#include \"memory.h\"\n\n// zzdeps\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/linux/memory-utils-linux.h\"\n#include \"zzdeps/posix/memory-utils-posix.h\"\n#include \"zzdeps/zz.h\"\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-posix/thread-posix.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"thread-posix.h\"\n\nzpointer ZzThreadNewThreadLocalKeyPtr() { return zz_posix_thread_new_thread_local_key_ptr(); }\n\nzpointer ZzThreadGetCurrentThreadData(zpointer key_ptr) {\n    return zz_posix_thread_get_current_thread_data(key_ptr);\n}\n\nzbool ZzThreadSetCurrentThreadData(zpointer key_ptr, zpointer data) {\n    return zz_posix_thread_set_current_thread_data(key_ptr, data);\n}\n\nlong ZzThreadGetCurrentThreadID() { return zz_posix_get_current_thread_id(); }"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-posix/thread-posix.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_backend_posix_thread_h\n#define platforms_backend_posix_thread_h\n\n// platforms\n\n// hookzz\n#include \"hookzz.h\"\n#include \"thread.h\"\n\n// zzdeps\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/posix/thread-utils-posix.h\"\n#include \"zzdeps/zz.h\"\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/interceptor-template-x86.s",
    "content": "// .section\t__TEXT,__text,regular,pure_instructions\n// .ios_version_min 11, 0\n.align 4\n.globl _ctx_save\n.globl _ctx_restore\n.globl _enter_thunk_template\n.globl _leave_thunk_template\n.globl _on_enter_trampoline_template\n.globl _on_invoke_trampoline_template\n.globl _on_leave_trampoline_template\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/interceptor-x86.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"interceptor-x86.h\"\n#include \"zzinfo.h\"\n#include <stdlib.h>\n#include <string.h>\n\n#define ZZ_X86_TINY_REDIRECT_SIZE 4\n#define ZZ_X86_FULL_REDIRECT_SIZE 16\n\nZzInterceptorBackend *ZzBuildInteceptorBackend(ZzAllocator *allocator) { return NULL; }\n\nZzCodeSlice *zz_code_patch_x86_writer(ZzX86Writer *x86_writer, ZzAllocator *allocator, zaddr target_addr,\n                                      zsize range_size) {\n    return NULL;\n}\nZzCodeSlice *zz_code_patch_x86_relocate_writer(ZzX86Relocator *relocator, ZzX86Writer *x86_writer,\n                                               ZzAllocator *allocator, zaddr target_addr, zsize range_size) {\n    return NULL;\n}\n\nZZSTATUS ZzPrepareTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }\n\nZZSTATUS ZzBuildEnterTransferTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }\n\nZZSTATUS ZzBuildEnterTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }\n\nZZSTATUS ZzBuildInvokeTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }\n\nZZSTATUS ZzBuildHalfTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }\n\nZZSTATUS ZzBuildLeaveTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }\n\nZZSTATUS ZzActivateTrampoline(ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) { return ZZ_FAILED; }\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/interceptor-x86.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_backend_x86_intercetor_x86\n#define platforms_backend_x86_intercetor_x86\n\n// platforms\n#include \"platforms/arch-x86/relocator-x86.h\"\n#include \"platforms/arch-x86/writer-x86.h\"\n\n// hookzz\n#include \"allocator.h\"\n#include \"interceptor.h\"\n#include \"thunker.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\n#define CTX_SAVE_STACK_OFFSET (8 + 30 * 8 + 8 * 16)\n\ntypedef struct _ZzInterceptorBackend {\n\n} ZzInterceptorBackend;\n\ntypedef struct _ZzX86HookFuntionEntryBackend {\n} ZzX86HookFunctionEntryBackend;\n\nvoid ctx_save();\nvoid ctx_restore();\nvoid enter_thunk_template();\nvoid leave_thunk_template();\nvoid on_enter_trampoline_template();\nvoid on_invoke_trampoline_template();\nvoid on_leave_trampoline_template();\n\nZzCodeSlice *zz_code_patch_x86_writer(ZzX86Writer *x86_writer, ZzAllocator *allocator, zaddr target_addr,\n                                      zsize range_size);\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/thunker-x86.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"thunker-x86.h\"\n#include \"zzinfo.h\"\n#include <string.h>\n\n\n// just like pre_call, wow!\nvoid function_context_begin_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs,\n                                       zpointer caller_ret_addr) {\n    Xinfo(\"target %p call begin-invocation\", entry->target_ptr);\n\n    ZzThreadStack *stack = ZzGetCurrentThreadStack(entry->thread_local_key);\n    if (!stack) {\n        stack = ZzNewThreadStack(entry->thread_local_key);\n    }\n    ZzCallStack *callstack = ZzNewCallStack();\n    ZzPushCallStack(stack, callstack);\n\n    /* call pre_call */\n    if (entry->pre_call) {\n        PRECALL pre_call;\n        pre_call = entry->pre_call;\n        (*pre_call)(rs, (ThreadStack *)stack, (CallStack *)callstack);\n    }\n\n    /* set next hop */\n    if (entry->replace_call) {\n        *(zpointer *)next_hop = entry->replace_call;\n    } else {\n        *(zpointer *)next_hop = entry->on_invoke_trampoline;\n    }\n\n    if (entry->hook_type == HOOK_FUNCTION_TYPE) {\n        callstack->caller_ret_addr = *(zpointer *)caller_ret_addr;\n        *(zpointer *)caller_ret_addr = entry->on_leave_trampoline;\n    }\n}\n\n// just like post_call, wow!\nvoid function_context_half_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs,\n                                      zpointer caller_ret_addr) {\n    Xdebug(\"target %p call half-invocation\", entry->target_ptr);\n\n    ZzThreadStack *stack = ZzGetCurrentThreadStack(entry->thread_local_key);\n    if (!stack) {\n#if defined(DEBUG_MODE)\n        debug_break();\n#endif\n    }\n    ZzCallStack *callstack = ZzPopCallStack(stack);\n\n    /* call half_call */\n    if (entry->half_call) {\n        HALFCALL half_call;\n        half_call = entry->half_call;\n        (*half_call)(rs, (ThreadStack *)stack, (CallStack *)callstack);\n    }\n\n    /*  set next hop */\n    *(zpointer *)next_hop = (zpointer)entry->target_half_ret_addr;\n\n    ZzFreeCallStack(callstack);\n}\n\n// just like post_call, wow!\nvoid function_context_end_invocation(ZzHookFunctionEntry *entry, zpointer next_hop, RegState *rs) {\n    Xdebug(\"%p call end-invocation\", entry->target_ptr);\n\n    ZzThreadStack *stack = ZzGetCurrentThreadStack(entry->thread_local_key);\n    if (!stack) {\n#if defined(DEBUG_MODE)\n        debug_break();\n#endif\n    }\n    ZzCallStack *callstack = ZzPopCallStack(stack);\n\n    /* call post_call */\n    if (entry->post_call) {\n        POSTCALL post_call;\n        post_call = entry->post_call;\n        (*post_call)(rs, (ThreadStack *)stack, (CallStack *)callstack);\n    }\n\n    /* set next hop */\n    *(zpointer *)next_hop = callstack->caller_ret_addr;\n    ZzFreeCallStack(callstack);\n}\n\nvoid zz_x86_thunker_build_enter_thunk(ZzWriter *writer) {\n}\n\nvoid zz_x86_thunker_build_half_thunk(ZzWriter *writer) {\n}\n\nvoid zz_x86_thunker_build_leave_thunk(ZzWriter *writer) {\n}\n\nZZSTATUS ZzThunkerBuildThunk(ZzInterceptorBackend *self) {\n    return ZZ_FAILED;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/backend-x86/thunker-x86.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef platforms_backend_x86_thunker_x86\n#define platforms_backend_x86_thunker_x86\n\n// platforms\n#include \"platforms/arch-x86/relocator-x86.h\"\n#include \"platforms/arch-x86/writer-x86.h\"\n\n#include \"interceptor-x86.h\"\n\n// hookzz\n#include \"stack.h\"\n#include \"thunker.h\"\n\n// zzdeps\n#include \"hookzz.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/x86/instructions.h",
    "content": "//    Copyright 2017 jmpews\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#ifndef instructions_h\n#define instructions_h\n\n#include \"../../../include/hookzz.h\"\n#include \"../../../include/zz.h\"\n#include \"capstone.h\"\n\n// Structs for writing x86/x64 instructions.\n\n// 8-bit relative jump.\ntypedef struct _JMP_REL_SHORT {\n    uint8_t opcode; // EB xx: JMP +2+xx\n    uint8_t operand;\n} __attribute__((packed)) JMP_REL_SHORT, *PJMP_REL_SHORT;\n\n// 32-bit direct relative jump/call.\ntypedef struct _JMP_REL {\n    uint8_t opcode;  // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx\n    zuint32 operand; // Relative destination address\n} __attribute__((packed)) JMP_REL, *PJMP_REL, CALL_REL;\n\n// 64-bit indirect absolute jump.\ntypedef struct _JMP_ABS {\n    uint8_t opcode0; // FF25 00000000: JMP [+6]\n    uint8_t opcode1;\n    zuint32 dummy;\n    uint64_t address; // Absolute destination address\n} __attribute__((packed)) JMP_ABS, *PJMP_ABS;\n\n// 64-bit indirect absolute call.\ntypedef struct _CALL_ABS {\n    uint8_t opcode0; // FF15 00000002: CALL [+6]\n    uint8_t opcode1;\n    zuint32 dummy0;\n    uint8_t dummy1; // EB 08:         JMP +10\n    uint8_t dummy2;\n    uint64_t address; // Absolute destination address\n} __attribute__((packed)) CALL_ABS;\n\n// 32-bit direct relative conditional jumps.\ntypedef struct _JCC_REL {\n    uint8_t opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx\n    uint8_t opcode1;\n    zuint32 operand; // Relative destination address\n} __attribute__((packed)) JCC_REL;\n\n/* must understand this, by jmpews */\n// 64bit indirect absolute conditional jumps that x64 lacks.\ntypedef struct _JCC_ABS {\n    /*\n        TODO:\n        need prefix ? like `uint8_t prefix;`\n     */\n    uint8_t opcode; // 7* 0E:         J** +16\n    uint8_t dummy0;\n    uint8_t dummy1; // FF25 00000000: JMP [+6]\n    uint8_t dummy2;\n    zuint32 dummy3;\n    uint64_t address; // Absolute destination address\n} __attribute__((packed)) JCC_ABS;\n\ntypedef struct _Instruction {\n    zpointer address;\n    cs_insn *ins_cs;\n    uint8_t size;\n    zbyte bytes[16];\n} ZzInstruction;\n\n// not use!!!\ntypedef struct _RelocatedInstruction {\n    ZzInstruction old_ins;\n    ZzInstruction new_ins;\n} RelocatedInstruction;\n\n// not use!!!\ntypedef struct _RelocatedTrampoline {\n    zpointer old_target;\n    zpointer new_target;\n\n    uint8_t old_size;\n    uint8_t new_size;\n\n    ZzInstruction old_inss[16];\n    ZzInstruction new_inss[16];\n} RelocatedTrampoline;\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/x86/reader.c",
    "content": "//    Copyright 2017 jmpews\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#include \"reader.h\"\n\n#include <string.h>\n\nstatic csh handle;\n\nvoid capstone_init(void)\n{\n    cs_err err = 0;\n\n#if defined(__x86_64__)\n    err = cs_open(CS_ARCH_X86, CS_MODE_64, &handle);\n#elif defined(__arm64__)\n    err = cs_open(CS_ARCH_ARM64, CS_MODE_ARM, &handle);\n#endif\n    if (err)\n    {\n        Xerror(\"Failed on cs_open() with error returned: %u\\n\", err);\n        exit(-1);\n    }\n\n    cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON);\n}\n\nstatic cs_insn *zz_arm64_reader_disassemble_at(zpointer address)\n{\n    if (!handle)\n        capstone_init();\n    cs_insn *insn;\n    size_t count;\n    count = cs_disasm(handle, address, 16, address, 0, &insn);\n    return insn;\n}\n\nvoid relocator_read_one(ZzInstruction *old_ins, ZzInstruction *new_ins)\n{\n\n    // capstone ins\n    cs_insn *ins_cs = zz_arm64_reader_disassemble_at(old_ins->address);\n\n    // capstone ins detail\n    // cs_detail *ins_csd = ins_cs->detail->x86;\n    cs_x86 ins_csd = ins_cs->detail->x86;\n\n    old_ins->ins_cs = ins_cs;\n    old_ins->size = ins_cs->size;\n    uint8_t needFix = 0;\n\n    zpointer copy_ins_start;\n    uint8_t copy_ins_size;\n\n    // https://c9x.me/x86/html/file_module_x86_id_146.html\n\n    /*\n    ATTENTION: why 0x01 ^ cond? because of use `method_1`\n\n    origin:\n        1: je <3>\n        2: push rax;\n        3: push rbx;\n\n    method_1:\n        1: jne <3>\n        2: jmp(abs) <4>\n        3: push rax\n        4: push rbx\n\n    method_2:\n        1: je <3>\n        2: jmp(near) <4>\n        3: jmp(abs) <5>\n        4: push rax\n        5: push rbx\n\n    */\n    if ((ins_csd.opcode[0] & 0xF0) == 0x70 || (ins_csd.opcode[0] & 0xFC) == 0xE0 ||\n        (ins_csd.opcode[1] & 0xF0) == 0x80)\n    {\n        // the imm is calculate by capstone, so the imm is dest;\n        zpointer dest = (zpointer)ins_csd.operands[0].imm;\n        zpointer offset = (zpointer)ins_csd.operands[0].imm - old_ins->address - old_ins->size;\n\n        zpointer new_offset = dest - new_ins->address + sizeof(JMP_ABS);\n\n        if (dest > new_ins->address && dest < (new_ins->address + sizeof(JMP_ABS)))\n        {\n            zpointer internal_jmp_dest = 0;\n            if (internal_jmp_dest < dest)\n            {\n                internal_jmp_dest = dest;\n                Xerror(\"origin: %p, trampoline: %p is trampoline-internal-jmp !\", old_ins->address, new_ins->address);\n                return;\n            }\n        }\n        else\n        {\n            needFix = 1;\n            uint8_t cond = ((ins_csd.opcode[0] != 0x0F ? ins_csd.opcode[0] : ins_csd.opcode[2]) & 0x0F);\n\n            jcc.opcode = 0x71 ^ cond;\n            jcc.address = dest;\n        }\n\n        copy_ins_start = &jcc;\n        copy_ins_size = sizeof(jcc);\n    }\n\n    if (needFix)\n    {\n        new_ins->size = copy_ins_size;\n        memcpy(new_ins->bytes, copy_ins_start, copy_ins_size);\n    }\n    else\n    {\n        /*\n            yes, we can just write to new_ins->address, according to the module of design patterns, we can't do `write` operation at here.\n            memcpy(new_ins->address, old_ins->address, old_ins->size);\n         */\n        new_ins->size = old_ins->size;\n        memcpy(new_ins->bytes, old_ins->address, old_ins->size);\n    }\n    memcpy(old_ins->bytes, old_ins->address, old_ins->size);\n}\n\nvoid relocator_invoke_trampoline(ZzTrampoline *trampoline, zpointer target, uint8_t *read_size, zpointer read_backup)\n{\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/x86/reader.h",
    "content": "//    Copyright 2017 jmpews\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#ifndef reader_h\n#define reader_h\n\n#include \"../../../include/zz.h\"\n#include \"../../../include/hookzz.h\"\n#include \"instructions.h\"\n\n#include \"../../trampoline.h\"\n\nvoid relocator_read_one(ZzInstruction *old_ins, ZzInstruction *new_ins);\n\nvoid relocator_invoke_trampoline(ZzTrampoline *trampoline, zpointer target, uint8_t *read_size, zpointer read_backup);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/x86/writer.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"writer.h\"\n#include <string.h>\n\nZzInstruction *writer_put_jmp(zpointer address)\n{\n\n    JMP_ABS jmp = {\n        0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6]\n        0x0000000000000000ULL   // Absolute destination address\n    };\n    ZzInstruction *ins = (ZzInstruction *)malloc(sizeof(ZzInstruction));\n    jmp.address = address;\n    ins->size = sizeof(jmp);\n    memcpy(ins->bytes, &jmp, sizeof(jmp));\n    return ins;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/platforms/x86/writer.h",
    "content": "//    Copyright 2017 jmpews\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#ifndef platforms_x86_writer_h\n#define platforms_x86_writer_h\n\n#include \"../../../include/zz.h\"\n#include \"../../../include/hookzz.h\"\n#include \"instructions.h\"\n\n#include \"../../trampoline.h\"\n\nZzInstruction *writer_put_jmp(zpointer address);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/relocator.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef relocator_h\n#define relocator_h\n\n#include \"hookzz.h\"\n#include \"interceptor.h\"\n#include \"writer.h\"\n\nvoid ZzRelocatorBuildInvokeTrampoline(ZzHookFunctionEntry *entry, ZzWriter *backup_writer,\n                                      ZzWriter *relocate_writer);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/stack.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include <stdlib.h>\n#include <string.h>\n\n#include \"stack.h\"\n\nZzThreadStack *ZzGetCurrentThreadStack(zpointer key_ptr) {\n    ZzThreadStack *stack = (ZzThreadStack *)ZzThreadGetCurrentThreadData(key_ptr);\n    if (!stack)\n        return NULL;\n    return stack;\n}\n\nZzThreadStack *ZzNewThreadStack(zpointer key_ptr) {\n    ZzThreadStack *stack;\n    stack = (ZzThreadStack *)malloc(sizeof(ZzThreadStack));\n    stack->capacity = 4;\n    ZzCallStack **callstacks = (ZzCallStack **)malloc(sizeof(ZzCallStack *) * (stack->capacity));\n    if (!callstacks)\n        return NULL;\n    stack->callstacks = callstacks;\n    stack->size = 0;\n    stack->key_ptr = key_ptr;\n    stack->thread_id = ZzThreadGetCurrentThreadID();\n    ZzThreadSetCurrentThreadData(key_ptr, (zpointer)stack);\n    return stack;\n}\n\nZzCallStack *ZzNewCallStack() {\n    ZzCallStack *callstack;\n    callstack = (ZzCallStack *)malloc(sizeof(ZzCallStack));\n\n    callstack->capacity = 4;\n\n    callstack->items = (ZzCallStackItem *)malloc(sizeof(ZzCallStackItem) * callstack->capacity);\n    if (!callstack->items)\n        return NULL;\n\n    callstack->size = 0;\n    return callstack;\n}\n\nvoid ZzFreeCallStack(ZzCallStack *callstack) {\n    free(callstack->items);\n    free(callstack);\n    callstack = NULL;\n}\n\nZzCallStack *ZzPopCallStack(ZzThreadStack *stack) {\n    if (stack->size > 0)\n        stack->size--;\n    else\n        return NULL;\n    ZzCallStack *callstack = stack->callstacks[stack->size];\n    return callstack;\n}\n\nzbool ZzPushCallStack(ZzThreadStack *stack, ZzCallStack *callstack) {\n    if (!stack)\n        return FALSE;\n\n    if (stack->size >= stack->capacity) {\n        ZzCallStack **callstacks =\n            (ZzCallStack **)realloc(stack->callstacks, sizeof(ZzCallStack *) * (stack->capacity) * 2);\n        if (!callstacks)\n            return FALSE;\n        stack->callstacks = callstacks;\n        stack->capacity = stack->capacity * 2;\n    }\n\n    callstack->call_id = stack->size;\n    callstack->threadstack = (ThreadStack *)stack;\n\n    stack->callstacks[stack->size++] = callstack;\n    return TRUE;\n}\n\nzpointer ZzGetCallStackData(CallStack *callstack_ptr, char *key) {\n    ZzCallStack *callstack = (ZzCallStack *)callstack_ptr;\n    if (!callstack)\n        return NULL;\n    int i;\n    for (i = 0; i < callstack->size; ++i) {\n        if (!strcmp(callstack->items[i].key, key)) {\n            return callstack->items[i].value;\n        }\n    }\n    return NULL;\n}\n\nZzCallStackItem *ZzNewCallStackData(ZzCallStack *callstack) {\n    if (!callstack)\n        return NULL;\n    if (callstack->size >= callstack->capacity) {\n        ZzCallStackItem *callstackitems =\n            (ZzCallStackItem *)realloc(callstack->items, sizeof(ZzCallStackItem) * callstack->capacity * 2);\n        if (!callstackitems)\n            return NULL;\n        callstack->items = callstackitems;\n        callstack->capacity = callstack->capacity * 2;\n    }\n    return &(callstack->items[callstack->size++]);\n}\n\nzbool ZzSetCallStackData(CallStack *callstack_ptr, char *key, zpointer value_ptr, zsize value_size) {\n    ZzCallStack *callstack = (ZzCallStack *)callstack_ptr;\n    if (!callstack)\n        return FALSE;\n\n    ZzCallStackItem *item = ZzNewCallStackData(callstack);\n\n    char *key_tmp = (char *)malloc(strlen(key) + 1);\n    strncpy(key_tmp, key, strlen(key) + 1);\n\n    zpointer value_tmp = (zpointer)malloc(value_size);\n    memcpy(value_tmp, value_ptr, value_size);\n    item->key = key_tmp;\n    item->value = value_tmp;\n    return TRUE;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/stack.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef stack_h\n#define stack_h\n\n// platforms\n\n// hookzz\n#include \"hookzz.h\"\n#include \"thread.h\"\n\n// zzdeps\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef struct _ZzCallStackItem {\n    char *key;\n    zpointer value;\n} ZzCallStackItem;\n\ntypedef struct _ZzCallStack {\n    zsize call_id;\n    ThreadStack *threadstack;\n    zsize size;\n    zsize capacity;\n    zpointer sp;\n    zpointer caller_ret_addr;\n    ZzCallStackItem *items;\n} ZzCallStack;\n\ntypedef struct _ZzThreadStack {\n    zsize thread_id;\n    zsize size;\n    zsize capacity;\n    zpointer key_ptr;\n    ZzCallStack **callstacks;\n} ZzThreadStack;\n\nZzThreadStack *ZzNewThreadStack(zpointer key_ptr);\nZzCallStack *ZzNewCallStack();\nZzThreadStack *ZzGetCurrentThreadStack(zpointer key_ptr);\nzbool ZzPushCallStack(ZzThreadStack *stack, ZzCallStack *callstack);\nZzCallStack *ZzPopCallStack(ZzThreadStack *stack);\nvoid ZzFreeCallStack(ZzCallStack *callstack);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/thread.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef thread_h\n#define thread_h\n\n// hookzz\n#include \"hookzz.h\"\n\nzpointer ZzThreadNewThreadLocalKeyPtr();\n\nzpointer ZzThreadGetCurrentThreadData(zpointer key_ptr);\n\nzbool ZzThreadSetCurrentThreadData(zpointer key_ptr, zpointer data);\n\nlong ZzThreadGetCurrentThreadID();\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/thunker.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef thunker_h\n#define thunker_h\n\n#include \"hookzz.h\"\n#include \"interceptor.h\"\n\nstruct _ZzInterceptorBackend;\nZZSTATUS ZzThunkerBuildThunk(struct _ZzInterceptorBackend *backend);\n\n#endif\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/trampoline.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include <stdlib.h>\n\n#include \"trampoline.h\"\n\nZZSTATUS ZzBuildTrampoline(struct _ZzInterceptorBackend *self, ZzHookFunctionEntry *entry) {\n\n    ZzPrepareTrampoline(self, entry);\n    ZzBuildEnterTrampoline(self, entry);\n\n    if (entry->hook_type == HOOK_ADDRESS_TYPE) {\n        ZzBuildHalfTrampoline(self, entry);\n        ZzBuildInvokeTrampoline(self, entry);\n    } else {\n        ZzBuildInvokeTrampoline(self, entry);\n        ZzBuildLeaveTrampoline(self, entry);\n    }\n\n    return ZZ_DONE;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/trampoline.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef trampoline_h\n#define trampoline_h\n\n// platforms\n\n// hookzz\n#include \"hookzz.h\"\n#include \"interceptor.h\"\n\n// zzdeps\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\n/*\n    prepare_trampline:\n        1. 判断跳板类型\n        2. 初始化\n\n    enter_trampoline:\n        1. 跳转到 `enter_thunk`\n\n    invoke_trampoline:\n        1. 之前保存的指令(涉及到指令修复)\n        2. 跳转到剩余的指令\n\n    leave_trampoline\n        1. 跳转到 `leave_thunk`\n */\n\ntypedef struct _ZzTrampoline {\n    ZzCodeSlice *code_slice;\n} ZzTrampoline;\n\nZZSTATUS ZzPrepareTrampoline(struct _ZzInterceptorBackend *self, ZzHookFunctionEntry *entry);\nZZSTATUS ZzBuildTrampoline(struct _ZzInterceptorBackend *self, ZzHookFunctionEntry *entry);\nZZSTATUS ZzActivateTrampoline(struct _ZzInterceptorBackend *self, ZzHookFunctionEntry *entry);\nstruct _ZzInterceptorBackend *ZzBuildInteceptorBackend(ZzAllocator *allocator);\n\nZZSTATUS ZzBuildEnterTrampoline(struct _ZzInterceptorBackend *self, ZzHookFunctionEntry *entry);\nZZSTATUS ZzBuildHalfTrampoline(struct _ZzInterceptorBackend *self, ZzHookFunctionEntry *entry);\nZZSTATUS ZzBuildInvokeTrampoline(struct _ZzInterceptorBackend *self, ZzHookFunctionEntry *entry);\nZZSTATUS ZzBuildLeaveTrampoline(struct _ZzInterceptorBackend *self, ZzHookFunctionEntry *entry);\n\n#ifdef TARGET_IS_IOS\nZZSTATUS ZzActivateSolidifyTrampoline(ZzHookFunctionEntry *entry, zaddr target_fileoff);\n#endif\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/writer.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef writer_h\n#define writer_h\n\n#include \"hookzz.h\"\n\n#define MAX_LITERAL_INSN_SIZE 128\n\ntypedef struct _ZzLiteralInstruction {\n    zpointer literal_insn_ptr;\n    zaddr *literal_address_ptr;\n} ZzLiteralInstruction;\n\ntypedef struct _ZzWriter {\n    zpointer codedata;\n    zpointer base;\n    zaddr pc;\n    zsize size;\n\n    ZzLiteralInstruction literal_insns[MAX_LITERAL_INSN_SIZE];\n    zsize literal_insn_size;\n\n} ZzWriter;\n\n#endif\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdefs.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#include <stdint.h> // for: uint64_t\n/*\n    -ARM64\n    http://infocenter.arm.com/help/topic/com.arm.doc.den0024a/DEN0024A_v8_architecture_PG.pdf (7.2.1\n   Floating-point) (4.6.1 Floating-point register organization in AArch64) use struct and union to\n   describe diagram in the above link, nice!\n\n    -X86\n    https://en.wikipedia.org/wiki/X86_calling_conventions\n*/\n\n#define ZZ_INT5_MASK 0x0000001f\n#define ZZ_INT8_MASK 0x000000ff\n#define ZZ_INT10_MASK 0x000003ff\n#define ZZ_INT11_MASK 0x000007ff\n#define ZZ_INT12_MASK 0x00000fff\n#define ZZ_INT14_MASK 0x00003fff\n#define ZZ_INT16_MASK 0x0000ffff\n#define ZZ_INT18_MASK 0x0003ffff\n#define ZZ_INT19_MASK 0x0007ffff\n#define ZZ_INT24_MASK 0x00ffffff\n#define ZZ_INT26_MASK 0x03ffffff\n#define ZZ_INT28_MASK 0x0fffffff\n\n#define THUMB_FUNCTION_ADDRESS(target_addr) (void *)((unsigned long)target_addr & ~(unsigned long)1)\n#define INSTRUCTION_IS_THUMB(insn_addr) ((insn_addr & 0x1) == 0x1)\n#define ALIGN_4(target_addr) ((unsigned long)target_addr & ~(unsigned long)3)"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/.gitignore",
    "content": ".DS_Store\n.idea/\n.vscode/\ncmake-build-debug/\nCMakeLists.txt\n\n# Prerequisites\n*.d\n\n# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Linker output\n*.ilk\n*.map\n*.exp\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Libraries\n*.lib\n*.a\n*.la\n*.lo\n\n# Shared objects (inc. Windows DLLs)\n*.dll\n*.so\n*.so.*\n*.dylib\n\n# Executables\n*.exe\n*.out\n*.app\n*.i*86\n*.x86_64\n*.hex\n\n# Debug files\n*.dSYM/\n*.su\n*.idb\n*.pdb\n\n# Kernel Module Compile Results\n*.mod*\n*.cmd\n.tmp_versions/\nmodules.order\nModule.symvers\nMkfile.old\ndkms.conf\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/README.md",
    "content": "## zzdeps\n\ncommon deps, C/C++ is ok and include darwin & common.\n\n#### common deps\n\nos independent.\n\n#### darwin deps\n\nabout darwin.\n\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/common/LEB128.h",
    "content": "/**\n *    Copyright 2017 jmpews\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/*\n *  refs: https://github.com/aquynh/capstone/blob/master/LEB128.h\n *  refs: http://llvm.org/docs/doxygen/html/LEB128_8h_source.html\n *  refs: ld64-274.2/src/other/dyldinfo.cpp:1760\n */\n\n#if !defined(_MSC_VER) || !defined(_KERNEL_MODE)\n\n#include <stdint.h>\n\n#endif\n\n/// Utility function to decode a ULEB128 value.\nstatic inline uint64_t decodeULEB128(const uint8_t *p, unsigned *n) {\n    const uint8_t *orig_p = p;\n    uint64_t Value = 0;\n    unsigned Shift = 0;\n    do {\n        Value += (*p & 0x7f) << Shift;\n        Shift += 7;\n    } while (*p++ >= 128);\n    if (n)\n        *n = (unsigned)(p - orig_p);\n    return Value;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/common/debugbreak.h",
    "content": "/* Copyright (c) 2011-2015, Scott Tsai\n *\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are met:\n *\n * 1. Redistributions of source code must retain the above copyright notice,\n *    this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice,\n *    this list of conditions and the following disclaimer in the documentation\n *    and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\n * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef DEBUG_BREAK_H\n#define DEBUG_BREAK_H\n\n#ifdef _MSC_VER\n\n#define debug_break __debugbreak\n\n#else\n\n#include <signal.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nenum {\n    /* gcc optimizers consider code after __builtin_trap() dead.\n     * Making __builtin_trap() unsuitable for breaking into the debugger */\n    DEBUG_BREAK_PREFER_BUILTIN_TRAP_TO_SIGTRAP = 0,\n};\n\n#if defined(__i386__) || defined(__x86_64__)\nenum {\n    HAVE_TRAP_INSTRUCTION = 1,\n};\n__attribute__((gnu_inline, always_inline)) __inline__ static void trap_instruction(void) {\n    __asm__ volatile(\"int $0x03\");\n}\n#elif defined(__thumb__) && defined(__APPLE__)\nenum {\n    HAVE_TRAP_INSTRUCTION = 1,\n};\n__attribute__((gnu_inline, always_inline)) static void __inline__ trap_instruction(void) {\n    __builtin_trap();\n}\n#elif defined(__thumb__)\nenum {\n    HAVE_TRAP_INSTRUCTION = 1,\n};\n/* FIXME: handle __THUMB_INTERWORK__ */\n__attribute__((gnu_inline, always_inline)) __inline__ static void trap_instruction(void) {\n/* See 'arm-linux-tdep.c' in GDB source.\n * Both instruction sequences below work. */\n#if 1\n    /* 'eabi_linux_thumb_le_breakpoint' */\n    __asm__ volatile(\".inst 0xde01\");\n#else\n    /* 'eabi_linux_thumb2_le_breakpoint' */\n    __asm__ volatile(\".inst.w 0xf7f0a000\");\n#endif\n\n    /* Known problem:\n     * After a breakpoint hit, can't stepi, step, or continue in GDB.\n     * 'step' stuck on the same instruction.\n     *\n     * Workaround: a new GDB command,\n     * 'debugbreak-step' is defined in debugbreak-gdb.py\n     * that does:\n     * (gdb) set $instruction_len = 2\n     * (gdb) tbreak *($pc + $instruction_len)\n     * (gdb) jump   *($pc + $instruction_len)\n     */\n}\n#elif defined(__arm__) && !defined(__thumb__) && defined(__APPLE__)\nenum {\n    HAVE_TRAP_INSTRUCTION = 1,\n};\n__attribute__((gnu_inline, always_inline)) static void __inline__ trap_instruction(void) {\n    __builtin_trap();\n}\n#elif defined(__arm__) && !defined(__thumb__)\nenum {\n    HAVE_TRAP_INSTRUCTION = 1,\n};\n__attribute__((gnu_inline, always_inline)) __inline__ static void trap_instruction(void) {\n    /* See 'arm-linux-tdep.c' in GDB source,\n     * 'eabi_linux_arm_le_breakpoint' */\n    __asm__ volatile(\".inst 0xe7f001f0\");\n    /* Has same known problem and workaround\n     * as Thumb mode */\n}\n#elif defined(__aarch64__) && defined(__APPLE__)\nenum {\n    HAVE_TRAP_INSTRUCTION = 1,\n};\n__attribute__((gnu_inline, always_inline)) static void __inline__ trap_instruction(void) {\n    __builtin_trap();\n}\n#elif defined(__aarch64__)\nenum {\n    HAVE_TRAP_INSTRUCTION = 1,\n};\n__attribute__((gnu_inline, always_inline)) __inline__ static void trap_instruction(void) {\n    /* See 'aarch64-tdep.c' in GDB source,\n     * 'aarch64_default_breakpoint' */\n    __asm__ volatile(\".inst 0xd4200000\");\n}\n#else\nenum {\n    HAVE_TRAP_INSTRUCTION = 0,\n};\n#endif\n\n__attribute__((gnu_inline, always_inline)) __inline__ static void debug_break(void) {\n    if (HAVE_TRAP_INSTRUCTION) {\n        trap_instruction();\n    } else if (DEBUG_BREAK_PREFER_BUILTIN_TRAP_TO_SIGTRAP) {\n        /* raises SIGILL on Linux x86{,-64}, to continue in gdb:\n         * (gdb) handle SIGILL stop nopass\n         * */\n        __builtin_trap();\n    } else {\n#ifdef _WIN32\n        /* SIGTRAP available only on POSIX-compliant operating systems\n         * use builtin trap instead */\n        __builtin_trap();\n#else\n        raise(SIGTRAP);\n#endif\n    }\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n\n#endif\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/common/memory-utils-common.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include <errno.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <unistd.h>\n\n#include \"memory-utils-common.h\"\n\nchar *zz_vm_read_string(const zpointer address) {\n    const char *start_addr = (const char *)address;\n    unsigned int string_limit = 1024;\n    unsigned int i;\n    char *result;\n\n    for (i = 0; i < string_limit; i++) {\n        if (*(start_addr + i) == '\\0')\n            break;\n    }\n    if (i == string_limit)\n        return NULL;\n    else {\n        result = (char *)malloc(i + 1);\n        memcpy(result, (const zpointer)start_addr, i + 1);\n        return result;\n    }\n}\n\nzpointer zz_vm_search_data(const zpointer start_addr, zpointer end_addr, zbyte *data,\n                           zsize data_len) {\n    zpointer curr_addr;\n    if (start_addr <= 0)\n        Xerror(\"search address start_addr(%p) < 0\", (zpointer)start_addr);\n    if (start_addr > end_addr)\n        Xerror(\"search start_add(%p) < end_addr(%p)\", (zpointer)start_addr, (zpointer)end_addr);\n\n    curr_addr = start_addr;\n\n    while (end_addr > curr_addr) {\n        if (!memcmp(curr_addr, data, data_len)) {\n            return curr_addr;\n        }\n        curr_addr += data_len;\n    }\n    return 0;\n}\n\nzaddr zz_vm_align_floor(zaddr address, zsize range_size) {\n    zaddr result;\n    result = address & ~(range_size - 1);\n    return result;\n}\n\nzaddr zz_vm_align_ceil(zaddr address, zsize range_size) {\n    zaddr result;\n    result = (address + range_size - 1) & ~(range_size - 1);\n    return result;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/common/memory-utils-common.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef zzdeps_common_memory_utils_common_h\n#define zzdeps_common_memory_utils_common_h\n\n#include <err.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"../zz.h\"\n\nchar *zz_vm_read_string(const zpointer address);\n\nzpointer zz_vm_search_data(const zpointer start_addr, const zpointer end_addr, zbyte *data,\n                           zsize data_len);\n\nzaddr zz_vm_align_floor(zaddr address, zsize range_size);\n\nzaddr zz_vm_align_ceil(zaddr address, zsize range_size);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/darwin/mach_vm.h",
    "content": "#ifndef    _mach_vm_user_\n#define    _mach_vm_user_\n\n/* Module mach_vm */\n\n#include <string.h>\n#include <mach/ndr.h>\n#include <mach/boolean.h>\n#include <mach/kern_return.h>\n#include <mach/notify.h>\n#include <mach/mach_types.h>\n#include <mach/message.h>\n#include <mach/mig_errors.h>\n#include <mach/port.h>\n\n/* BEGIN MIG_STRNCPY_ZEROFILL CODE */\n\n#if defined(__has_include)\n#if __has_include(<mach/mig_strncpy_zerofill_support.h>)\n#ifndef USING_MIG_STRNCPY_ZEROFILL\n#define USING_MIG_STRNCPY_ZEROFILL\n#endif\n#ifndef __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__\n#define __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nextern int mig_strncpy_zerofill(char *dest, const char *src, int len) __attribute__((weak_import));\n#ifdef __cplusplus\n}\n#endif\n#endif /* __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__ */\n#endif /* __has_include(<mach/mig_strncpy_zerofill_support.h>) */\n#endif /* __has_include */\n\n/* END MIG_STRNCPY_ZEROFILL CODE */\n\n\n#ifdef AUTOTEST\n#ifndef FUNCTION_PTR_T\n#define FUNCTION_PTR_T\ntypedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t);\ntypedef struct {\n        char            *name;\n        function_ptr_t  function;\n} function_table_entry;\ntypedef function_table_entry   *function_table_t;\n#endif /* FUNCTION_PTR_T */\n#endif /* AUTOTEST */\n\n#ifndef    mach_vm_MSG_COUNT\n#define    mach_vm_MSG_COUNT    20\n#endif    /* mach_vm_MSG_COUNT */\n\n#include <mach/std_types.h>\n#include <mach/mig.h>\n#include <mach/mig.h>\n#include <mach/mach_types.h>\n#include <mach_debug/mach_debug_types.h>\n\n#ifdef __BeforeMigUserHeader\n__BeforeMigUserHeader\n#endif /* __BeforeMigUserHeader */\n\n#include <sys/cdefs.h>\n\n__BEGIN_DECLS\n\n\n/* Routine mach_vm_allocate */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_allocate\n        (\n                vm_map_t target,\n                mach_vm_address_t *address,\n                mach_vm_size_t size,\n                int flags\n        );\n\n/* Routine mach_vm_deallocate */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_deallocate\n        (\n                vm_map_t target,\n                mach_vm_address_t address,\n                mach_vm_size_t size\n        );\n\n/* Routine mach_vm_protect */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_protect\n        (\n                vm_map_t target_task,\n                mach_vm_address_t address,\n                mach_vm_size_t size,\n                boolean_t set_maximum,\n                vm_prot_t new_protection\n        );\n\n/* Routine mach_vm_inherit */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_inherit\n        (\n                vm_map_t target_task,\n                mach_vm_address_t address,\n                mach_vm_size_t size,\n                vm_inherit_t new_inheritance\n        );\n\n/* Routine mach_vm_read */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_read\n        (\n                vm_map_t target_task,\n                mach_vm_address_t address,\n                mach_vm_size_t size,\n                vm_offset_t *data,\n                mach_msg_type_number_t *dataCnt\n        );\n\n/* Routine mach_vm_read_list */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_read_list\n        (\n                vm_map_t target_task,\n                mach_vm_read_entry_t data_list,\n                natural_t count\n        );\n\n/* Routine mach_vm_write */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_write\n        (\n                vm_map_t target_task,\n                mach_vm_address_t address,\n                vm_offset_t data,\n                mach_msg_type_number_t dataCnt\n        );\n\n/* Routine mach_vm_copy */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_copy\n        (\n                vm_map_t target_task,\n                mach_vm_address_t source_address,\n                mach_vm_size_t size,\n                mach_vm_address_t dest_address\n        );\n\n/* Routine mach_vm_read_overwrite */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_read_overwrite\n        (\n                vm_map_t target_task,\n                mach_vm_address_t address,\n                mach_vm_size_t size,\n                mach_vm_address_t data,\n                mach_vm_size_t *outsize\n        );\n\n/* Routine mach_vm_msync */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_msync\n        (\n                vm_map_t target_task,\n                mach_vm_address_t address,\n                mach_vm_size_t size,\n                vm_sync_t sync_flags\n        );\n\n/* Routine mach_vm_behavior_set */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_behavior_set\n        (\n                vm_map_t target_task,\n                mach_vm_address_t address,\n                mach_vm_size_t size,\n                vm_behavior_t new_behavior\n        );\n\n/* Routine mach_vm_map */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_map\n        (\n                vm_map_t target_task,\n                mach_vm_address_t *address,\n                mach_vm_size_t size,\n                mach_vm_offset_t mask,\n                int flags,\n                mem_entry_name_port_t object,\n                memory_object_offset_t offset,\n                boolean_t copy,\n                vm_prot_t cur_protection,\n                vm_prot_t max_protection,\n                vm_inherit_t inheritance\n        );\n\n/* Routine mach_vm_machine_attribute */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_machine_attribute\n        (\n                vm_map_t target_task,\n                mach_vm_address_t address,\n                mach_vm_size_t size,\n                vm_machine_attribute_t attribute,\n                vm_machine_attribute_val_t *value\n        );\n\n/* Routine mach_vm_remap */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_remap\n        (\n                vm_map_t target_task,\n                mach_vm_address_t *target_address,\n                mach_vm_size_t size,\n                mach_vm_offset_t mask,\n                int flags,\n                vm_map_t src_task,\n                mach_vm_address_t src_address,\n                boolean_t copy,\n                vm_prot_t *cur_protection,\n                vm_prot_t *max_protection,\n                vm_inherit_t inheritance\n        );\n\n/* Routine mach_vm_page_query */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_page_query\n        (\n                vm_map_t target_map,\n                mach_vm_offset_t offset,\n                integer_t *disposition,\n                integer_t *ref_count\n        );\n\n/* Routine mach_vm_region_recurse */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_region_recurse\n        (\n                vm_map_t target_task,\n                mach_vm_address_t *address,\n                mach_vm_size_t *size,\n                natural_t *nesting_depth,\n                vm_region_recurse_info_t info,\n                mach_msg_type_number_t *infoCnt\n        );\n\n/* Routine mach_vm_region */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_region\n        (\n                vm_map_t target_task,\n                mach_vm_address_t *address,\n                mach_vm_size_t *size,\n                vm_region_flavor_t flavor,\n                vm_region_info_t info,\n                mach_msg_type_number_t *infoCnt,\n                mach_port_t *object_name\n        );\n\n/* Routine _mach_make_memory_entry */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t _mach_make_memory_entry\n        (\n                vm_map_t target_task,\n                memory_object_size_t *size,\n                memory_object_offset_t offset,\n                vm_prot_t permission,\n                mem_entry_name_port_t *object_handle,\n                mem_entry_name_port_t parent_handle\n        );\n\n/* Routine mach_vm_purgable_control */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_purgable_control\n        (\n                vm_map_t target_task,\n                mach_vm_address_t address,\n                vm_purgable_t control,\n                int *state\n        );\n\n/* Routine mach_vm_page_info */\n#ifdef    mig_external\nmig_external\n#else\nextern\n#endif    /* mig_external */\nkern_return_t mach_vm_page_info\n        (\n                vm_map_t target_task,\n                mach_vm_address_t address,\n                vm_page_info_flavor_t flavor,\n                vm_page_info_t info,\n                mach_msg_type_number_t *infoCnt\n        );\n\n__END_DECLS\n\n/********************** Caution **************************/\n/* The following data types should be used to calculate  */\n/* maximum message sizes only. The actual message may be */\n/* smaller, and the position of the arguments within the */\n/* message layout may vary from what is presented here.  */\n/* For example, if any of the arguments are variable-    */\n/* sized, and less than the maximum is sent, the data    */\n/* will be packed tight in the actual message to reduce  */\n/* the presence of holes.                                */\n/********************** Caution **************************/\n\n/* typedefs for all requests */\n\n#ifndef __Request__mach_vm_subsystem__defined\n#define __Request__mach_vm_subsystem__defined\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    mach_vm_size_t size;\n    int flags;\n} __Request__mach_vm_allocate_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    mach_vm_size_t size;\n} __Request__mach_vm_deallocate_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    mach_vm_size_t size;\n    boolean_t set_maximum;\n    vm_prot_t new_protection;\n} __Request__mach_vm_protect_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    mach_vm_size_t size;\n    vm_inherit_t new_inheritance;\n} __Request__mach_vm_inherit_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    mach_vm_size_t size;\n} __Request__mach_vm_read_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_read_entry_t data_list;\n    natural_t count;\n} __Request__mach_vm_read_list_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    /* start of the kernel processed data */\n    mach_msg_body_t msgh_body;\n    mach_msg_ool_descriptor_t data;\n    /* end of the kernel processed data */\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    mach_msg_type_number_t dataCnt;\n} __Request__mach_vm_write_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t source_address;\n    mach_vm_size_t size;\n    mach_vm_address_t dest_address;\n} __Request__mach_vm_copy_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    mach_vm_size_t size;\n    mach_vm_address_t data;\n} __Request__mach_vm_read_overwrite_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    mach_vm_size_t size;\n    vm_sync_t sync_flags;\n} __Request__mach_vm_msync_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    mach_vm_size_t size;\n    vm_behavior_t new_behavior;\n} __Request__mach_vm_behavior_set_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    /* start of the kernel processed data */\n    mach_msg_body_t msgh_body;\n    mach_msg_port_descriptor_t object;\n    /* end of the kernel processed data */\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    mach_vm_size_t size;\n    mach_vm_offset_t mask;\n    int flags;\n    memory_object_offset_t offset;\n    boolean_t copy;\n    vm_prot_t cur_protection;\n    vm_prot_t max_protection;\n    vm_inherit_t inheritance;\n} __Request__mach_vm_map_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    mach_vm_size_t size;\n    vm_machine_attribute_t attribute;\n    vm_machine_attribute_val_t value;\n} __Request__mach_vm_machine_attribute_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    /* start of the kernel processed data */\n    mach_msg_body_t msgh_body;\n    mach_msg_port_descriptor_t src_task;\n    /* end of the kernel processed data */\n    NDR_record_t NDR;\n    mach_vm_address_t target_address;\n    mach_vm_size_t size;\n    mach_vm_offset_t mask;\n    int flags;\n    mach_vm_address_t src_address;\n    boolean_t copy;\n    vm_inherit_t inheritance;\n} __Request__mach_vm_remap_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_offset_t offset;\n} __Request__mach_vm_page_query_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    natural_t nesting_depth;\n    mach_msg_type_number_t infoCnt;\n} __Request__mach_vm_region_recurse_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    vm_region_flavor_t flavor;\n    mach_msg_type_number_t infoCnt;\n} __Request__mach_vm_region_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    /* start of the kernel processed data */\n    mach_msg_body_t msgh_body;\n    mach_msg_port_descriptor_t parent_handle;\n    /* end of the kernel processed data */\n    NDR_record_t NDR;\n    memory_object_size_t size;\n    memory_object_offset_t offset;\n    vm_prot_t permission;\n} __Request___mach_make_memory_entry_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    vm_purgable_t control;\n    int state;\n} __Request__mach_vm_purgable_control_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    vm_page_info_flavor_t flavor;\n    mach_msg_type_number_t infoCnt;\n} __Request__mach_vm_page_info_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n#endif /* !__Request__mach_vm_subsystem__defined */\n\n/* union of all requests */\n\n#ifndef __RequestUnion__mach_vm_subsystem__defined\n#define __RequestUnion__mach_vm_subsystem__defined\nunion __RequestUnion__mach_vm_subsystem {\n    __Request__mach_vm_allocate_t Request_mach_vm_allocate;\n    __Request__mach_vm_deallocate_t Request_mach_vm_deallocate;\n    __Request__mach_vm_protect_t Request_mach_vm_protect;\n    __Request__mach_vm_inherit_t Request_mach_vm_inherit;\n    __Request__mach_vm_read_t Request_mach_vm_read;\n    __Request__mach_vm_read_list_t Request_mach_vm_read_list;\n    __Request__mach_vm_write_t Request_mach_vm_write;\n    __Request__mach_vm_copy_t Request_mach_vm_copy;\n    __Request__mach_vm_read_overwrite_t Request_mach_vm_read_overwrite;\n    __Request__mach_vm_msync_t Request_mach_vm_msync;\n    __Request__mach_vm_behavior_set_t Request_mach_vm_behavior_set;\n    __Request__mach_vm_map_t Request_mach_vm_map;\n    __Request__mach_vm_machine_attribute_t Request_mach_vm_machine_attribute;\n    __Request__mach_vm_remap_t Request_mach_vm_remap;\n    __Request__mach_vm_page_query_t Request_mach_vm_page_query;\n    __Request__mach_vm_region_recurse_t Request_mach_vm_region_recurse;\n    __Request__mach_vm_region_t Request_mach_vm_region;\n    __Request___mach_make_memory_entry_t Request__mach_make_memory_entry;\n    __Request__mach_vm_purgable_control_t Request_mach_vm_purgable_control;\n    __Request__mach_vm_page_info_t Request_mach_vm_page_info;\n};\n#endif /* !__RequestUnion__mach_vm_subsystem__defined */\n/* typedefs for all replies */\n\n#ifndef __Reply__mach_vm_subsystem__defined\n#define __Reply__mach_vm_subsystem__defined\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n    mach_vm_address_t address;\n} __Reply__mach_vm_allocate_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n} __Reply__mach_vm_deallocate_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n} __Reply__mach_vm_protect_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n} __Reply__mach_vm_inherit_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    /* start of the kernel processed data */\n    mach_msg_body_t msgh_body;\n    mach_msg_ool_descriptor_t data;\n    /* end of the kernel processed data */\n    NDR_record_t NDR;\n    mach_msg_type_number_t dataCnt;\n} __Reply__mach_vm_read_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n    mach_vm_read_entry_t data_list;\n} __Reply__mach_vm_read_list_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n} __Reply__mach_vm_write_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n} __Reply__mach_vm_copy_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n    mach_vm_size_t outsize;\n} __Reply__mach_vm_read_overwrite_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n} __Reply__mach_vm_msync_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n} __Reply__mach_vm_behavior_set_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n    mach_vm_address_t address;\n} __Reply__mach_vm_map_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n    vm_machine_attribute_val_t value;\n} __Reply__mach_vm_machine_attribute_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n    mach_vm_address_t target_address;\n    vm_prot_t cur_protection;\n    vm_prot_t max_protection;\n} __Reply__mach_vm_remap_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n    integer_t disposition;\n    integer_t ref_count;\n} __Reply__mach_vm_page_query_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n    mach_vm_address_t address;\n    mach_vm_size_t size;\n    natural_t nesting_depth;\n    mach_msg_type_number_t infoCnt;\n    int info[19];\n} __Reply__mach_vm_region_recurse_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    /* start of the kernel processed data */\n    mach_msg_body_t msgh_body;\n    mach_msg_port_descriptor_t object_name;\n    /* end of the kernel processed data */\n    NDR_record_t NDR;\n    mach_vm_address_t address;\n    mach_vm_size_t size;\n    mach_msg_type_number_t infoCnt;\n    int info[10];\n} __Reply__mach_vm_region_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    /* start of the kernel processed data */\n    mach_msg_body_t msgh_body;\n    mach_msg_port_descriptor_t object_handle;\n    /* end of the kernel processed data */\n    NDR_record_t NDR;\n    memory_object_size_t size;\n} __Reply___mach_make_memory_entry_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n    int state;\n} __Reply__mach_vm_purgable_control_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n\n#ifdef  __MigPackStructs\n#pragma pack(4)\n#endif\ntypedef struct {\n    mach_msg_header_t Head;\n    NDR_record_t NDR;\n    kern_return_t RetCode;\n    mach_msg_type_number_t infoCnt;\n    int info[32];\n} __Reply__mach_vm_page_info_t __attribute__((unused));\n#ifdef  __MigPackStructs\n#pragma pack()\n#endif\n#endif /* !__Reply__mach_vm_subsystem__defined */\n\n/* union of all replies */\n\n#ifndef __ReplyUnion__mach_vm_subsystem__defined\n#define __ReplyUnion__mach_vm_subsystem__defined\nunion __ReplyUnion__mach_vm_subsystem {\n    __Reply__mach_vm_allocate_t Reply_mach_vm_allocate;\n    __Reply__mach_vm_deallocate_t Reply_mach_vm_deallocate;\n    __Reply__mach_vm_protect_t Reply_mach_vm_protect;\n    __Reply__mach_vm_inherit_t Reply_mach_vm_inherit;\n    __Reply__mach_vm_read_t Reply_mach_vm_read;\n    __Reply__mach_vm_read_list_t Reply_mach_vm_read_list;\n    __Reply__mach_vm_write_t Reply_mach_vm_write;\n    __Reply__mach_vm_copy_t Reply_mach_vm_copy;\n    __Reply__mach_vm_read_overwrite_t Reply_mach_vm_read_overwrite;\n    __Reply__mach_vm_msync_t Reply_mach_vm_msync;\n    __Reply__mach_vm_behavior_set_t Reply_mach_vm_behavior_set;\n    __Reply__mach_vm_map_t Reply_mach_vm_map;\n    __Reply__mach_vm_machine_attribute_t Reply_mach_vm_machine_attribute;\n    __Reply__mach_vm_remap_t Reply_mach_vm_remap;\n    __Reply__mach_vm_page_query_t Reply_mach_vm_page_query;\n    __Reply__mach_vm_region_recurse_t Reply_mach_vm_region_recurse;\n    __Reply__mach_vm_region_t Reply_mach_vm_region;\n    __Reply___mach_make_memory_entry_t Reply__mach_make_memory_entry;\n    __Reply__mach_vm_purgable_control_t Reply_mach_vm_purgable_control;\n    __Reply__mach_vm_page_info_t Reply_mach_vm_page_info;\n};\n#endif /* !__RequestUnion__mach_vm_subsystem__defined */\n\n#ifndef subsystem_to_name_map_mach_vm\n#define subsystem_to_name_map_mach_vm \\\n    { \"mach_vm_allocate\", 4800 },\\\n    { \"mach_vm_deallocate\", 4801 },\\\n    { \"mach_vm_protect\", 4802 },\\\n    { \"mach_vm_inherit\", 4803 },\\\n    { \"mach_vm_read\", 4804 },\\\n    { \"mach_vm_read_list\", 4805 },\\\n    { \"mach_vm_write\", 4806 },\\\n    { \"mach_vm_copy\", 4807 },\\\n    { \"mach_vm_read_overwrite\", 4808 },\\\n    { \"mach_vm_msync\", 4809 },\\\n    { \"mach_vm_behavior_set\", 4810 },\\\n    { \"mach_vm_map\", 4811 },\\\n    { \"mach_vm_machine_attribute\", 4812 },\\\n    { \"mach_vm_remap\", 4813 },\\\n    { \"mach_vm_page_query\", 4814 },\\\n    { \"mach_vm_region_recurse\", 4815 },\\\n    { \"mach_vm_region\", 4816 },\\\n    { \"_mach_make_memory_entry\", 4817 },\\\n    { \"mach_vm_purgable_control\", 4818 },\\\n    { \"mach_vm_page_info\", 4819 }\n#endif\n\n#ifdef __AfterMigUserHeader\n__AfterMigUserHeader\n#endif /* __AfterMigUserHeader */\n\n#endif     /* _mach_vm_user_ */\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/darwin/macho-utils-darwin.c",
    "content": "\n/**\n *    Copyright 2017 jmpews\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#include <mach-o/dyld.h>\n#include <mach-o/dyld_images.h>\n#include <mach-o/nlist.h>\n#include <mach/task_info.h>\n\n#include \"../common/debugbreak.h\"\n#include \"../darwin/memory-utils-darwin.h\"\n#include \"macho-utils-darwin.h\"\n\n#if 0\n#ifdef __LP64__\n#define mach_hdr struct mach_header_64\n#define sgmt_cmd struct segment_command_64\n#define sect_cmd struct section_64\n#define nlist_ struct nlist_64\n#define LC_SGMT LC_SEGMENT_64\n#define MH_MAGIC_ MH_MAGIC_64\n#else\n#define mach_hdr struct mach_header\n#define sgmt_cmd struct segment_command\n#define sect_cmd struct section\n#define nlist_ struct nlist\n#define LC_SGMT LC_SEGMENT\n#define MH_MAGIC_ MH_MAGIC\n#endif\n#define load_cmd struct load_command\n#endif\n\n// get dyld load address by task_info, TASK_DYLD_INFO\nzpointer zz_macho_get_dyld_load_address_via_task(task_t task) {\n    // http://stackoverflow.com/questions/4309117/determining-programmatically-what-modules-are-loaded-in-another-process-os-x\n    kern_return_t kr;\n    task_flavor_t flavor = TASK_DYLD_INFO;\n    task_dyld_info_data_t infoData;\n    mach_msg_type_number_t task_info_outCnt = TASK_DYLD_INFO_COUNT;\n    kr = task_info(task, flavor, (task_info_t)&infoData, &task_info_outCnt);\n    if (kr != KERN_SUCCESS) {\n        KR_ERROR(kr);\n        return 0;\n    }\n    struct dyld_all_image_infos *allImageInfos = (struct dyld_all_image_infos *)infoData.all_image_info_addr;\n    allImageInfos = (struct dyld_all_image_infos *)malloc(sizeof(struct dyld_all_image_infos));\n    if (zz_vm_read_data_via_task(task, infoData.all_image_info_addr, allImageInfos,\n                                 sizeof(struct dyld_all_image_infos))) {\n        return (zpointer)(allImageInfos->dyldImageLoadAddress);\n    } else {\n        return NULL;\n    }\n}\n\ntask_t zz_darwin_get_task_via_pid(int pid) {\n    task_t t;\n    kern_return_t kr = task_for_pid(mach_task_self(), pid, &t);\n    if (kr != KERN_SUCCESS) {\n        KR_ERROR(kr);\n        return 0;\n    }\n    return t;\n}\n\nstruct segment_command_64 *zz_macho_get_segment_64_via_name(struct mach_header_64 *header, char *segment_name) {\n    struct load_command *load_cmd;\n    struct segment_command_64 *seg_cmd_64;\n    struct section_64 *sect_64;\n\n    load_cmd = (zpointer)header + sizeof(struct mach_header_64);\n    zsize i;\n    for (i = 0; i < header->ncmds; i++, load_cmd = (zpointer)load_cmd + load_cmd->cmdsize) {\n        if (load_cmd->cmd == LC_SEGMENT_64) {\n            seg_cmd_64 = (struct segment_command_64 *)load_cmd;\n            if (!strcmp(seg_cmd_64->segname, segment_name)) {\n                return seg_cmd_64;\n            }\n        }\n    }\n    return NULL;\n}\n\nstruct section_64 *zz_macho_get_section_64_via_name(struct mach_header_64 *header, char *sect_name) {\n    struct load_command *load_cmd;\n    struct segment_command_64 *seg_cmd_64;\n    struct section_64 *sect_64;\n\n    load_cmd = (zpointer)header + sizeof(struct mach_header_64);\n    zsize i;\n    zsize j;\n    for (i = 0; i < header->ncmds; i++, load_cmd = (zpointer)load_cmd + load_cmd->cmdsize) {\n        if (load_cmd->cmd == LC_SEGMENT_64) {\n            seg_cmd_64 = (struct segment_command_64 *)load_cmd;\n            sect_64 = (struct section_64 *)((zpointer)seg_cmd_64 + sizeof(struct segment_command_64));\n            for (j = 0; j < seg_cmd_64->nsects; j++, sect_64 = (zpointer)sect_64 + sizeof(struct section_64)) {\n                if (!strcmp(sect_64->sectname, sect_name)) {\n                    return sect_64;\n                }\n            }\n        }\n    }\n    return NULL;\n}\n\nstruct load_command *zz_macho_get_load_command_via_cmd(struct mach_header_64 *header, zuint32 cmd) {\n    struct load_command *load_cmd;\n    struct segment_command_64 *seg_cmd_64;\n    struct section_64 *sect_64;\n    zsize i;\n\n    load_cmd = (zpointer)header + sizeof(struct mach_header_64);\n    for (i = 0; i < header->ncmds; i++, load_cmd = (zpointer)load_cmd + load_cmd->cmdsize) {\n        if (load_cmd->cmd == cmd) {\n            return load_cmd;\n        }\n    }\n    return NULL;\n}\n\nzpointer zz_macho_get_symbol_via_name(struct mach_header_64 *header, const char *name) {\n\n    struct segment_command_64 *seg_cmd_64 =\n        zz_macho_get_segment_64_via_name((struct mach_header_64 *)header, (char *)\"__TEXT\");\n    struct segment_command_64 *seg_cmd_64_linkedit =\n        zz_macho_get_segment_64_via_name((struct mach_header_64 *)header, (char *)\"__LINKEDIT\");\n    zsize slide = (zaddr)header - (zaddr)seg_cmd_64->vmaddr;\n    zsize linkEditBase = seg_cmd_64_linkedit->vmaddr - seg_cmd_64_linkedit->fileoff + slide;\n    struct symtab_command *symtab = (struct symtab_command *)zz_macho_get_load_command_via_cmd(header, LC_SYMTAB);\n\n    char *sym_str_table = (char *)linkEditBase + symtab->stroff;\n    struct nlist_64 *sym_table = (struct nlist_64 *)(linkEditBase + symtab->symoff);\n\n    int i;\n    for (i = 0; i < symtab->nsyms; i++) {\n        if (sym_table[i].n_value && !strcmp(name, &sym_str_table[sym_table[i].n_un.n_strx])) {\n            return (void *)(uint64_t)(sym_table[i].n_value + slide);\n        }\n    }\n    return 0;\n}\n\nzpointer zz_macho_get_section_64_address_via_name(struct mach_header_64 *header, char *sect_name) {\n    struct load_command *load_cmd;\n    struct segment_command_64 *seg_cmd_64;\n    struct section_64 *sect_64;\n    zsize slide, linkEditBase;\n    zsize i, j;\n\n    load_cmd = (zpointer)header + sizeof(struct mach_header_64);\n    for (i = 0; i < header->ncmds; i++, load_cmd = (zpointer)load_cmd + load_cmd->cmdsize) {\n        if (load_cmd->cmd == LC_SEGMENT_64) {\n            seg_cmd_64 = (struct segment_command_64 *)load_cmd;\n            if ((seg_cmd_64->fileoff == 0) && (seg_cmd_64->filesize != 0)) {\n                slide = (uintptr_t)header - seg_cmd_64->vmaddr;\n            }\n            if (strcmp(seg_cmd_64->segname, \"__LINKEDIT\") == 0) {\n                linkEditBase = seg_cmd_64->vmaddr - seg_cmd_64->fileoff + slide;\n            }\n            sect_64 = (struct section_64 *)((zpointer)seg_cmd_64 + sizeof(struct segment_command_64));\n            for (j = 0; j < seg_cmd_64->nsects; j++, sect_64 = (zpointer)sect_64 + sizeof(struct section_64)) {\n                if (!strcmp(sect_64->sectname, sect_name)) {\n                    return (zpointer)(sect_64->addr + slide);\n                }\n            }\n        }\n    }\n    return NULL;\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/darwin/macho-utils-darwin.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef zzdeps_darwin_macho_utils_h\n#define zzdeps_darwin_macho_utils_h\n\n#include <err.h>\n#include <mach/task.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include <mach-o/dyld.h>\n\n#include \"../zz.h\"\n\nzpointer zz_macho_get_dyld_load_address_via_task(task_t task);\n\ntask_t zz_darwin_get_task_via_pid(int pid);\n\nstruct section_64 *zz_macho_get_section_64_via_name(struct mach_header_64 *header, char *sect_name);\n\nstruct segment_command_64 *zz_macho_get_segment_64_via_name(struct mach_header_64 *header,\n                                                            char *segment_name);\n\nzpointer zz_macho_get_section_64_address_via_name(struct mach_header_64 *header, char *sect_name);\n\nzpointer zz_macho_get_symbol_via_name(struct mach_header_64 *header, const char *name);\n\nstruct load_command *zz_macho_get_load_command_via_cmd(struct mach_header_64 *header, zuint32 cmd);\n\n#endif\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/darwin/memory-utils-darwin.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include <errno.h>\n#include <sys/mman.h>\n\n#include <mach-o/dyld.h>\n#include <mach/mach.h>\n\n// for : getpagesize,\n#include <unistd.h>\n\n// for : vm_read_overwrite\n#include <mach/vm_map.h>\n\n#include \"../common/debugbreak.h\"\n#include \"../common/memory-utils-common.h\"\n#include \"../memory-utils.h\"\n#include \"../posix/memory-utils-posix.h\"\n\n#include \"macho-utils-darwin.h\"\n#include \"memory-utils-darwin.h\"\n\n// --- about read function ---\n\nzbool zz_vm_read_data_via_task(task_t task, const zaddr address, zpointer buffer, zsize length) {\n    vm_size_t dataCnt;\n    dataCnt = 0;\n    if (address <= 0) {\n        Xerror(\"read address %p< 0\", (zpointer)address);\n        return FALSE;\n    }\n    if (length <= 0) {\n        Xerror(\"read length %p <0\", (zpointer)address);\n        return FALSE;\n    }\n    dataCnt = length;\n    kern_return_t kr = vm_read_overwrite(task, address, length, (zaddr)buffer, (vm_size_t *)&dataCnt);\n\n    if (kr != KERN_SUCCESS) {\n        // KR_ERROR_AT(kr, address);\n        return FALSE;\n    }\n    if (length != dataCnt) {\n        warnx(\"rt_read size return not match!\");\n        return FALSE;\n    }\n\n    return TRUE;\n}\n\nchar *zz_vm_read_string_via_task(task_t task, const zaddr address) {\n    char end_c = '\\0';\n    zaddr end_addr;\n    char *result = NULL;\n\n    // string upper limit 0x1000\n    end_addr = zz_vm_search_data_via_task(task, address, address + 0x1000, (zbyte *)&end_c, 1);\n    if (!end_addr) {\n        return NULL;\n    }\n    result = (char *)malloc(end_addr - address + 1);\n    if (result && zz_vm_read_data_via_task(task, address, result, end_addr - address + 1)) {\n        return result;\n    }\n    return NULL;\n}\n\n// --- end ---\n\nzaddr zz_vm_search_data_via_task(task_t task, const zaddr start_addr, const zaddr end_addr, zbyte *data,\n                                 zsize data_len) {\n    zaddr curr_addr;\n    zbyte *temp_buf;\n    if (start_addr <= 0)\n        Xerror(\"search address start_addr(%p) < 0\", (zpointer)start_addr);\n    if (start_addr > end_addr)\n        Xerror(\"search start_add(%p) < end_addr(%p)\", (zpointer)start_addr, (zpointer)end_addr);\n\n    curr_addr = (zaddr)start_addr;\n    temp_buf = (zbyte *)malloc(data_len);\n\n    while (end_addr > curr_addr) {\n        if (zz_vm_read_data_via_task(task, curr_addr, temp_buf, data_len))\n            if (!memcmp(temp_buf, data, data_len)) {\n                return curr_addr;\n            }\n        curr_addr += data_len;\n    }\n    return 0;\n}\n\nzbool zz_vm_check_address_valid_via_task(task_t task, const zaddr address) {\n    if (address <= 0)\n        return FALSE;\n#define CHECK_LEN 1\n    char n_read_bytes[1];\n    zuint len;\n    kern_return_t kr = vm_read_overwrite(task, address, CHECK_LEN, (zaddr)&n_read_bytes, (vm_size_t *)&len);\n\n    if (kr != KERN_SUCCESS || len != CHECK_LEN)\n        KR_ERROR_AT(kr, address);\n    return FALSE;\n    return TRUE;\n}\n\nzbool zz_vm_get_page_info_via_task(task_t task, const zaddr address, vm_prot_t *prot_p, vm_inherit_t *inherit_p) {\n\n    vm_address_t region = (zaddr)address;\n    vm_size_t region_len = 0;\n    struct vm_region_submap_short_info_64 info;\n    mach_msg_type_number_t info_count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;\n    natural_t max_depth = 99999;\n    kern_return_t kr =\n        vm_region_recurse_64(task, &region, &region_len, &max_depth, (vm_region_recurse_info_t)&info, &info_count);\n    if (kr != KERN_SUCCESS) {\n        KR_ERROR_AT(kr, address);\n        return FALSE;\n    }\n    *prot_p = info.protection & (PROT_READ | PROT_WRITE | PROT_EXEC);\n    *inherit_p = info.inheritance;\n    return TRUE;\n}\n\nzbool zz_vm_protect_via_task(task_t task, const zaddr address, zsize size, vm_prot_t page_prot) {\n    kern_return_t kr;\n\n    zsize page_size;\n    zaddr aligned_addr;\n    zsize aligned_size;\n\n    page_size = zz_posix_vm_get_page_size();\n    aligned_addr = (zaddr)address & ~(page_size - 1);\n    aligned_size = (1 + ((address + size - 1 - aligned_addr) / page_size)) * page_size;\n\n    kr = mach_vm_protect(task, (vm_address_t)aligned_addr, aligned_size, FALSE, page_prot);\n    if (kr != KERN_SUCCESS) {\n        KR_ERROR_AT(kr, address);\n        Xerror(\"kr = %d, at (%p) error!\", kr, (zpointer)address);\n        return FALSE;\n    }\n    return TRUE;\n}\n\nzbool zz_vm_protect_as_executable_via_task(task_t task, const zaddr address, zsize size) {\n    return zz_vm_protect_via_task(task, address, size, (VM_PROT_READ | VM_PROT_EXECUTE));\n}\n\nzbool zz_vm_protect_as_writable_via_task(task_t task, const zaddr address, zsize size) {\n    if (!zz_vm_protect_via_task(task, address, size, (VM_PROT_ALL | VM_PROT_COPY))) {\n        return zz_vm_protect_via_task(task, address, size, (VM_PROT_DEFAULT | VM_PROT_COPY));\n    }\n    return FALSE;\n}\n\nzpointer zz_vm_allocate_pages_via_task(task_t task, zsize n_pages) {\n    mach_vm_address_t result;\n    kern_return_t kr;\n    zsize page_size;\n    page_size = zz_posix_vm_get_page_size();\n\n    if (n_pages <= 0) {\n        n_pages = 1;\n    }\n\n    kr = mach_vm_allocate(task, &result, page_size * n_pages, VM_FLAGS_ANYWHERE);\n\n    if (kr != KERN_SUCCESS) {\n        KR_ERROR(kr);\n        return NULL;\n    }\n\n    if (!zz_vm_protect_via_task(task, (zaddr)result, page_size * n_pages, (VM_PROT_DEFAULT | VM_PROT_COPY)))\n        return NULL;\n\n    return (zpointer)result;\n}\n\nbool zz_vm_can_allocate_rx_page() {\n    vm_prot_t prot;\n    vm_inherit_t inherit;\n    kern_return_t kr;\n    mach_port_t task_self = mach_task_self();\n    mach_vm_address_t result;\n\n    unsigned long temp_page_addr = (unsigned long)zz_vm_allocate_pages_via_task(mach_task_self(), 1);\n    zz_vm_protect_as_executable_via_task(mach_task_self(), temp_page_addr, zz_posix_vm_get_page_size());\n    if (!zz_vm_get_page_info_via_task(task_self, temp_page_addr, &prot, &inherit)) {\n        return FALSE;\n    }\n    kr = mach_vm_deallocate(task_self, temp_page_addr, zz_posix_vm_get_page_size());\n    if (kr != KERN_SUCCESS) {\n        KR_ERROR(kr);\n        return FALSE;\n    }\n    if (prot & VM_PROT_EXECUTE) {\n        return TRUE;\n    }\n    return FALSE;\n}\n\nzpointer zz_vm_allocate_via_task(task_t task, zsize size) {\n    zsize page_size;\n    zpointer result;\n    zsize n_pages;\n\n    page_size = zz_posix_vm_get_page_size();\n    n_pages = ((size + page_size - 1) & ~(page_size - 1)) / page_size;\n\n    result = zz_vm_allocate_pages_via_task(task, n_pages);\n    return (zpointer)result;\n}\n\nzpointer zz_vm_allocate_near_pages_via_task(task_t task, zaddr address, zsize range_size, zsize n_pages) {\n    mach_vm_address_t aligned_addr;\n    kern_return_t kr;\n    mach_vm_address_t tmp_addr;\n    zsize page_size;\n    page_size = zz_posix_vm_get_page_size();\n\n    if (n_pages <= 0) {\n        n_pages = 1;\n    }\n    aligned_addr = (zaddr)address & ~(page_size - 1);\n\n    vm_address_t target_start_addr = zz_vm_align_floor(address - range_size, page_size);\n    vm_address_t target_end_addr = zz_vm_align_floor(address + range_size, page_size);\n\n    for (tmp_addr = target_start_addr; tmp_addr < target_end_addr; tmp_addr += page_size) {\n        kr = mach_vm_allocate(task, &tmp_addr, page_size * n_pages, VM_FLAGS_FIXED);\n        if (kr == KERN_SUCCESS) {\n            return (zpointer)tmp_addr;\n        }\n    }\n    return NULL;\n}\n\nzpointer zz_vm_search_text_code_cave_via_task(task_t task, zaddr address, zsize range_size, zsize *size_ptr) {\n    char zeroArray[128];\n    char readZeroArray[128];\n    mach_vm_address_t aligned_addr, tmp_addr, target_search_start, target_search_end;\n    kern_return_t kr;\n    zsize page_size;\n\n    memset(zeroArray, 0, 128);\n\n    page_size = zz_posix_vm_get_page_size();\n    aligned_addr = (zaddr)address & ~(page_size - 1);\n    target_search_start = aligned_addr - range_size;\n    target_search_end = aligned_addr + range_size;\n\n    Xdebug(\"searching for %p cave...\", (zpointer)address);\n    // TODO: check the memory region attributes\n    for (tmp_addr = target_search_start; tmp_addr < target_search_end; tmp_addr += 0x1000) {\n        if (zz_vm_read_data_via_task(task, tmp_addr, readZeroArray, 128)) {\n            if (!memcmp(readZeroArray, zeroArray, 128)) {\n                *size_ptr = 0x1000;\n                Xdebug(\"found a cave at %p, size %d\", (zpointer)tmp_addr, 0x1000);\n                return (void *)tmp_addr;\n            }\n        }\n    }\n    return NULL;\n}\n\nMemoryLayout *zz_vm_get_memory_layout_via_task(task_t task) {\n    mach_msg_type_number_t count;\n    struct vm_region_submap_info_64 info;\n    zuint32 nesting_depth;\n\n    kern_return_t kr = KERN_SUCCESS;\n    vm_address_t address_tmp = 0;\n    vm_size_t size_tmp = 0;\n\n    MemoryLayout *mlayout = (MemoryLayout *)malloc(sizeof(MemoryLayout));\n    memset(mlayout, 0, sizeof(MemoryLayout));\n\n    while (1) {\n        mach_msg_type_number_t count;\n        struct vm_region_submap_info_64 info;\n        zuint32 nesting_depth;\n\n        count = VM_REGION_SUBMAP_INFO_COUNT_64;\n        kr = vm_region_recurse_64(task, &address_tmp, &size_tmp, &nesting_depth, (vm_region_info_64_t)&info, &count);\n        if (kr == KERN_INVALID_ADDRESS) {\n            break;\n        } else if (kr) {\n            mach_error(\"vm_region:\", kr);\n            break; /* last region done */\n        }\n\n        if (info.is_submap) {\n            nesting_depth++;\n        } else {\n            address_tmp += size_tmp;\n\n            mlayout->mem[mlayout->size].start = (zpointer)(address_tmp - size_tmp);\n            mlayout->mem[mlayout->size].end = (zpointer)address_tmp;\n            mlayout->mem[mlayout->size++].flags = ((info.protection & PROT_READ) ? (1 << 0) : 0) |\n                                                  ((info.protection & PROT_WRITE) ? (1 << 1) : 0) |\n                                                  ((info.protection & PROT_EXEC) ? (1 << 2) : 0);\n        }\n    }\n    return mlayout;\n}\n\n// https://github.com/kpwn/935csbypass/blob/master/cs_bypass.m\nzpointer zz_vm_search_code_cave(zaddr address, zsize range_size, zsize size) {\n    char zeroArray[128];\n    char readZeroArray[128];\n    zaddr aligned_addr, tmp_addr, search_start, search_end, search_start_limit, search_end_limit;\n    zsize page_size;\n\n    zpointer result_ptr;\n    memset(zeroArray, 0, 128);\n\n    search_start_limit = address - range_size;\n    search_end_limit = address + range_size;\n\n    MemoryLayout *mlayout = zz_vm_get_memory_layout_via_task(mach_task_self());\n\n    int i;\n    for (i = 0; i < mlayout->size; i++) {\n        if (mlayout->mem[i].flags == (1 << 0 | 1 << 2)) {\n            search_start = (zaddr)mlayout->mem[i].start;\n            search_end = (zaddr)mlayout->mem[i].end;\n\n            if (search_start < search_start_limit) {\n\n                if (search_end > search_start_limit && search_end < search_end_limit) {\n                    search_start = search_start_limit;\n                } else if (search_end > search_end_limit) {\n                    search_start = search_start_limit;\n                    search_end = search_end_limit;\n                } else {\n                    continue;\n                }\n            } else if (search_start >= search_start_limit && search_start <= search_end_limit) {\n                if (search_end > search_start_limit && search_end < search_end_limit) {\n                } else if (search_end > search_end_limit) {\n                    search_end = search_end_limit;\n                } else {\n                    continue;\n                }\n            } else {\n                continue;\n            }\n\n            result_ptr = zz_vm_search_data((zpointer)search_start, (zpointer)search_end, (zbyte *)zeroArray, size);\n            if (result_ptr) {\n                free(mlayout);\n                return result_ptr;\n            }\n        }\n    }\n    free(mlayout);\n    return NULL;\n}\n\n// TODO: vm_region_recurse_64 is better ?\nzpointer zz_vm_search_text_code_cave_via_dylibs(zaddr address, zsize range_size, zsize size) {\n    char zeroArray[128];\n    char readZeroArray[128];\n    zaddr aligned_addr, tmp_addr, search_start, search_end, search_start_limit, search_end_limit;\n    zsize page_size;\n\n    zpointer result_ptr;\n\n    memset(zeroArray, 0, 128);\n\n    page_size = zz_posix_vm_get_page_size();\n    search_start_limit = address - range_size;\n    search_end_limit = address + range_size;\n\n    zsize n_dylibs = _dyld_image_count();\n    for (size_t i = 0; i < n_dylibs; i++) {\n        struct mach_header_64 *header = (struct mach_header_64 *)_dyld_get_image_header(i);\n        struct segment_command_64 *seg_cmd_64 = zz_macho_get_segment_64_via_name(header, \"__TEXT\");\n\n        // ATTENTION: as the __TEXT segment region is 'r-x', diffrent from\n        // others, so it's page align.\n        search_start = (zaddr)header;\n        // no need align again.\n        search_start = zz_vm_align_floor(search_start, page_size);\n        search_end = (zaddr)header + seg_cmd_64->vmsize;\n        // no need align again.\n        search_end = zz_vm_align_ceil(search_end, page_size);\n\n        if (search_start < search_start_limit) {\n\n            if (search_end > search_start_limit && search_end < search_end_limit) {\n                search_start = search_start_limit;\n            } else if (search_end > search_end_limit) {\n                search_start = search_start_limit;\n                search_end = search_end_limit;\n            } else {\n                continue;\n            }\n        } else if (search_start >= search_start_limit && search_start <= search_end_limit) {\n            if (search_end > search_start_limit && search_end < search_end_limit) {\n            } else if (search_end > search_end_limit) {\n                search_end = search_end_limit;\n            } else {\n                continue;\n            }\n        } else {\n            continue;\n        }\n\n        result_ptr = zz_vm_search_data((zpointer)search_start, (zpointer)search_end, (zbyte *)zeroArray, size);\n        if (result_ptr) {\n            return result_ptr;\n        }\n    }\n    return NULL;\n}\n\n/*\n  ref:\n  substitute/lib/darwin/execmem.c:execmem_foreign_write_with_pc_patch\n  frida-gum-master/gum/gummemory.c:gum_memory_patch_code\n\n  frida-gum-master/gum/backend-darwin/gummemory-darwin.c:gum_alloc_n_pages\n\n  mach mmap use __vm_allocate and __vm_map\n  https://github.com/bminor/glibc/blob/master/sysdeps/mach/hurd/mmap.c\n  https://github.com/bminor/glibc/blob/master/sysdeps/mach/munmap.c\n\n  http://shakthimaan.com/downloads/hurd/A.Programmers.Guide.to.the.Mach.System.Calls.pdf\n*/\n\nzbool zz_vm_patch_code_via_task(task_t task, const zaddr address, const zpointer codedata, zuint codedata_size) {\n    zsize page_size;\n    zaddr start_page_addr, end_page_addr;\n    zsize page_offset, range_size;\n\n    page_size = zz_posix_vm_get_page_size();\n    /*\n      https://www.gnu.org/software/hurd/gnumach-doc/Memory-Attributes.html\n     */\n    start_page_addr = (address) & ~(page_size - 1);\n    end_page_addr = ((address + codedata_size - 1)) & ~(page_size - 1);\n    page_offset = address - start_page_addr;\n    range_size = (end_page_addr + page_size) - start_page_addr;\n\n    vm_prot_t prot;\n    vm_inherit_t inherit;\n    kern_return_t kr;\n    mach_port_t task_self = mach_task_self();\n\n    if (!zz_vm_get_page_info_via_task(task_self, (const zaddr)start_page_addr, &prot, &inherit)) {\n        return FALSE;\n    }\n\n    /*\n      another method, pelease read `REF`;\n\n     */\n    // zpointer code_mmap = mmap(NULL, range_size, PROT_READ | PROT_WRITE,\n    //                           MAP_ANON | MAP_SHARED, -1, 0);\n    // if (code_mmap == MAP_FAILED) {\n    //   return;\n    // }\n\n    zpointer code_mmap = zz_vm_allocate_via_task(task_self, range_size);\n\n    kr = vm_copy(task_self, start_page_addr, range_size, (vm_address_t)code_mmap);\n\n    if (kr != KERN_SUCCESS) {\n        KR_ERROR_AT(kr, start_page_addr);\n        return FALSE;\n    }\n    memcpy(code_mmap + page_offset, codedata, codedata_size);\n\n    /* SAME: mprotect(code_mmap, range_size, prot); */\n    if (!zz_vm_protect_via_task(task_self, (zaddr)code_mmap, range_size, prot))\n        return FALSE;\n\n    // TODO: need check `memory region` again.\n    /*\n        TODO:\n        // if only this, `memory region` is `r-x`\n        vm_protect((vm_map_t)mach_task_self(), 0x00000001816b2030, 16, FALSE,\n       0x13);\n        // and with this, `memory region` is `rwx`\n        *(char *)0x00000001816b01a8 = 'a';\n     */\n\n    mach_vm_address_t target = (zaddr)start_page_addr;\n    vm_prot_t c, m;\n    kr = mach_vm_remap(task_self, &target, range_size, 0, VM_FLAGS_OVERWRITE, task_self, (mach_vm_address_t)code_mmap,\n                       /*copy*/ TRUE, &c, &m, inherit);\n\n    if (kr != KERN_SUCCESS) {\n        KR_ERROR(kr);\n        return FALSE;\n    }\n    /*\n      read `REF`\n     */\n    // munmap(code_mmap, range_size);\n    kr = mach_vm_deallocate(task_self, (zaddr)code_mmap, range_size);\n    if (kr != KERN_SUCCESS) {\n        KR_ERROR(kr);\n        return FALSE;\n    }\n    return TRUE;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/darwin/memory-utils-darwin.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef zzdeps_darwin_memory_utils_darwin_h\n#define zzdeps_darwin_memory_utils_darwin_h\n\n#include <err.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include <mach/mach_error.h>\n#include <mach/task.h>\n\n#if defined(__arm64__) || defined(__arm__)\n\n#include \"mach_vm.h\"\n\n#else\n#include <mach/mach_vm.h>\n#endif\n\n#include \"../posix/memory-utils-posix.h\"\n#include \"../zz.h\"\n\n#define KR_ERROR(kr)                                                                                                   \\\n    {                                                                                                                  \\\n        Xerror(\"kr = %d, reason: %s!\", kr, mach_error_string(kr));                                                     \\\n        debug_break();                                                                                                 \\\n    }\n#define KR_ERROR_AT(kr, address)                                                                                       \\\n    {                                                                                                                  \\\n        Xerror(\"kr = %d, at %p, reason: %s!\", kr, (zpointer)address, mach_error_string(kr));                           \\\n        debug_break();                                                                                                 \\\n    }\n\nzbool zz_vm_read_data_via_task(task_t task, const zaddr address, zpointer buffer, zsize length);\n\nchar *zz_vm_read_string_via_task(task_t task, const zaddr address);\n\nzaddr zz_vm_search_data_via_task(task_t task, const zaddr start_addr, const zaddr end_addr, zbyte *data,\n                                 zsize data_len);\n\nzbool zz_vm_check_address_valid_via_task(task_t task, const zaddr address);\n\nzbool zz_vm_can_allocate_rx_page();\n\nzbool zz_vm_protect_via_task(task_t task, const zaddr address, zsize size, vm_prot_t page_prot);\n\nzbool zz_vm_protect_as_executable_via_task(task_t task, const zaddr address, zsize size);\n\nzbool zz_vm_protect_as_writable_via_task(task_t task, const zaddr address, zsize size);\n\nzbool zz_vm_get_page_info_via_task(task_t task, const zaddr address, vm_prot_t *prot_p, vm_inherit_t *inherit_p);\n\nzpointer zz_vm_allocate_pages_via_task(task_t task, zsize n_pages);\n\nzpointer zz_vm_allocate_near_pages_via_task(task_t task, zaddr address, zsize range_size, zsize n_pages);\n\nzpointer zz_vm_allocate_via_task(task_t task, zsize size);\n\nzpointer zz_vm_search_text_code_cave_via_task(task_t task, zaddr address, zsize range_size, zsize *size_ptr);\n\nzpointer zz_vm_search_text_code_cave_via_dylibs(zaddr address, zsize range_size, zsize size);\n\nzpointer zz_vm_search_code_cave(zaddr address, zsize range_size, zsize size);\n\nzbool zz_vm_patch_code_via_task(task_t task, const zaddr address, const zpointer codedata, zuint codedata_size);\n\n#endif\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/linux/memory-utils-linux.c",
    "content": "#include \"memory-utils-linux.h\"\n#include \"../common/memory-utils-common.h\"\n#include \"../memory-utils.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n\nMemoryLayout *zz_linux_vm_get_memory_layout_via_pid(pid_t pid) {\n    char filename[64];\n    char buf[256];\n    FILE *fp;\n    MemoryLayout *mlayout;\n\n    mlayout = (MemoryLayout *)malloc(sizeof(MemoryLayout));\n    memset(mlayout, 0, sizeof(MemoryLayout));\n\n    // given pid, open /proc/pid/maps; or not, open current maps.\n    if (pid > 0) {\n        sprintf(filename, \"/proc/%d/maps\", pid);\n    } else {\n        sprintf(filename, \"/proc/self/maps\");\n    }\n\n    fp = fopen(filename, \"r\");\n    if (fp < 0) {\n        return NULL;\n    }\n\n    while (fgets(buf, sizeof(buf), fp) != NULL) {\n        zaddr start, end;\n        unsigned dev, sdev;\n        unsigned long inode;\n        unsigned long long offset;\n        char prot[5];\n        char path[64];\n        int len;\n\n        /* format in /proc/pid/maps is constructed as below in fs/proc/task_mmu.c\n        167\tseq_printf(m,\n        168\t\t\t   \"%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu \",\n        169\t\t\t   vma->vm_start,\n        170\t\t\t   vma->vm_end,\n        171\t\t\t   flags & VM_READ ? 'r' : '-',\n        172\t\t\t   flags & VM_WRITE ? 'w' : '-',\n        173\t\t\t   flags & VM_EXEC ? 'x' : '-',\n        174\t\t\t   flags & VM_MAYSHARE ? flags & VM_SHARED ? 'S' : 's' : 'p',\n        175\t\t\t   pgoff,\n        176\t\t\t   MAJOR(dev), MINOR(dev), ino);\n        177\n        178\t\tif (file) {\n        179\t\t\tseq_pad(m, ' ');\n        180\t\t\tseq_file_path(m, file, \"\");\n        181\t\t} else if (mm && is_stack(priv, vma)) {\n        182\t\t\tseq_pad(m, ' ');\n        183\t\t\tseq_printf(m, \"[stack]\");\n        184\t\t}\n         */\n        if (sscanf(buf, \"%lx-%lx %s %llx %x:%x %lu %s\", &start, &end, prot, &offset, &dev, &sdev, &inode, path) != 8)\n            continue;\n        mlayout->mem[mlayout->size].start = (zpointer)start;\n        mlayout->mem[mlayout->size].end = (zpointer)end;\n        mlayout->mem[mlayout->size++].flags =\n            (prot[0] == 'r' ? (1 << 0) : 0) | (prot[1] == 'w' ? (1 << 1) : 0) | (prot[2] == 'x' ? (1 << 2) : 0);\n    }\n    return mlayout;\n}\n\nzpointer zz_linux_vm_search_code_cave(zaddr address, zsize range_size, zsize size) {\n    char zeroArray[128];\n    char readZeroArray[128];\n    zaddr aligned_addr, tmp_addr, search_start, search_end, search_start_limit, search_end_limit;\n    zsize page_size;\n\n    zpointer result_ptr;\n    memset(zeroArray, 0, 128);\n\n    search_start_limit = address - range_size;\n    search_end_limit = address + range_size;\n\n    MemoryLayout *mlayout = zz_linux_vm_get_memory_layout_via_pid(-1);\n\n    int i;\n    for (i = 0; i < mlayout->size; i++) {\n        if (mlayout->mem[i].flags == (1 << 0 | 1 << 2)) {\n            search_start = (zaddr)mlayout->mem[i].start;\n            search_end = (zaddr)mlayout->mem[i].end;\n\n            if (search_start < search_start_limit) {\n\n                if (search_end > search_start_limit && search_end < search_end_limit) {\n                    search_start = search_start_limit;\n                } else if (search_end > search_end_limit) {\n                    search_start = search_start_limit;\n                    search_end = search_end_limit;\n                } else {\n                    continue;\n                }\n            } else if (search_start >= search_start_limit && search_start <= search_end_limit) {\n                if (search_end > search_start_limit && search_end < search_end_limit) {\n                } else if (search_end > search_end_limit) {\n                    search_end = search_end_limit;\n                } else {\n                    continue;\n                }\n            } else {\n                continue;\n            }\n\n            result_ptr = zz_vm_search_data((zpointer)search_start, (zpointer)search_end, (zbyte *)zeroArray, size);\n            if (result_ptr) {\n                free(mlayout);\n                return result_ptr;\n            }\n        }\n    }\n    free(mlayout);\n    return NULL;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/linux/memory-utils-linux.h",
    "content": "#ifndef zzdeps_linux_memory_utils_linux_h\n#define zzdeps_linux_memory_utils_linux_h\n\n#include <err.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"../zz.h\"\n\nzpointer zz_linux_vm_search_code_cave(zaddr address, zsize range_size, zsize size);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/memory-utils.h",
    "content": "#ifndef zzdeps_memory_utils_h\n#define zzdeps_memory_utils_h\n\n#include \"zz.h\"\n\ntypedef struct _MemoryLayout {\n    int size;\n    struct {\n        int flags;\n        zpointer start;\n        zpointer end;\n    } mem[4096];\n} MemoryLayout;\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/posix/memory-utils-posix.c",
    "content": "\n/**\n *    Copyright 2017 jmpews\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#include <errno.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <unistd.h>\n\n#include \"memory-utils-posix.h\"\n// http://renatocunha.com/blog/2015/12/msync-pointer-validity/\nzbool zz_posix_vm_check_address_valid_via_msync(const zpointer p) {\n    int ret = 0;\n    zsize page_size;\n    zpointer base;\n    /* get the page size */\n    page_size = zz_posix_vm_get_page_size();\n    /* find the address of the page that contains p */\n    base = (void *)((((size_t)p) / page_size) * page_size);\n    /* call msync, if it returns non-zero, return FALSE */\n    ret = msync(base, page_size, MS_ASYNC) != -1;\n    return ret ? ret : errno != ENOMEM;\n}\n\n// ATTENTION !!!\n// lldb is still catch EXC_BAD_ACCESS, without lldb is ok.\n// https://www.cocoawithlove.com/2010/10/testing-if-arbitrary-pointer-is-valid.html\n// https://stackoverflow.com/questions/26829119/how-to-make-lldb-ignore-exc-bad-access-exception\n// ---check start---\n#include <setjmp.h>\n#include <signal.h>\n\nstatic sigjmp_buf sigjmp_env;\n\nvoid PointerReadFailedHandler(int signum) { siglongjmp(sigjmp_env, 1); }\n\nzbool zz_posix_vm_check_address_valid_via_signal(zpointer p) {\n    // Set up SIGSEGV and SIGBUS handlers\n    struct sigaction new_segv_action, old_segv_action;\n    struct sigaction new_bus_action, old_bus_action;\n    new_segv_action.sa_handler = PointerReadFailedHandler;\n    new_bus_action.sa_handler = PointerReadFailedHandler;\n    sigemptyset(&new_segv_action.sa_mask);\n    sigemptyset(&new_bus_action.sa_mask);\n    new_segv_action.sa_flags = 0;\n    new_bus_action.sa_flags = 0;\n    sigaction(SIGSEGV, &new_segv_action, &old_segv_action);\n    sigaction(SIGBUS, &new_bus_action, &old_bus_action);\n\n    // The signal handler will return us to here if a signal is raised\n    if (sigsetjmp(sigjmp_env, 1)) {\n        sigaction(SIGSEGV, &old_segv_action, NULL);\n        sigaction(SIGBUS, &old_bus_action, NULL);\n        return FALSE;\n    }\n    // ATTENTION !!! this function is conflict with LLDB, reason is below.\n    // lldb is still catch EXC_BAD_ACCESS, without lldb is ok.\n    // or you can use `zz_check_address_valid_via_mem` replace\n    // https://stackoverflow.com/questions/26829119/how-to-make-lldb-ignore-exc-bad-access-exception\n    char x = *(char *)p;\n    return TRUE;\n}\n\nzsize zz_posix_vm_get_page_size() { return getpagesize(); }\n\n// int mprotect(void *addr, size_t len, int prot);\nzbool zz_posix_vm_protect(const zaddr address, zsize size, int page_prot) {\n    int r;\n\n    zsize page_size;\n    zaddr aligned_addr;\n    zsize aligned_size;\n\n    page_size = zz_posix_vm_get_page_size();\n    aligned_addr = (zaddr)address & ~(page_size - 1);\n    aligned_size = (1 + ((address + size - 1 - aligned_addr) / page_size)) * page_size;\n\n    r = mprotect((zpointer)aligned_addr, aligned_size, page_prot);\n    if (r == -1) {\n        Xerror(\"r = %d, at (%p) error!\", r, (zpointer)address);\n        return FALSE;\n    }\n    return TRUE;\n}\n\nzbool zz_posix_vm_protect_as_executable(const zaddr address, zsize size) {\n    return zz_posix_vm_protect(address, size, (PROT_READ | PROT_EXEC | PROT_WRITE));\n}\n\nzbool zz_posxi_vm_protect_as_writable(const zaddr address, zsize size) {\n    if (!zz_posix_vm_protect(address, size, (PROT_READ | PROT_EXEC | PROT_WRITE)))\n        return FALSE;\n    return TRUE;\n}\n\n//  void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);\nzpointer zz_posix_vm_allocate_pages(zsize n_pages) {\n    zpointer page_mmap;\n    int kr;\n    zsize page_size;\n    page_size = zz_posix_vm_get_page_size();\n\n    if (n_pages <= 0) {\n        n_pages = 1;\n    }\n\n    page_mmap = mmap(0, page_size * n_pages, PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);\n\n    if (page_mmap == MAP_FAILED) {\n        perror(\"mmap\");\n        return NULL;\n    }\n\n    if (!zz_posix_vm_protect((zaddr)page_mmap, page_size * n_pages, (PROT_WRITE | PROT_READ)))\n        return NULL;\n    return (zpointer)page_mmap;\n}\n\nzpointer zz_posix_vm_allocate(zsize size) {\n    zsize page_size;\n    zpointer result;\n    zsize n_pages;\n\n    page_size = zz_posix_vm_get_page_size();\n    n_pages = ((size + page_size - 1) & ~(page_size - 1)) / page_size;\n\n    result = zz_posix_vm_allocate_pages(n_pages);\n    return (zpointer)result;\n}\n\nzpointer zz_posix_vm_allocate_near_pages(zaddr address, zsize range_size, zsize n_pages) {\n    zaddr aligned_addr;\n    zpointer page_mmap;\n    zaddr t;\n    zsize page_size;\n    page_size = zz_posix_vm_get_page_size();\n\n    if (n_pages <= 0) {\n        n_pages = 1;\n    }\n    aligned_addr = (zaddr)address & ~(page_size - 1);\n\n    zaddr target_start_addr = aligned_addr - range_size;\n    zaddr target_end_addr = aligned_addr + range_size;\n\n    for (t = target_start_addr; t < target_end_addr; t += page_size) {\n        page_mmap = mmap((zpointer)t, page_size * n_pages, PROT_WRITE | PROT_READ,\n                         MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);\n        if (page_mmap != MAP_FAILED) {\n            return (zpointer)page_mmap;\n        }\n    }\n    return NULL;\n}\n\nzpointer zz_posix_vm_search_text_code_cave(zaddr address, zsize range_size, zsize size) {\n    char zeroArray[128];\n    char readZeroArray[128];\n    zaddr aligned_addr, tmp_addr, target_search_start, target_search_end;\n    zsize page_size;\n\n    memset(zeroArray, 0, 128);\n\n    page_size = zz_posix_vm_get_page_size();\n    aligned_addr = (zaddr)address & ~(page_size - 1);\n    target_search_start = aligned_addr - range_size;\n    target_search_end = aligned_addr + range_size;\n\n    Xdebug(\"searching for %p cave, use 0x1000 interval.\", (zpointer)address);\n    for (tmp_addr = target_search_start; tmp_addr < target_search_end; tmp_addr += 0x1000) {\n        if (zz_posix_vm_check_address_valid_via_signal((zpointer)tmp_addr))\n            if (memcpy(readZeroArray, (zpointer)tmp_addr, 128)) {\n                if (!memcmp(readZeroArray, zeroArray, 128)) {\n                    return (void *)tmp_addr;\n                }\n            }\n    }\n    return NULL;\n}\n\n/*\n  ref:\n  substitute/lib/darwin/execmem.c:execmem_foreign_write_with_pc_patch\n  frida-gum-master/gum/gummemory.c:gum_memory_patch_code\n\n  frida-gum-master/gum/backend-darwin/gummemory-darwin.c:gum_alloc_n_pages\n\n  mach mmap use __vm_allocate and __vm_map\n  https://github.com/bminor/glibc/blob/master/sysdeps/mach/hurd/mmap.c\n  https://github.com/bminor/glibc/blob/master/sysdeps/mach/munmap.c\n\n  http://shakthimaan.com/downloads/hurd/A.Programmers.Guide.to.the.Mach.System.Calls.pdf\n*/\n\nzbool zz_posix_vm_patch_code(const zaddr address, const zpointer codedata, zuint codedata_size) {\n    zsize page_size;\n    zaddr start_page_addr, end_page_addr;\n    zsize page_offset, range_size;\n\n    page_size = zz_posix_vm_get_page_size();\n    /*\n      https://www.gnu.org/software/hurd/gnumach-doc/Memory-Attributes.html\n     */\n    start_page_addr = (address) & ~(page_size - 1);\n    end_page_addr = ((address + codedata_size - 1)) & ~(page_size - 1);\n    page_offset = address - start_page_addr;\n    range_size = (end_page_addr + page_size) - start_page_addr;\n\n    //  another method, pelease read `REF`;\n\n    // zpointer code_mmap = mmap(NULL, range_size, PROT_READ | PROT_WRITE,\n    //                           MAP_ANON | MAP_SHARED, -1, 0);\n    // if (code_mmap == MAP_FAILED) {\n    //   return;\n    // }\n\n    zpointer code_mmap = zz_posix_vm_allocate(range_size);\n\n    memcpy(code_mmap, (void *)start_page_addr, range_size);\n\n    memcpy(code_mmap + page_offset, codedata, codedata_size);\n\n    /* SAME: mprotect(code_mmap, range_size, prot); */\n    // if (!zz_posix_vm_protect((zaddr)code_mmap, range_size, PROT_READ | PROT_EXEC))\n    //     return FALSE;\n\n    zaddr target = (zaddr)start_page_addr;\n    zz_posxi_vm_protect_as_writable(start_page_addr, range_size);\n    memcpy((zpointer)start_page_addr, (zpointer)code_mmap, range_size);\n    zz_posix_vm_protect_as_executable(start_page_addr, range_size);\n    munmap(code_mmap, range_size);\n    return TRUE;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/posix/memory-utils-posix.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef zzdeps_posix_memory_utils_posix_h\n#define zzdeps_posix_memory_utils_posix_h\n\n#include <err.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"../common/memory-utils-common.h\"\n#include \"../zz.h\"\n\nzsize zz_posix_vm_get_page_size();\n\nzbool zz_posix_vm_check_address_valid_via_msync(const zpointer p);\n\nzbool zz_posix_vm_check_address_valid_via_signal(zpointer p);\n\nzbool zz_posix_vm_protect(const zaddr address, zsize size, int page_prot);\n\nzbool zz_posix_vm_protect_as_executable(const zaddr address, zsize size);\n\nzbool zz_posxi_vm_protect_as_writable(const zaddr address, zsize size);\n\nzpointer zz_posix_vm_allocate_pages(zsize n_pages);\n\nzpointer zz_posix_vm_allocate(zsize size);\n\nzpointer zz_posix_vm_allocate_near_pages(zaddr address, zsize range_size, zsize n_pages);\n\nzpointer zz_posix_vm_search_text_code_cave(zaddr address, zsize range_size, zsize size);\n\nzbool zz_posix_vm_patch_code(const zaddr address, const zpointer codedata, zuint codedata_size);\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/posix/thread-utils-posix.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"thread-utils-posix.h\"\n#include <pthread.h>\n\nThreadLocalKeyList *g_thread_local_key_list = 0;\n\nThreadLocalKeyList *zz_posix_thread_new_thread_local_key_list() {\n    ThreadLocalKeyList *keylist_tmp = (ThreadLocalKeyList *)malloc(sizeof(ThreadLocalKeyList));\n    keylist_tmp->capacity = 4;\n    keylist_tmp->keys = (ThreadLocalKey **)malloc(sizeof(ThreadLocalKey *) * keylist_tmp->capacity);\n    if (!keylist_tmp->keys) {\n        return NULL;\n    }\n    keylist_tmp->size = 0;\n    return keylist_tmp;\n}\n\nzbool zz_posix_thread_add_thread_local_key(ThreadLocalKeyList *keylist, ThreadLocalKey *key) {\n    if (!keylist)\n        return FALSE;\n\n    if (keylist->size >= keylist->capacity) {\n        ThreadLocalKey **keys_tmp =\n            (ThreadLocalKey **)realloc(keylist->keys, sizeof(ThreadLocalKey *) * keylist->capacity * 2);\n        if (!keys_tmp)\n            return FALSE;\n        keylist->keys = keys_tmp;\n        keylist->capacity = keylist->capacity * 2;\n    }\n    keylist->keys[keylist->size++] = key;\n    return TRUE;\n}\n\nvoid zz_posix_thread_initialize_thread_local_key_list() {\n    if (!g_thread_local_key_list) {\n        g_thread_local_key_list = zz_posix_thread_new_thread_local_key_list();\n    }\n}\n\nzpointer zz_posix_thread_new_thread_local_key_ptr() {\n    if (!g_thread_local_key_list) {\n        zz_posix_thread_initialize_thread_local_key_list();\n    }\n    ThreadLocalKey *key = (ThreadLocalKey *)malloc(sizeof(ThreadLocalKey));\n    zz_posix_thread_add_thread_local_key(g_thread_local_key_list, key);\n\n    pthread_key_create(&(key->key), NULL);\n    return (zpointer)key;\n}\n\nzpointer zz_posix_thread_get_current_thread_data(zpointer key_ptr) {\n    ThreadLocalKeyList *g_keys = g_thread_local_key_list;\n    zsize i;\n\n    if (!key_ptr)\n        return NULL;\n    for (i = 0; i < g_keys->size; i++) {\n        if (g_keys->keys[i] == key_ptr)\n            return (zpointer)pthread_getspecific(g_keys->keys[i]->key);\n    }\n    return NULL;\n}\n\nzbool zz_posix_thread_set_current_thread_data(zpointer key_ptr, zpointer data) {\n    ThreadLocalKeyList *g_keys = g_thread_local_key_list;\n    zsize i;\n\n    for (i = 0; i < g_keys->size; i++) {\n        if (g_keys->keys[i] == key_ptr)\n            return pthread_setspecific(g_keys->keys[i]->key, data);\n    }\n    return FALSE;\n}\n\nlong zz_posix_get_current_thread_id() { return (long)pthread_self(); }\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/posix/thread-utils-posix.h",
    "content": "/**\n *    Copyright 2017 jmpews\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#ifndef zzdeps_posix_thread_utils_h\n#define zzdeps_posix_thread_utils_h\n\n#include <err.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include <pthread.h>\n\n#include \"../zz.h\"\n\ntypedef struct _ThreadLocalKey {\n    pthread_key_t key;\n} ThreadLocalKey;\n\ntypedef struct _ThreadLocalKeyList {\n    zsize size;\n    zsize capacity;\n    ThreadLocalKey **keys;\n} ThreadLocalKeyList;\n\nvoid zz_posix_thread_initialize_thread_local_key_list();\n\nzpointer zz_posix_thread_new_thread_local_key_ptr();\n\nzpointer zz_posix_thread_get_current_thread_data(zpointer key_ptr);\n\nzbool zz_posix_thread_set_current_thread_data(zpointer key_ptr, zpointer data);\n\nlong zz_posix_get_current_thread_id();\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzdeps/zz.h",
    "content": "#ifndef zz_h\n#define zz_h\n\n// Created by jmpews on 2017/5/3.\n//\n#define PROGRAM_NAME \"zz\"\n#define PROGRAM_VER \"1.0.0\"\n#define PROGRAM_AUTHOR \"jmpews@gmail.com\"\n\n#include <stdbool.h>\n#include <stdint.h>\n\n// --- custom type ---\n\n// 1. zpointer and zaddr is different\n\n#define DEBUG_MODE 0\n\n#ifndef zz_type\n#define zz_type\n\ntypedef void *zpointer;\ntypedef unsigned long zsize;\ntypedef unsigned long zaddr;\n\ntypedef uint64_t zuint64;\ntypedef uint32_t zuint32;\ntypedef uint16_t zuint16;\ntypedef uint8_t zuint8;\n\ntypedef int32_t zint32;\ntypedef int16_t zint16;\ntypedef int8_t zint8;\n\ntypedef unsigned long zuint;\ntypedef long zint;\ntypedef unsigned char zbyte;\ntypedef bool zbool;\n\n#endif\n\n#if defined(FALSE)\n#else\n#define FALSE 0\n#define TRUE 1\n#endif\n\n// --- log configuration ---\n\n#define GLOBAL_DEBUG 0\n#define GLOBAL_INFO 1\n#define SYSLOG 0\n#define COLOR_LOG 0\n\n#if (COLOR_LOG)\n#define RED \"\\x1B[31m\"\n#define GRN \"\\x1B[32m\"\n#define YEL \"\\x1B[33m\"\n#define BLU \"\\x1B[34m\"\n#define MAG \"\\x1B[35m\"\n#define CYN \"\\x1B[36m\"\n#define WHT \"\\x1B[37m\"\n#define RESET \"\\x1B[0m\"\n#else\n#define RED \"\"\n#define GRN \"\"\n#define YEL \"\"\n#define BLU \"\"\n#define MAG \"\"\n#define CYN \"\"\n#define WHT \"\"\n#define RESET \"\"\n#endif\n\n#include <stdio.h>\n\n// Important!!!\n// STDERR before STDOUT, because sync\n\n#if (SYSLOG)\n#include <sys/syslog.h>\n#define Xinfo(fmt, ...)                                                                                                \\\n    do {                                                                                                               \\\n        if (GLOBAL_INFO)                                                                                               \\\n            syslog(LOG_WARNING, RESET fmt, __VA_ARGS__);                                                               \\\n    } while (0)\n#define Sinfo(MSG) Xinfo(\"%s\", MSG)\n#define Xdebug(fmt, ...)                                                                                               \\\n    do {                                                                                                               \\\n        if (GLOBAL_DEBUG)                                                                                              \\\n            syslog(LOG_WARNING, RESET fmt, __VA_ARGS__);                                                               \\\n    } while (0)\n#define Sdebug(MSG) Xdebug(\"%s\", MSG)\n#define Xerror(fmt, ...)                                                                                               \\\n    do {                                                                                                               \\\n        syslog(LOG_DEBUG,                                                                                              \\\n               RED \"[!] \"                                                                                              \\\n                   \"%s:%d:%s(): \" fmt RESET \"\\n\",                                                                      \\\n               __FILE__, __LINE__, __func__, __VA_ARGS__);                                                             \\\n    } while (0)\n\n#define Serror(MSG) Xerror(\"%s\", MSG)\n#else\n#define Xinfo(fmt, ...)                                                                                                \\\n    do {                                                                                                               \\\n        if (GLOBAL_INFO)                                                                                               \\\n            fprintf(stdout, RESET fmt \"\\n\", __VA_ARGS__);                                                              \\\n    } while (0)\n#define Sinfo(MSG) Xinfo(\"%s\", MSG)\n\n#define Xdebug(fmt, ...)                                                                                               \\\n    do {                                                                                                               \\\n        if (GLOBAL_DEBUG)                                                                                              \\\n            fprintf(stdout, RESET fmt \"\\n\", __VA_ARGS__);                                                              \\\n    } while (0)\n#define Sdebug(MSG) Xdebug(\"%s\", MSG)\n#define Xerror(fmt, ...)                                                                                               \\\n    do {                                                                                                               \\\n        fprintf(stderr,                                                                                                \\\n                RED \"[!] \"                                                                                             \\\n                    \"%s:%d:%s(): \" fmt RESET \"\\n\",                                                                     \\\n                __FILE__, __LINE__, __func__, __VA_ARGS__);                                                            \\\n    } while (0)\n\n#define Serror(MSG) Xerror(\"%s\", MSG)\n#endif\n\n// --- common macro ---\n#undef ABS\n#define ABS(a) (((a) < 0) ? -(a) : (a))\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzinfo.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"zzinfo.h\"\n\nZzInfo g_zz;\n\nvoid ZzEnableDebugMode() { g_zz.g_enable_debug_flag = TRUE; }\n\nzbool ZzIsEnableDebugMode() { return g_zz.g_enable_debug_flag; }\n\nZzInfo *ZzInfoObtain(void) { return &g_zz; }\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/src/zzinfo.h",
    "content": "//    Copyright 2017 jmpews\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#ifndef zzinfo_h\n#define zzinfo_h\n\n// platforms\n\n// hookzz\n#include \"hookzz.h\"\n\n// zzdeps\n#include \"zzdefs.h\"\n#include \"zzdeps/common/debugbreak.h\"\n#include \"zzdeps/zz.h\"\n\ntypedef struct _ZzInfo {\n    zbool g_enable_debug_flag;\n} ZzInfo;\n\nZzInfo *ZzInfoObtain(void);\nzbool ZzIsEnableDebugMode();\n\n#if defined(__ANDROID__)\n#include <android/log.h>\n#define ZzInfoLog(fmt, ...)                                                                                            \\\n    { __android_log_print(ANDROID_LOG_INFO, \"zzinfo\", fmt, __VA_ARGS__); }\n#else\n#define ZzInfoLog(fmt, ...)                                                                                            \\\n    { Xinfo(fmt, __VA_ARGS__); }\n#endif\n\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm-android/makefile",
    "content": "NO_COLOR=\\x1b[0m\nOK_COLOR=\\x1b[32;01m\nERROR_COLOR=\\x1b[31;01m\nWARN_COLOR=\\x1b[33;01m\n\nHOOKZZ_INCLUDE_DIR := $(abspath ../../include)\nHOOKZZ_LIB_DIR := $(abspath ../../build/android-armv7)\n\nCFLAGS ?= -O0 -g -std=c99 -pie -fPIE\n\nHOST ?= $(shell uname -s)\nHOST_ARCH ?= $(shell uname -m)\n\nARCH := arm\n\nifeq ($(ARCH), arm)\n\tZZ_ARCH := armv7\n\tZZ_API_LEVEL := android-19\n\tZZ_CROSS_PREFIX := arm-linux-androideabi-\nelse ifeq ($(ARCH), arm64)\n\tZZ_ARCH := arm64\n\tZZ_API_LEVEL := android-21\n\tZZ_CROSS_PREFIX := aarch64-linux-android-\nendif\n\nHOST_DIR := $(shell echo $(HOST) | tr A-Z a-z)-$(HOST_ARCH)\nZZ_NDK_HOME := $(shell dirname `which ndk-build`)\nZZ_SDK_ROOT := $(ZZ_NDK_HOME)/platforms/$(ZZ_API_LEVEL)/arch-$(ARCH)\nZZ_GCC_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_CROSS_PREFIX)gcc\nZZ_GXX_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_CROSS_PREFIX)g++\nZZ_AR_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_CROSS_PREFIX)ar\nZZ_RANLIB_BIN := $(ZZ_NDK_HOME)/toolchains/$(ZZ_CROSS_PREFIX)4.9/prebuilt/$(HOST_DIR)/bin/$(ZZ_CROSS_PREFIX)ranlib\n\nZZ_GCC_SOURCE := $(ZZ_GCC_BIN) --sysroot=$(ZZ_SDK_ROOT)\nZZ_GXX_SOURCE := $(ZZ_GXX_BIN) --sysroot=$(ZZ_SDK_ROOT)\nZZ_GCC_TEST := $(ZZ_GCC_BIN) --sysroot=$(ZZ_SDK_ROOT)\nZZ_GXX_TEST := $(ZZ_GXX_BIN) --sysroot=$(ZZ_SDK_ROOT)\n\ntest: \n\t@$(ZZ_GCC_TEST) $(CFLAGS) -I$(HOOKZZ_INCLUDE_DIR) -c test_hook_open_arm.c -o test_hook_open_arm.o\n\t@$(ZZ_GCC_TEST) $(CFLAGS) -I$(HOOKZZ_INCLUDE_DIR) test_hook_open_arm.o  -L$(HOOKZZ_LIB_DIR) -lhookzz.static -o $(HOOKZZ_LIB_DIR)/test_hook_open_arm\n\t@echo \"$(OK_COLOR)build [test_hook_open_arm.dylib] success for armv7-ios! $(NO_COLOR)\"\n\n\t@$(ZZ_GCC_TEST) $(CFLAGS) -I$(HOOKZZ_INCLUDE_DIR) -c test_hook_address_thumb.c -o test_hook_address_thumb.o\n\t@$(ZZ_GCC_TEST) $(CFLAGS) -I$(HOOKZZ_INCLUDE_DIR) test_hook_address_thumb.o  -L$(HOOKZZ_LIB_DIR) -lhookzz.static -o $(HOOKZZ_LIB_DIR)/test_hook_address_thumb\n\t@echo \"$(OK_COLOR)build [test_hook_address_thumb.dylib] success for armv7-ios! $(NO_COLOR)\"\n\n\t@$(ZZ_GCC_TEST) $(CFLAGS) -I$(HOOKZZ_INCLUDE_DIR) -c test_hook_printf.c -o test_hook_printf.o\n\t@$(ZZ_GCC_TEST) $(CFLAGS) -I$(HOOKZZ_INCLUDE_DIR) test_hook_printf.o  -L$(HOOKZZ_LIB_DIR) -lhookzz.static -o $(HOOKZZ_LIB_DIR)/test_hook_printf\n\t@echo \"$(OK_COLOR)build [test_hook_printf.dylib] success for armv7-ios! $(NO_COLOR)\"\n\n\t@echo \"$(OK_COLOR)build [test] success for armv7-android-hookzz! $(NO_COLOR)\"\n\nclean:\n\t@rm -rf $(shell find ./ -name \"*\\.o\" | xargs echo)\n\t@rm -rf $(shell find $(HOOKZZ_LIB_DIR) -name \"test_*\" | xargs echo)\n\t@echo \"$(OK_COLOR)clean all *.o success!$(NO_COLOR)\""
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm-android/test_hook_address_thumb.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"hookzz.h\"\n#include <stdio.h>\n#include <unistd.h>\n\n__attribute__((__naked__)) static void hack_this_function() {\n#ifdef __arm__\n    __asm__ volatile(\".code 32\\n\"\n                     \"mov r0, #0\\n\"\n                     \"mov r12, #20\\n\"\n                     \"svc #0x80\\n\"\n                     \"nop\\n\"\n                     \"nop\\n\"\n                     \"nop\\n\"\n                     \"nop\\n\"\n                     \"nop\\n\"\n                     \"nop\");\n#endif\n}\n\n__attribute__((__naked__)) static void sorry_to_exit() {\n#ifdef __arm__\n    __asm__ volatile(\".code 32\\n\"\n                     \"mov r0, #0\\n\"\n                     \"mov r12, #1\\n\"\n                     \"svc #0x80\");\n#endif\n}\n\nvoid getpid_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    unsigned long request = *(unsigned long *)(&rs->general.regs.r12);\n    printf(\"request(r12) is: %ld\\n\", request);\n    printf(\"r0 is: %ld\\n\", (long)rs->general.regs.r0);\n}\n\nvoid getpid_half_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    pid_t r0 = (pid_t)(rs->general.regs.r0);\n    printf(\"getpid() return at r0 is: %d\\n\", r0);\n}\n\n__attribute__((constructor)) void test_hook_address() {\n    void *hack_this_function_ptr = (void *)hack_this_function;\n    // hook address with only `pre_call`\n    // ZzBuildHookAddress(hack_this_function_ptr + 8, hack_this_function_ptr + 12, (void\n    // *)getpid_pre_call, NULL);\n\n    // hook address with only `half_call`\n    // ZzBuildHookAddress(hack_this_function_ptr + 8, hack_this_function_ptr + 12, NULL, (void\n    // *)getpid_half_call);\n\n    // hook address with both `half_call` and `pre_call`\n    ZzEnableDebugMode();\n    ZzHookAddress(hack_this_function_ptr + 8, hack_this_function_ptr + 8 + 4, getpid_pre_call, getpid_half_call);\n\n    void *sorry_to_exit_ptr = (void *)sorry_to_exit;\n    unsigned long nop_bytes = 0xE1A00000;\n    ZzRuntimeCodePatch((unsigned long)sorry_to_exit_ptr + 8, (zpointer)&nop_bytes, 4);\n\n    hack_this_function();\n    sorry_to_exit();\n\n    printf(\"hack success -.0\\n\");\n}\n\nint main(int args, char **argv) {}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm-android/test_hook_open_arm.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"hookzz.h\"\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nvoid open_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    zpointer t = (void *)0x1234;\n    // STACK_SET(callstack ,\"key_x\", t, void *);\n    // STACK_SET(callstack ,\"key_y\", t, zpointer);\n    // NSLog(@\"hookzz OC-Method: -[UIViewController %s]\",\n    // (zpointer)(rs->general.regs.x1));\n}\n\nvoid open_post_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    // zpointer x = STACK_GET(callstack, \"key_x\", void *);\n    // zpointer y = STACK_GET(callstack, \"key_y\", zpointer);\n    // NSLog(@\"function over, and get 'key_x' is: %p\", x);\n    // NSLog(@\"function over, and get 'key_y' is: %p\", y);\n}\n\n__attribute__((constructor)) void test_hook_printf() {\n    void *open_ptr = (void *)open;\n\n    ZzEnableDebugMode();\n    ZzHookPrePost((void *)open_ptr, open_pre_call, open_post_call);\n\n    open(\"/home/zz\", O_RDONLY);\n}\n\nint main(int args, char **argv) {}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm-android/test_hook_printf.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"hookzz.h\"\n#include <stdarg.h>\n#include <stdio.h>\n#include <string.h>\n\nint (*orig_printf)(const char *format, ...);\nint fake_printf(const char *format, ...) {\n    puts(\"call printf\");\n\n    char *stack[16];\n    va_list args;\n    va_start(args, format);\n    // *(void **)&args for android\n    memcpy(stack, *(void **)&args, sizeof(char *) * 16);\n    va_end(args);\n\n    // how to hook variadic function? fake a original copy stack.\n    // [move to\n    // detail-1](http://jmpews.github.io/2017/08/29/pwn/%E7%9F%AD%E5%87%BD%E6%95%B0%E5%92%8C%E4%B8%8D%E5%AE%9A%E5%8F%82%E6%95%B0%E7%9A%84hook/)\n    // [move to detail-2](https://github.com/jmpews/HookZzModules/tree/master/AntiDebugBypass)\n    int x = orig_printf(format, stack[0], stack[1], stack[2], stack[3], stack[4], stack[5], stack[6], stack[7],\n                        stack[8], stack[9], stack[10], stack[11], stack[12], stack[13], stack[14], stack[15]);\n    return x;\n}\n\nvoid printf_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    puts((char *)rs->general.regs.r0);\n    STACK_SET(callstack, \"format\", rs->general.regs.r0, char *);\n    puts(\"printf-pre-call\");\n}\n\nvoid printf_post_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    if (STACK_CHECK_KEY(callstack, \"format\")) {\n        char *format = STACK_GET(callstack, \"format\", char *);\n        puts(format);\n    }\n    puts(\"printf-post-call\");\n}\n\n__attribute__((constructor)) void test_hook_printf() {\n    void *printf_ptr = (void *)printf;\n\n    ZzEnableDebugMode();\n    ZzHook((void *)printf_ptr, (void *)fake_printf, (void **)&orig_printf, printf_pre_call, printf_post_call, FALSE);\n    printf(\"HookZzzzzzz, %d, %p, %d, %d, %d, %d, %d, %d, %d\\n\", 1, (void *)2, 3, (char)4, (char)5, (char)6, 7, 8, 9);\n}\n\nint main(int args, char **argv) {}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm-insn-fix/makefile",
    "content": "NO_COLOR=\\x1b[0m\nOK_COLOR=\\x1b[32;01m\nERROR_COLOR=\\x1b[31;01m\nWARN_COLOR=\\x1b[33;01m\n\n\nHOOKZZ_INCLUDE_DIR := -I$(abspath ../../include) -I$(abspath ../../src)\nHOOKZZ_LIB_DIR := -L$(abspath ../../build/ios-armv7)\n\nZZ_GCC_TEST := $(shell xcrun --sdk iphoneos --find clang) -isysroot $(shell xcrun --sdk iphoneos --show-sdk-path) -arch armv7 -O0 -g\n\n# -undefined dynamic_lookup\ntest: \n\t@$(ZZ_GCC_TEST) $(HOOKZZ_INCLUDE_DIR) -c test_insn_fix.c -o test_insn_fix.o\n\t@$(ZZ_GCC_TEST) -dynamiclib $(HOOKZZ_LIB_DIR) -lhookzz.static test_insn_fix.o -o test_insn_fix.dylib\n\t@echo \"$(OK_COLOR)build [test_insn_fix.dylib] success for armv7! $(NO_COLOR)\"\nclean:\n\trm -rf test_insn_fix.o\n\trm -rf test_insn_fix.dylib\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm-insn-fix/test_insn_fix.c",
    "content": "#include \"hookzz.h\"\n#include <stdio.h>\n#include <unistd.h>\n\nstatic void thumb_insn_need_fix() {\n    __asm__ volatile(\".code 16\\n\"\n\n                     \"add r0, pc\\n\"\n\n                     \"ldr r0, [pc, #8]\\n\"\n                     \"ldr.W r0, [pc, #8]\\n\"\n\n                     \"adr r0, #8\\n\"\n                     \"adr.W r0, #8\\n\"\n                     \"adr.W r0, #-8\\n\"\n\n                     \"beq #8\\n\"\n                     \"b #8\\n\"\n                     \"beq.W #8\\n\"\n                     \"b.W #8\\n\"\n\n                     \"bl #8\\n\"\n                     \"blx #8\\n\"\n                     \"nop\");\n}\n\n#include \"platforms/backend-arm64/interceptor-arm64.h\"\n#include <stdlib.h>\n\n#if 1\n__attribute__((constructor)) void test_insn_fix_thumb() {\n\n    ZzInterceptorBackend *backend = (ZzInterceptorBackend *)malloc(sizeof(ZzInterceptorBackend));\n    zbyte temp_code_slice_data[256] = {0};\n\n    zz_arm_writer_init(&backend->arm_writer, NULL);\n    zz_arm_relocator_init(&backend->arm_relocator, NULL, &backend->arm_writer);\n    zz_thumb_writer_init(&backend->thumb_writer, NULL);\n    zz_thumb_relocator_init(&backend->thumb_relocator, NULL, &backend->thumb_writer);\n\n    ZzThumbRelocator *thumb_relocator;\n    ZzThumbWriter *thumb_writer;\n    thumb_relocator = &backend->thumb_relocator;\n    thumb_writer = &backend->thumb_writer;\n\n    zz_thumb_writer_reset(thumb_writer, temp_code_slice_data);\n\n    zz_thumb_relocator_reset(thumb_relocator, (zpointer)((zaddr)thumb_insn_need_fix & ~(zaddr)1), thumb_writer);\n    zsize tmp_relocator_insn_size = 0;\n\n    do {\n        zz_thumb_relocator_read_one(thumb_relocator, NULL);\n        zz_thumb_relocator_write_one(thumb_relocator);\n        tmp_relocator_insn_size = thumb_relocator->input_cur - thumb_relocator->input_start;\n    } while (tmp_relocator_insn_size < 36);\n}\n#endif\n\n#if 0\n__attribute__((__naked__)) void arm_insn_need_fix() {\n    __asm__ volatile(\".arm\\n\"\n                     \"add r0, pc, r0\\n\"\n\n                     \"ldr r0, [pc, #8]\\n\"\n\n                     \"adr r0, #8\\n\"\n                     \"adr r0, #-8\\n\"\n\n                     \"beq #8\\n\"\n                     \"b #8\\n\"\n\n                     \"bl #8\\n\"\n                     \"blx #8\\n\"\n                     \"nop\");\n}\n\n#include \"platforms/backend-arm/interceptor-arm.h\"\n#include <stdlib.h>\n\n__attribute__((constructor)) void test_insn_fix_arm() {\n\n    ZzInterceptorBackend *backend = (ZzInterceptorBackend *)malloc(sizeof(ZzInterceptorBackend));\n    zbyte temp_code_slice_data[256] = {0};\n\n    zz_arm_writer_init(&backend->arm_writer, NULL);\n    zz_arm_relocator_init(&backend->arm_relocator, NULL, &backend->arm_writer);\n    zz_thumb_writer_init(&backend->thumb_writer, NULL);\n    zz_thumb_relocator_init(&backend->thumb_relocator, NULL, &backend->thumb_writer);\n\n    ZzArmRelocator *arm_relocator;\n    ZzArmWriter *arm_writer;\n    arm_relocator = &backend->arm_relocator;\n    arm_writer = &backend->arm_writer;\n\n    zz_arm_writer_reset(arm_writer, temp_code_slice_data);\n\n    zz_arm_relocator_reset(arm_relocator, (zpointer)((zaddr)arm_insn_need_fix & ~(zaddr)1), arm_writer);\n    zsize tmp_relocator_insn_size = 0;\n\n    do {\n        zz_arm_relocator_read_one(arm_relocator, NULL);\n        zz_arm_relocator_write_one(arm_relocator);\n        tmp_relocator_insn_size = arm_relocator->input_cur - arm_relocator->input_start;\n    } while (tmp_relocator_insn_size < 36);\n}\n#endif"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm-ios/makefile",
    "content": "NO_COLOR=\\x1b[0m\nOK_COLOR=\\x1b[32;01m\nERROR_COLOR=\\x1b[31;01m\nWARN_COLOR=\\x1b[33;01m\n\n\nHOOKZZ_INCLUDE_DIR := $(abspath ../../include)\nHOOKZZ_LIB_DIR := $(abspath ../../build/ios-armv7)\n\nZZ_GCC_TEST := $(shell xcrun --sdk iphoneos --find clang) -isysroot $(shell xcrun --sdk iphoneos --show-sdk-path) -arch armv7 -O0 -g\n\n# -undefined dynamic_lookup\ntest: \n\t@$(ZZ_GCC_TEST) -I$(HOOKZZ_INCLUDE_DIR) -c test_hook_oc_thumb.m -o test_hook_oc_thumb.o\n\t@$(ZZ_GCC_TEST) -dynamiclib  -Wl,-U,_func -framework Foundation -L$(HOOKZZ_LIB_DIR) -lhookzz.static test_hook_oc_thumb.o -arch armv7 -o $(HOOKZZ_LIB_DIR)/test_hook_oc_thumb.dylib\n\t@echo \"$(OK_COLOR)build [test_hook_oc_thumb.dylib] success for armv7-ios! $(NO_COLOR)\"\n\n\t@$(ZZ_GCC_TEST) -I$(HOOKZZ_INCLUDE_DIR) -c test_hook_open_arm.c -o test_hook_open_arm.o\n\t@$(ZZ_GCC_TEST) -dynamiclib -Wl,-U,_func -framework Foundation -L$(HOOKZZ_LIB_DIR) -lhookzz.static test_hook_open_arm.o -o $(HOOKZZ_LIB_DIR)/test_hook_open_arm.dylib\n\t@echo \"$(OK_COLOR)build [test_hook_open_arm.dylib] success for armv7-ios! $(NO_COLOR)\"\n\n\t@$(ZZ_GCC_TEST) -I$(HOOKZZ_INCLUDE_DIR) -c test_hook_address_thumb.c -o test_hook_address_thumb.o\n\t@$(ZZ_GCC_TEST) -dynamiclib -Wl,-U,_func -framework Foundation -L$(HOOKZZ_LIB_DIR) -lhookzz.static test_hook_address_thumb.o -o $(HOOKZZ_LIB_DIR)/test_hook_address_thumb.dylib\n\t@echo \"$(OK_COLOR)build [test_hook_address_thumb.dylib] success for armv7-ios! $(NO_COLOR)\"\n\n\t@$(ZZ_GCC_TEST) -I$(HOOKZZ_INCLUDE_DIR) -c test_hook_printf.c -o test_hook_printf.o\n\t@$(ZZ_GCC_TEST) -dynamiclib -Wl,-U,_func -framework Foundation -L$(HOOKZZ_LIB_DIR) -lhookzz.static test_hook_printf.o -o $(HOOKZZ_LIB_DIR)/test_hook_printf.dylib\n\t@echo \"$(OK_COLOR)build [test_hook_printf.dylib] success for armv7-ios! $(NO_COLOR)\"\n\n\n\t@$(ZZ_GCC_TEST) -I$(HOOKZZ_INCLUDE_DIR) -c test_hook_freeaddr.c -o test_hook_freeaddr.o\n\t@$(ZZ_GCC_TEST) -dynamiclib -Wl,-U,_func -framework Foundation -L$(HOOKZZ_LIB_DIR) -lhookzz.static test_hook_freeaddr.o -o $(HOOKZZ_LIB_DIR)/test_hook_freeaddr.dylib\n\t@echo \"$(OK_COLOR)build [test_hook_freeaddr.dylib] success for armv7-ios! $(NO_COLOR)\"\n\n\n\t@echo \"$(OK_COLOR)build [test] success for armv7-ios-hookzz! $(NO_COLOR)\"\n\nclean:\n\t@rm -rf $(shell find ./ -name \"*\\.o\" | xargs echo)\n\t@rm -rf $(shell find $(HOOKZZ_LIB_DIR) -name \"test_*\" | xargs echo)\n\t@echo \"$(OK_COLOR)clean all *.o success!$(NO_COLOR)\""
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm-ios/test_hook_address_thumb.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"hookzz.h\"\n#include <stdio.h>\n#include <unistd.h>\n\nstatic void hack_this_function() {\n#ifdef __arm__\n    __asm__ volatile(\".code 16\\n\"\n                     \"mov r0, #0\\n\"\n                     \"mov r12, #20\\n\"\n                     \"svc #0x80\\n\"\n                     \"nop\\n\"\n                     \"nop\\n\"\n                     \"nop\\n\"\n                     \"nop\\n\"\n                     \"nop\\n\"\n                     \"nop\");\n#endif\n}\n\nstatic void sorry_to_exit() {\n#ifdef __arm__\n    __asm__ volatile(\".code 16\\n\"\n                     \"mov r0, #0\\n\"\n                     \"mov r12, #1\\n\"\n                     \"svc #0x80\");\n#endif\n}\n\nvoid getpid_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    unsigned long request = *(unsigned long *)(&rs->general.regs.r12);\n    printf(\"request(r12) is: %ld\\n\", request);\n    printf(\"r0 is: %ld\\n\", (long)rs->general.regs.r0);\n}\n\nvoid getpid_half_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    pid_t r0 = (pid_t)(rs->general.regs.r0);\n    printf(\"getpid() return at r0 is: %d\\n\", r0);\n}\n\n__attribute__((constructor)) void test_hook_address() {\n    void *hack_this_function_ptr = (void *)hack_this_function;\n    // hook address with only `pre_call`\n    // ZzBuildHookAddress(hack_this_function_ptr + 8, hack_this_function_ptr + 12, (void\n    // *)getpid_pre_call, NULL);\n\n    // hook address with only `half_call`\n    // ZzBuildHookAddress(hack_this_function_ptr + 8, hack_this_function_ptr + 12, NULL, (void\n    // *)getpid_half_call);\n\n    // hook address with both `half_call` and `pre_call`\n    ZzBuildHookAddress(hack_this_function_ptr + 8, hack_this_function_ptr + 10, getpid_pre_call, getpid_half_call,\n                       FALSE);\n    ZzEnableHook((void *)hack_this_function_ptr + 8);\n\n    void *sorry_to_exit_ptr = (void *)sorry_to_exit;\n    unsigned long nop_bytes = 0x46c0;\n    ZzRuntimeCodePatch((unsigned long)sorry_to_exit_ptr + 8, (zpointer)&nop_bytes, 2);\n\n    hack_this_function();\n    sorry_to_exit();\n\n    printf(\"hack success -.0\\n\");\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm-ios/test_hook_freeaddr.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"hookzz.h\"\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include <netdb.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n\nvoid (*orig_freeaddrinfo)(struct addrinfo *ai);\nvoid fake_freeaddrinfo(struct addrinfo *ai) { orig_freeaddrinfo(ai); }\n\n__attribute__((constructor)) void test_hook_freeaddrinfo() {\n    ZzEnableDebugMode();\n    ZzHook((void *)freeaddrinfo, (void *)fake_freeaddrinfo, &orig_freeaddrinfo, NULL, NULL, FALSE);\n\n    int sockfd;\n    struct addrinfo hints, *servinfo, *p;\n    int rv;\n\n    memset(&hints, 0, sizeof hints);\n    hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6\n    hints.ai_socktype = SOCK_STREAM;\n    hints.ai_flags = AI_PASSIVE; // use my IP address\n\n    if ((rv = getaddrinfo(NULL, \"3490\", &hints, &servinfo)) != 0) {\n        fprintf(stderr, \"getaddrinfo: %s\\n\", gai_strerror(rv));\n        exit(1);\n    }\n\n    // loop through all the results and bind to the first we can\n    for (p = servinfo; p != NULL; p = p->ai_next) {\n        if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {\n            perror(\"socket\");\n            continue;\n        }\n\n        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {\n            close(sockfd);\n            perror(\"bind\");\n            continue;\n        }\n\n        break; // if we get here, we must have connected successfully\n    }\n\n    if (p == NULL) {\n        // looped off the end of the list with no successful bind\n        fprintf(stderr, \"failed to bind socket\\n\");\n        exit(2);\n    }\n\n    freeaddrinfo(servinfo); // all done with this structure\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm-ios/test_hook_oc_thumb.m",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"hookzz.h\"\n#import <Foundation/Foundation.h>\n#import <dlfcn.h>\n#import <mach-o/dyld.h>\n#import <objc/runtime.h>\n\n@interface HookZz : NSObject\n\n@end\n\n@implementation HookZz\n\n+ (void)load {\n    [self zzMethodSwizzlingHook];\n}\n\nvoid objcMethod_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    zpointer t = (void *)0x1234;\n    // STACK_SET(callstack ,\"key_x\", t, void *);\n    // STACK_SET(callstack ,\"key_y\", t, zpointer);\n    // NSLog(@\"hookzz OC-Method: -[UIViewController %s]\",\n    // (zpointer)(rs->general.regs.x1));\n}\n\nvoid objcMethod_post_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    // zpointer x = STACK_GET(callstack, \"key_x\", void *);\n    // zpointer y = STACK_GET(callstack, \"key_y\", zpointer);\n    // NSLog(@\"function over, and get 'key_x' is: %p\", x);\n    // NSLog(@\"function over, and get 'key_y' is: %p\", y);\n}\n\n+ (void)zzMethodSwizzlingHook {\n    Class hookClass = objc_getClass(\"UIViewController\");\n    SEL oriSEL = @selector(viewWillAppear:);\n    Method oriMethod = class_getInstanceMethod(hookClass, oriSEL);\n    IMP oriImp = method_getImplementation(oriMethod);\n\n    ZzEnableDebugMode();\n    ZzHookPrePost((void *)oriImp, objcMethod_pre_call, objcMethod_post_call);\n}\n\n@end\n\n/*\n(lldb) disass -n \"-[UIViewController viewWillAppear:]\" -c 3\nUIKit`-[UIViewController viewWillAppear:]:\n    0x18881c10c <+0>: adrp   x8, 126868\n    0x18881c110 <+4>: ldrsw  x8, [x8, #0x280]\n    0x18881c114 <+8>: ldr    x9, [x0, x8]\n\n(lldb) c\nProcess 41637 resuming\n(lldb) c\nProcess 41637 resuming\n(lldb) c\nProcess 41637 resuming\n2017-08-30 02:01:58.954875+0800 T007[41637:10198806] hookzz OC-Method:\n-[UIViewController viewWillAppear:] 2017-08-30 02:01:58.956558+0800\nT007[41637:10198806] function over, and get 'key_x' is: 0x1234 2017-08-30\n02:01:58.956654+0800 T007[41637:10198806] function over, and get 'key_y' is:\n0x1234 (lldb) disass -n \"-[UIViewController viewWillAppear:]\" -c 3\nUIKit`-[UIViewController viewWillAppear:]:\n    0x18881c10c <+0>: b      0x1810b0b4c\n    0x18881c110 <+4>: ldrsw  x8, [x8, #0x280]\n    0x18881c114 <+8>: ldr    x9, [x0, x8]\n*/"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm-ios/test_hook_open_arm.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"hookzz.h\"\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n\nvoid open_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    char *path = (char *)rs->general.regs.r0;\n    printf(\"open file: %s\\n\", path);\n}\n\nvoid open_post_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {}\n\n__attribute__((constructor)) void test_hook_printf() {\n    void *open_ptr = (void *)open;\n\n    ZzEnableDebugMode();\n    // ZzHookPrePost((void *)open_ptr, open_pre_call, open_post_call);\n    ZzHook((void *)open_ptr, NULL, NULL, open_pre_call, open_post_call, TRUE);\n\n    open(\"/home/zz\", O_RDONLY);\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm-ios/test_hook_printf.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"hookzz.h\"\n#include <stdarg.h>\n#include <stdio.h>\n#include <string.h>\n\nint (*orig_printf)(const char *restrict format, ...);\nint fake_printf(const char *restrict format, ...) {\n    puts(\"call printf\");\n\n    char *stack[16];\n    va_list args;\n    va_start(args, format);\n    // *(void **)&args for android\n    memcpy(stack, *(void **)&args, sizeof(char *) * 16);\n    va_end(args);\n\n    // how to hook variadic function? fake a original copy stack.\n    // [move to\n    // detail-1](http://jmpews.github.io/2017/08/29/pwn/%E7%9F%AD%E5%87%BD%E6%95%B0%E5%92%8C%E4%B8%8D%E5%AE%9A%E5%8F%82%E6%95%B0%E7%9A%84hook/)\n    // [move to detail-2](https://github.com/jmpews/HookZzModules/tree/master/AntiDebugBypass)\n    int x = orig_printf(format, stack[0], stack[1], stack[2], stack[3], stack[4], stack[5], stack[6], stack[7],\n                        stack[8], stack[9], stack[10], stack[11], stack[12], stack[13], stack[14], stack[15]);\n    return x;\n}\n\nvoid printf_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    puts((char *)rs->general.regs.r0);\n    STACK_SET(callstack, \"format\", rs->general.regs.r0, char *);\n    puts(\"printf-pre-call\");\n}\n\nvoid printf_post_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    if (STACK_CHECK_KEY(callstack, \"format\")) {\n        char *format = STACK_GET(callstack, \"format\", char *);\n        puts(format);\n    }\n    puts(\"printf-post-call\");\n}\n\n__attribute__((constructor)) void test_hook_printf() {\n    void *printf_ptr = (void *)printf;\n\n    ZzEnableDebugMode();\n    ZzHook((void *)printf_ptr, (void *)fake_printf, (void **)&orig_printf, printf_pre_call, printf_post_call, TRUE);\n    printf(\"HookZzzzzzz, %d, %p, %d, %d, %d, %d, %d, %d, %d\\n\", 1, (void *)2, 3, (char)4, (char)5, (char)6, 7, 8, 9);\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm64-insn-fix/makefile",
    "content": "NO_COLOR=\\x1b[0m\nOK_COLOR=\\x1b[32;01m\nERROR_COLOR=\\x1b[31;01m\nWARN_COLOR=\\x1b[33;01m\n\n\nHOOKZZ_INCLUDE_DIR := -I$(abspath ../../include) -I$(abspath ../../src)\nHOOKZZ_LIB_DIR := -L$(abspath ../../build/ios-arm64)\n\nZZ_GCC_TEST := $(shell xcrun --sdk iphoneos --find clang) -isysroot $(shell xcrun --sdk iphoneos --show-sdk-path) -arch arm64 -O0 -g\n\n# -undefined dynamic_lookup\ntest: \n\t@$(ZZ_GCC_TEST) $(HOOKZZ_INCLUDE_DIR) -c test_insn_fix.c -o test_insn_fix.o\n\t@$(ZZ_GCC_TEST) -dynamiclib $(HOOKZZ_LIB_DIR) -lhookzz.static test_insn_fix.o -o test_insn_fix.dylib\n\t@echo \"$(OK_COLOR)build [test_insn_fix.dylib] success for arm64! $(NO_COLOR)\"\nclean:\n\trm -rf test_insn_fix.o\n\trm -rf test_insn_fix.dylib\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm64-insn-fix/test_insn_fix.c",
    "content": "#include \"hookzz.h\"\n#include <stdio.h>\n#include <unistd.h>\n\nstatic void arm64_insn_need_fix() {\n    __asm__ volatile(\"bl #40\\n\"\n                     \"nop\");\n}\n\n#include \"platforms/backend-arm64/interceptor-arm64.h\"\n#include <stdlib.h>\n\n#if 1\n__attribute__((constructor)) void test_insn_fix_arm64() {\n\n    ZzInterceptorBackend *backend = (ZzInterceptorBackend *)malloc(sizeof(ZzInterceptorBackend));\n    zbyte temp_code_slice_data[256] = {0};\n\n    zz_arm64_writer_init(&backend->arm64_writer, NULL);\n    zz_arm64_relocator_init(&backend->arm64_relocator, NULL, &backend->arm64_writer);\n    zz_arm64_writer_init(&backend->arm64_writer, NULL);\n    zz_arm64_relocator_init(&backend->arm64_relocator, NULL, &backend->arm64_writer);\n\n    ZzArm64Relocator *arm64_relocator;\n    ZzArm64Writer *arm64_writer;\n    arm64_relocator = &backend->arm64_relocator;\n    arm64_writer = &backend->arm64_writer;\n\n    zz_arm64_writer_reset(arm64_writer, temp_code_slice_data);\n\n    zz_arm64_relocator_reset(arm64_relocator, (zpointer)((zaddr)arm64_insn_need_fix & ~(zaddr)1), arm64_writer);\n    zsize tmp_relocator_insn_size = 0;\n\n    do {\n        zz_arm64_relocator_read_one(arm64_relocator, NULL);\n        zz_arm64_relocator_write_one(arm64_relocator);\n        tmp_relocator_insn_size = arm64_relocator->input_cur - arm64_relocator->input_start;\n    } while (tmp_relocator_insn_size < 36);\n}\n#endif\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm64-ios/makefile",
    "content": "NO_COLOR=\\x1b[0m\nOK_COLOR=\\x1b[32;01m\nERROR_COLOR=\\x1b[31;01m\nWARN_COLOR=\\x1b[33;01m\n\n\nHOOKZZ_INCLUDE_DIR := $(abspath ../../include)\nHOOKZZ_LIB_DIR := $(abspath ../../build/ios-arm64)\n\nZZ_GCC_TEST := $(shell xcrun --sdk iphoneos --find clang) -isysroot $(shell xcrun --sdk iphoneos --show-sdk-path) -arch arm64 -O0 -g\n\n# -undefined dynamic_lookup\ntest: \n\t@$(ZZ_GCC_TEST) -I$(HOOKZZ_INCLUDE_DIR) -c test_hook_oc.m -o test_hook_oc.o\n\t@$(ZZ_GCC_TEST) -dynamiclib  -Wl,-U,_func -framework Foundation -L$(HOOKZZ_LIB_DIR) -lhookzz.static test_hook_oc.o -o $(HOOKZZ_LIB_DIR)/test_hook_oc.dylib\n\t@echo \"$(OK_COLOR)build [test_hook_oc.dylib] success for arm64-ios! $(NO_COLOR)\"\n\n\t@$(ZZ_GCC_TEST) -I$(HOOKZZ_INCLUDE_DIR) -c test_hook_address.c -o test_hook_address.o\n\t@$(ZZ_GCC_TEST) -dynamiclib -Wl,-U,_func -framework Foundation -L$(HOOKZZ_LIB_DIR) -lhookzz.static test_hook_address.o -o $(HOOKZZ_LIB_DIR)/test_hook_address.dylib\n\t@echo \"$(OK_COLOR)build [test_hook_address.dylib] success for arm64-ios! $(NO_COLOR)\"\n\n\t@$(ZZ_GCC_TEST) -I$(HOOKZZ_INCLUDE_DIR) -c test_hook_printf.c -o test_hook_printf.o\n\t@$(ZZ_GCC_TEST) -dynamiclib -Wl,-U,_func -framework Foundation -L$(HOOKZZ_LIB_DIR) -lhookzz.static test_hook_printf.o -o $(HOOKZZ_LIB_DIR)/test_hook_printf.dylib\n\t@echo \"$(OK_COLOR)build [test_hook_printf.dylib] success for arm64-ios! $(NO_COLOR)\"\n\n\t@echo \"$(OK_COLOR)build [test] success for arm64-ios-hookzz! $(NO_COLOR)\"\n\nclean:\n\t@rm -rf $(shell find ./ -name \"*\\.o\" | xargs echo)\n\t@rm -rf $(shell find $(HOOKZZ_LIB_DIR) -name \"test_*\" | xargs echo)\n\t@echo \"$(OK_COLOR)clean all *.o success!$(NO_COLOR)\""
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm64-ios/test_hook_address.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"hookzz.h\"\n#include <stdio.h>\n#include <unistd.h>\n\nstatic void hack_this_function() {\n#ifdef __arm64__\n    __asm__(\"mov X0, #0\\n\"\n            \"mov w16, #20\\n\"\n            \"svc #0x80\\n\"\n            \"nop\\n\"\n            \"nop\\n\"\n            \"nop\\n\"\n            \"nop\");\n#endif\n}\n\nstatic void sorry_to_exit() {\n#ifdef __arm64__\n    __asm__(\"mov X0, #0\\n\"\n            \"mov w16, #1\\n\"\n            \"svc #0x80\");\n#endif\n}\n\nvoid getpid_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    unsigned long request = *(unsigned long *)(&rs->general.regs.x16);\n    printf(\"request(x16) is: %ld\\n\", request);\n    printf(\"x0 is: %ld\\n\", (long)rs->general.regs.x0);\n}\n\nvoid getpid_half_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    pid_t x0 = (pid_t)(rs->general.regs.x0);\n    printf(\"getpid() return at x0 is: %d\\n\", x0);\n}\n\n__attribute__((constructor)) void test_hook_address() {\n    void *hack_this_function_ptr = (void *)hack_this_function;\n    // hook address with only `pre_call`\n    // ZzBuildHookAddress(hack_this_function_ptr + 8, hack_this_function_ptr + 12, (void\n    // *)getpid_pre_call, NULL);\n\n    // hook address with only `half_call`\n    // ZzBuildHookAddress(hack_this_function_ptr + 8, hack_this_function_ptr + 12, NULL, (void\n    // *)getpid_half_call);\n\n    // hook address with both `half_call` and `pre_call`\n    ZzBuildHookAddress(hack_this_function_ptr + 8, hack_this_function_ptr + 12, getpid_pre_call, getpid_half_call,\n                       TRUE);\n    ZzEnableHook((void *)hack_this_function_ptr + 8);\n\n    void *sorry_to_exit_ptr = (void *)sorry_to_exit;\n    unsigned long nop_bytes = 0xD503201F;\n    ZzRuntimeCodePatch((unsigned long)sorry_to_exit_ptr + 8, (zpointer)&nop_bytes, 4);\n\n    hack_this_function();\n    sorry_to_exit();\n\n    printf(\"hack success -.0\\n\");\n}\n\n/*\n(lldb) disass -n hack_this_function\ntest_hook_address.dylib`hack_this_function:\n    0x1000b0280 <+0>:  mov    x0, #0x0\n    0x1000b0284 <+4>:  mov    w16, #0x14\n    0x1000b0288 <+8>:  svc    #0x80\n    0x1000b028c <+12>: ret\n\n(lldb) disass -n sorry_to_exit\ntest_hook_address.dylib`sorry_to_exit:\n    0x1000b0290 <+0>:  mov    x0, #0x0\n    0x1000b0294 <+4>:  mov    w16, #0x1\n    0x1000b0298 <+8>:  svc    #0x80\n    0x1000b029c <+12>: ret\n\n(lldb) c\nProcess 41414 resuming\nrequest(x16) is: 20\nx0 is: 0\ngetpid() return at x0 is: 41414\nhack success -.0\n(lldb) disass -n hack_this_function\ntest_hook_address.dylib`hack_this_function:\n    0x1000b0280 <+0>:  mov    x0, #0x0\n    0x1000b0284 <+4>:  mov    w16, #0x14\n    0x1000b0288 <+8>:  b      0x1001202cc\n    0x1000b028c <+12>: ret\n\n(lldb) disass -n sorry_to_exit\ntest_hook_address.dylib`sorry_to_exit:\n    0x1000b0290 <+0>:  mov    x0, #0x0\n    0x1000b0294 <+4>:  mov    w16, #0x1\n    0x1000b0298 <+8>:  nop\n    0x1000b029c <+12>: ret\n*/"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm64-ios/test_hook_oc.m",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"hookzz.h\"\n#import <Foundation/Foundation.h>\n#import <dlfcn.h>\n#import <mach-o/dyld.h>\n#import <objc/runtime.h>\n\n@interface HookZz : NSObject\n\n@end\n\n@implementation HookZz\n\n+ (void)load {\n    [self zzMethodSwizzlingHook];\n}\n\nvoid objcMethod_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    zpointer t = 0x1234;\n    STACK_SET(callstack, \"key_x\", t, void *);\n    STACK_SET(callstack, \"key_y\", t, zpointer);\n    NSLog(@\"hookzz OC-Method: -[UIViewController %s]\", (zpointer)(rs->general.regs.x1));\n}\n\nvoid objcMethod_post_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    zpointer x = STACK_GET(callstack, \"key_x\", void *);\n    zpointer y = STACK_GET(callstack, \"key_y\", zpointer);\n    NSLog(@\"function over, and get 'key_x' is: %p\", x);\n    NSLog(@\"function over, and get 'key_y' is: %p\", y);\n}\n\n+ (void)zzMethodSwizzlingHook {\n    Class hookClass = objc_getClass(\"UIViewController\");\n    SEL oriSEL = @selector(viewWillAppear:);\n    Method oriMethod = class_getInstanceMethod(hookClass, oriSEL);\n    IMP oriImp = method_getImplementation(oriMethod);\n\n    ZzHookPrePost((void *)oriImp, objcMethod_pre_call, objcMethod_post_call);\n}\n\n@end\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tests/arm64-ios/test_hook_printf.c",
    "content": "/**\n *    Copyright 2017 jmpews\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#include \"hookzz.h\"\n#include <stdarg.h>\n#include <stdio.h>\n#include <string.h>\n\nint (*orig_printf)(const char *restrict format, ...);\nint fake_printf(const char *restrict format, ...) {\n    puts(\"call printf\");\n\n    char *stack[16];\n    va_list args;\n    va_start(args, format);\n    // *(void **)&args for android\n    memcpy(stack, *(void **)&args, sizeof(char *) * 16);\n    va_end(args);\n\n    // how to hook variadic function? fake a original copy stack.\n    // [move to\n    // detail-1](http://jmpews.github.io/2017/08/29/pwn/%E7%9F%AD%E5%87%BD%E6%95%B0%E5%92%8C%E4%B8%8D%E5%AE%9A%E5%8F%82%E6%95%B0%E7%9A%84hook/)\n    // [move to detail-2](https://github.com/jmpews/HookZzModules/tree/master/AntiDebugBypass)\n    int x = orig_printf(format, stack[0], stack[1], stack[2], stack[3], stack[4], stack[5], stack[6], stack[7],\n                        stack[8], stack[9], stack[10], stack[11], stack[12], stack[13], stack[14], stack[15]);\n    return x;\n}\n\nvoid printf_pre_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    puts((char *)rs->general.regs.x0);\n    STACK_SET(callstack, \"format\", rs->general.regs.x0, char *);\n    puts(\"printf-pre-call\");\n}\n\nvoid printf_post_call(RegState *rs, ThreadStack *threadstack, CallStack *callstack) {\n    if (STACK_CHECK_KEY(callstack, \"format\")) {\n        char *format = STACK_GET(callstack, \"format\", char *);\n        puts(format);\n    }\n    puts(\"printf-post-call\");\n}\n\n__attribute__((constructor)) void test_hook_printf() {\n    void *printf_ptr = (void *)printf;\n\n    ZzEnableDebugMode();\n    ZzHook((void *)printf_ptr, (void *)fake_printf, (void **)&orig_printf, printf_pre_call, printf_post_call, TRUE);\n    printf(\"HookZzzzzzz, %d, %p, %d, %d, %d, %d, %d, %d, %d\\n\", 1, (void *)2, 3, (char)4, (char)5, (char)6, 7, 8, 9);\n}\n\n// int main(int args, char **argv) {}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tools/ZzSolidifyHook/solidifyhook.cpp",
    "content": "/*\n clang++ -L/Users/jmpews/Desktop/SpiderZz/project/HookZz/tools/deps/MachoParser/build \\\n -lmachoparser -o solidifyhook solidifyhook.cpp\n */\n\n#include <iostream>\n#include <stdio.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <mach-o/loader.h>\n\n#ifndef zz_h\n#define zz_h\n\n// Created by jmpews on 2017/5/3.\n//\n#define PROGRAM_NAME \"zz\"\n#define PROGRAM_VER \"1.0.0\"\n#define PROGRAM_AUTHOR \"jmpews@gmail.com\"\n\n#include <stdbool.h>\n#include <stdint.h>\n\n// --- custom type ---\n\n// 1. zpointer and zaddr is different\n\n#define DEBUG_MODE 0\n\n#ifndef zz_type\n#define zz_type\n\ntypedef void *zpointer;\ntypedef unsigned long zsize;\ntypedef unsigned long zaddr;\ntypedef uint32_t zuint32;\ntypedef uint16_t zuint16;\ntypedef uint8_t zuint8;\ntypedef int32_t zint32;\ntypedef int16_t zint16;\ntypedef int8_t zint8;\ntypedef unsigned long zuint;\ntypedef long zint;\ntypedef unsigned char zbyte;\ntypedef bool zbool;\n\n#endif\n\n#if defined(FALSE)\n#else\n#define FALSE 0\n#define TRUE 1\n#endif\n\n// --- log configuration ---\n\n#define GLOBAL_DEBUG 0\n#define GLOBAL_INFO 1\n#define SYSLOG 0\n#define COLOR_LOG 0\n\n#if (COLOR_LOG)\n#define RED \"\\x1B[31m\"\n#define GRN \"\\x1B[32m\"\n#define YEL \"\\x1B[33m\"\n#define BLU \"\\x1B[34m\"\n#define MAG \"\\x1B[35m\"\n#define CYN \"\\x1B[36m\"\n#define WHT \"\\x1B[37m\"\n#define RESET \"\\x1B[0m\"\n#else\n#define RED \"\"\n#define GRN \"\"\n#define YEL \"\"\n#define BLU \"\"\n#define MAG \"\"\n#define CYN \"\"\n#define WHT \"\"\n#define RESET \"\"\n#endif\n\n#include <stdio.h>\n\n// Important!!!\n// STDERR before STDOUT, because sync\n\n#if (SYSLOG)\n#include <sys/syslog.h>\n#define Xinfo(fmt, ...)                                                                                                \\\n    do {                                                                                                               \\\n        if (GLOBAL_INFO)                                                                                               \\\n            syslog(LOG_WARNING, RESET fmt, __VA_ARGS__);                                                               \\\n    } while (0)\n#define Sinfo(MSG) Xinfo(\"%s\", MSG)\n#define Xdebug(fmt, ...)                                                                                               \\\n    do {                                                                                                               \\\n        if (GLOBAL_DEBUG)                                                                                              \\\n            syslog(LOG_WARNING, RESET fmt, __VA_ARGS__);                                                               \\\n    } while (0)\n#define Sdebug(MSG) Xdebug(\"%s\", MSG)\n#define Xerror(fmt, ...)                                                                                               \\\n    do {                                                                                                               \\\n        syslog(LOG_DEBUG,                                                                                              \\\n               RED \"[!] \"                                                                                              \\\n                   \"%s:%d:%s(): \" fmt RESET \"\\n\",                                                                      \\\n               __FILE__, __LINE__, __func__, __VA_ARGS__);                                                             \\\n    } while (0)\n\n#define Serror(MSG) Xerror(\"%s\", MSG)\n#else\n#define Xinfo(fmt, ...)                                                                                                \\\n    do {                                                                                                               \\\n        if (GLOBAL_INFO)                                                                                               \\\n            fprintf(stdout, RESET fmt \"\\n\", __VA_ARGS__);                                                              \\\n    } while (0)\n#define Sinfo(MSG) Xinfo(\"%s\", MSG)\n\n#define Xdebug(fmt, ...)                                                                                               \\\n    do {                                                                                                               \\\n        if (GLOBAL_DEBUG)                                                                                              \\\n            fprintf(stdout, RESET fmt \"\\n\", __VA_ARGS__);                                                              \\\n    } while (0)\n#define Sdebug(MSG) Xdebug(\"%s\", MSG)\n#define Xerror(fmt, ...)                                                                                               \\\n    do {                                                                                                               \\\n        fprintf(stderr,                                                                                                \\\n                RED \"[!] \"                                                                                             \\\n                    \"%s:%d:%s(): \" fmt RESET \"\\n\",                                                                     \\\n                __FILE__, __LINE__, __func__, __VA_ARGS__);                                                            \\\n    } while (0)\n\n#define Serror(MSG) Xerror(\"%s\", MSG)\n#endif\n\n// --- common macro ---\n#undef ABS\n#define ABS(a) (((a) < 0) ? -(a) : (a))\n\n#endif\n//#include \"../deps/MachoParser/include/MachoFD.h\"\n#include \"MachoFD.h\"\n\nvoid zz_debug() {\n#ifdef DEBUGMODE\n#ifdef ZZDEPS\n    debug_break();\n#else\n    perror(NULL);\n#endif\n#endif\n}\n\nusing namespace std;\n\nunsigned long zz_file_get_size(const char *target_path) {\n    unsigned long filesize = -1;\n    struct stat statbuff;\n    if (stat(target_path, &statbuff) < 0) {\n        return filesize;\n    } else {\n        filesize = statbuff.st_size;\n    }\n    return filesize;\n}\n\nbool zz_file_is_exist(const char *target_path) {\n    if ((access(target_path, F_OK)) != -1) {\n        return true;\n    }\n    return FALSE;\n}\n\nbool zz_file_remove(const char *target_path) {\n    if (zz_file_is_exist(target_path)) {\n        if (!remove(target_path)) {\n            return TRUE;\n        }\n    }\n    zz_debug();\n    return FALSE;\n}\n\nvoid zz_write_file_to_file(const char *src_path, const char *dst_path, unsigned long src_offset,\n                           unsigned long dst_offset, unsigned long size) {\n    FILE *src_fd;\n    FILE *dst_fd;\n    src_fd = fopen(src_path, \"rb\");\n    fseek(src_fd, src_offset, SEEK_SET);\n\n    if (!zz_file_is_exist(dst_path))\n        dst_fd = fopen(dst_path, \"wb\");\n    else\n        dst_fd = fopen(dst_path, \"rb+\");\n\n    fseek(dst_fd, dst_offset, SEEK_SET);\n\n    unsigned int WRITE_BLOCK_SIZE = 1024;\n    unsigned char tmp_block[1024];\n\n    for (int i = 0; i < size / WRITE_BLOCK_SIZE; i++) {\n        fread(tmp_block, WRITE_BLOCK_SIZE, 1, src_fd);\n        fwrite(tmp_block, WRITE_BLOCK_SIZE, 1, dst_fd);\n    }\n\n    if (size % WRITE_BLOCK_SIZE) {\n        fread(tmp_block, size % WRITE_BLOCK_SIZE, 1, src_fd);\n        fwrite(tmp_block, size % WRITE_BLOCK_SIZE, 1, dst_fd);\n    }\n\n    fclose(src_fd);\n    fclose(dst_fd);\n}\n\nvoid zz_copy_file_to_file(const char *src_path, const char *dst_path) {\n    unsigned long file_size;\n    file_size = zz_file_get_size(src_path);\n    zz_write_file_to_file(src_path, dst_path, 0, 0, file_size);\n}\n\nvoid zz_file_write(const char *dst_path, unsigned long offset, void *content, unsigned long size) {\n    FILE *dst_fd = fopen(dst_path, \"rb+\");\n    fseek(dst_fd, offset, SEEK_SET);\n    fwrite(content, size, 1, dst_fd);\n    fclose(dst_fd);\n}\n\nvoid zz_file_read(const char *dst_path, unsigned long offset, void *content, unsigned long size) {\n    FILE *dst_fd = fopen(dst_path, \"rb\");\n    fseek(dst_fd, offset, SEEK_SET);\n    fread(content, size, 1, dst_fd);\n    fclose(dst_fd);\n}\n\nvoid zz_write_append_to_file(const char *dst_path, void *content, unsigned long size) {\n    FILE *dst_fd = fopen(dst_path, \"ab+\");\n    fwrite(content, size, 1, dst_fd);\n    fclose(dst_fd);\n}\nunsigned long get_linkedit_loadcmd_offset(MachoFD *machofd) {\n    for (const auto &loadcmd : machofd->loadcommands.load_command_infos) {\n        /* iterate dump section */\n        if (loadcmd.load_cmd->cmd == LC_SEGMENT_64) {\n            if (!strcmp(((struct segment_command_64 *)loadcmd.cmd_info)->segname, \"__LINKEDIT\"))\n                return loadcmd.fileoff;\n        }\n    }\n    return 0;\n}\n\nvoid macho_fix_load_command(const char *target_path) {\n    MachoFD *machofd = new MachoFD(target_path);\n    if (machofd->isFat) {\n        printf(\"use lipo to thin it.\");\n    }\n    machofd->parse_macho();\n\n    for (const auto &loadcmd : machofd->loadcommands.load_command_infos) {\n        if (loadcmd.load_cmd->cmd == LC_DYLD_INFO_ONLY) {\n            struct dyld_info_command *tmp = (struct dyld_info_command *)loadcmd.cmd_info;\n            struct dyld_info_command new_tmp = *tmp;\n            new_tmp.rebase_off += 0x8000;\n            new_tmp.bind_off += 0x8000;\n            if (new_tmp.weak_bind_off)\n                new_tmp.weak_bind_off += 0x8000;\n            if (new_tmp.lazy_bind_off)\n                new_tmp.lazy_bind_off += 0x8000;\n            if (new_tmp.export_off)\n                new_tmp.export_off += 0x8000;\n            zz_file_write(target_path, loadcmd.fileoff, &new_tmp, sizeof(new_tmp));\n            Sinfo(\"[*] fix LC_DYLD_INFO_ONLY done\");\n        }\n        if (loadcmd.load_cmd->cmd == LC_SYMTAB) {\n            struct symtab_command *tmp = (struct symtab_command *)loadcmd.cmd_info;\n            struct symtab_command new_tmp = *tmp;\n            if (new_tmp.symoff)\n                new_tmp.symoff += 0x8000;\n            if (new_tmp.stroff)\n                new_tmp.stroff += 0x8000;\n            zz_file_write(target_path, loadcmd.fileoff, &new_tmp, sizeof(new_tmp));\n            Sinfo(\"[*] fix LC_SYMTAB done\");\n        }\n        if (loadcmd.load_cmd->cmd == LC_DYSYMTAB) {\n            struct dysymtab_command *tmp = (struct dysymtab_command *)loadcmd.cmd_info;\n            struct dysymtab_command new_tmp = *tmp;\n            if (new_tmp.tocoff)\n                new_tmp.tocoff += 0x8000;\n            if (new_tmp.modtaboff)\n                new_tmp.modtaboff += 0x8000;\n            if (new_tmp.extrefsymoff)\n                new_tmp.extrefsymoff += 0x8000;\n            if (new_tmp.indirectsymoff)\n                new_tmp.indirectsymoff += 0x8000;\n            if (new_tmp.extreloff)\n                new_tmp.extreloff += 0x8000;\n            if (new_tmp.locreloff)\n                new_tmp.locreloff += 0x8000;\n            zz_file_write(target_path, loadcmd.fileoff, &new_tmp, sizeof(new_tmp));\n            Sinfo(\"[*] fix LC_DYSYMTAB done\");\n        }\n        if (loadcmd.load_cmd->cmd == LC_FUNCTION_STARTS || loadcmd.load_cmd->cmd == LC_DATA_IN_CODE) {\n            struct linkedit_data_command *tmp = (struct linkedit_data_command *)loadcmd.cmd_info;\n            struct linkedit_data_command new_tmp = *tmp;\n            if (new_tmp.dataoff)\n                new_tmp.dataoff += 0x8000;\n            zz_file_write(target_path, loadcmd.fileoff, &new_tmp, sizeof(new_tmp));\n\n            Sinfo(\"[*] fix LC_FUNCTION_STARTS/LC_DATA_IN_CODE done\");\n        }\n    }\n}\n\nvoid zz_file_move_offset_to_offset(const char *target_path, unsigned long src_offset, unsigned long dst_offset,\n                                   unsigned long size) {\n    FILE *target_fd;\n    target_fd = fopen(target_path, \"rb+\");\n    unsigned char *data = (unsigned char *)malloc(size);\n    fseek(target_fd, src_offset, SEEK_SET);\n    fread(data, size, 1, target_fd);\n    fseek(target_fd, dst_offset, SEEK_SET);\n    fwrite(data, size, 1, target_fd);\n    free(data);\n    fclose(target_fd);\n}\n\nvoid macho_insert_segment(string target_path, string new_target_path) {\n\n    char *rx_segment_name = (char *)\"HookZzCode\";\n    char *rw_segment_name = (char *)\"HookZzData\";\n    MachoFD *machofd = new MachoFD(target_path.c_str());\n\n    Sinfo(\"[*] start insert rw- and r-x segments\");\n\n    if (machofd->isFat) {\n        printf(\"use lipo to thin it.\");\n    }\n    machofd->parse_macho();\n    Xinfo(\"[*] parse origin file %s\", target_path.c_str());\n\n    const segment_command_64_info_t *seg_linkedit = machofd->get_seg_by_name(\"__LINKEDIT\");\n\n    struct mach_header_64 new_target_header;\n    struct segment_command_64 new_target_rx_segment;\n    struct segment_command_64 new_target_rw_segment;\n    struct segment_command_64 new_target_linkedit_segment;\n\n    memcpy(&new_target_header, machofd->header.header64, sizeof(struct mach_header_64));\n    memcpy(&new_target_rx_segment, seg_linkedit->seg_cmd_64, sizeof(struct segment_command_64));\n    memcpy(&new_target_rw_segment, seg_linkedit->seg_cmd_64, sizeof(struct segment_command_64));\n    memcpy(&new_target_linkedit_segment, seg_linkedit->seg_cmd_64, sizeof(struct segment_command_64));\n\n    // add new rx segment\n    memcpy(new_target_rx_segment.segname, rx_segment_name, strlen(rx_segment_name));\n    new_target_rx_segment.vmsize = 0x4000;\n    new_target_rx_segment.filesize = 0x4000;\n    new_target_rx_segment.vmaddr = new_target_rx_segment.vmaddr;\n    new_target_rx_segment.fileoff = new_target_rx_segment.fileoff;\n    new_target_rx_segment.maxprot = 5;\n    new_target_rx_segment.initprot = 5;\n\n    // add new rw segment\n    memcpy(new_target_rw_segment.segname, rw_segment_name, strlen(rw_segment_name));\n    new_target_rw_segment.vmsize = 0x4000;\n    new_target_rw_segment.filesize = 0x4000;\n    new_target_rw_segment.vmaddr = new_target_rw_segment.vmaddr + 0x4000;\n    new_target_rw_segment.fileoff = new_target_rw_segment.fileoff + 0x4000;\n    new_target_rw_segment.maxprot = 3;\n    new_target_rw_segment.initprot = 3;\n\n    // fix linkedit segment\n    new_target_linkedit_segment.vmaddr = new_target_linkedit_segment.vmaddr + 0x4000 + 0x4000;\n    new_target_linkedit_segment.fileoff = new_target_linkedit_segment.fileoff + 0x4000 + 0x4000;\n\n    // fix header\n    new_target_header.ncmds += 2;\n    new_target_header.sizeofcmds += (new_target_rx_segment.cmdsize + new_target_rw_segment.cmdsize);\n\n    //    zz_copy_file_to_file(target_path.c_str(), new_target_path.c_str());\n    zz_write_file_to_file(target_path.c_str(), new_target_path.c_str(), 0, 0, seg_linkedit->seg_cmd_64->fileoff);\n\n    unsigned long orig_linkedit_offset = get_linkedit_loadcmd_offset(machofd);\n    unsigned long move_size =\n        machofd->header.header64->sizeofcmds + sizeof(struct mach_header_64) - orig_linkedit_offset;\n    unsigned long new_linkedit_offset =\n        orig_linkedit_offset + (new_target_rx_segment.cmdsize + new_target_rw_segment.cmdsize);\n    zz_file_move_offset_to_offset(new_target_path.c_str(), orig_linkedit_offset, new_linkedit_offset, move_size);\n\n    zz_file_write(new_target_path.c_str(), 0, &new_target_header, sizeof(new_target_header));\n    Sinfo(\"[*] fix macho header\");\n    zz_file_write(new_target_path.c_str(), orig_linkedit_offset, &new_target_rx_segment,\n                  sizeof(struct segment_command_64));\n    Sinfo(\"[*] add \\'HookZzCode(r-x)\\' Segment\");\n    zz_file_write(new_target_path.c_str(), orig_linkedit_offset + new_target_rx_segment.cmdsize, &new_target_rw_segment,\n                  sizeof(struct segment_command_64));\n    Sinfo(\"[*] add \\'HookZzData(rw-)\\' Segment\");\n    zz_file_write(new_target_path.c_str(),\n                  orig_linkedit_offset + new_target_rx_segment.cmdsize + new_target_rw_segment.cmdsize,\n                  &new_target_linkedit_segment, sizeof(struct segment_command_64));\n    Sinfo(\"[*] fix \\'__LINKEDIT\\' Segment\");\n\n    char segment_blank[0x4000] = {0};\n    zz_write_append_to_file(new_target_path.c_str(), &segment_blank, 0x4000);\n    Xinfo(\"[*] reserve %p size space to HookZzCode Segment\", (zpointer)0x4000);\n    zz_write_append_to_file(new_target_path.c_str(), &segment_blank, 0x4000);\n    Xinfo(\"[*] reserve %p size space to HookZzData Segment\", (zpointer)0x4000);\n    zz_write_file_to_file(target_path.c_str(), new_target_path.c_str(), seg_linkedit->seg_cmd_64->fileoff,\n                          new_target_linkedit_segment.fileoff,\n                          zz_file_get_size(target_path.c_str()) - seg_linkedit->seg_cmd_64->fileoff);\n\n    /* fix load command */\n    macho_fix_load_command(new_target_path.c_str());\n}\n\ntypedef struct _ZzInterceptorBackendNoJB {\n    void *enter_thunk; // hardcode\n    void *leave_thunk; // hardcode\n    void *function_context_begin_invocation;\n    void *function_context_end_invocation;\n    unsigned long num_of_entry;\n    unsigned long code_seg_offset;\n    unsigned long data_seg_offset;\n} ZzInterceptorBackendNoJB;\n\ntypedef struct _ZzHookFunctionEntryNoJB {\n    void *target_fileoff;\n    unsigned long is_near_jump;\n    void *entry_address;\n    void *on_enter_trampoline;  // HookZzData, 99% hardcode\n    void *on_invoke_trampoline; // HookZzData, fixed instructions\n    void *on_leave_trampoline;  // HookZzData, 99% hardcode\n} ZzHookFunctionEntryNoJB;\n\n//------------------------------------------------------------\n//-----------       Created with 010 Editor        -----------\n//------         www.sweetscape.com/010editor/          ------\n//\n// File    : /Users/jmpews/Desktop/SpiderZz/project/HookZz/tools/ZzSolidifyHook/solidifytrampoline.dylib\n// Address : 32456 (0x7EC8)\n// Size    : 84 (0x54)\n//------------------------------------------------------------\nunsigned char on_enter_trampoline_template[84] = {\n    0xFF, 0x43, 0x00, 0xD1, 0xF0, 0x03, 0x1F, 0xF8, 0x10, 0x00, 0x00, 0x10, 0x51, 0x00, 0x00, 0x58, 0x03,\n    0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x02, 0x10, 0x8B, 0xF0, 0x07,\n    0x41, 0xF8, 0x31, 0x02, 0x40, 0xF9, 0xF1, 0x03, 0x00, 0xF9, 0xF0, 0x03, 0x1F, 0xF8, 0x10, 0x00, 0x00,\n    0x10, 0x51, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x31, 0x02, 0x10, 0x8B, 0xF0, 0x07, 0x41, 0xF8, 0x31, 0x02, 0x40, 0xF9, 0x20, 0x02, 0x1F, 0xD6};\n#define ON_ENTER_TRAMPOLINE_SIZE 84\n\n//------------------------------------------------------------\n//-----------       Created with 010 Editor        -----------\n//------         www.sweetscape.com/010editor/          ------\n//\n// File    : /Users/jmpews/Desktop/SpiderZz/project/HookZz/tools/ZzSolidifyHook/solidifytrampoline.dylib\n// Address : 32540 (0x7F1C)\n// Size    : 72 (0x48)\n//------------------------------------------------------------\nunsigned char on_invoke_trampoline_template[72] = {\n    0x1F, 0x20, 0x03, 0xD5, 0x1F, 0x20, 0x03, 0xD5, 0x1F, 0x20, 0x03, 0xD5, 0x1F, 0x20, 0x03, 0xD5, 0x1F, 0x20,\n    0x03, 0xD5, 0x1F, 0x20, 0x03, 0xD5, 0x1F, 0x20, 0x03, 0xD5, 0x1F, 0x20, 0x03, 0xD5, 0xF0, 0x03, 0x1F, 0xF8,\n    0x10, 0x00, 0x00, 0x10, 0x51, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x31, 0x02, 0x10, 0x8B, 0xF0, 0x07, 0x41, 0xF8, 0x31, 0x02, 0x40, 0xF9, 0x20, 0x02, 0x1F, 0xD6};\n#define ON_INVOKE_TRAMPOLINE_SIZE 72\n\n//------------------------------------------------------------\n//-----------       Created with 010 Editor        -----------\n//------         www.sweetscape.com/010editor/          ------\n//\n// File    : /Users/jmpews/Desktop/SpiderZz/project/HookZz/tools/ZzSolidifyHook/solidifytrampoline.dylib\n// Address : 32612 (0x7F64)\n// Size    : 84 (0x54)\n//------------------------------------------------------------\n\nunsigned char on_leave_trampoline_template[84] = {\n    0xFF, 0x43, 0x00, 0xD1, 0xF0, 0x03, 0x1F, 0xF8, 0x10, 0x00, 0x00, 0x10, 0x51, 0x00, 0x00, 0x58, 0x03,\n    0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x02, 0x10, 0x8B, 0xF0, 0x07,\n    0x41, 0xF8, 0x31, 0x02, 0x40, 0xF9, 0xF1, 0x03, 0x00, 0xF9, 0xF0, 0x03, 0x1F, 0xF8, 0x10, 0x00, 0x00,\n    0x10, 0x51, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x31, 0x02, 0x10, 0x8B, 0xF0, 0x07, 0x41, 0xF8, 0x31, 0x02, 0x40, 0xF9, 0x20, 0x02, 0x1F, 0xD6};\n\n#define ON_LEAVE_TRAMPOLINE_SIZE 84\n\nvoid ZzSolidifyBuildEnterTrampoline(ZzInterceptorBackendNoJB *nojb_backend, ZzHookFunctionEntryNoJB *nojb_entry,\n                                    MachoFD *machofd) {\n\n    const char *target_path = machofd->getPath();\n    const segment_command_64_info_t *code_seg_info = machofd->get_seg_by_name((char *)\"HookZzCode\");\n    const segment_command_64_info_t *data_seg_info = machofd->get_seg_by_name((char *)\"HookZzData\");\n\n    unsigned char on_enter_trampoline_tp[256] = {0};\n    zsize ENTER_TRAMPOLINE_SIZE = 21 * 4;\n\n    Sinfo(\"[*] solidify build on_enter_trampoline\");\n    memcpy(on_enter_trampoline_tp, (void *)on_enter_trampoline_template, ENTER_TRAMPOLINE_SIZE);\n\n    zaddr data_vmaddr = (zaddr)data_seg_info->vmaddr + sizeof(ZzInterceptorBackendNoJB) +\n                        nojb_backend->num_of_entry * sizeof(ZzHookFunctionEntryNoJB) + 2 * sizeof(void *);\n    zaddr codepatch_vmaddr = code_seg_info->vmaddr + +2 * 4;\n    zaddr offset = data_vmaddr - codepatch_vmaddr;\n    *(unsigned long *)&on_enter_trampoline_tp[5 * 4] = (unsigned long)offset;\n\n    data_vmaddr = data_seg_info->vmaddr + 0x8 * 0;\n    codepatch_vmaddr = code_seg_info->vmaddr + (zaddr)nojb_backend->code_seg_offset + 15 * 4;\n    offset = data_vmaddr - codepatch_vmaddr;\n    *(unsigned long *)&on_enter_trampoline_tp[15 * 4] = (unsigned long)offset;\n\n    zz_file_write(target_path, code_seg_info->fileoff + nojb_backend->code_seg_offset, on_enter_trampoline_tp,\n                  ENTER_TRAMPOLINE_SIZE);\n    nojb_entry->on_enter_trampoline = (zpointer)(code_seg_info->vmaddr + nojb_backend->code_seg_offset);\n    nojb_backend->code_seg_offset += ENTER_TRAMPOLINE_SIZE;\n}\n\nvoid ZzSolidifyBuildInvokeTrampoline(ZzInterceptorBackendNoJB *nojb_backend, ZzHookFunctionEntryNoJB *nojb_entry,\n                                     MachoFD *machofd) {\n    const char *target_path = machofd->getPath();\n    const segment_command_64_info_t *code_seg_info = machofd->get_seg_by_name((char *)\"HookZzCode\");\n    const segment_command_64_info_t *data_seg_info = machofd->get_seg_by_name((char *)\"HookZzData\");\n    zpointer target_fileoff = nojb_entry->target_fileoff;\n    unsigned char on_invoke_trampoline_tp[256] = {0};\n    zsize INVOKE_TRAMPOLINE_SIZE = 18 * 4;\n\n    uint32_t insn;\n    Sinfo(\"[*] solidify build on_invoke_trampoline\");\n    memcpy(on_invoke_trampoline_tp, (void *)on_invoke_trampoline_template, INVOKE_TRAMPOLINE_SIZE);\n\n    zz_file_read(target_path, (zaddr)target_fileoff, &insn, 4);\n    memcpy(on_invoke_trampoline_tp, &insn, 4);\n\n    // from on_invoke_trampoline jump to origin rest code\n    // TODO:\n    zaddr offset = (zaddr)target_fileoff + 4 - (zaddr)nojb_entry->on_enter_trampoline - 4;\n    insn = 0x14000000;\n    insn = insn | ((offset / 4) & 0x03ffffff);\n    *(uint32_t *)&on_invoke_trampoline_tp[4] = insn;\n\n    nojb_entry->is_near_jump = TRUE;\n\n    zz_file_write(target_path, code_seg_info->fileoff + nojb_backend->code_seg_offset, on_invoke_trampoline_tp,\n                  INVOKE_TRAMPOLINE_SIZE);\n    nojb_entry->on_invoke_trampoline = (zpointer)(code_seg_info->vmaddr + nojb_backend->code_seg_offset);\n    nojb_backend->code_seg_offset += INVOKE_TRAMPOLINE_SIZE;\n}\n\nvoid ZzSolidifyBuildLeaveTrampoline(ZzInterceptorBackendNoJB *nojb_backend, ZzHookFunctionEntryNoJB *nojb_entry,\n                                    MachoFD *machofd) {\n\n    const char *target_path = machofd->getPath();\n    const segment_command_64_info_t *code_seg_info = machofd->get_seg_by_name((char *)\"HookZzCode\");\n    const segment_command_64_info_t *data_seg_info = machofd->get_seg_by_name((char *)\"HookZzData\");\n\n    unsigned char on_leave_trampoline_tp[256] = {0};\n    zsize LEAVE_TRAMPOLINE_SIZE = 21 * 4;\n\n    Sinfo(\"[*] solidify build on_leave_trampoline\");\n    memcpy(on_leave_trampoline_tp, (void *)on_leave_trampoline_template, LEAVE_TRAMPOLINE_SIZE);\n\n    zaddr data_vmaddr = (zaddr)data_seg_info->vmaddr + sizeof(ZzInterceptorBackendNoJB) +\n                        nojb_backend->num_of_entry * sizeof(ZzHookFunctionEntryNoJB) + 2 * sizeof(void *);\n    zaddr codepatch_vmaddr = code_seg_info->vmaddr + +2 * 4;\n    zaddr offset = data_vmaddr - codepatch_vmaddr;\n    *(unsigned long *)&on_leave_trampoline_tp[5 * 4] = (unsigned long)offset;\n\n    data_vmaddr = data_seg_info->vmaddr + 0x8 * 1;\n    codepatch_vmaddr = code_seg_info->vmaddr + (zaddr)nojb_backend->code_seg_offset + 12 * 4;\n    offset = data_vmaddr - codepatch_vmaddr;\n    *(unsigned long *)&on_leave_trampoline_tp[15 * 4] = (unsigned long)offset;\n\n    zz_file_write(target_path, code_seg_info->fileoff + nojb_backend->code_seg_offset, on_leave_trampoline_tp,\n                  LEAVE_TRAMPOLINE_SIZE);\n    nojb_entry->on_leave_trampoline = (zpointer)(code_seg_info->vmaddr + nojb_backend->code_seg_offset);\n    nojb_backend->code_seg_offset += LEAVE_TRAMPOLINE_SIZE;\n}\n\nvoid ZzSolidifyHookInitilize(ZzInterceptorBackendNoJB *nojb_backend, MachoFD *machofd) {\n    const char *target_path = machofd->getPath();\n    const segment_command_64_info_t *code_seg_info = machofd->get_seg_by_name((char *)\"HookZzCode\");\n    const segment_command_64_info_t *data_seg_info = machofd->get_seg_by_name((char *)\"HookZzData\");\n\n    Sinfo(\"[*] solidify hook initilize\");\n    zz_file_read(target_path, data_seg_info->fileoff, nojb_backend, sizeof(ZzInterceptorBackendNoJB));\n    nojb_backend->code_seg_offset = 0;\n}\n\nvoid ZzSolidifyHookActivate(ZzHookFunctionEntryNoJB *nojb_entry, MachoFD *machofd) {\n    zpointer target_fileoff = nojb_entry->target_fileoff;\n    const char *target_path = machofd->getPath();\n\n    // try near jump\n    if (nojb_entry->is_near_jump) {\n        zaddr near_jump_offset = (zaddr)nojb_entry->on_enter_trampoline - (zaddr)target_fileoff;\n        uint32_t insn = 0x14000000;\n        insn = insn | ((near_jump_offset / 4) & 0x03ffffff);\n        zz_file_write(target_path, (zaddr)target_fileoff, &insn, 4);\n    }\n}\n\nvoid ZzSolidifyHook(zpointer target_fileoff, MachoFD *machofd) {\n    const char *target_path = machofd->getPath();\n    const segment_command_64_info_t *code_seg_info = machofd->get_seg_by_name((char *)\"HookZzCode\");\n    const segment_command_64_info_t *data_seg_info = machofd->get_seg_by_name((char *)\"HookZzData\");\n\n    ZzInterceptorBackendNoJB nojb_backend;\n    ZzHookFunctionEntryNoJB nojb_entry;\n    ZzSolidifyHookInitilize(&nojb_backend, machofd);\n\n    Xinfo(\"[*] start solidify hook at %p\", target_fileoff);\n\n    nojb_entry.target_fileoff = target_fileoff;\n    ZzSolidifyBuildEnterTrampoline(&nojb_backend, &nojb_entry, machofd);\n    ZzSolidifyBuildInvokeTrampoline(&nojb_backend, &nojb_entry, machofd);\n    ZzSolidifyBuildLeaveTrampoline(&nojb_backend, &nojb_entry, machofd);\n\n    /* solidify new hook entry to file */\n    zz_file_write(target_path, data_seg_info->fileoff + sizeof(ZzHookFunctionEntryNoJB) * nojb_backend.num_of_entry,\n                  &nojb_entry, sizeof(ZzHookFunctionEntryNoJB));\n\n    /* solidify update interceptor backend to file */\n    nojb_backend.num_of_entry += 1;\n    zz_file_write(target_path, data_seg_info->fileoff, &nojb_backend, sizeof(ZzInterceptorBackendNoJB));\n\n    ZzSolidifyHookActivate(&nojb_entry, machofd);\n}\n\nint main(int args, const char **argv) {\n    string target_file_path = \"/Users/jmpews/Desktop/test/test.dylib\";\n    string new_target_file_path = target_file_path;\n    new_target_file_path.insert(new_target_file_path.rfind('.'), \".hook\");\n\n    zz_file_remove(new_target_file_path.c_str());\n\n    macho_insert_segment(target_file_path, new_target_file_path);\n\n    MachoFD *machofd = new MachoFD(new_target_file_path.c_str());\n    if (machofd->isFat) {\n        printf(\"use lipo to thin it.\");\n    }\n    machofd->parse_macho();\n\n    ZzSolidifyHook((zpointer)0x7f0c, machofd);\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/HookZz/tools/ZzSolidifyHook/solidifytrampoline.c",
    "content": "/*\n`xcrun --sdk iphoneos --find clang` \\\n-fPIC -shared -dynamiclib \\\n-arch arm64 \\\n-isysroot `xcrun --sdk iphoneos --show-sdk-path` \\\nsolidifytrampoline.c \\\n-o  solidifytrampoline.dylib\n*/\n\n__attribute__((__naked__)) void on_enter_trampoline_template() {\n    __asm__ volatile(\n        /* store entry address and reserve space for next hop */\n        \"sub sp, sp, 0x10\\n\"\n\n        /* push x16 */\n        \"str x16,[sp, #-0x10]\\n\"\n        \"adr x16, #0\\n\"\n        \"ldr x17, #0x8\\n\"\n        \"b #0xc\\n\"\n        /* entry address'address offset!!! */\n        \".long 0x0\\n\"\n        \".long 0x0\\n\"\n        \"add x17, x17, x16\\n\"\n        /* pop x16 */\n        \"ldr x16, [sp], #0x10\\n\"\n        \"ldr x17, [x17]\\n\"\n\n        \"str x17, [sp]\\n\"\n\n        /* push x16 */\n        \"str x16,[sp, #-0x10]\\n\"\n        \"adr x16, #0\\n\"\n        \"ldr x17, #0x8\\n\"\n        \"b #0xc\\n\"\n        /* enter_thunk address'address offset!!! */\n        \".long 0x0\\n\"\n        \".long 0x0\\n\"\n        \"add x17, x17, x16\\n\"\n        /* pop x16 */\n        \"ldr x16, [sp], #0x10\\n\"\n        \"ldr x17, [x17]\\n\"\n\n        \"br x17\");\n}\n\n__attribute__((__naked__)) void on_inovke_trampoline_template() {\n    __asm__ volatile(\n        /* fixed instruction */\n        \"nop\\n\"\n        \"nop\\n\"\n        \"nop\\n\"\n        \"nop\\n\"\n        \"nop\\n\"\n        \"nop\\n\"\n        \"nop\\n\"\n        \"nop\\n\"\n\n        /* push x16 */\n        \"str x16,[sp, #-0x10]\\n\"\n        \"adr x16, #0\\n\"\n        \"ldr x17, #0x8\\n\"\n        \"b #0xc\\n\"\n        /* rest of orgin function address'address offset !!! */\n        \".long 0x0\\n\"\n        \".long 0x0\\n\"\n        \"add x17, x17, x16\\n\"\n        /* pop x16 */\n        \"ldr x16, [sp], #0x10\\n\"\n        \"ldr x17, [x17]\\n\"\n\n        \"br x17\");\n}\n\n__attribute__((__naked__)) void on_leave_trampoline_template() {\n    __asm__ volatile(\n        /* store entry address and reserve space for next hop */\n        \"sub sp, sp, 0x10\\n\"\n\n        /* push x16 */\n        \"str x16,[sp, #-0x10]\\n\"\n        \"adr x16, #0\\n\"\n        \"ldr x17, #0x8\\n\"\n        \"b #0xc\\n\"\n        /* entry address'address offset!!! */\n        \".long 0x0\\n\"\n        \".long 0x0\\n\"\n        \"add x17, x17, x16\\n\"\n        /* pop x16 */\n        \"ldr x16, [sp], #0x10\\n\"\n        \"ldr x17, [x17]\\n\"\n\n        \"str x17, [sp]\\n\"\n\n        /* push x16 */\n        \"str x16,[sp, #-0x10]\\n\"\n        \"adr x16, #0\\n\"\n        \"ldr x17, #0x8\\n\"\n        \"b #0xc\\n\"\n        /* leave_thunk address'address offset!!! */\n        \".long 0x0\\n\"\n        \".long 0x0\\n\"\n        \"add x17, x17, x16\\n\"\n        /* pop x16 */\n        \"ldr x16, [sp], #0x10\\n\"\n        \"ldr x17, [x17]\\n\"\n\n        \"br x17\");\n}"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Jni/Helper.h",
    "content": "//\n// VirtualApp Native Project\n//\n\n#ifndef NDK_LOG_H\n#define NDK_LOG_H\n\n#include <fb/include/fb/fbjni.h>\n\n#define NATIVE_METHOD(func_ptr, func_name, signature) { func_name, signature, reinterpret_cast<void*>(func_ptr) }\n\nclass ScopeUtfString {\npublic:\n    ScopeUtfString(jstring j_str) : _j_str(j_str),\n                                    _c_str(facebook::jni::Environment::current()->GetStringUTFChars(j_str, NULL)) {\n    }\n\n    const char *c_str() {\n        return _c_str;\n    }\n\n    ~ScopeUtfString() {\n        facebook::jni::Environment::current()->ReleaseStringUTFChars(_j_str, _c_str);\n    }\n\nprivate:\n    jstring _j_str;\n    const char *_c_str;\n};\n\n#endif //NDK_LOG_H\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Jni/VAJni.cpp",
    "content": "#include <elf.h>//\n// VirtualApp Native Project\n//\n#include <Foundation/IOUniformer.h>\n#include <fb/include/fb/Build.h>\n#include <fb/include/fb/ALog.h>\n#include <fb/include/fb/fbjni.h>\n#include \"VAJni.h\"\n\nusing namespace facebook::jni;\n\nstatic void jni_nativeLaunchEngine(alias_ref<jclass> clazz, JArrayClass<jobject> javaMethods,\n                                   jstring packageName,\n                                   jboolean isArt, jint apiLevel, jint cameraMethodType) {\n    hookAndroidVM(javaMethods, packageName, isArt, apiLevel, cameraMethodType);\n}\n\n\nstatic void jni_nativeEnableIORedirect(alias_ref<jclass>, jstring selfSoPath, jint apiLevel,\n                                       jint preview_api_level) {\n    ScopeUtfString so_path(selfSoPath);\n    IOUniformer::startUniformer(so_path.c_str(), apiLevel, preview_api_level);\n}\n\nstatic void jni_nativeIOWhitelist(alias_ref<jclass> jclazz, jstring _path) {\n    ScopeUtfString path(_path);\n    IOUniformer::whitelist(path.c_str());\n}\n\nstatic void jni_nativeIOForbid(alias_ref<jclass> jclazz, jstring _path) {\n    ScopeUtfString path(_path);\n    IOUniformer::forbid(path.c_str());\n}\n\n\nstatic void jni_nativeIORedirect(alias_ref<jclass> jclazz, jstring origPath, jstring newPath) {\n    ScopeUtfString orig_path(origPath);\n    ScopeUtfString new_path(newPath);\n    IOUniformer::redirect(orig_path.c_str(), new_path.c_str());\n\n}\n\nstatic jstring jni_nativeGetRedirectedPath(alias_ref<jclass> jclazz, jstring origPath) {\n    ScopeUtfString orig_path(origPath);\n    const char *redirected_path = IOUniformer::query(orig_path.c_str());\n    if (redirected_path != NULL) {\n        return Environment::current()->NewStringUTF(redirected_path);\n    }\n    return NULL;\n}\n\nstatic jstring jni_nativeReverseRedirectedPath(alias_ref<jclass> jclazz, jstring redirectedPath) {\n    ScopeUtfString redirected_path(redirectedPath);\n    const char *orig_path = IOUniformer::reverse(redirected_path.c_str());\n    return Environment::current()->NewStringUTF(orig_path);\n}\n\n\nalias_ref<jclass> nativeEngineClass;\n\n\nJNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {\n    return initialize(vm, [] {\n        nativeEngineClass = findClassStatic(\"com/lody/virtual/client/NativeEngine\");\n        nativeEngineClass->registerNatives({\n                        makeNativeMethod(\"nativeEnableIORedirect\",\n                                         jni_nativeEnableIORedirect),\n                        makeNativeMethod(\"nativeIOWhitelist\",\n                                         jni_nativeIOWhitelist),\n                        makeNativeMethod(\"nativeIOForbid\",\n                                         jni_nativeIOForbid),\n                        makeNativeMethod(\"nativeIORedirect\",\n                                         jni_nativeIORedirect),\n                        makeNativeMethod(\"nativeGetRedirectedPath\",\n                                         jni_nativeGetRedirectedPath),\n                        makeNativeMethod(\"nativeReverseRedirectedPath\",\n                                         jni_nativeReverseRedirectedPath),\n                        makeNativeMethod(\"nativeLaunchEngine\",\n                                         jni_nativeLaunchEngine),\n                }\n        );\n    });\n}\n\nextern \"C\" __attribute__((constructor)) void _init(void) {\n    IOUniformer::init_env_before_all();\n}\n\n\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Jni/VAJni.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\nextern alias_ref<jclass> nativeEngineClass;\n\nJNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved);\nJNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved);\n\n\n#endif //NDK_CORE_H\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Substrate/Buffer.hpp",
    "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_BUFFER_HPP\n#define SUBSTRATE_BUFFER_HPP\n\n#include <string.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#endif//SUBSTRATE_BUFFER_HPP\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Substrate/CydiaSubstrate.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_H_\n#define SUBSTRATE_H_\n\n#ifdef __APPLE__\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n#include <mach-o/nlist.h>\n#ifdef __cplusplus\n}\n#endif\n\n#include <objc/runtime.h>\n#include <objc/message.h>\n#endif\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\n#define _extern \\\n    extern \"C\" __attribute__((__visibility__(\"default\")))\n\n#ifdef __cplusplus\n#define _default(value) = value\n#else\n#define _default(value)\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nbool MSHookProcess(pid_t pid, const char *library);\n\ntypedef const void *MSImageRef;\n\nMSImageRef MSGetImageByName(const char *file);\nvoid *MSFindSymbol(MSImageRef image, const char *name);\n\nvoid MSHookFunction(void *symbol, void *replace, void **result);\n\n#ifdef __APPLE__\n#ifdef __arm__\n__attribute__((__deprecated__))\nIMP MSHookMessage(Class _class, SEL sel, IMP imp, const char *prefix _default(NULL));\n#endif\nvoid MSHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result);\n#endif\n\n#ifdef SubstrateInternal\ntypedef void *SubstrateAllocatorRef;\ntypedef struct __SubstrateProcess *SubstrateProcessRef;\ntypedef struct __SubstrateMemory *SubstrateMemoryRef;\n\nSubstrateProcessRef SubstrateProcessCreate(SubstrateAllocatorRef allocator, pid_t pid);\nvoid SubstrateProcessRelease(SubstrateProcessRef process);\n\nSubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size);\nvoid SubstrateMemoryRelease(SubstrateMemoryRef memory);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#ifdef __cplusplus\n\n#ifdef SubstrateInternal\nstruct SubstrateHookMemory {\n    SubstrateMemoryRef handle_;\n\n    SubstrateHookMemory(SubstrateProcessRef process, void *data, size_t size) :\n        handle_(SubstrateMemoryCreate(NULL, NULL, data, size))\n    {\n    }\n\n    ~SubstrateHookMemory() {\n        if (handle_ != NULL)\n            SubstrateMemoryRelease(handle_);\n    }\n};\n#endif\n\n\ntemplate<typename Type_>\nstatic inline void MSHookFunction(Type_ *symbol, Type_ *replace, Type_ **result) {\n    MSHookFunction(\n            reinterpret_cast<void *>(symbol),\n            reinterpret_cast<void *>(replace),\n            reinterpret_cast<void **>(result)\n    );\n}\n\ntemplate<typename Type_>\nstatic inline void MSHookFunction(Type_ *symbol, Type_ *replace) {\n    return MSHookFunction(symbol, replace, reinterpret_cast<Type_ **>(NULL));\n}\n\ntemplate<typename Type_>\nstatic inline void MSHookSymbol(Type_ *&value, const char *name, MSImageRef image = NULL) {\n    value = reinterpret_cast<Type_ *>(MSFindSymbol(image, name));\n}\n\ntemplate<typename Type_>\nstatic inline void MSHookFunction(const char *name, Type_ *replace, Type_ **result = NULL) {\n    Type_ *symbol;\n    MSHookSymbol(symbol, name);\n    return MSHookFunction(symbol, replace, result);\n}\n\n#endif\n\n#define MSHook(type, name, args...) \\\n    _disused static type (*_ ## name)(args); \\\n    static type $ ## name(args)\n\n#ifdef __cplusplus\n#define MSHake(name) \\\n    &$ ## name, &_ ## name\n#else\n#define MSHake(name) \\\n    &$ ## name, (void **) &_ ## name\n#endif\n\n\n#endif//SUBSTRATE_H_\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Substrate/SubstrateARM.hpp",
    "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\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\n#endif//SUBSTRATE_ARM_HPP\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Substrate/SubstrateDebug.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 \"SubstrateHook.h\"\n#include \"SubstrateDebug.hpp\"\n\n#include <stdint.h>\n#include <stdlib.h>\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\nvoid MSLogHexEx(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, \"\\n[%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 MSLogHex(const void *vdata, size_t size, const char *mark) {\n    return MSLogHexEx(vdata, size, 1, mark);\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Substrate/SubstrateDebug.hpp",
    "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 \"SubstrateLog.hpp\"\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": "VirtualApp/lib/src/main/jni/Substrate/SubstrateHook.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#define SubstrateInternal\n#include \"CydiaSubstrate.h\"\n\n#include <sys/mman.h>\n\n#define _trace() do { \\\n    MSLog(MSLogLevelNotice, \"_trace(%u)\", __LINE__); \\\n} while (false)\n\n#if defined(__i386__) || defined(__x86_64__)\n#include \"hde64.h\"\n#endif\n\n#include \"SubstrateDebug.hpp\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <string.h>\n\n#ifdef __arm__\n/* WebCore (ARM) PC-Relative:\nX    1  ldr r*,[pc,r*] !=\n     2 fldd d*,[pc,#*]\nX    5  str r*,[pc,r*] !=\n     8 flds s*,[pc,#*]\n   400  ldr r*,[pc,r*] ==\n   515  add r*, pc,r*  ==\nX 4790  ldr r*,[pc,#*]    */\n\n// x=0; while IFS= read -r line; do if [[ ${#line} -ne 0 && $line == +([^\\;]): ]]; then x=2; elif [[ $line == ' +'* && $x -ne 0 ]]; then ((--x)); echo \"$x${line}\"; fi; done <WebCore.asm >WebCore.pc\n// grep pc WebCore.pc | cut -c 40- | sed -Ee 's/^ldr *(ip|r[0-9]*),\\[pc,\\#0x[0-9a-f]*\\].*/ ldr r*,[pc,#*]/;s/^add *r[0-9]*,pc,r[0-9]*.*/ add r*, pc,r*/;s/^(st|ld)r *r([0-9]*),\\[pc,r([0-9]*)\\].*/ \\1r r\\2,[pc,r\\3]/;s/^fld(s|d) *(s|d)[0-9]*,\\[pc,#0x[0-9a-f]*].*/fld\\1 \\2*,[pc,#*]/' | sort | uniq -c | sort -n\n\n#include \"SubstrateARM.hpp\"\n\n#define T$Label(l, r) \\\n    (((r) - (l)) * 2 - 4 + ((l) % 2 == 0 ? 0 : 2))\n\n#define T$pop_$r0$ 0xbc01 // pop {r0}\n#define T$b(im) /* b im */ \\\n    (0xde00 | (im & 0xff))\n#define T$blx(rm) /* blx rm */ \\\n    (0x4780 | (rm << 3))\n#define T$bx(rm) /* bx rm */ \\\n    (0x4700 | (rm << 3))\n#define T$nop /* nop */ \\\n    (0x46c0)\n\n#define T$add_rd_rm(rd, rm) /* add rd, rm */ \\\n    (0x4400 | (((rd) & 0x8) >> 3 << 7) | (((rm) & 0x8) >> 3 << 6) | (((rm) & 0x7) << 3) | ((rd) & 0x7))\n#define T$push_r(r) /* push r... */ \\\n    (0xb400 | (((r) & (1 << A$lr)) >> A$lr << 8) | ((r) & 0xff))\n#define T$pop_r(r) /* pop r... */ \\\n    (0xbc00 | (((r) & (1 << A$pc)) >> A$pc << 8) | ((r) & 0xff))\n#define T$mov_rd_rm(rd, rm) /* mov rd, rm */ \\\n    (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    (0x6800 | (((im) & 0x1f) << 6) | ((rn) << 3) | (rd))\n#define T$ldr_rd_$pc_im_4$(rd, im) /* ldr rd, [PC, #im * 4] */ \\\n    (0x4800 | ((rd) << 8) | ((im) & 0xff))\n#define T$cmp_rn_$im(rn, im) /* cmp rn, #im */ \\\n    (0x2000 | ((rn) << 8) | ((im) & 0xff))\n#define T$it$_cd(cd, ms) /* it<ms>, cd */ \\\n    (0xbf00 | ((cd) << 4) | (ms))\n#define T$cbz$_rn_$im(op,rn,im) /* cb<op>z rn, #im */ \\\n    (0xb100 | ((op) << 11) | (((im) & 0x40) >> 6 << 9) | (((im) & 0x3e) >> 1 << 3) | (rn))\n#define T$b$_$im(cond,im) /* b<cond> #im */ \\\n    (cond == A$al ? 0xe000 | (((im) >> 1) & 0x7ff) : 0xd000 | ((cond) << 8) | (((im) >> 1) & 0xff))\n\n#define T1$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \\\n    (0xf850 | ((im < 0 ? 0 : 1) << 7) | (rn))\n#define T2$ldr_rt_$rn_im$(rt, rn, im) /* ldr rt, [rn, #im] */ \\\n    (((rt) << 12) | abs(im))\n\n#define T1$mrs_rd_apsr(rd) /* mrs rd, apsr */ \\\n    (0xf3ef)\n#define T2$mrs_rd_apsr(rd) /* mrs rd, apsr */ \\\n    (0x8000 | ((rd) << 8))\n\n#define T1$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \\\n    (0xf380 | (rn))\n#define T2$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \\\n    (0x8c00)\n#define T$msr_apsr_nzcvqg_rn(rn) /* msr apsr, rn */ \\\n    (T2$msr_apsr_nzcvqg_rn(rn) << 16 | T1$msr_apsr_nzcvqg_rn(rn))\n\nstatic inline bool A$pcrel$r(uint32_t ic) {\n    return (ic & 0x0c000000) == 0x04000000 && (ic & 0xf0000000) != 0xf0000000 && (ic & 0x000f0000) == 0x000f0000;\n}\n\nstatic inline bool T$32bit$i(uint16_t ic) {\n    return ((ic & 0xe000) == 0xe000 && (ic & 0x1800) != 0x0000);\n}\n\nstatic inline bool T$pcrel$cbz(uint16_t ic) {\n    return (ic & 0xf500) == 0xb100;\n}\n\nstatic inline bool T$pcrel$b(uint16_t ic) {\n    return (ic & 0xf000) == 0xd000 && (ic & 0x0e00) != 0x0e00;\n}\n\nstatic inline bool T2$pcrel$b(uint16_t *ic) {\n    return (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    return (ic[0] & 0xf800) == 0xf000 && ((ic[1] & 0xd000) == 0xd000 || (ic[1] & 0xd001) == 0xc000);\n}\n\nstatic inline bool T$pcrel$ldr(uint16_t ic) {\n    return (ic & 0xf800) == 0x4800;\n}\n\nstatic inline bool T$pcrel$add(uint16_t ic) {\n    return (ic & 0xff78) == 0x4478;\n}\n\nstatic inline bool T$pcrel$ldrw(uint16_t ic) {\n    return (ic & 0xff7f) == 0xf85f;\n}\n\nstatic size_t MSGetInstructionWidthThumb(void *start) {\n    uint16_t *thumb(reinterpret_cast<uint16_t *>(start));\n    return T$32bit$i(thumb[0]) ? 4 : 2;\n}\n\nstatic size_t MSGetInstructionWidthARM(void *start) {\n    return 4;\n}\n\nextern \"C\" size_t MSGetInstructionWidth(void *start) {\n    if ((reinterpret_cast<uintptr_t>(start) & 0x1) == 0)\n        return MSGetInstructionWidthARM(start);\n    else\n        return MSGetInstructionWidthThumb(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(start) & ~0x1));\n}\n\nstatic size_t SubstrateHookFunctionThumb(SubstrateProcessRef process, void *symbol, void *replace, void **result) {\n    if (symbol == NULL)\n        return 0;\nprintf(\"SubstrateHookFunctionThumb\\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 sizeof(arm[0]);\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 0;\n    }\n\n    if (false) fail: {\n        munmap(buffer, length);\n        *result = NULL;\n        return 0;\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 0;\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\t\n\treturn used;\n}\n\nstatic size_t SubstrateHookFunctionARM(SubstrateProcessRef process, void *symbol, void *replace, void **result) {\n    if (symbol == NULL)\n        return 0;\nprintf(\"SubstrateHookFunctionARM\\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        \n\t\treturn sizeof(backup[0]);\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 0;\n    }\n\n    if (false) fail: {\n        munmap(buffer, length);\n        *result = NULL;\n        return 0;\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\t\n\treturn used;\n}\n\nstatic size_t SubstrateHookFunction(SubstrateProcessRef process, void *symbol, void *replace, void **result) {\n    if (MSDebug)\n        MSLog(MSLogLevelNotice, \"SubstrateHookFunction(%p, %p, %p, %p)\\n\", process, symbol, replace, result);\n    if ((reinterpret_cast<uintptr_t>(symbol) & 0x1) == 0)\n        return SubstrateHookFunctionARM(process, symbol, replace, result);\n    else\n        return SubstrateHookFunctionThumb(process, reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(symbol) & ~0x1), replace, result);\n}\n#endif\n\n#if defined(__i386__) || defined(__x86_64__)\n\n#include \"SubstrateX86.hpp\"\n\nstatic size_t MSGetInstructionWidthIntel(void *start) {\n    hde64s decode;\n    return hde64_disasm(start, &decode);\n}\n\nstatic void SubstrateHookFunction(SubstrateProcessRef process, void *symbol, void *replace, void **result) {\n    if (MSDebug)\n        MSLog(MSLogLevelNotice, \"MSHookFunction(%p, %p, %p)\\n\", 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#endif\n\n_extern void MSHookFunction(void *symbol, void *replace, void **result) {\n     SubstrateHookFunction(NULL, symbol, replace, result);\n}\n\n#if defined(__APPLE__) && defined(__arm__)\n_extern void _Z14MSHookFunctionPvS_PS_(void *symbol, void *replace, void **result) {\n    return MSHookFunction(symbol, replace, result);\n}\n#endif\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Substrate/SubstrateHook.h",
    "content": "#ifndef __SUBSTRATEHOOK_H__\n#define __SUBSTRATEHOOK_H__\n\n\n#include <stdlib.h>\n\n#define _extern extern \"C\" __attribute__((__visibility__(\"default\")))\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid MSHookFunction(void *symbol, void *replace, void **result);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Substrate/SubstrateLog.hpp",
    "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#if 0\n#include <android/log.h>\n\n#define MSLog(level, format, ...) ((void)__android_log_print(level, \"NNNN\", format, __VA_ARGS__))\n\n#define MSLogLevelNotice ANDROID_LOG_INFO\n#define MSLogLevelWarning ANDROID_LOG_WARN\n#define MSLogLevelError ANDROID_LOG_ERROR\n\n#else\n\n#define MSLog(level, format, ...) printf(format, __VA_ARGS__)\n\n#endif\n\n#endif//SUBSTRATE_LOG_HPP\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Substrate/SubstratePosixMemory.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#define SubstrateInternal\n#include \"CydiaSubstrate.h\"\n#include \"SubstrateLog.hpp\"\n\n#include <sys/mman.h>\n\n#include <errno.h>\n#include <stdio.h>\n#include <unistd.h>\n\nextern \"C\" void __clear_cache (void *beg, void *end);\n\nstruct __SubstrateMemory {\n    void *address_;\n    size_t width_;\n\n    __SubstrateMemory(void *address, size_t width) :\n        address_(address),\n        width_(width)\n    {\n    }\n};\n\nextern \"C\" SubstrateMemoryRef SubstrateMemoryCreate(SubstrateAllocatorRef allocator, SubstrateProcessRef process, void *data, size_t size) {\n    if (allocator != NULL) {\n        MSLog(MSLogLevelError, \"MS:Error:allocator != %d\", 0);\n        return NULL;\n    }\n\n    if (size == 0)\n        return NULL;\n\n    long page(sysconf(_SC_PAGESIZE)); // Portable applications should employ sysconf(_SC_PAGESIZE) instead of getpagesize\n\n    uintptr_t base(reinterpret_cast<uintptr_t>(data) / page * page);\n    size_t width(((reinterpret_cast<uintptr_t>(data) + size - 1) / page + 1) * page - base);\n    void *address(reinterpret_cast<void *>(base));\n\n    if (mprotect(address, width, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {\n        MSLog(MSLogLevelError, \"MS:Error:mprotect() = %d\", errno);\n        return NULL;\n    }\n\n    return new __SubstrateMemory(address, width);\n}\n\nextern \"C\" void SubstrateMemoryRelease(SubstrateMemoryRef memory) {\n    if (mprotect(memory->address_, memory->width_, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)\n        MSLog(MSLogLevelError, \"MS:Error:mprotect() = %d\", errno);\n\n    __clear_cache(reinterpret_cast<char *>(memory->address_), reinterpret_cast<char *>(memory->address_) + memory->width_);\n\n    delete memory;\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Substrate/SubstrateX86.hpp",
    "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_X86_HPP\n#define SUBSTRATE_X86_HPP\n\n#include \"Buffer.hpp\"\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\n#endif//SUBSTRATE_X86_HPP\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Substrate/hde64.c",
    "content": "/*\n * Hacker Disassembler Engine 64 C\n * Copyright (c) 2008-2009, Vyacheslav Patkov.\n * All rights reserved.\n *\n */\n\n#include <stdint.h>\n#include <string.h>\n\n#include \"hde64.h\"\n#include \"table64.h\"\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    char *tmp=(char*)hs;\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"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/Substrate/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": "VirtualApp/lib/src/main/jni/Substrate/table64.h",
    "content": "/*\n * Hacker Disassembler Engine 64 C\n * Copyright (c) 2008-2009, Vyacheslav Patkov.\n * All rights reserved.\n *\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\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"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/Android.mk",
    "content": "LOCAL_PATH := $(call my-dir)\n\ninclude $(CLEAR_VARS)\n\nLOCAL_SRC_FILES:= \\\n       assert.cpp \\\n       jni/ByteBuffer.cpp \\\n       jni/Countable.cpp \\\n       jni/Environment.cpp \\\n       jni/Exceptions.cpp \\\n       jni/fbjni.cpp \\\n       jni/Hybrid.cpp \\\n       jni/jni_helpers.cpp \\\n       jni/LocalString.cpp \\\n       jni/OnLoad.cpp \\\n       jni/References.cpp \\\n       jni/WeakReference.cpp \\\n       log.cpp \\\n       lyra/lyra.cpp \\\n       onload.cpp \\\n\nLOCAL_C_INCLUDES := $(LOCAL_PATH)/include\nLOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include\n\nLOCAL_CFLAGS := -DLOG_TAG=\\\"libfb\\\" -DDISABLE_CPUCAP -DDISABLE_XPLAT -fexceptions -frtti\nLOCAL_CFLAGS += -Wall -Werror\n# include/utils/threads.h has unused parameters\nLOCAL_CFLAGS += -Wno-unused-parameter\nifeq ($(TOOLCHAIN_PERMISSIVE),true)\n  LOCAL_CFLAGS += -Wno-error=unused-but-set-variable\nendif\nLOCAL_CFLAGS += -DHAVE_POSIX_CLOCKS\n\nCXX11_FLAGS := -std=gnu++11\nLOCAL_CFLAGS += $(CXX11_FLAGS)\n\nLOCAL_EXPORT_CPPFLAGS := $(CXX11_FLAGS)\n\nLOCAL_LDLIBS := -llog -ldl -landroid\nLOCAL_EXPORT_LDLIBS := -llog\n\nLOCAL_MODULE := libfb\n\ninclude $(BUILD_STATIC_LIBRARY)\n\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/BUCK",
    "content": "include_defs(\"//ReactAndroid/DEFS\")\n\n# This target is only used in open source\nif IS_OSS_BUILD:\n  cxx_library(\n    name = 'jni',\n    soname = 'libfb.$(ext)',\n    srcs = glob(['*.cpp', 'jni/*.cpp', 'lyra/*.cpp']),\n    header_namespace = '',\n    compiler_flags = [\n      '-fno-omit-frame-pointer',\n      '-fexceptions',\n      '-Wall',\n      '-Werror',\n      '-std=c++11',\n      '-DDISABLE_CPUCAP',\n      '-DDISABLE_XPLAT',\n    ],\n    exported_headers = subdir_glob([\n      ('include', 'fb/**/*.h'),\n      ('include', 'jni/*.h'),\n    ]),\n    deps = [\n      JNI_TARGET,\n    ],\n    visibility = ['PUBLIC'],\n  )\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/Doxyfile",
    "content": "PROJECT_NAME           = \"Facebook Android Support\"\nJAVADOC_AUTOBRIEF      = YES\nEXTRACT_ALL            = YES\nRECURSIVE              = YES\nEXCLUDE                = tests\nEXCLUDE_PATTERNS       = *.cpp\nGENERATE_HTML          = YES\nGENERATE_LATEX         = NO\nENABLE_PREPROCESSING   = YES\nHIDE_UNDOC_MEMBERS     = YES\nHIDE_SCOPE_NAMES       = YES\nHIDE_FRIEND_COMPOUNDS  = YES\nHIDE_UNDOC_CLASSES     = YES\nSHOW_INCLUDE_FILES     = NO\n#ENABLED_SECTIONS       = INTERNAL\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/assert.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include <cstdarg>\n#include <stdio.h>\n\n#include <fb/assert.h>\n#include <fb/log.h>\n\nnamespace facebook {\n\n#define ASSERT_BUF_SIZE 4096\nstatic char sAssertBuf[ASSERT_BUF_SIZE];\nstatic AssertHandler gAssertHandler;\n\nvoid assertInternal(const char* formatstr ...) {\n    va_list va_args;\n    va_start(va_args, formatstr);\n    vsnprintf(sAssertBuf, sizeof(sAssertBuf), formatstr, va_args);\n    va_end(va_args);\n    if (gAssertHandler != NULL) {\n        gAssertHandler(sAssertBuf);\n    }\n    FBLOG(LOG_FATAL, \"fbassert\", \"%s\", sAssertBuf);\n    // crash at this specific address so that we can find our crashes easier\n    *(int*)0xdeadb00c = 0;\n    // let the compiler know we won't reach the end of the function\n     __builtin_unreachable();\n}\n\nvoid setAssertHandler(AssertHandler assertHandler) {\n    gAssertHandler = assertHandler;\n}\n\n} // namespace facebook\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/ALog.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n/** @file ALog.h\n *\n *  Very simple android only logging. Define LOG_TAG to enable the macros.\n */\n\n#pragma once\n\n\n#ifdef __ANDROID__\n\n#include <android/log.h>\n\nnamespace facebook {\nnamespace alog {\n\ntemplate<typename... ARGS>\ninline void log(int level, const char* tag, const char* msg, ARGS... args) noexcept {\n  __android_log_print(level, tag, msg, args...);\n}\n\ntemplate<typename... ARGS>\ninline void log(int level, const char* tag, const char* msg) noexcept {\n  __android_log_write(level, tag, msg);\n}\n\ntemplate<typename... ARGS>\ninline void logv(const char* tag, const char* msg, ARGS... args) noexcept {\n  log(ANDROID_LOG_VERBOSE, tag, msg, args...);\n}\n\ntemplate<typename... ARGS>\ninline void logd(const char* tag, const char* msg, ARGS... args) noexcept {\n  log(ANDROID_LOG_DEBUG, tag, msg, args...);\n}\n\ntemplate<typename... ARGS>\ninline void logi(const char* tag, const char* msg, ARGS... args) noexcept {\n  log(ANDROID_LOG_INFO, tag, msg, args...);\n}\n\ntemplate<typename... ARGS>\ninline void logw(const char* tag, const char* msg, ARGS... args) noexcept {\n  log(ANDROID_LOG_WARN, tag, msg, args...);\n}\n\ntemplate<typename... ARGS>\ninline void loge(const char* tag, const char* msg, ARGS... args) noexcept {\n  log(ANDROID_LOG_ERROR, tag, msg, args...);\n}\n\ntemplate<typename... ARGS>\ninline void logf(const char* tag, const char* msg, ARGS... args) noexcept {\n  log(ANDROID_LOG_FATAL, tag, msg, args...);\n}\n\n\n#ifdef LOG_TAG\n# define ALOGV(...) ::facebook::alog::logv(LOG_TAG, __VA_ARGS__)\n# define ALOGD(...) ::facebook::alog::logd(LOG_TAG, __VA_ARGS__)\n# define ALOGI(...) ::facebook::alog::logi(LOG_TAG, __VA_ARGS__)\n# define ALOGW(...) ::facebook::alog::logw(LOG_TAG, __VA_ARGS__)\n# define ALOGE(...) ::facebook::alog::loge(LOG_TAG, __VA_ARGS__)\n# define ALOGF(...) ::facebook::alog::logf(LOG_TAG, __VA_ARGS__)\n#endif\n\n}}\n\n#else\n# define ALOGV(...) ((void)0)\n# define ALOGD(...) ((void)0)\n# define ALOGI(...) ((void)0)\n# define ALOGW(...) ((void)0)\n# define ALOGE(...) ((void)0)\n# define ALOGF(...) ((void)0)\n#endif\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/Build.h",
    "content": "// Copyright 2004-present Facebook. All Rights Reserved.\n\n#pragma once\n\n#include <stdlib.h>\n\n#if defined(__ANDROID__)\n#  include <sys/system_properties.h>\n#endif\n\nnamespace facebook {\nnamespace build {\n\nstruct Build {\n  static int getAndroidSdk() {\n    static auto android_sdk = ([] {\n       char sdk_version_str[PROP_VALUE_MAX];\n       __system_property_get(\"ro.build.version.sdk\", sdk_version_str);\n       return atoi(sdk_version_str);\n    })();\n    return android_sdk;\n  }\n};\n\n} // build\n} // facebook\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/Countable.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n#include <atomic>\n#include <fb/assert.h>\n#include <fb/noncopyable.h>\n#include <fb/nonmovable.h>\n#include <fb/RefPtr.h>\n\nnamespace facebook {\n\nclass Countable : public noncopyable, public nonmovable {\npublic:\n  // RefPtr expects refcount to start at 0\n  Countable() : m_refcount(0) {}\n  virtual ~Countable()\n  {\n    FBASSERT(m_refcount == 0);\n  }\n\nprivate:\n  void ref() {\n    ++m_refcount;\n  }\n\n  void unref() {\n    if (0 == --m_refcount) {\n      delete this;\n    }\n  }\n\n  bool hasOnlyOneRef() const {\n    return m_refcount == 1;\n  }\n\n  template <typename T> friend class RefPtr;\n  std::atomic<int> m_refcount;\n};\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/Doxyfile",
    "content": "PROJECT_NAME           = \"Facebook JNI\"\nPROJECT_BRIEF          = \"Helper library to provide safe and convenient access to JNI with very low overhead\"\nJAVADOC_AUTOBRIEF      = YES\nEXTRACT_ALL            = YES\nRECURSIVE              = YES\nEXCLUDE                = tests Asserts.h Countable.h GlobalReference.h LocalReference.h LocalString.h Registration.h WeakReference.h jni_helpers.h Environment.h\nEXCLUDE_PATTERNS       = *-inl.h *.cpp\nGENERATE_HTML          = YES\nGENERATE_LATEX         = NO\nENABLE_PREPROCESSING   = YES\nHIDE_UNDOC_MEMBERS     = YES\nHIDE_SCOPE_NAMES       = YES\nHIDE_FRIEND_COMPOUNDS  = YES\nHIDE_UNDOC_CLASSES     = YES\nSHOW_INCLUDE_FILES     = NO\nPREDEFINED             = LOG_TAG=fbjni\nEXAMPLE_PATH           = samples\n#ENABLED_SECTIONS       = INTERNAL\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/Environment.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n#include <functional>\n#include <string>\n#include <jni.h>\n\n#include <fb/visibility.h>\n\nnamespace facebook {\nnamespace jni {\n\nnamespace internal {\n  struct CacheEnvTag {};\n}\n\n// Keeps a thread-local reference to the current thread's JNIEnv.\nstruct Environment {\n  // May be null if this thread isn't attached to the JVM\n  FBEXPORT static JNIEnv* current();\n  static void initialize(JavaVM* vm);\n\n  // There are subtle issues with calling the next functions directly. It is\n  // much better to always use a ThreadScope to manage attaching/detaching for\n  // you.\n  FBEXPORT static JNIEnv* ensureCurrentThreadIsAttached();\n  FBEXPORT static void detachCurrentThread();\n};\n\n/**\n * RAII Object that attaches a thread to the JVM. Failing to detach from a thread before it\n * exits will cause a crash, as will calling Detach an extra time, and this guard class helps\n * keep that straight. In addition, it remembers whether it performed the attach or not, so it\n * is safe to nest it with itself or with non-fbjni code that manages the attachment correctly.\n *\n * Potential concerns:\n *  - Attaching to the JVM is fast (~100us on MotoG), but ideally you would attach while the\n *    app is not busy.\n *  - Having a thread detach at arbitrary points is not safe in Dalvik; you need to be sure that\n *    there is no Java code on the current stack or you run the risk of a crash like:\n *      ERROR: detaching thread with interp frames (count=18)\n *    (More detail at https://groups.google.com/forum/#!topic/android-ndk/2H8z5grNqjo)\n *    ThreadScope won't do a detach if the thread was already attached before the guard is\n *    instantiated, but there's probably some usage that could trip this up.\n *  - Newly attached C++ threads only get the bootstrap class loader -- i.e. java language\n *    classes, not any of our application's classes. This will be different behavior than threads\n *    that were initiated on the Java side. A workaround is to pass a global reference for a\n *    class or instance to the new thread; this bypasses the need for the class loader.\n *    (See http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#attach_current_thread)\n *    If you need access to the application's classes, you can use ThreadScope::WithClassLoader.\n */\nclass FBEXPORT ThreadScope {\n public:\n  ThreadScope();\n  ThreadScope(ThreadScope&) = delete;\n  ThreadScope(ThreadScope&&) = default;\n  ThreadScope& operator=(ThreadScope&) = delete;\n  ThreadScope& operator=(ThreadScope&&) = delete;\n  ~ThreadScope();\n\n  /**\n   * This runs the closure in a scope with fbjni's classloader. This should be\n   * the same classloader as the rest of the application and thus anything\n   * running in the closure will have access to the same classes as in a normal\n   * java-create thread.\n   */\n  static void WithClassLoader(std::function<void()>&& runnable);\n\n  static void OnLoad();\n\n  // This constructor is only used internally by fbjni.\n  ThreadScope(JNIEnv*, internal::CacheEnvTag);\n private:\n  friend struct Environment;\n  ThreadScope* previous_;\n  // If the JNIEnv* is set, it is guaranteed to be valid at least through the\n  // lifetime of this ThreadScope. The only case where that guarantee can be\n  // made is when there is a java frame in the stack below this.\n  JNIEnv* env_;\n  bool attachedWithThisScope_;\n};\n}\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/ProgramLocation.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n#include <cstring>\n#include <string>\n#include <sstream>\n\nnamespace facebook {\n\n#define FROM_HERE facebook::ProgramLocation(__FUNCTION__, __FILE__, __LINE__)\n\nclass ProgramLocation {\npublic:\n  ProgramLocation() : m_functionName(\"Unspecified\"), m_fileName(\"Unspecified\"), m_lineNumber(0) {}\n\n  ProgramLocation(const char* functionName, const char* fileName, int line) :\n      m_functionName(functionName),\n      m_fileName(fileName),\n      m_lineNumber(line)\n    {}\n\n  const char* functionName() const { return m_functionName; }\n  const char* fileName() const { return m_fileName; }\n  int lineNumber() const { return m_lineNumber; }\n\n  std::string asFormattedString() const {\n    std::stringstream str;\n    str << \"Function \" << m_functionName << \" in file \" << m_fileName << \":\" << m_lineNumber;\n    return str.str();\n  }\n\n  bool operator==(const ProgramLocation& other) const {\n    // Assumes that the strings are static\n    return (m_functionName == other.m_functionName) && (m_fileName == other.m_fileName) && m_lineNumber == other.m_lineNumber;\n  }\n\nprivate:\n  const char* m_functionName;\n  const char* m_fileName;\n  int m_lineNumber;\n};\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/RefPtr.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n#include <utility>\n#include <fb/assert.h>\n\nnamespace facebook {\n\n// Reference counting smart pointer. This is designed to work with the\n// Countable class or other implementations in the future. It is designed in a\n// way to be both efficient and difficult to misuse. Typical usage is very\n// simple once you learn the patterns (and the compiler will help!):\n//\n// By default, the internal pointer is null.\n//   RefPtr<Foo> ref;\n//\n// Object creation requires explicit construction:\n//   RefPtr<Foo> ref = createNew<Foo>(...);\n//\n// Or if the constructor is not public:\n//   RefPtr<Foo> ref = adoptRef(new Foo(...));\n//\n// But you can implicitly create from nullptr:\n//   RefPtr<Foo> maybeRef = cond ? ref : nullptr;\n//\n// Move/Copy Construction/Assignment are straightforward:\n//   RefPtr<Foo> ref2 = ref;\n//   ref = std::move(ref2);\n//\n// Destruction automatically drops the RefPtr's reference as expected.\n//\n// Upcasting is implicit but downcasting requires an explicit cast:\n//   struct Bar : public Foo {};\n//   RefPtr<Bar> barRef = static_cast<RefPtr<Bar>>(ref);\n//   ref = barRef;\n//\ntemplate <class T>\nclass RefPtr {\npublic:\n  constexpr RefPtr() :\n    m_ptr(nullptr)\n  {}\n\n  // Allow implicit construction from a pointer only from nullptr\n  constexpr RefPtr(std::nullptr_t ptr) :\n    m_ptr(nullptr)\n  {}\n\n  RefPtr(const RefPtr<T>& ref) :\n    m_ptr(ref.m_ptr)\n  {\n    refIfNecessary(m_ptr);\n  }\n\n  // Only allow implicit upcasts. A downcast will result in a compile error\n  // unless you use static_cast (which will end up invoking the explicit\n  // operator below).\n  template <typename U>\n  RefPtr(const RefPtr<U>& ref, typename std::enable_if<std::is_base_of<T,U>::value, U>::type* = nullptr) :\n    m_ptr(ref.get())\n  {\n    refIfNecessary(m_ptr);\n  }\n\n  RefPtr(RefPtr<T>&& ref) :\n    m_ptr(nullptr)\n  {\n    *this = std::move(ref);\n  }\n\n  // Only allow implicit upcasts. A downcast will result in a compile error\n  // unless you use static_cast (which will end up invoking the explicit\n  // operator below).\n  template <typename U>\n  RefPtr(RefPtr<U>&& ref, typename std::enable_if<std::is_base_of<T,U>::value, U>::type* = nullptr) :\n    m_ptr(nullptr)\n  {\n    *this = std::move(ref);\n  }\n\n  ~RefPtr() {\n    unrefIfNecessary(m_ptr);\n    m_ptr = nullptr;\n  }\n\n  RefPtr<T>& operator=(const RefPtr<T>& ref) {\n    if (m_ptr != ref.m_ptr) {\n      unrefIfNecessary(m_ptr);\n      m_ptr = ref.m_ptr;\n      refIfNecessary(m_ptr);\n    }\n    return *this;\n  }\n\n  // The STL assumes rvalue references are unique and for simplicity's sake, we\n  // make the same assumption here, that &ref != this.\n  RefPtr<T>& operator=(RefPtr<T>&& ref) {\n    unrefIfNecessary(m_ptr);\n    m_ptr = ref.m_ptr;\n    ref.m_ptr = nullptr;\n    return *this;\n  }\n\n  template <typename U>\n  RefPtr<T>& operator=(RefPtr<U>&& ref) {\n    unrefIfNecessary(m_ptr);\n    m_ptr = ref.m_ptr;\n    ref.m_ptr = nullptr;\n    return *this;\n  }\n\n  void reset() {\n    unrefIfNecessary(m_ptr);\n    m_ptr = nullptr;\n  }\n\n  T* get() const {\n    return m_ptr;\n  }\n\n  T* operator->() const {\n    return m_ptr;\n  }\n\n  T& operator*() const {\n    return *m_ptr;\n  }\n\n  template <typename U>\n  explicit operator RefPtr<U> () const;\n\n  explicit operator bool() const {\n    return m_ptr ? true : false;\n  }\n\n  bool isTheLastRef() const {\n    FBASSERT(m_ptr);\n    return m_ptr->hasOnlyOneRef();\n  }\n\n  // Creates a strong reference from a raw pointer, assuming that is already\n  // referenced from some other RefPtr. This should be used sparingly.\n  static inline RefPtr<T> assumeAlreadyReffed(T* ptr) {\n    return RefPtr<T>(ptr, ConstructionMode::External);\n  }\n\n  // Creates a strong reference from a raw pointer, assuming that it points to a\n  // freshly-created object. See the documentation for RefPtr for usage.\n  static inline RefPtr<T> adoptRef(T* ptr) {\n    return RefPtr<T>(ptr, ConstructionMode::Adopted);\n  }\n\nprivate:\n  enum class ConstructionMode {\n    Adopted,\n    External\n  };\n\n  RefPtr(T* ptr, ConstructionMode mode) :\n    m_ptr(ptr)\n  {\n    FBASSERTMSGF(ptr, \"Got null pointer in %s construction mode\", mode == ConstructionMode::Adopted ? \"adopted\" : \"external\");\n    ptr->ref();\n    if (mode == ConstructionMode::Adopted) {\n      FBASSERT(ptr->hasOnlyOneRef());\n    }\n  }\n\n  static inline void refIfNecessary(T* ptr) {\n    if (ptr) {\n      ptr->ref();\n    }\n  }\n  static inline void unrefIfNecessary(T* ptr) {\n    if (ptr) {\n      ptr->unref();\n    }\n  }\n\n  template <typename U> friend class RefPtr;\n\n  T* m_ptr;\n};\n\n// Creates a strong reference from a raw pointer, assuming that is already\n// referenced from some other RefPtr and that it is non-null. This should be\n// used sparingly.\ntemplate <typename T>\nstatic inline RefPtr<T> assumeAlreadyReffed(T* ptr) {\n  return RefPtr<T>::assumeAlreadyReffed(ptr);\n}\n\n// As above, but tolerant of nullptr.\ntemplate <typename T>\nstatic inline RefPtr<T> assumeAlreadyReffedOrNull(T* ptr) {\n  return ptr ? RefPtr<T>::assumeAlreadyReffed(ptr) : nullptr;\n}\n\n// Creates a strong reference from a raw pointer, assuming that it points to a\n// freshly-created object. See the documentation for RefPtr for usage.\ntemplate <typename T>\nstatic inline RefPtr<T> adoptRef(T* ptr) {\n  return RefPtr<T>::adoptRef(ptr);\n}\n\ntemplate <typename T, typename ...Args>\nstatic inline RefPtr<T> createNew(Args&&... arguments) {\n  return RefPtr<T>::adoptRef(new T(std::forward<Args>(arguments)...));\n}\n\ntemplate <typename T> template <typename U>\nRefPtr<T>::operator RefPtr<U>() const {\n  static_assert(std::is_base_of<T, U>::value, \"Invalid static cast\");\n  return assumeAlreadyReffedOrNull<U>(static_cast<U*>(m_ptr));\n}\n\ntemplate <typename T, typename U>\ninline bool operator==(const RefPtr<T>& a, const RefPtr<U>& b) {\n  return a.get() == b.get();\n}\n\ntemplate <typename T, typename U>\ninline bool operator!=(const RefPtr<T>& a, const RefPtr<U>& b) {\n  return a.get() != b.get();\n}\n\ntemplate <typename T, typename U>\ninline bool operator==(const RefPtr<T>& ref, U* ptr) {\n  return ref.get() == ptr;\n}\n\ntemplate <typename T, typename U>\ninline bool operator!=(const RefPtr<T>& ref, U* ptr) {\n  return ref.get() != ptr;\n}\n\ntemplate <typename T, typename U>\ninline bool operator==(U* ptr, const RefPtr<T>& ref) {\n  return ref.get() == ptr;\n}\n\ntemplate <typename T, typename U>\ninline bool operator!=(U* ptr, const RefPtr<T>& ref) {\n  return ref.get() != ptr;\n}\n\ntemplate <typename T>\ninline bool operator==(const RefPtr<T>& ref, std::nullptr_t ptr) {\n  return ref.get() == ptr;\n}\n\ntemplate <typename T>\ninline bool operator!=(const RefPtr<T>& ref, std::nullptr_t ptr) {\n  return ref.get() != ptr;\n}\n\ntemplate <typename T>\ninline bool operator==(std::nullptr_t ptr, const RefPtr<T>& ref) {\n  return ref.get() == ptr;\n}\n\ntemplate <typename T>\ninline bool operator!=(std::nullptr_t ptr, const RefPtr<T>& ref) {\n  return ref.get() != ptr;\n}\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/StaticInitialized.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n#include <fb/assert.h>\n#include <utility>\n\nnamespace facebook {\n\n// Class that lets you declare a global but does not add a static constructor\n// to the binary. Eventually I'd like to have this auto-initialize in a\n// multithreaded environment but for now it's easiest just to use manual\n// initialization.\ntemplate <typename T>\nclass StaticInitialized {\npublic:\n  constexpr StaticInitialized() :\n    m_instance(nullptr)\n  {}\n\n  template <typename ...Args>\n  void initialize(Args&&... arguments) {\n    FBASSERT(!m_instance);\n    m_instance = new T(std::forward<Args>(arguments)...);\n  }\n\n  T* operator->() const {\n    return m_instance;\n  }\nprivate:\n  T* m_instance;\n};\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/ThreadLocal.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <pthread.h>\n#include <errno.h>\n\n#include <fb/assert.h>\n\nnamespace facebook {\n\n///////////////////////////////////////////////////////////////////////////////\n\n/**\n * A thread-local object is a \"global\" object within a thread. This is useful\n * for writing apartment-threaded code, where nothing is actullay shared\n * between different threads (hence no locking) but those variables are not\n * on stack in local scope. To use it, just do something like this,\n *\n *   ThreadLocal<MyClass> static_object;\n *     static_object->data_ = ...;\n *     static_object->doSomething();\n *\n *   ThreadLocal<int> static_number;\n *     int value = *static_number;\n *\n * So, syntax-wise it's similar to pointers. T can be primitive types, and if\n * it's a class, there has to be a default constructor.\n */\ntemplate<typename T>\nclass ThreadLocal {\npublic:\n  /**\n   * Constructor that has to be called from a thread-neutral place.\n   */\n  ThreadLocal() :\n    m_key(0),\n    m_cleanup(OnThreadExit) {\n    initialize();\n  }\n\n  /**\n   * As above but with a custom cleanup function\n   */\n  typedef void (*CleanupFunction)(void* obj);\n  explicit ThreadLocal(CleanupFunction cleanup) :\n    m_key(0),\n    m_cleanup(cleanup) {\n    FBASSERT(cleanup);\n    initialize();\n  }\n\n  /**\n   * Access object's member or method through this operator overload.\n   */\n  T *operator->() const {\n    return get();\n  }\n\n  T &operator*() const {\n    return *get();\n  }\n\n  T *get() const {\n    return (T*)pthread_getspecific(m_key);\n  }\n\n  T* release() {\n    T* obj = get();\n    pthread_setspecific(m_key, NULL);\n    return obj;\n  }\n\n  void reset(T* other = NULL) {\n    T* old = (T*)pthread_getspecific(m_key);\n    if (old != other) {\n      FBASSERT(m_cleanup);\n      m_cleanup(old);\n      pthread_setspecific(m_key, other);\n    }\n  }\n\nprivate:\n  void initialize() {\n    int ret = pthread_key_create(&m_key, m_cleanup);\n    if (ret != 0) {\n      const char *msg = \"(unknown error)\";\n      switch (ret) {\n      case EAGAIN:\n        msg = \"PTHREAD_KEYS_MAX (1024) is exceeded\";\n        break;\n      case ENOMEM:\n        msg = \"Out-of-memory\";\n        break;\n      }\n      (void) msg;\n      FBASSERTMSGF(0, \"pthread_key_create failed: %d %s\", ret, msg);\n    }\n  }\n\n  static void OnThreadExit(void *obj) {\n    if (NULL != obj) {\n      delete (T*)obj;\n    }\n  }\n\n  pthread_key_t m_key;\n  CleanupFunction m_cleanup;\n};\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/assert.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#ifndef FBASSERT_H\n#define FBASSERT_H\n\n#include <fb/visibility.h>\n\nnamespace facebook {\n#define ENABLE_FBASSERT 1\n\n#if ENABLE_FBASSERT\n#define FBASSERTMSGF(expr, msg, ...) !(expr) ? facebook::assertInternal(\"Assert (%s:%d): \" msg, __FILE__, __LINE__, ##__VA_ARGS__) : (void) 0\n#else\n#define FBASSERTMSGF(expr, msg, ...)\n#endif // ENABLE_FBASSERT\n\n#define FBASSERT(expr) FBASSERTMSGF(expr, \"%s\", #expr)\n\n#define FBCRASH(msg, ...) facebook::assertInternal(\"Fatal error (%s:%d): \" msg, __FILE__, __LINE__, ##__VA_ARGS__)\n#define FBUNREACHABLE() facebook::assertInternal(\"This code should be unreachable (%s:%d)\", __FILE__, __LINE__)\n\nFBEXPORT void assertInternal(const char* formatstr, ...) __attribute__((noreturn));\n\n// This allows storing the assert message before the current process terminates due to a crash\ntypedef void (*AssertHandler)(const char* message);\nvoid setAssertHandler(AssertHandler assertHandler);\n\n} // namespace facebook\n#endif // FBASSERT_H\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/Boxed.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include \"CoreClasses.h\"\n\nnamespace facebook {\nnamespace jni {\n\nnamespace detail {\ntemplate <typename T, typename jprim>\nstruct JPrimitive : JavaClass<T> {\n  using typename JavaClass<T>::javaobject;\n  using JavaClass<T>::javaClassStatic;\n  static local_ref<javaobject> valueOf(jprim val) {\n    static auto cls = javaClassStatic();\n    static auto method =\n      cls->template getStaticMethod<javaobject(jprim)>(\"valueOf\");\n    return method(cls, val);\n  }\n  jprim value() const {\n    static auto method =\n      javaClassStatic()->template getMethod<jprim()>(T::kValueMethod);\n    return method(this->self());\n  }\n};\n\n} // namespace detail\n\n\n#define DEFINE_BOXED_PRIMITIVE(LITTLE, BIG)                          \\\n  struct J ## BIG : detail::JPrimitive<J ## BIG, j ## LITTLE> {      \\\n    static auto constexpr kJavaDescriptor = \"Ljava/lang/\" #BIG \";\";  \\\n    static auto constexpr kValueMethod = #LITTLE \"Value\";            \\\n    j ## LITTLE LITTLE ## Value() const {                            \\\n      return value();                                                \\\n    }                                                                \\\n  };                                                                 \\\n  inline local_ref<jobject> autobox(j ## LITTLE val) {               \\\n    return J ## BIG::valueOf(val);                                   \\\n  }\n\nDEFINE_BOXED_PRIMITIVE(boolean, Boolean)\nDEFINE_BOXED_PRIMITIVE(byte, Byte)\nDEFINE_BOXED_PRIMITIVE(char, Character)\nDEFINE_BOXED_PRIMITIVE(short, Short)\nDEFINE_BOXED_PRIMITIVE(int, Integer)\nDEFINE_BOXED_PRIMITIVE(long, Long)\nDEFINE_BOXED_PRIMITIVE(float, Float)\nDEFINE_BOXED_PRIMITIVE(double, Double)\n\n#undef DEFINE_BOXED_PRIMITIVE\n\nstruct JVoid : public jni::JavaClass<JVoid> {\n  static auto constexpr kJavaDescriptor = \"Ljava/lang/Void;\";\n};\n\ninline local_ref<jobject> autobox(alias_ref<jobject> val) {\n  return make_local(val);\n}\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/ByteBuffer.h",
    "content": "/*\n * Copyright (c) 2016-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <fb/visibility.h>\n\n#include \"CoreClasses.h\"\n#include \"References-forward.h\"\n\nnamespace facebook {\nnamespace jni {\n\n// JNI's NIO support has some awkward preconditions and error reporting. This\n// class provides much more user-friendly access.\nclass FBEXPORT JByteBuffer : public JavaClass<JByteBuffer> {\n public:\n  static constexpr const char* kJavaDescriptor = \"Ljava/nio/ByteBuffer;\";\n\n  static local_ref<JByteBuffer> wrapBytes(uint8_t* data, size_t size);\n\n  bool isDirect() const;\n\n  uint8_t* getDirectBytes() const;\n  size_t getDirectSize() const;\n};\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/Common.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n/** @file Common.h\n *\n * Defining the stuff that don't deserve headers of their own...\n */\n\n#pragma once\n\n#include <functional>\n\n#include <jni.h>\n\n#include <fb/visibility.h>\n#include <fb/Environment.h>\n\n#ifdef FBJNI_DEBUG_REFS\n# ifdef __ANDROID__\n#  include <android/log.h>\n# else\n#  include <cstdio>\n# endif\n#endif\n\n// If a pending JNI Java exception is found, wraps it in a JniException object and throws it as\n// a C++ exception.\n#define FACEBOOK_JNI_THROW_PENDING_EXCEPTION() \\\n  ::facebook::jni::throwPendingJniExceptionAsCppException()\n\n// If the condition is true, throws a JniException object, which wraps the pending JNI Java\n// exception if any. If no pending exception is found, throws a JniException object that wraps a\n// RuntimeException throwable. \n#define FACEBOOK_JNI_THROW_EXCEPTION_IF(CONDITION) \\\n  ::facebook::jni::throwCppExceptionIf(CONDITION)\n\n/// @cond INTERNAL\n\nnamespace facebook {\nnamespace jni {\n\nFBEXPORT void throwPendingJniExceptionAsCppException();\nFBEXPORT void throwCppExceptionIf(bool condition);\n\n[[noreturn]] FBEXPORT void throwNewJavaException(jthrowable);\n[[noreturn]] FBEXPORT void throwNewJavaException(const char* throwableName, const char* msg);\ntemplate<typename... Args>\n[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args);\n\n\n/**\n * This needs to be called at library load time, typically in your JNI_OnLoad method.\n *\n * The intended use is to return the result of initialize() directly\n * from JNI_OnLoad and to do nothing else there. Library specific\n * initialization code should go in the function passed to initialize\n * (which can be, and probably should be, a C++ lambda). This approach\n * provides correct error handling and translation errors during\n * initialization into Java exceptions when appropriate.\n *\n * Failure to call this will cause your code to crash in a remarkably\n * unhelpful way (typically a segfault) while trying to handle an exception\n * which occurs later.\n */\nFBEXPORT jint initialize(JavaVM*, std::function<void()>&&) noexcept;\n\nnamespace internal {\n\n/**\n * Retrieve a pointer the JNI environment of the current thread.\n *\n * @pre The current thread must be attached to the VM\n */\ninline JNIEnv* getEnv() noexcept {\n  // TODO(T6594868) Benchmark against raw JNI access\n  return Environment::current();\n}\n\n// Define to get extremely verbose logging of references and to enable reference stats\n#ifdef FBJNI_DEBUG_REFS\ntemplate<typename... Args>\ninline void dbglog(const char* msg, Args... args) {\n# ifdef __ANDROID__\n  __android_log_print(ANDROID_LOG_VERBOSE, \"fbjni_dbg\", msg, args...);\n# else\n  std::fprintf(stderr, msg, args...);\n# endif\n}\n\n#else\n\ntemplate<typename... Args>\ninline void dbglog(const char*, Args...) {\n}\n\n#endif\n\n}}}\n\n/// @endcond\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/Context.h",
    "content": "/*\n * Copyright (c) 2016-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include \"CoreClasses.h\"\n#include \"File.h\"\n\nnamespace facebook {\nnamespace jni {\n\nclass AContext : public JavaClass<AContext> {\n public:\n  static constexpr const char* kJavaDescriptor = \"Landroid/content/Context;\";\n\n  // Define a method that calls into the represented Java class\n  local_ref<JFile::javaobject> getCacheDir() {\n    static auto method = getClass()->getMethod<JFile::javaobject()>(\"getCacheDir\");\n    return method(self());\n  }\n\n  local_ref<JFile::javaobject> getFilesDir() {\n    static auto method = getClass()->getMethod<JFile::javaobject()>(\"getFilesDir\");\n    return method(self());\n  }\n};\n\n}\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/CoreClasses-inl.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <string.h>\n#include <type_traits>\n#include <stdlib.h>\n\n#include \"Common.h\"\n#include \"Exceptions.h\"\n#include \"Meta.h\"\n#include \"MetaConvert.h\"\n\nnamespace facebook {\nnamespace jni {\n\n// jobject /////////////////////////////////////////////////////////////////////////////////////////\n\ninline bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept {\n  return internal::getEnv()->IsSameObject(lhs.get(), rhs.get()) != JNI_FALSE;\n}\n\ninline local_ref<JClass> JObject::getClass() const noexcept {\n  return adopt_local(internal::getEnv()->GetObjectClass(self()));\n}\n\ninline bool JObject::isInstanceOf(alias_ref<JClass> cls) const noexcept {\n  return internal::getEnv()->IsInstanceOf(self(), cls.get()) != JNI_FALSE;\n}\n\ntemplate<typename T>\ninline T JObject::getFieldValue(JField<T> field) const noexcept {\n  return field.get(self());\n}\n\ntemplate<typename T>\ninline local_ref<T*> JObject::getFieldValue(JField<T*> field) const noexcept {\n  return adopt_local(field.get(self()));\n}\n\ntemplate<typename T>\ninline void JObject::setFieldValue(JField<T> field, T value) noexcept {\n  field.set(self(), value);\n}\n\ninline std::string JObject::toString() const {\n  static auto method = findClassLocal(\"java/lang/Object\")->getMethod<jstring()>(\"toString\");\n\n  return method(self())->toStdString();\n}\n\n\n// Class is here instead of CoreClasses.h because we need\n// alias_ref to be complete.\nclass MonitorLock {\n public:\n  inline MonitorLock() noexcept;\n  inline MonitorLock(alias_ref<JObject> object) noexcept;\n  inline ~MonitorLock() noexcept;\n\n  inline MonitorLock(MonitorLock&& other) noexcept;\n  inline MonitorLock& operator=(MonitorLock&& other) noexcept;\n\n  inline MonitorLock(const MonitorLock&) = delete;\n  inline MonitorLock& operator=(const MonitorLock&) = delete;\n\n private:\n  inline void reset() noexcept;\n  alias_ref<JObject> owned_;\n};\n\nMonitorLock::MonitorLock() noexcept : owned_(nullptr) {}\n\nMonitorLock::MonitorLock(alias_ref<JObject> object) noexcept\n    : owned_(object) {\n  internal::getEnv()->MonitorEnter(object.get());\n}\n\nvoid MonitorLock::reset() noexcept {\n  if (owned_) {\n    internal::getEnv()->MonitorExit(owned_.get());\n    if (internal::getEnv()->ExceptionCheck()) {\n      abort(); // Lock mismatch\n    }\n    owned_ = nullptr;\n  }\n}\n\nMonitorLock::~MonitorLock() noexcept {\n  reset();\n}\n\nMonitorLock::MonitorLock(MonitorLock&& other) noexcept\n    : owned_(other.owned_)\n{\n  other.owned_ = nullptr;\n}\n\nMonitorLock& MonitorLock::operator=(MonitorLock&& other) noexcept {\n  reset();\n  owned_ = other.owned_;\n  other.owned_ = nullptr;\n  return *this;\n}\n\ninline MonitorLock JObject::lock() const noexcept {\n  return MonitorLock(this_);\n}\n\ninline jobject JObject::self() const noexcept {\n  return this_;\n}\n\ninline void swap(JObject& a, JObject& b) noexcept {\n  using std::swap;\n  swap(a.this_, b.this_);\n}\n\n// JavaClass ///////////////////////////////////////////////////////////////////////////////////////\n\nnamespace detail {\ntemplate<typename JC, typename... Args>\nstatic local_ref<JC> newInstance(Args... args) {\n  static auto cls = JC::javaClassStatic();\n  static auto constructor = cls->template getConstructor<typename JC::javaobject(Args...)>();\n  return cls->newObject(constructor, args...);\n}\n}\n\n\ntemplate <typename T, typename B, typename J>\nauto JavaClass<T, B, J>::self() const noexcept -> javaobject {\n  return static_cast<javaobject>(JObject::self());\n}\n\n// jclass //////////////////////////////////////////////////////////////////////////////////////////\n\nnamespace detail {\n\n// This is not a real type.  It is used so people won't accidentally\n// use a void* to initialize a NativeMethod.\nstruct NativeMethodWrapper;\n\n}\n\nstruct NativeMethod {\n  const char* name;\n  std::string descriptor;\n  detail::NativeMethodWrapper* wrapper;\n};\n\ninline local_ref<JClass> JClass::getSuperclass() const noexcept {\n  return adopt_local(internal::getEnv()->GetSuperclass(self()));\n}\n\ninline void JClass::registerNatives(std::initializer_list<NativeMethod> methods) {\n  const auto env = internal::getEnv();\n\n  JNINativeMethod jnimethods[methods.size()];\n  size_t i = 0;\n  for (auto it = methods.begin(); it < methods.end(); ++it, ++i) {\n    jnimethods[i].name = it->name;\n    jnimethods[i].signature = it->descriptor.c_str();\n    jnimethods[i].fnPtr = reinterpret_cast<void*>(it->wrapper);\n  }\n\n  auto result = env->RegisterNatives(self(), jnimethods, methods.size());\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(result != JNI_OK);\n}\n\ninline bool JClass::isAssignableFrom(alias_ref<JClass> other) const noexcept {\n  const auto env = internal::getEnv();\n  // Ths method has behavior compatible with the\n  // java.lang.Class#isAssignableFrom method.  The order of the\n  // arguments to the JNI IsAssignableFrom C function is \"opposite\"\n  // from what some might expect, which makes this code look a little\n  // odd, but it is correct.\n  const auto result = env->IsAssignableFrom(other.get(), self());\n  return result;\n}\n\ntemplate<typename F>\ninline JConstructor<F> JClass::getConstructor() const {\n  return getConstructor<F>(jmethod_traits_from_cxx<F>::constructor_descriptor().c_str());\n}\n\ntemplate<typename F>\ninline JConstructor<F> JClass::getConstructor(const char* descriptor) const {\n  constexpr auto constructor_method_name = \"<init>\";\n  return getMethod<F>(constructor_method_name, descriptor);\n}\n\ntemplate<typename F>\ninline JMethod<F> JClass::getMethod(const char* name) const {\n  return getMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());\n}\n\ntemplate<typename F>\ninline JMethod<F> JClass::getMethod(\n    const char* name,\n    const char* descriptor) const {\n  const auto env = internal::getEnv();\n  const auto method = env->GetMethodID(self(), name, descriptor);\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);\n  return JMethod<F>{method};\n}\n\ntemplate<typename F>\ninline JStaticMethod<F> JClass::getStaticMethod(const char* name) const {\n  return getStaticMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());\n}\n\ntemplate<typename F>\ninline JStaticMethod<F> JClass::getStaticMethod(\n    const char* name,\n    const char* descriptor) const {\n  const auto env = internal::getEnv();\n  const auto method = env->GetStaticMethodID(self(), name, descriptor);\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);\n  return JStaticMethod<F>{method};\n}\n\ntemplate<typename F>\ninline JNonvirtualMethod<F> JClass::getNonvirtualMethod(const char* name) const {\n  return getNonvirtualMethod<F>(name, jmethod_traits_from_cxx<F>::descriptor().c_str());\n}\n\ntemplate<typename F>\ninline JNonvirtualMethod<F> JClass::getNonvirtualMethod(\n    const char* name,\n    const char* descriptor) const {\n  const auto env = internal::getEnv();\n  const auto method = env->GetMethodID(self(), name, descriptor);\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(!method);\n  return JNonvirtualMethod<F>{method};\n}\n\ntemplate<typename T>\ninline JField<enable_if_t<IsJniScalar<T>(), T>>\nJClass::getField(const char* name) const {\n  return getField<T>(name, jtype_traits<T>::descriptor().c_str());\n}\n\ntemplate<typename T>\ninline JField<enable_if_t<IsJniScalar<T>(), T>> JClass::getField(\n    const char* name,\n    const char* descriptor) const {\n  const auto env = internal::getEnv();\n  auto field = env->GetFieldID(self(), name, descriptor);\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);\n  return JField<T>{field};\n}\n\ntemplate<typename T>\ninline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(\n    const char* name) const {\n  return getStaticField<T>(name, jtype_traits<T>::descriptor().c_str());\n}\n\ntemplate<typename T>\ninline JStaticField<enable_if_t<IsJniScalar<T>(), T>> JClass::getStaticField(\n    const char* name,\n    const char* descriptor) const {\n  const auto env = internal::getEnv();\n  auto field = env->GetStaticFieldID(self(), name, descriptor);\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(!field);\n  return JStaticField<T>{field};\n}\n\ntemplate<typename T>\ninline T JClass::getStaticFieldValue(JStaticField<T> field) const noexcept {\n  return field.get(self());\n}\n\ntemplate<typename T>\ninline local_ref<T*> JClass::getStaticFieldValue(JStaticField<T*> field) noexcept {\n  return adopt_local(field.get(self()));\n}\n\ntemplate<typename T>\ninline void JClass::setStaticFieldValue(JStaticField<T> field, T value) noexcept {\n  field.set(self(), value);\n}\n\ntemplate<typename R, typename... Args>\ninline local_ref<R> JClass::newObject(\n    JConstructor<R(Args...)> constructor,\n    Args... args) const {\n  const auto env = internal::getEnv();\n  auto object = env->NewObject(self(), constructor.getId(),\n      detail::callToJni(\n        detail::Convert<typename std::decay<Args>::type>::toCall(args))...);\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(!object);\n  return adopt_local(static_cast<R>(object));\n}\n\ninline jclass JClass::self() const noexcept {\n  return static_cast<jclass>(JObject::self());\n}\n\ninline void registerNatives(const char* name, std::initializer_list<NativeMethod> methods) {\n  findClassLocal(name)->registerNatives(methods);\n}\n\n\n// jstring /////////////////////////////////////////////////////////////////////////////////////////\n\ninline local_ref<JString> make_jstring(const std::string& modifiedUtf8) {\n  return make_jstring(modifiedUtf8.c_str());\n}\n\nnamespace detail {\n// convert to std::string from jstring\ntemplate <>\nstruct Convert<std::string> {\n  typedef jstring jniType;\n  static std::string fromJni(jniType t) {\n    return wrap_alias(t)->toStdString();\n  }\n  static jniType toJniRet(const std::string& t) {\n    return make_jstring(t).release();\n  }\n  static local_ref<JString> toCall(const std::string& t) {\n    return make_jstring(t);\n  }\n};\n\n// convert return from const char*\ntemplate <>\nstruct Convert<const char*> {\n  typedef jstring jniType;\n  // no automatic synthesis of const char*.  (It can't be freed.)\n  static jniType toJniRet(const char* t) {\n    return make_jstring(t).release();\n  }\n  static local_ref<JString> toCall(const char* t) {\n    return make_jstring(t);\n  }\n};\n}\n\n// jtypeArray //////////////////////////////////////////////////////////////////////////////////////\n\nnamespace detail {\ninline size_t JArray::size() const noexcept {\n  const auto env = internal::getEnv();\n  return env->GetArrayLength(self());\n}\n}\n\nnamespace detail {\ntemplate<typename Target>\ninline ElementProxy<Target>::ElementProxy(\n    Target* target,\n    size_t idx)\n    : target_{target}, idx_{idx} {}\n\ntemplate<typename Target>\ninline ElementProxy<Target>& ElementProxy<Target>::operator=(const T& o) {\n  target_->setElement(idx_, o);\n  return *this;\n}\n\ntemplate<typename Target>\ninline ElementProxy<Target>& ElementProxy<Target>::operator=(alias_ref<T>& o) {\n  target_->setElement(idx_, o.get());\n  return *this;\n}\n\ntemplate<typename Target>\ninline ElementProxy<Target>& ElementProxy<Target>::operator=(alias_ref<T>&& o) {\n  target_->setElement(idx_, o.get());\n  return *this;\n}\n\ntemplate<typename Target>\ninline ElementProxy<Target>& ElementProxy<Target>::operator=(const ElementProxy<Target>& o) {\n  auto src = o.target_->getElement(o.idx_);\n  target_->setElement(idx_, src.get());\n  return *this;\n}\n\ntemplate<typename Target>\ninline ElementProxy<Target>::ElementProxy::operator const local_ref<T> () const {\n  return target_->getElement(idx_);\n}\n\ntemplate<typename Target>\ninline ElementProxy<Target>::ElementProxy::operator local_ref<T> () {\n  return target_->getElement(idx_);\n}\n}\n\ntemplate <typename T>\nstd::string JArrayClass<T>::get_instantiated_java_descriptor() {\n  return \"[\" + jtype_traits<T>::descriptor();\n};\n\ntemplate <typename T>\nstd::string JArrayClass<T>::get_instantiated_base_name() {\n  return get_instantiated_java_descriptor();\n};\n\ntemplate<typename T>\nauto JArrayClass<T>::newArray(size_t size) -> local_ref<javaobject> {\n  static auto elementClass = findClassStatic(jtype_traits<T>::base_name().c_str());\n  const auto env = internal::getEnv();\n  auto rawArray = env->NewObjectArray(size, elementClass.get(), nullptr);\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(!rawArray);\n  return adopt_local(static_cast<javaobject>(rawArray));\n}\n\ntemplate<typename T>\ninline void JArrayClass<T>::setElement(size_t idx, const T& value) {\n  const auto env = internal::getEnv();\n  env->SetObjectArrayElement(this->self(), idx, value);\n}\n\ntemplate<typename T>\ninline local_ref<T> JArrayClass<T>::getElement(size_t idx) {\n  const auto env = internal::getEnv();\n  auto rawElement = env->GetObjectArrayElement(this->self(), idx);\n  return adopt_local(static_cast<T>(rawElement));\n}\n\ntemplate<typename T>\ninline detail::ElementProxy<JArrayClass<T>> JArrayClass<T>::operator[](size_t index) {\n  return detail::ElementProxy<JArrayClass<T>>(this, index);\n}\n\n// jarray /////////////////////////////////////////////////////////////////////////////////////////\n\ntemplate <typename JArrayType>\nauto JPrimitiveArray<JArrayType>::getRegion(jsize start, jsize length)\n    -> std::unique_ptr<T[]> {\n  using T = typename jtype_traits<JArrayType>::entry_type;\n  auto buf = std::unique_ptr<T[]>{new T[length]};\n  getRegion(start, length, buf.get());\n  return buf;\n}\n\ntemplate <typename JArrayType>\nstd::string JPrimitiveArray<JArrayType>::get_instantiated_java_descriptor() {\n  return jtype_traits<JArrayType>::descriptor();\n}\ntemplate <typename JArrayType>\nstd::string JPrimitiveArray<JArrayType>::get_instantiated_base_name() {\n  return JPrimitiveArray::get_instantiated_java_descriptor();\n}\n\ntemplate <typename JArrayType>\nauto JPrimitiveArray<JArrayType>::pin() -> PinnedPrimitiveArray<T, PinnedArrayAlloc<T>> {\n  return PinnedPrimitiveArray<T, PinnedArrayAlloc<T>>{this->self(), 0, 0};\n}\n\ntemplate <typename JArrayType>\nauto JPrimitiveArray<JArrayType>::pinRegion(jsize start, jsize length)\n    -> PinnedPrimitiveArray<T, PinnedRegionAlloc<T>> {\n  return PinnedPrimitiveArray<T, PinnedRegionAlloc<T>>{this->self(), start, length};\n}\n\ntemplate <typename JArrayType>\nauto JPrimitiveArray<JArrayType>::pinCritical()\n    -> PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>> {\n  return PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>>{this->self(), 0, 0};\n}\n\ntemplate <typename T>\nclass PinnedArrayAlloc {\n public:\n  static void allocate(\n      alias_ref<typename jtype_traits<T>::array_type> array,\n      jsize start,\n      jsize length,\n      T** elements,\n      size_t* size,\n      jboolean* isCopy) {\n    (void) start;\n    (void) length;\n    *elements = array->getElements(isCopy);\n    *size = array->size();\n  }\n  static void release(\n      alias_ref<typename jtype_traits<T>::array_type> array,\n      T* elements,\n      jint start,\n      jint size,\n      jint mode) {\n    (void) start;\n    (void) size;\n    array->releaseElements(elements, mode);\n  }\n};\n\ntemplate <typename T>\nclass PinnedCriticalAlloc {\n public:\n  static void allocate(\n      alias_ref<typename jtype_traits<T>::array_type> array,\n      jsize start,\n      jsize length,\n      T** elements,\n      size_t* size,\n      jboolean* isCopy) {\n    const auto env = internal::getEnv();\n    *elements = static_cast<T*>(env->GetPrimitiveArrayCritical(array.get(), isCopy));\n    FACEBOOK_JNI_THROW_EXCEPTION_IF(!elements);\n    *size = array->size();\n  }\n  static void release(\n      alias_ref<typename jtype_traits<T>::array_type> array,\n      T* elements,\n      jint start,\n      jint size,\n      jint mode) {\n    const auto env = internal::getEnv();\n    env->ReleasePrimitiveArrayCritical(array.get(), elements, mode);\n  }\n};\n\ntemplate <typename T>\nclass PinnedRegionAlloc {\n public:\n  static void allocate(\n      alias_ref<typename jtype_traits<T>::array_type> array,\n      jsize start,\n      jsize length,\n      T** elements,\n      size_t* size,\n      jboolean* isCopy) {\n    auto buf = array->getRegion(start, length);\n    FACEBOOK_JNI_THROW_EXCEPTION_IF(!buf);\n    *elements = buf.release();\n    *size = length;\n    *isCopy = true;\n  }\n  static void release(\n      alias_ref<typename jtype_traits<T>::array_type> array,\n      T* elements,\n      jint start,\n      jint size,\n      jint mode) {\n    std::unique_ptr<T[]> holder;\n    if (mode == 0 || mode == JNI_ABORT) {\n      holder.reset(elements);\n    }\n    if (mode == 0 || mode == JNI_COMMIT) {\n      array->setRegion(start, size, elements);\n    }\n  }\n};\n\n// PinnedPrimitiveArray ///////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T, typename Alloc>\nPinnedPrimitiveArray<T, Alloc>::PinnedPrimitiveArray(PinnedPrimitiveArray&& o) {\n  *this = std::move(o);\n}\n\ntemplate<typename T, typename Alloc>\nPinnedPrimitiveArray<T, Alloc>&\nPinnedPrimitiveArray<T, Alloc>::operator=(PinnedPrimitiveArray&& o) {\n  if (array_) {\n    release();\n  }\n  array_ = std::move(o.array_);\n  elements_ = o.elements_;\n  isCopy_ = o.isCopy_;\n  size_ = o.size_;\n  start_ = o.start_;\n  o.clear();\n  return *this;\n}\n\ntemplate<typename T, typename Alloc>\nT* PinnedPrimitiveArray<T, Alloc>::get() {\n  return elements_;\n}\n\ntemplate<typename T, typename Alloc>\ninline void PinnedPrimitiveArray<T, Alloc>::release() {\n  releaseImpl(0);\n  clear();\n}\n\ntemplate<typename T, typename Alloc>\ninline void PinnedPrimitiveArray<T, Alloc>::commit() {\n  releaseImpl(JNI_COMMIT);\n}\n\ntemplate<typename T, typename Alloc>\ninline void PinnedPrimitiveArray<T, Alloc>::abort() {\n  releaseImpl(JNI_ABORT);\n  clear();\n}\n\ntemplate <typename T, typename Alloc>\ninline void PinnedPrimitiveArray<T, Alloc>::releaseImpl(jint mode) {\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(array_.get() == nullptr);\n  Alloc::release(array_, elements_, start_, size_, mode);\n}\n\ntemplate<typename T, typename Alloc>\ninline void PinnedPrimitiveArray<T, Alloc>::clear() noexcept {\n  array_ = nullptr;\n  elements_ = nullptr;\n  isCopy_ = false;\n  start_ = 0;\n  size_ = 0;\n}\n\ntemplate<typename T, typename Alloc>\ninline T& PinnedPrimitiveArray<T, Alloc>::operator[](size_t index) {\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(elements_ == nullptr);\n  return elements_[index];\n}\n\ntemplate<typename T, typename Alloc>\ninline bool PinnedPrimitiveArray<T, Alloc>::isCopy() const noexcept {\n  return isCopy_ == JNI_TRUE;\n}\n\ntemplate<typename T, typename Alloc>\ninline size_t PinnedPrimitiveArray<T, Alloc>::size() const noexcept {\n  return size_;\n}\n\ntemplate<typename T, typename Alloc>\ninline PinnedPrimitiveArray<T, Alloc>::~PinnedPrimitiveArray() noexcept {\n  if (elements_) {\n    release();\n  }\n}\n\ntemplate<typename T, typename Alloc>\ninline PinnedPrimitiveArray<T, Alloc>::PinnedPrimitiveArray(alias_ref<typename jtype_traits<T>::array_type> array, jint start, jint length) {\n  array_ = array;\n  start_ = start;\n  Alloc::allocate(array, start, length, &elements_, &size_, &isCopy_);\n}\n\ntemplate<typename T, typename Base, typename JType>\ninline alias_ref<JClass> JavaClass<T, Base, JType>::javaClassStatic() {\n  static auto cls = findClassStatic(jtype_traits<typename T::javaobject>::base_name().c_str());\n  return cls;\n}\n\ntemplate<typename T, typename Base, typename JType>\ninline local_ref<JClass> JavaClass<T, Base, JType>::javaClassLocal() {\n  std::string className(jtype_traits<typename T::javaobject>::base_name().c_str());\n  return findClassLocal(className.c_str());\n}\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/CoreClasses.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n/** @file CoreClasses.h\n *\n * In CoreClasses.h wrappers for the core classes (jobject, jclass, and jstring) is defined\n * to provide access to corresponding JNI functions + some conveniance.\n */\n\n#include \"References-forward.h\"\n#include \"Meta-forward.h\"\n#include \"TypeTraits.h\"\n\n#include <memory>\n\n#include <jni.h>\n\n#include <fb/visibility.h>\n\nnamespace facebook {\nnamespace jni {\n\nclass JClass;\nclass JObject;\n\n/// Lookup a class by name. Note this functions returns an alias_ref that\n/// points to a leaked global reference.  This is appropriate for classes\n/// that are never unloaded (which is any class in an Android app and most\n/// Java programs).\n///\n/// The most common use case for this is storing the result\n/// in a \"static auto\" variable, or a static global.\n///\n/// @return Returns a leaked global reference to the class\nFBEXPORT alias_ref<JClass> findClassStatic(const char* name);\n\n/// Lookup a class by name. Note this functions returns a local reference,\n/// which means that it must not be stored in a static variable.\n///\n/// The most common use case for this is one-time initialization\n/// (like caching method ids).\n///\n/// @return Returns a global reference to the class\nFBEXPORT local_ref<JClass> findClassLocal(const char* name);\n\n/// Check to see if two references refer to the same object. Comparison with nullptr\n/// returns true if and only if compared to another nullptr. A weak reference that\n/// refers to a reclaimed object count as nullptr.\nFBEXPORT bool isSameObject(alias_ref<JObject> lhs, alias_ref<JObject> rhs) noexcept;\n\n// Together, these classes allow convenient use of any class with the fbjni\n// helpers.  To use:\n//\n// struct MyClass : public JavaClass<MyClass> {\n//   constexpr static auto kJavaDescriptor = \"Lcom/example/package/MyClass;\";\n// };\n//\n// Then, an alias_ref<MyClass::javaobject> will be backed by an instance of\n// MyClass. JavaClass provides a convenient way to add functionality to these\n// smart references.\n//\n// For example:\n//\n// struct MyClass : public JavaClass<MyClass> {\n//   constexpr static auto kJavaDescriptor = \"Lcom/example/package/MyClass;\";\n//\n//   void foo() {\n//     static auto method = javaClassStatic()->getMethod<void()>(\"foo\");\n//     method(self());\n//   }\n//\n//   static local_ref<javaobject> create(int i) {\n//     return newInstance(i);\n//   }\n// };\n//\n// auto obj = MyClass::create(10);\n// obj->foo();\n//\n// While users of a JavaClass-type can lookup methods and fields through the\n// underlying JClass, those calls can only be checked at runtime. It is recommended\n// that the JavaClass-type instead explicitly expose it's methods as in the example\n// above.\n\nnamespace detail {\ntemplate<typename JC, typename... Args>\nstatic local_ref<JC> newInstance(Args... args);\n}\n\nclass MonitorLock;\n\nclass FBEXPORT JObject : detail::JObjectBase {\npublic:\n  static constexpr auto kJavaDescriptor = \"Ljava/lang/Object;\";\n\n  static constexpr const char* get_instantiated_java_descriptor() { return nullptr; }\n  static constexpr const char* get_instantiated_base_name() { return nullptr; }\n\n  /// Get a @ref local_ref of the object's class\n  local_ref<JClass> getClass() const noexcept;\n\n  /// Checks if the object is an instance of a class\n  bool isInstanceOf(alias_ref<JClass> cls) const noexcept;\n\n  /// Get the primitive value of a field\n  template<typename T>\n  T getFieldValue(JField<T> field) const noexcept;\n\n  /// Get and wrap the value of a field in a @ref local_ref\n  template<typename T>\n  local_ref<T*> getFieldValue(JField<T*> field) const noexcept;\n\n  /// Set the value of field. Any Java type is accepted, including the primitive types\n  /// and raw reference types.\n  template<typename T>\n  void setFieldValue(JField<T> field, T value) noexcept;\n\n  /// Convenience method to create a std::string representing the object\n  std::string toString() const;\n\n  // Take this object's monitor lock\n  MonitorLock lock() const noexcept;\n\n  typedef _jobject _javaobject;\n  typedef _javaobject* javaobject;\n\nprotected:\n  jobject self() const noexcept;\nprivate:\n  friend void swap(JObject& a, JObject& b) noexcept;\n  template<typename>\n  friend struct detail::ReprAccess;\n  template<typename, typename, typename>\n  friend class JavaClass;\n\n  template <typename, typename>\n  friend class JObjectWrapper;\n};\n\n// This is only to maintain backwards compatibility with things that are\n// already providing a specialization of JObjectWrapper. Any such instances\n// should be updated to use a JavaClass.\ntemplate<>\nclass JObjectWrapper<jobject> : public JObject {\n};\n\n\nnamespace detail {\ntemplate <typename, typename Base, typename JType>\nstruct JTypeFor {\n  static_assert(\n      std::is_base_of<\n        std::remove_pointer<jobject>::type,\n        typename std::remove_pointer<JType>::type\n      >::value, \"\");\n  using _javaobject = typename std::remove_pointer<JType>::type;\n  using javaobject = JType;\n};\n\ntemplate <typename T, typename Base>\nstruct JTypeFor<T, Base, void> {\n  // JNI pattern for jobject assignable pointer\n  struct _javaobject :  Base::_javaobject {\n    // This allows us to map back to the defining type (in ReprType, for\n    // example).\n    typedef T JniRefRepr;\n  };\n  using javaobject = _javaobject*;\n};\n}\n\n// JavaClass provides a method to inform fbjni about user-defined Java types.\n// Given a class:\n// struct Foo : JavaClass<Foo> {\n//   static constexpr auto kJavaDescriptor = \"Lcom/example/package/Foo;\";\n// };\n// fbjni can determine the java type/method signatures for Foo::javaobject and\n// smart refs (like alias_ref<Foo::javaobject>) will hold an instance of Foo\n// and provide access to it through the -> and * operators.\n//\n// The \"Base\" template argument can be used to specify the JavaClass superclass\n// of this type (for instance, JString's Base is JObject).\n//\n// The \"JType\" template argument is used to provide a jni type (like jstring,\n// jthrowable) to be used as javaobject. This should only be necessary for\n// built-in jni types and not user-defined ones.\ntemplate <typename T, typename Base = JObject, typename JType = void>\nclass FBEXPORT JavaClass : public Base {\n  using JObjType = typename detail::JTypeFor<T, Base, JType>;\npublic:\n  using _javaobject = typename JObjType::_javaobject;\n  using javaobject = typename JObjType::javaobject;\n\n  using JavaBase = JavaClass;\n\n  static alias_ref<JClass> javaClassStatic();\n  static local_ref<JClass> javaClassLocal();\nprotected:\n  /// Allocates a new object and invokes the specified constructor\n  /// Like JClass's getConstructor, this function can only check at runtime if\n  /// the class actually has a constructor that accepts the corresponding types.\n  /// While a JavaClass-type can expose this function directly, it is recommended\n  /// to instead to use this to explicitly only expose those constructors that\n  /// the Java class actually has (i.e. with static create() functions).\n  template<typename... Args>\n  static local_ref<T> newInstance(Args... args) {\n    return detail::newInstance<T>(args...);\n  }\n\n  javaobject self() const noexcept;\n};\n\n/// Wrapper to provide functionality to jclass references\nstruct NativeMethod;\n\nclass FBEXPORT JClass : public JavaClass<JClass, JObject, jclass> {\n public:\n  /// Java type descriptor\n  static constexpr const char* kJavaDescriptor = \"Ljava/lang/Class;\";\n\n  /// Get a @local_ref to the super class of this class\n  local_ref<JClass> getSuperclass() const noexcept;\n\n  /// Register native methods for the class.  Usage looks like this:\n  ///\n  /// classRef->registerNatives({\n  ///     makeNativeMethod(\"nativeMethodWithAutomaticDescriptor\",\n  ///                      methodWithAutomaticDescriptor),\n  ///     makeNativeMethod(\"nativeMethodWithExplicitDescriptor\",\n  ///                      \"(Lcom/facebook/example/MyClass;)V\",\n  ///                      methodWithExplicitDescriptor),\n  ///  });\n  ///\n  /// By default, C++ exceptions raised will be converted to Java exceptions.\n  /// To avoid this and get the \"standard\" JNI behavior of a crash when a C++\n  /// exception is crashing out of the JNI method, declare the method noexcept.\n  void registerNatives(std::initializer_list<NativeMethod> methods);\n\n  /// Check to see if the class is assignable from another class\n  /// @pre cls != nullptr\n  bool isAssignableFrom(alias_ref<JClass> cls) const noexcept;\n\n  /// Convenience method to lookup the constructor with descriptor as specified by the\n  /// type arguments\n  template<typename F>\n  JConstructor<F> getConstructor() const;\n\n  /// Convenience method to lookup the constructor with specified descriptor\n  template<typename F>\n  JConstructor<F> getConstructor(const char* descriptor) const;\n\n  /// Look up the method with given name and descriptor as specified with the type arguments\n  template<typename F>\n  JMethod<F> getMethod(const char* name) const;\n\n  /// Look up the method with given name and descriptor\n  template<typename F>\n  JMethod<F> getMethod(const char* name, const char* descriptor) const;\n\n  /// Lookup the field with the given name and deduced descriptor\n  template<typename T>\n  JField<enable_if_t<IsJniScalar<T>(), T>> getField(const char* name) const;\n\n  /// Lookup the field with the given name and descriptor\n  template<typename T>\n  JField<enable_if_t<IsJniScalar<T>(), T>> getField(const char* name, const char* descriptor) const;\n\n  /// Lookup the static field with the given name and deduced descriptor\n  template<typename T>\n  JStaticField<enable_if_t<IsJniScalar<T>(), T>> getStaticField(const char* name) const;\n\n  /// Lookup the static field with the given name and descriptor\n  template<typename T>\n  JStaticField<enable_if_t<IsJniScalar<T>(), T>> getStaticField(\n      const char* name,\n      const char* descriptor) const;\n\n  /// Get the primitive value of a static field\n  template<typename T>\n  T getStaticFieldValue(JStaticField<T> field) const noexcept;\n\n  /// Get and wrap the value of a field in a @ref local_ref\n  template<typename T>\n  local_ref<T*> getStaticFieldValue(JStaticField<T*> field) noexcept;\n\n  /// Set the value of field. Any Java type is accepted, including the primitive types\n  /// and raw reference types.\n  template<typename T>\n  void setStaticFieldValue(JStaticField<T> field, T value) noexcept;\n\n  /// Allocates a new object and invokes the specified constructor\n  template<typename R, typename... Args>\n  local_ref<R> newObject(JConstructor<R(Args...)> constructor, Args... args) const;\n\n  /// Look up the static method with given name and descriptor as specified with the type arguments\n  template<typename F>\n  JStaticMethod<F> getStaticMethod(const char* name) const;\n\n  /// Look up the static method with given name and descriptor\n  template<typename F>\n  JStaticMethod<F> getStaticMethod(const char* name, const char* descriptor) const;\n\n  /// Look up the non virtual method with given name and descriptor as specified with the\n  /// type arguments\n  template<typename F>\n  JNonvirtualMethod<F> getNonvirtualMethod(const char* name) const;\n\n  /// Look up the non virtual method with given name and descriptor\n  template<typename F>\n  JNonvirtualMethod<F> getNonvirtualMethod(const char* name, const char* descriptor) const;\n\nprivate:\n  jclass self() const noexcept;\n};\n\n// Convenience method to register methods on a class without holding\n// onto the class object.\nvoid registerNatives(const char* name, std::initializer_list<NativeMethod> methods);\n\n/// Wrapper to provide functionality to jstring references\nclass FBEXPORT JString : public JavaClass<JString, JObject, jstring> {\n public:\n  /// Java type descriptor\n  static constexpr const char* kJavaDescriptor = \"Ljava/lang/String;\";\n\n  /// Convenience method to convert a jstring object to a std::string\n  std::string toStdString() const;\n};\n\n/// Convenience functions to convert a std::string or const char* into a @ref local_ref to a\n/// jstring\nFBEXPORT local_ref<JString> make_jstring(const char* modifiedUtf8);\nFBEXPORT local_ref<JString> make_jstring(const std::string& modifiedUtf8);\n\nnamespace detail {\ntemplate<typename Target>\nclass ElementProxy {\n private:\n  Target* target_;\n  size_t idx_;\n\n public:\n  using T = typename Target::javaentry;\n  ElementProxy(Target* target, size_t idx);\n\n  ElementProxy& operator=(const T& o);\n\n  ElementProxy& operator=(alias_ref<T>& o);\n\n  ElementProxy& operator=(alias_ref<T>&& o);\n\n  ElementProxy& operator=(const ElementProxy& o);\n\n  operator const local_ref<T> () const;\n\n  operator local_ref<T> ();\n};\n}\n\nnamespace detail {\nclass FBEXPORT JArray : public JavaClass<JArray, JObject, jarray> {\n public:\n  // This cannot be used in a scope that derives a descriptor (like in a method\n  // signature). Use a more derived type instead (like JArrayInt or\n  // JArrayClass<T>).\n  static constexpr const char* kJavaDescriptor = nullptr;\n  size_t size() const noexcept;\n};\n\n// This is used so that the JArrayClass<T> javaobject extends jni's\n// jobjectArray. This class should not be used directly. A general Object[]\n// should use JArrayClass<jobject>.\nclass FBEXPORT JTypeArray : public JavaClass<JTypeArray, JArray, jobjectArray> {\n  // This cannot be used in a scope that derives a descriptor (like in a method\n  // signature).\n  static constexpr const char* kJavaDescriptor = nullptr;\n};\n}\n\ntemplate<typename T>\nclass JArrayClass : public JavaClass<JArrayClass<T>, detail::JTypeArray> {\n public:\n  static_assert(is_plain_jni_reference<T>(), \"\");\n  // javaentry is the jni type of an entry in the array (i.e. jint).\n  using javaentry = T;\n  // javaobject is the jni type of the array.\n  using javaobject = typename JavaClass<JArrayClass<T>, detail::JTypeArray>::javaobject;\n  static constexpr const char* kJavaDescriptor = nullptr;\n  static std::string get_instantiated_java_descriptor();\n  static std::string get_instantiated_base_name();\n\n  /// Allocate a new array from Java heap, for passing as a JNI parameter or return value.\n  /// NOTE: if using as a return value, you want to call release() instead of get() on the\n  /// smart pointer.\n  static local_ref<javaobject> newArray(size_t count);\n\n  /// Assign an object to the array.\n  /// Typically you will use the shorthand (*ref)[idx]=value;\n  void setElement(size_t idx, const T& value);\n\n  /// Read an object from the array.\n  /// Typically you will use the shorthand\n  ///   T value = (*ref)[idx];\n  /// If you use auto, you'll get an ElementProxy, which may need to be cast.\n  local_ref<T> getElement(size_t idx);\n\n  /// EXPERIMENTAL SUBSCRIPT SUPPORT\n  /// This implementation of [] returns a proxy object which then has a bunch of specializations\n  /// (adopt_local free function, operator= and casting overloads on the ElementProxy) that can\n  /// make code look like it is dealing with a T rather than an obvious proxy. In particular, the\n  /// proxy in this iteration does not read a value and therefore does not create a LocalRef\n  /// until one of these other operators is used. There are certainly holes that you may find\n  /// by using idioms that haven't been tried yet. Consider yourself warned. On the other hand,\n  /// it does make for some idiomatic assignment code; see TestBuildStringArray in fbjni_tests\n  /// for some examples.\n  detail::ElementProxy<JArrayClass> operator[](size_t idx);\n};\n\ntemplate <typename T>\nusing jtypeArray = typename JArrayClass<T>::javaobject;\n\ntemplate<typename T>\nlocal_ref<typename JArrayClass<T>::javaobject> adopt_local_array(jobjectArray ref) {\n  return adopt_local(static_cast<typename JArrayClass<T>::javaobject>(ref));\n}\n\ntemplate<typename Target>\nlocal_ref<typename Target::javaentry> adopt_local(detail::ElementProxy<Target> elementProxy) {\n  return static_cast<local_ref<typename Target::javaentry>>(elementProxy);\n}\n\ntemplate <typename T, typename PinAlloc>\nclass PinnedPrimitiveArray;\n\ntemplate <typename T> class PinnedArrayAlloc;\ntemplate <typename T> class PinnedRegionAlloc;\ntemplate <typename T> class PinnedCriticalAlloc;\n\n/// Wrapper to provide functionality to jarray references.\n/// This is an empty holder by itself. Construct a PinnedPrimitiveArray to actually interact with\n/// the elements of the array.\ntemplate <typename JArrayType>\nclass FBEXPORT JPrimitiveArray :\n    public JavaClass<JPrimitiveArray<JArrayType>, detail::JArray, JArrayType> {\n  static_assert(is_jni_primitive_array<JArrayType>(), \"\");\n public:\n  static constexpr const char* kJavaDescriptor = nullptr;\n  static std::string get_instantiated_java_descriptor();\n  static std::string get_instantiated_base_name();\n\n  using T = typename jtype_traits<JArrayType>::entry_type;\n\n  static local_ref<JArrayType> newArray(size_t count);\n\n  void getRegion(jsize start, jsize length, T* buf);\n  std::unique_ptr<T[]> getRegion(jsize start, jsize length);\n  void setRegion(jsize start, jsize length, const T* buf);\n\n  /// Returns a view of the underlying array. This will either be a \"pinned\"\n  /// version of the array (in which case changes to one immediately affect the\n  /// other) or a copy of the array (in which cases changes to the view will take\n  /// affect when destroyed or on calls to release()/commit()).\n  PinnedPrimitiveArray<T, PinnedArrayAlloc<T>> pin();\n\n  /// Returns a view of part of the underlying array. A pinned region is always\n  /// backed by a copy of the region.\n  PinnedPrimitiveArray<T, PinnedRegionAlloc<T>> pinRegion(jsize start, jsize length);\n\n  /// Returns a view of the underlying array like pin(). However, while the pin\n  /// is held, the code is considered within a \"critical region\". In a critical\n  /// region, native code must not call JNI functions or make any calls that may\n  /// block on other Java threads. These restrictions make it more likely that\n  /// the view will be \"pinned\" rather than copied (for example, the VM may\n  /// suspend garbage collection within a critical region).\n  PinnedPrimitiveArray<T, PinnedCriticalAlloc<T>> pinCritical();\n\nprivate:\n  friend class PinnedArrayAlloc<T>;\n  T* getElements(jboolean* isCopy);\n  void releaseElements(T* elements, jint mode);\n};\n\nFBEXPORT local_ref<jbooleanArray> make_boolean_array(jsize size);\nFBEXPORT local_ref<jbyteArray> make_byte_array(jsize size);\nFBEXPORT local_ref<jcharArray> make_char_array(jsize size);\nFBEXPORT local_ref<jshortArray> make_short_array(jsize size);\nFBEXPORT local_ref<jintArray> make_int_array(jsize size);\nFBEXPORT local_ref<jlongArray> make_long_array(jsize size);\nFBEXPORT local_ref<jfloatArray> make_float_array(jsize size);\nFBEXPORT local_ref<jdoubleArray> make_double_array(jsize size);\n\nusing JArrayBoolean = JPrimitiveArray<jbooleanArray>;\nusing JArrayByte = JPrimitiveArray<jbyteArray>;\nusing JArrayChar = JPrimitiveArray<jcharArray>;\nusing JArrayShort = JPrimitiveArray<jshortArray>;\nusing JArrayInt = JPrimitiveArray<jintArray>;\nusing JArrayLong = JPrimitiveArray<jlongArray>;\nusing JArrayFloat = JPrimitiveArray<jfloatArray>;\nusing JArrayDouble = JPrimitiveArray<jdoubleArray>;\n\n/// RAII class for pinned primitive arrays\n/// This currently only supports read/write access to existing java arrays. You can't create a\n/// primitive array this way yet. This class also pins the entire array into memory during the\n/// lifetime of the PinnedPrimitiveArray. If you need to unpin the array manually, call the\n/// release() or abort() functions. During a long-running block of code, you\n/// should unpin the array as soon as you're done with it, to avoid holding up\n/// the Java garbage collector.\ntemplate <typename T, typename PinAlloc>\nclass PinnedPrimitiveArray {\n  public:\n   static_assert(is_jni_primitive<T>::value,\n       \"PinnedPrimitiveArray requires primitive jni type.\");\n\n   using ArrayType = typename jtype_traits<T>::array_type;\n\n   PinnedPrimitiveArray(PinnedPrimitiveArray&&);\n   PinnedPrimitiveArray(const PinnedPrimitiveArray&) = delete;\n   ~PinnedPrimitiveArray() noexcept;\n\n   PinnedPrimitiveArray& operator=(PinnedPrimitiveArray&&);\n   PinnedPrimitiveArray& operator=(const PinnedPrimitiveArray&) = delete;\n\n   T* get();\n   void release();\n   /// Unpins the array. If the array is a copy, pending changes are discarded.\n   void abort();\n   /// If the array is a copy, copies pending changes to the underlying java array.\n   void commit();\n\n   bool isCopy() const noexcept;\n\n   const T& operator[](size_t index) const;\n   T& operator[](size_t index);\n   size_t size() const noexcept;\n\n  private:\n   alias_ref<ArrayType> array_;\n   size_t start_;\n   T* elements_;\n   jboolean isCopy_;\n   size_t size_;\n\n   void allocate(alias_ref<ArrayType>, jint start, jint length);\n   void releaseImpl(jint mode);\n   void clear() noexcept;\n\n   PinnedPrimitiveArray(alias_ref<ArrayType>, jint start, jint length);\n\n   friend class JPrimitiveArray<typename jtype_traits<T>::array_type>;\n};\n\nstruct FBEXPORT JStackTraceElement : JavaClass<JStackTraceElement> {\n  static auto constexpr kJavaDescriptor = \"Ljava/lang/StackTraceElement;\";\n\n  static local_ref<javaobject> create(const std::string& declaringClass, const std::string& methodName, const std::string& file, int line);\n\n  std::string getClassName() const;\n  std::string getMethodName() const;\n  std::string getFileName() const;\n  int getLineNumber() const;\n};\n\n/// Wrapper to provide functionality to jthrowable references\nclass FBEXPORT JThrowable : public JavaClass<JThrowable, JObject, jthrowable> {\n public:\n  static constexpr const char* kJavaDescriptor = \"Ljava/lang/Throwable;\";\n\n  using JStackTrace = JArrayClass<JStackTraceElement::javaobject>;\n\n  local_ref<JThrowable> initCause(alias_ref<JThrowable> cause);\n  local_ref<JStackTrace> getStackTrace();\n  void setStackTrace(alias_ref<JArrayClass<JStackTraceElement::javaobject>>);\n};\n\n#pragma push_macro(\"PlainJniRefMap\")\n#undef PlainJniRefMap\n#define PlainJniRefMap(rtype, jtype) \\\nnamespace detail { \\\ntemplate<> \\\nstruct RefReprType<jtype> { \\\n  using type = rtype; \\\n}; \\\n}\n\nPlainJniRefMap(JArrayBoolean, jbooleanArray);\nPlainJniRefMap(JArrayByte, jbyteArray);\nPlainJniRefMap(JArrayChar, jcharArray);\nPlainJniRefMap(JArrayShort, jshortArray);\nPlainJniRefMap(JArrayInt, jintArray);\nPlainJniRefMap(JArrayLong, jlongArray);\nPlainJniRefMap(JArrayFloat, jfloatArray);\nPlainJniRefMap(JArrayDouble, jdoubleArray);\nPlainJniRefMap(JObject, jobject);\nPlainJniRefMap(JClass, jclass);\nPlainJniRefMap(JString, jstring);\nPlainJniRefMap(JThrowable, jthrowable);\n\n#pragma pop_macro(\"PlainJniRefMap\")\n\n}}\n\n#include \"CoreClasses-inl.h\"\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/Exceptions.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n/**\n * @file Exceptions.h\n *\n * After invoking a JNI function that can throw a Java exception, the macro\n * @ref FACEBOOK_JNI_THROW_PENDING_EXCEPTION() or @ref FACEBOOK_JNI_THROW_EXCEPTION_IF()\n * should be invoked.\n *\n * IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT! IMPORTANT!\n * To use these methods you MUST call initExceptionHelpers() when your library is loaded.\n */\n\n#pragma once\n\n#include <alloca.h>\n#include <stdexcept>\n#include <string>\n\n#include <jni.h>\n\n#include <fb/visibility.h>\n\n#include \"Common.h\"\n#include \"References.h\"\n#include \"CoreClasses.h\"\n\n#if defined(__ANDROID__) && defined(__ARM_ARCH_5TE__) && !defined(FBJNI_NO_EXCEPTION_PTR)\n// ARMv5 NDK does not support exception_ptr so we cannot use that when building for it.\n#define FBJNI_NO_EXCEPTION_PTR\n#endif\n\nnamespace facebook {\nnamespace jni {\n\nclass JThrowable;\n\nclass JCppException : public JavaClass<JCppException, JThrowable> {\n public:\n  static auto constexpr kJavaDescriptor = \"Lcom/facebook/jni/CppException;\";\n\n  static local_ref<JCppException> create(const char* str) {\n    return newInstance(make_jstring(str));\n  }\n\n  static local_ref<JCppException> create(const std::exception& ex) {\n    return newInstance(make_jstring(ex.what()));\n  }\n};\n\n// JniException ////////////////////////////////////////////////////////////////////////////////////\n\n/**\n * This class wraps a Java exception into a C++ exception; if the exception is routed back\n * to the Java side, it can be unwrapped and just look like a pure Java interaction. The class\n * is resilient to errors while creating the exception, falling back to some pre-allocated\n * exceptions if a new one cannot be allocated or populated.\n *\n * Note: the what() method of this class is not thread-safe (t6900503).\n */\nclass FBEXPORT JniException : public std::exception {\n public:\n  JniException();\n  ~JniException();\n\n  explicit JniException(alias_ref<jthrowable> throwable);\n\n  JniException(JniException &&rhs);\n\n  JniException(const JniException &other);\n\n  local_ref<JThrowable> getThrowable() const noexcept;\n\n  virtual const char* what() const noexcept;\n\n  void setJavaException() const noexcept;\n\n private:\n  global_ref<JThrowable> throwable_;\n  mutable std::string what_;\n  mutable bool isMessageExtracted_;\n  const static std::string kExceptionMessageFailure_;\n\n  void populateWhat() const noexcept;\n};\n\n// Exception throwing & translating functions //////////////////////////////////////////////////////\n\n// Functions that throw C++ exceptions\n\nstatic const int kMaxExceptionMessageBufferSize = 512;\n\n// These methods are the preferred way to throw a Java exception from\n// a C++ function.  They create and throw a C++ exception which wraps\n// a Java exception, so the C++ flow is interrupted. Then, when\n// translatePendingCppExceptionToJavaException is called at the\n// topmost level of the native stack, the wrapped Java exception is\n// thrown to the java caller.\ntemplate<typename... Args>\n[[noreturn]] void throwNewJavaException(const char* throwableName, const char* fmt, Args... args) {\n  int msgSize = snprintf(nullptr, 0, fmt, args...);\n\n  char *msg = (char*) alloca(msgSize + 1);\n  snprintf(msg, kMaxExceptionMessageBufferSize, fmt, args...);\n  throwNewJavaException(throwableName, msg);\n}\n\n// Identifies any pending C++ exception and throws it as a Java exception. If the exception can't\n// be thrown, it aborts the program.\nFBEXPORT void translatePendingCppExceptionToJavaException();\n\n#ifndef FBJNI_NO_EXCEPTION_PTR\nFBEXPORT local_ref<JThrowable> getJavaExceptionForCppException(std::exception_ptr ptr);\n#endif\n\nFBEXPORT local_ref<JThrowable> getJavaExceptionForCppBackTrace();\n\nFBEXPORT local_ref<JThrowable> getJavaExceptionForCppBackTrace(const char* msg);\n// For convenience, some exception names in java.lang are available here.\n\nconst char* const gJavaLangIllegalArgumentException = \"java/lang/IllegalArgumentException\";\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/File.h",
    "content": "/*\n * Copyright (c) 2016-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include \"CoreClasses.h\"\n\nnamespace facebook {\nnamespace jni {\n\nclass JFile : public JavaClass<JFile> {\n public:\n  static constexpr const char* kJavaDescriptor = \"Ljava/io/File;\";\n\n  // Define a method that calls into the represented Java class\n  std::string getAbsolutePath() {\n    static auto method = getClass()->getMethod<jstring()>(\"getAbsolutePath\");\n    return method(self())->toStdString();\n  }\n\n};\n\n}\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/Hybrid.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <memory>\n#include <type_traits>\n\n#include <fb/assert.h>\n#include <fb/visibility.h>\n\n#include \"CoreClasses.h\"\n\nnamespace facebook {\nnamespace jni {\n\nnamespace detail {\n\nclass BaseHybridClass {\npublic:\n  virtual ~BaseHybridClass() {}\n};\n\nstruct FBEXPORT HybridData : public JavaClass<HybridData> {\n  constexpr static auto kJavaDescriptor = \"Lcom/facebook/jni/HybridData;\";\n  static local_ref<HybridData> create();\n};\n\nclass HybridDestructor : public JavaClass<HybridDestructor> {\n  public:\n    static auto constexpr kJavaDescriptor = \"Lcom/facebook/jni/HybridData$Destructor;\";\n\n  template <typename T=detail::BaseHybridClass>\n  T* getNativePointer() {\n    static auto pointerField = javaClassStatic()->getField<jlong>(\"mNativePointer\");\n    auto* value = reinterpret_cast<detail::BaseHybridClass*>(getFieldValue(pointerField));\n    if (!value) {\n      throwNewJavaException(\"java/lang/NullPointerException\", \"java.lang.NullPointerException\");\n    }\n    return value;\n  }\n\n  template <typename T=detail::BaseHybridClass>\n  void setNativePointer(std::unique_ptr<T> new_value) {\n    static auto pointerField = javaClassStatic()->getField<jlong>(\"mNativePointer\");\n    auto old_value = std::unique_ptr<T>(reinterpret_cast<T*>(getFieldValue(pointerField)));\n    if (new_value && old_value) {\n        FBCRASH(\"Attempt to set C++ native pointer twice\");\n    }\n    setFieldValue(pointerField, reinterpret_cast<jlong>(new_value.release()));\n  }\n};\n\ntemplate<typename T>\ndetail::BaseHybridClass* getNativePointer(T t) {\n  return getHolder(t)->getNativePointer();\n}\n\ntemplate<typename T>\nvoid setNativePointer(T t, std::unique_ptr<detail::BaseHybridClass> new_value) {\n  getHolder(t)->setNativePointer(std::move(new_value));\n}\n\ntemplate<typename T>\nlocal_ref<HybridDestructor> getHolder(T t) {\n  static auto holderField = t->getClass()->template getField<HybridDestructor::javaobject>(\"mDestructor\");\n  return t->getFieldValue(holderField);\n}\n\n// JavaClass for HybridClassBase\nstruct FBEXPORT HybridClassBase : public JavaClass<HybridClassBase> {\n  constexpr static auto kJavaDescriptor = \"Lcom/facebook/jni/HybridClassBase;\";\n\n  static bool isHybridClassBase(alias_ref<jclass> jclass) {\n    return HybridClassBase::javaClassStatic()->isAssignableFrom(jclass);\n  }\n};\n\ntemplate <typename Base, typename Enabled = void>\nstruct HybridTraits {\n  // This static assert should actually always fail if we don't use one of the\n  // specializations below.\n  static_assert(\n      std::is_base_of<JObject, Base>::value ||\n      std::is_base_of<BaseHybridClass, Base>::value,\n      \"The base of a HybridClass must be either another HybridClass or derived from JObject.\");\n};\n\ntemplate <>\nstruct HybridTraits<BaseHybridClass> {\n using CxxBase = BaseHybridClass;\n using JavaBase = JObject;\n};\n\ntemplate <typename Base>\nstruct HybridTraits<\n    Base,\n    typename std::enable_if<std::is_base_of<BaseHybridClass, Base>::value>::type> {\n using CxxBase = Base;\n using JavaBase = typename Base::JavaPart;\n};\n\ntemplate <typename Base>\nstruct HybridTraits<\n    Base,\n    typename std::enable_if<std::is_base_of<JObject, Base>::value>::type> {\n using CxxBase = BaseHybridClass;\n using JavaBase = Base;\n};\n\n// convert to HybridClass* from jhybridobject\ntemplate <typename T>\nstruct FBEXPORT Convert<\n  T, typename std::enable_if<\n    std::is_base_of<BaseHybridClass, typename std::remove_pointer<T>::type>::value>::type> {\n  typedef typename std::remove_pointer<T>::type::jhybridobject jniType;\n  static T fromJni(jniType t) {\n    if (t == nullptr) {\n      return nullptr;\n    }\n    return wrap_alias(t)->cthis();\n  }\n  // There is no automatic return conversion for objects.\n};\n\ntemplate<typename T>\nstruct RefReprType<T, typename std::enable_if<std::is_base_of<BaseHybridClass, T>::value, void>::type> {\n  static_assert(std::is_same<T, void>::value,\n      \"HybridFoo (where HybridFoo derives from HybridClass<HybridFoo>) is not supported in this context. \"\n      \"For an xxx_ref<HybridFoo>, you may want: xxx_ref<HybridFoo::javaobject> or HybridFoo*.\");\n  using Repr = T;\n};\n\n\n}\n\ntemplate <typename T, typename Base = detail::BaseHybridClass>\nclass FBEXPORT HybridClass : public detail::HybridTraits<Base>::CxxBase {\npublic:\n  struct JavaPart : JavaClass<JavaPart, typename detail::HybridTraits<Base>::JavaBase> {\n    // At this point, T is incomplete, and so we cannot access\n    // T::kJavaDescriptor directly. jtype_traits support this escape hatch for\n    // such a case.\n    static constexpr const char* kJavaDescriptor = nullptr;\n    static std::string get_instantiated_java_descriptor();\n    static std::string get_instantiated_base_name();\n\n    using HybridType = T;\n\n    // This will reach into the java object and extract the C++ instance from\n    // the mHybridData and return it.\n    T* cthis();\n\n    friend class HybridClass;\n  };\n\n  using jhybridobject = typename JavaPart::javaobject;\n  using javaobject = typename JavaPart::javaobject;\n  typedef detail::HybridData::javaobject jhybriddata;\n\n  static alias_ref<JClass> javaClassStatic() {\n    return JavaPart::javaClassStatic();\n  }\n\n  static local_ref<JClass> javaClassLocal() {\n    std::string className(T::kJavaDescriptor + 1, strlen(T::kJavaDescriptor) - 2);\n    return findClassLocal(className.c_str());\n  }\n\nprotected:\n  typedef HybridClass HybridBase;\n\n  // This ensures that a C++ hybrid part cannot be created on its own\n  // by default.  If a hybrid wants to enable this, it can provide its\n  // own public ctor, or change the accessibility of this to public.\n  using detail::HybridTraits<Base>::CxxBase::CxxBase;\n\n  static void registerHybrid(std::initializer_list<NativeMethod> methods) {\n    javaClassStatic()->registerNatives(methods);\n  }\n\n  static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) {\n    auto hybridData = detail::HybridData::create();\n    setNativePointer(hybridData, std::move(cxxPart));\n    return hybridData;\n  }\n\n  template <typename... Args>\n  static local_ref<detail::HybridData> makeCxxInstance(Args&&... args) {\n    return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...)));\n  }\n\n  template <typename... Args>\n  static void setCxxInstance(alias_ref<jhybridobject> o, Args&&... args) {\n    setNativePointer(o, std::unique_ptr<T>(new T(std::forward<Args>(args)...)));\n  }\n\npublic:\n  // Factory method for creating a hybrid object where the arguments\n  // are used to initialize the C++ part directly without passing them\n  // through java.  This method requires the Java part to have a ctor\n  // which takes a HybridData, and for the C++ part to have a ctor\n  // compatible with the arguments passed here.  For safety, the ctor\n  // can be private, and the hybrid declared a friend of its base, so\n  // the hybrid can only be created from here.\n  //\n  // Exception behavior: This can throw an exception if creating the\n  // C++ object fails, or any JNI methods throw.\n  template <typename... Args>\n  static local_ref<JavaPart> newObjectCxxArgs(Args&&... args) {\n    static bool isHybrid = detail::HybridClassBase::isHybridClassBase(javaClassStatic());\n    auto cxxPart = std::unique_ptr<T>(new T(std::forward<Args>(args)...));\n\n    local_ref<JavaPart> result;\n    if (isHybrid) {\n      result = JavaPart::newInstance();\n      setNativePointer(result, std::move(cxxPart));\n    }\n    else {\n      auto hybridData = makeHybridData(std::move(cxxPart));\n      result = JavaPart::newInstance(hybridData);\n    }\n\n    return result;\n  }\n\n // TODO? Create reusable interface for Allocatable classes and use it to\n  // strengthen type-checking (and possibly provide a default\n  // implementation of allocate().)\n  template <typename... Args>\n  static local_ref<jhybridobject> allocateWithCxxArgs(Args&&... args) {\n    auto hybridData = makeCxxInstance(std::forward<Args>(args)...);\n    static auto allocateMethod =\n        javaClassStatic()->template getStaticMethod<jhybridobject(jhybriddata)>(\"allocate\");\n    return allocateMethod(javaClassStatic(), hybridData.get());\n  }\n\n  // Factory method for creating a hybrid object where the arguments\n  // are passed to the java ctor.\n  template <typename... Args>\n  static local_ref<JavaPart> newObjectJavaArgs(Args&&... args) {\n    return JavaPart::newInstance(std::move(args)...);\n  }\n\n  // If a hybrid class throws an exception which derives from\n  // std::exception, it will be passed to mapException on the hybrid\n  // class, or nearest ancestor.  This allows boilerplate exception\n  // translation code (for example, calling throwNewJavaException on a\n  // particular java class) to be hoisted to a common function.  If\n  // mapException returns, then the std::exception will be translated\n  // to Java.\n  static void mapException(const std::exception& ex) {}\n};\n\ntemplate <typename T, typename B>\ninline T* HybridClass<T, B>::JavaPart::cthis() {\n  detail::BaseHybridClass* result = 0;\n  static bool isHybrid = detail::HybridClassBase::isHybridClassBase(this->getClass());\n  if (isHybrid) {\n    result = getNativePointer(this);\n  } else {\n    static auto field =\n      HybridClass<T, B>::JavaPart::javaClassStatic()->template getField<detail::HybridData::javaobject>(\"mHybridData\");\n    auto hybridData = this->getFieldValue(field);\n    if (!hybridData) {\n      throwNewJavaException(\"java/lang/NullPointerException\", \"java.lang.NullPointerException\");\n    }\n\n    result = getNativePointer(hybridData);\n  }\n\n  // This would require some serious programmer error.\n  FBASSERTMSGF(result != 0, \"Incorrect C++ type in hybrid field\");\n  // I'd like to use dynamic_cast here, but -fno-rtti is the default.\n  return static_cast<T*>(result);\n};\n\ntemplate <typename T, typename B>\n/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_java_descriptor() {\n  return T::kJavaDescriptor;\n}\n\ntemplate <typename T, typename B>\n/* static */ inline std::string HybridClass<T, B>::JavaPart::get_instantiated_base_name() {\n  auto name = get_instantiated_java_descriptor();\n  return name.substr(1, name.size() - 2);\n}\n\n// Given a *_ref object which refers to a hybrid class, this will reach inside\n// of it, find the mHybridData, extract the C++ instance pointer, cast it to\n// the appropriate type, and return it.\ntemplate <typename T>\ninline auto cthis(T jthis) -> decltype(jthis->cthis()) {\n  return jthis->cthis();\n}\n\nvoid HybridDataOnLoad();\n\n}\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/Iterator-inl.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\nnamespace facebook {\nnamespace jni {\n\nnamespace detail {\n\ntemplate <typename E>\nstruct IteratorHelper : public JavaClass<IteratorHelper<E>> {\n  constexpr static auto kJavaDescriptor = \"Lcom/facebook/jni/IteratorHelper;\";\n\n  typedef local_ref<E> value_type;\n  typedef ptrdiff_t difference_type;\n  typedef value_type* pointer;\n  typedef value_type& reference;\n  typedef std::forward_iterator_tag iterator_category;\n\n  typedef JavaClass<IteratorHelper<E>> JavaBase_;\n\n  bool hasNext() const {\n    static auto hasNextMethod =\n      JavaBase_::javaClassStatic()->template getMethod<jboolean()>(\"hasNext\");\n    return hasNextMethod(JavaBase_::self());\n  }\n\n  value_type next() {\n    static auto elementField =\n      JavaBase_::javaClassStatic()->template getField<jobject>(\"mElement\");\n    return dynamic_ref_cast<E>(JavaBase_::getFieldValue(elementField));\n  }\n\n  static void reset(value_type& v) {\n    v.reset();\n  }\n};\n\ntemplate <typename K, typename V>\nstruct MapIteratorHelper : public JavaClass<MapIteratorHelper<K,V>> {\n  constexpr static auto kJavaDescriptor = \"Lcom/facebook/jni/MapIteratorHelper;\";\n\n  typedef std::pair<local_ref<K>, local_ref<V>> value_type;\n\n  typedef JavaClass<MapIteratorHelper<K,V>> JavaBase_;\n\n  bool hasNext() const {\n    static auto hasNextMethod =\n      JavaBase_::javaClassStatic()->template getMethod<jboolean()>(\"hasNext\");\n    return hasNextMethod(JavaBase_::self());\n  }\n\n  value_type next() {\n    static auto keyField = JavaBase_::javaClassStatic()->template getField<jobject>(\"mKey\");\n    static auto valueField = JavaBase_::javaClassStatic()->template getField<jobject>(\"mValue\");\n    return std::make_pair(dynamic_ref_cast<K>(JavaBase_::getFieldValue(keyField)),\n                          dynamic_ref_cast<V>(JavaBase_::getFieldValue(valueField)));\n  }\n\n  static void reset(value_type& v) {\n    v.first.reset();\n    v.second.reset();\n  }\n};\n\ntemplate <typename T>\nclass Iterator {\n public:\n  typedef typename T::value_type value_type;\n  typedef ptrdiff_t difference_type;\n  typedef value_type* pointer;\n  typedef value_type& reference;\n  typedef std::input_iterator_tag iterator_category;\n\n  // begin ctor\n  Iterator(global_ref<typename T::javaobject>&& helper)\n      : helper_(std::move(helper))\n      , i_(-1) {\n    ++(*this);\n  }\n\n  // end ctor\n  Iterator()\n      : i_(-1) {}\n\n  bool operator==(const Iterator& it) const { return i_ == it.i_; }\n  bool operator!=(const Iterator& it) const { return !(*this == it); }\n  const value_type& operator*() const { assert(i_ != -1); return entry_; }\n  const value_type* operator->() const { assert(i_ != -1); return &entry_; }\n  Iterator& operator++() {  // preincrement\n    bool hasNext = helper_->hasNext();\n    if (hasNext) {\n      ++i_;\n      entry_ = helper_->next();\n    } else {\n      i_ = -1;\n      helper_->reset(entry_);\n    }\n    return *this;\n  }\n  Iterator operator++(int) {  // postincrement\n    Iterator ret;\n    ret.i_ = i_;\n    ret.entry_ = std::move(entry_);\n    ++(*this);\n    return ret;\n  }\n\n  global_ref<typename T::javaobject> helper_;\n  // set to -1 at end\n  std::ptrdiff_t i_;\n  value_type entry_;\n};\n\n}\n\ntemplate <typename E>\nstruct JIterator<E>::Iterator : public detail::Iterator<detail::IteratorHelper<E>> {\n  using detail::Iterator<detail::IteratorHelper<E>>::Iterator;\n};\n\ntemplate <typename E>\ntypename JIterator<E>::Iterator JIterator<E>::begin() const {\n  static auto ctor = detail::IteratorHelper<E>::javaClassStatic()->\n    template getConstructor<typename detail::IteratorHelper<E>::javaobject(\n                              typename JIterator<E>::javaobject)>();\n  return Iterator(\n    make_global(\n      detail::IteratorHelper<E>::javaClassStatic()->newObject(ctor, this->self())));\n}\n\ntemplate <typename E>\ntypename JIterator<E>::Iterator JIterator<E>::end() const {\n  return Iterator();\n}\n\ntemplate <typename E>\nstruct JIterable<E>::Iterator : public detail::Iterator<detail::IteratorHelper<E>> {\n  using detail::Iterator<detail::IteratorHelper<E>>::Iterator;\n};\n\ntemplate <typename E>\ntypename JIterable<E>::Iterator JIterable<E>::begin() const {\n  static auto ctor = detail::IteratorHelper<E>::javaClassStatic()->\n    template getConstructor<typename detail::IteratorHelper<E>::javaobject(\n                              typename JIterable<E>::javaobject)>();\n  return Iterator(\n    make_global(\n      detail::IteratorHelper<E>::javaClassStatic()->newObject(ctor, this->self())));\n}\n\ntemplate <typename E>\ntypename JIterable<E>::Iterator JIterable<E>::end() const {\n  return Iterator();\n}\n\ntemplate <typename E>\nsize_t JCollection<E>::size() const {\n  static auto sizeMethod =\n    JCollection<E>::javaClassStatic()->template getMethod<jint()>(\"size\");\n  return sizeMethod(this->self());\n}\n\ntemplate <typename K, typename V>\nstruct JMap<K,V>::Iterator : public detail::Iterator<detail::MapIteratorHelper<K,V>> {\n  using detail::Iterator<detail::MapIteratorHelper<K,V>>::Iterator;\n};\n\ntemplate <typename K, typename V>\nsize_t JMap<K,V>::size() const {\n  static auto sizeMethod =\n    JMap<K,V>::javaClassStatic()->template getMethod<jint()>(\"size\");\n  return sizeMethod(this->self());\n}\n\ntemplate <typename K, typename V>\ntypename JMap<K,V>::Iterator JMap<K,V>::begin() const {\n  static auto ctor = detail::MapIteratorHelper<K,V>::javaClassStatic()->\n    template getConstructor<typename detail::MapIteratorHelper<K,V>::javaobject(\n                              typename JMap<K,V>::javaobject)>();\n  return Iterator(\n    make_global(\n      detail::MapIteratorHelper<K,V>::javaClassStatic()->newObject(ctor, this->self())));\n}\n\ntemplate <typename K, typename V>\ntypename JMap<K,V>::Iterator JMap<K,V>::end() const {\n  return Iterator();\n}\n\n}\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/Iterator.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include \"CoreClasses.h\"\n\nnamespace facebook {\nnamespace jni {\n\n/**\n * JavaClass which represents a reference to a java.util.Iterator instance.  It\n * provides begin()/end() methods to provide C++-style iteration over the\n * underlying collection.  The class has a template parameter for the element\n * type, which defaults to jobject.  For example:\n *\n * alias_ref<JIterator<jstring>::javaobject> my_iter = ...;\n *\n * In the simplest case, it can be used just as alias_ref<JIterator<>::javaobject>,\n * for example in a method declaration.\n */\ntemplate <typename E = jobject>\nstruct JIterator : JavaClass<JIterator<E>> {\n  constexpr static auto kJavaDescriptor = \"Ljava/util/Iterator;\";\n\n  struct Iterator;\n\n  /**\n   * To iterate:\n   *\n   * for (const auto& element : *jiter) { ... }\n   *\n   * The JIterator iterator value_type is local_ref<E>, containing a reference\n   * to an element instance.\n   *\n   * If the Iterator returns objects whch are not convertible to the given\n   * element type, iteration will throw a java ClassCastException.\n   *\n   * For example, to convert an iterator over a collection of java strings to\n   * an std::vector of std::strings:\n   *\n   * std::vector<std::string> vs;\n   * for (const auto& elem : *jiter) {\n   *    vs.push_back(elem->toStdString());\n   * }\n   *\n   * Or if you prefer using std algorithms:\n   *\n   * std::vector<std::string> vs;\n   * std::transform(jiter->begin(), jiter->end(), std::back_inserter(vs),\n   *                [](const local_ref<jstring>& elem) { return elem->toStdString(); });\n   *\n   * The iterator is a InputIterator.\n   */\n  Iterator begin() const;\n  Iterator end() const;\n};\n\n/**\n * Similar to JIterator, except this represents any object which implements the\n * java.lang.Iterable interface. It will create the Java Iterator as a part of\n * begin().\n */\ntemplate <typename E = jobject>\nstruct JIterable : JavaClass<JIterable<E>> {\n  constexpr static auto kJavaDescriptor = \"Ljava/lang/Iterable;\";\n\n  struct Iterator;\n\n  Iterator begin() const;\n  Iterator end() const;\n};\n\n/**\n * JavaClass types which represent Collection, List, and Set are also provided.\n * These preserve the Java class heirarchy.\n */\ntemplate <typename E = jobject>\nstruct JCollection : JavaClass<JCollection<E>, JIterable<E>> {\n  constexpr static auto kJavaDescriptor = \"Ljava/util/Collection;\";\n\n  /**\n   * Returns the number of elements in the collection.\n   */\n  size_t size() const;\n};\n\ntemplate <typename E = jobject>\nstruct JList : JavaClass<JList<E>, JCollection<E>> {\n  constexpr static auto kJavaDescriptor = \"Ljava/util/List;\";\n};\n\ntemplate <typename E = jobject>\nstruct JSet : JavaClass<JSet<E>, JCollection<E>> {\n  constexpr static auto kJavaDescriptor = \"Ljava/util/Set;\";\n};\n\n/**\n * JavaClass which represents a reference to a java.util.Map instance.  It adds\n * wrappers around Java methods, including begin()/end() methods to provide\n * C++-style iteration over the Java Map.  The class has template parameters\n * for the key and value types, which default to jobject.  For example:\n *\n * alias_ref<JMap<jstring, MyJClass::javaobject>::javaobject> my_map = ...;\n *\n * In the simplest case, it can be used just as alias_ref<JMap<>::javaobject>,\n * for example in a method declaration.\n */\ntemplate <typename K = jobject, typename V = jobject>\nstruct JMap : JavaClass<JMap<K,V>> {\n  constexpr static auto kJavaDescriptor = \"Ljava/util/Map;\";\n\n  struct Iterator;\n\n  /**\n   * Returns the number of pairs in the map.\n   */\n  size_t size() const;\n\n  /**\n   * To iterate over the Map:\n   *\n   * for (const auto& entry : *jmap) { ... }\n   *\n   * The JMap iterator value_type is std::pair<local_ref<K>, local_ref<V>>\n   * containing references to key and value instances.\n   *\n   * If the Map contains objects whch are not convertible to the given key and\n   * value types, iteration will throw a java ClassCastException.\n   *\n   * The iterator is a InputIterator.\n   */\n  Iterator begin() const;\n  Iterator end() const;\n};\n\n}\n}\n\n#include \"Iterator-inl.h\"\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/JThread.h",
    "content": "/*\n * Copyright (c) 2016-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include \"CoreClasses.h\"\n#include \"NativeRunnable.h\"\n\nnamespace facebook {\nnamespace jni {\n\nclass JThread : public JavaClass<JThread> {\n public:\n  static constexpr const char* kJavaDescriptor = \"Ljava/lang/Thread;\";\n\n  void start() {\n    static auto method = javaClassStatic()->getMethod<void()>(\"start\");\n    method(self());\n  }\n\n  void join() {\n    static auto method = javaClassStatic()->getMethod<void()>(\"join\");\n    method(self());\n  }\n\n  static local_ref<JThread> create(std::function<void()>&& runnable) {\n    auto jrunnable = JNativeRunnable::newObjectCxxArgs(std::move(runnable));\n    return newInstance(static_ref_cast<JRunnable::javaobject>(jrunnable));\n  }\n};\n\n}\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/JWeakReference.h",
    "content": "// Copyright 2004-present Facebook. All Rights Reserved.\n\n#pragma once\n\n#include <fb/visibility.h>\n\n#include \"CoreClasses.h\"\n\nnamespace facebook {\nnamespace jni {\n\n/**\n * Wrap Java's WeakReference instead of using JNI WeakGlobalRefs.\n * A WeakGlobalRef can yield a strong reference even after the object has been\n  * finalized. See comment in the djinni library.\n * https://github.com/dropbox/djinni/blob/master/support-lib/jni/djinni_support.hpp\n */\ntemplate<typename T = jobject>\nclass JWeakReference : public JavaClass<JWeakReference<T>> {\n\n typedef JavaClass<JWeakReference<T>> JavaBase_;\n\n public:\n  static constexpr const char* kJavaDescriptor = \"Ljava/lang/ref/WeakReference;\";\n\n  static local_ref<JWeakReference<T>> newInstance(alias_ref<T> object) {\n    return JavaBase_::newInstance(static_ref_cast<jobject>(object));\n  }\n\n  local_ref<T> get() const {\n    static auto method = JavaBase_::javaClassStatic()->template getMethod<jobject()>(\"get\");\n    return static_ref_cast<T>(method(JavaBase_::self()));\n  }\n};\n\n}\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/Meta-forward.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\nnamespace facebook {\nnamespace jni {\n\ntemplate<typename F>\nclass JMethod;\ntemplate<typename F>\nclass JStaticMethod;\ntemplate<typename F>\nclass JNonvirtualMethod;\ntemplate<typename F>\nstruct JConstructor;\ntemplate<typename F>\nclass JField;\ntemplate<typename F>\nclass JStaticField;\n\n/// Type traits for Java types (currently providing Java type descriptors)\ntemplate<typename T>\nstruct jtype_traits;\n\n/// Type traits for Java methods (currently providing Java type descriptors)\ntemplate<typename F>\nstruct jmethod_traits;\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/Meta-inl.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <jni.h>\n\n#include \"Common.h\"\n#include \"Exceptions.h\"\n#include \"MetaConvert.h\"\n#include \"References.h\"\n#include \"Boxed.h\"\n\n#if defined(__ANDROID__)\n#  include <fb/Build.h>\n#endif\n\nnamespace facebook {\nnamespace jni {\n\n// JMethod /////////////////////////////////////////////////////////////////////////////////////////\n\ninline JMethodBase::JMethodBase(jmethodID method_id) noexcept\n  : method_id_{method_id}\n{}\n\ninline JMethodBase::operator bool() const noexcept {\n  return method_id_ != nullptr;\n}\n\ninline jmethodID JMethodBase::getId() const noexcept {\n  return method_id_;\n}\n\nnamespace {\n\ntemplate <int idx, typename... Args>\nstruct ArgsArraySetter;\n\ntemplate <int idx, typename Arg, typename... Args>\nstruct ArgsArraySetter<idx, Arg, Args...> {\n  static void set(alias_ref<JArrayClass<jobject>::javaobject> array, Arg arg0, Args... args) {\n    // TODO(xxxxxxxx): Use Convert<Args>... to do conversions like the fast path.\n    (*array)[idx] = autobox(arg0);\n    ArgsArraySetter<idx + 1, Args...>::set(array, args...);\n  }\n};\n\ntemplate <int idx>\nstruct ArgsArraySetter<idx> {\n  static void set(alias_ref<JArrayClass<jobject>::javaobject> array) {\n  }\n};\n\ntemplate <typename... Args>\nlocal_ref<JArrayClass<jobject>::javaobject> makeArgsArray(Args... args) {\n  auto arr = JArrayClass<jobject>::newArray(sizeof...(args));\n  ArgsArraySetter<0, Args...>::set(arr, args...);\n  return arr;\n}\n\n\ninline bool needsSlowPath(alias_ref<jobject> obj) {\n#if defined(__ANDROID__)\n  // On Android 6.0, art crashes when attempting to call a function on a Proxy.\n  // So, when we detect that case we must use the safe, slow workaround. That is,\n  // we resolve the method id to the corresponding java.lang.reflect.Method object\n  // and make the call via it's invoke() method.\n  static auto is_bad_android = build::Build::getAndroidSdk() == 23;\n\n  if (!is_bad_android) return false;\n  static auto proxy_class = findClassStatic(\"java/lang/reflect/Proxy\");\n  return obj->isInstanceOf(proxy_class);\n#else\n  return false;\n#endif\n}\n\n}\n\ntemplate<typename... Args>\ninline void JMethod<void(Args...)>::operator()(alias_ref<jobject> self, Args... args) {\n  const auto env = Environment::current();\n  env->CallVoidMethod(\n        self.get(),\n        getId(),\n        detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n}\n\n#pragma push_macro(\"DEFINE_PRIMITIVE_CALL\")\n#undef DEFINE_PRIMITIVE_CALL\n#define DEFINE_PRIMITIVE_CALL(TYPE, METHOD)                                                    \\\ntemplate<typename... Args>                                                                     \\\ninline TYPE JMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, Args... args) {        \\\n  const auto env = internal::getEnv();                                                         \\\n  auto result = env->Call ## METHOD ## Method(                                                 \\\n        self.get(),                                                                            \\\n        getId(),                                                                               \\\n        detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \\\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();                                                      \\\n  return result;                                                                               \\\n}\n\nDEFINE_PRIMITIVE_CALL(jboolean, Boolean)\nDEFINE_PRIMITIVE_CALL(jbyte, Byte)\nDEFINE_PRIMITIVE_CALL(jchar, Char)\nDEFINE_PRIMITIVE_CALL(jshort, Short)\nDEFINE_PRIMITIVE_CALL(jint, Int)\nDEFINE_PRIMITIVE_CALL(jlong, Long)\nDEFINE_PRIMITIVE_CALL(jfloat, Float)\nDEFINE_PRIMITIVE_CALL(jdouble, Double)\n#pragma pop_macro(\"DEFINE_PRIMITIVE_CALL\")\n\n/// JMethod specialization for references that wraps the return value in a @ref local_ref\ntemplate<typename R, typename... Args>\nclass JMethod<R(Args...)> : public JMethodBase {\n public:\n   // TODO: static_assert is jobject-derived or local_ref jobject\n  using JniRet = typename detail::Convert<typename std::decay<R>::type>::jniType;\n  static_assert(IsPlainJniReference<JniRet>(), \"JniRet must be a JNI reference\");\n  using JMethodBase::JMethodBase;\n  JMethod() noexcept {};\n  JMethod(const JMethod& other) noexcept = default;\n\n  /// Invoke a method and return a local reference wrapping the result\n  local_ref<JniRet> operator()(alias_ref<jobject> self, Args... args);\n\n  friend class JClass;\n};\n\ntemplate<typename R, typename... Args>\ninline auto JMethod<R(Args...)>::operator()(alias_ref<jobject> self, Args... args) -> local_ref<JniRet> {\n  const auto env = Environment::current();\n  auto result = env->CallObjectMethod(\n      self.get(),\n      getId(),\n      detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n  return adopt_local(static_cast<JniRet>(result));\n}\n\ntemplate<typename... Args>\ninline void JStaticMethod<void(Args...)>::operator()(alias_ref<jclass> cls, Args... args) {\n  const auto env = internal::getEnv();\n  env->CallStaticVoidMethod(\n        cls.get(),\n        getId(),\n        detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n}\n\n#pragma push_macro(\"DEFINE_PRIMITIVE_STATIC_CALL\")\n#undef DEFINE_PRIMITIVE_STATIC_CALL\n#define DEFINE_PRIMITIVE_STATIC_CALL(TYPE, METHOD)                                             \\\ntemplate<typename... Args>                                                                     \\\ninline TYPE JStaticMethod<TYPE(Args...)>::operator()(alias_ref<jclass> cls, Args... args) {    \\\n  const auto env = internal::getEnv();                                                         \\\n  auto result = env->CallStatic ## METHOD ## Method(                                           \\\n        cls.get(),                                                                             \\\n        getId(),                                                                               \\\n        detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...); \\\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();                                                      \\\n        return result;                                                                         \\\n}\n\nDEFINE_PRIMITIVE_STATIC_CALL(jboolean, Boolean)\nDEFINE_PRIMITIVE_STATIC_CALL(jbyte, Byte)\nDEFINE_PRIMITIVE_STATIC_CALL(jchar, Char)\nDEFINE_PRIMITIVE_STATIC_CALL(jshort, Short)\nDEFINE_PRIMITIVE_STATIC_CALL(jint, Int)\nDEFINE_PRIMITIVE_STATIC_CALL(jlong, Long)\nDEFINE_PRIMITIVE_STATIC_CALL(jfloat, Float)\nDEFINE_PRIMITIVE_STATIC_CALL(jdouble, Double)\n#pragma pop_macro(\"DEFINE_PRIMITIVE_STATIC_CALL\")\n\n/// JStaticMethod specialization for references that wraps the return value in a @ref local_ref\ntemplate<typename R, typename... Args>\nclass JStaticMethod<R(Args...)> : public JMethodBase {\n\n public:\n  using JniRet = typename detail::Convert<typename std::decay<R>::type>::jniType;\n  static_assert(IsPlainJniReference<JniRet>(), \"T* must be a JNI reference\");\n  using JMethodBase::JMethodBase;\n  JStaticMethod() noexcept {};\n  JStaticMethod(const JStaticMethod& other) noexcept = default;\n\n  /// Invoke a method and return a local reference wrapping the result\n  local_ref<JniRet> operator()(alias_ref<jclass> cls, Args... args) {\n    const auto env = internal::getEnv();\n    auto result = env->CallStaticObjectMethod(\n          cls.get(),\n          getId(),\n          detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);\n    FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n    return adopt_local(static_cast<JniRet>(result));\n  }\n\n  friend class JClass;\n};\n\ntemplate<typename... Args>\ninline void\nJNonvirtualMethod<void(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) {\n  const auto env = internal::getEnv();\n  env->CallNonvirtualVoidMethod(\n        self.get(),\n        cls.get(),\n        getId(),\n        detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n}\n\n#pragma push_macro(\"DEFINE_PRIMITIVE_NON_VIRTUAL_CALL\")\n#undef DEFINE_PRIMITIVE_NON_VIRTUAL_CALL\n#define DEFINE_PRIMITIVE_NON_VIRTUAL_CALL(TYPE, METHOD)                                                      \\\ntemplate<typename... Args>                                                                                   \\\ninline TYPE                                                                                                  \\\nJNonvirtualMethod<TYPE(Args...)>::operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args) { \\\n  const auto env = internal::getEnv();                                                                       \\\n  auto result = env->CallNonvirtual ## METHOD ## Method(                                                     \\\n        self.get(),                                                                                          \\\n        cls.get(),                                                                                           \\\n        getId(),                                                                                             \\\n        detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);               \\\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();                                                                    \\\n  return result;                                                                                             \\\n}\n\nDEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jboolean, Boolean)\nDEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jbyte, Byte)\nDEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jchar, Char)\nDEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jshort, Short)\nDEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jint, Int)\nDEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jlong, Long)\nDEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jfloat, Float)\nDEFINE_PRIMITIVE_NON_VIRTUAL_CALL(jdouble, Double)\n#pragma pop_macro(\"DEFINE_PRIMITIVE_NON_VIRTUAL_CALL\")\n\n/// JNonvirtualMethod specialization for references that wraps the return value in a @ref local_ref\ntemplate<typename R, typename... Args>\nclass JNonvirtualMethod<R(Args...)> : public JMethodBase {\n public:\n  using JniRet = typename detail::Convert<typename std::decay<R>::type>::jniType;\n  static_assert(IsPlainJniReference<JniRet>(), \"T* must be a JNI reference\");\n  using JMethodBase::JMethodBase;\n  JNonvirtualMethod() noexcept {};\n  JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default;\n\n  /// Invoke a method and return a local reference wrapping the result\n  local_ref<JniRet> operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args){\n    const auto env = internal::getEnv();\n    auto result = env->CallNonvirtualObjectMethod(\n          self.get(),\n          cls.get(),\n          getId(),\n          detail::callToJni(detail::Convert<typename std::decay<Args>::type>::toCall(args))...);\n    FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n    return adopt_local(static_cast<JniRet>(result));\n  }\n\n  friend class JClass;\n};\n\ntemplate <typename... Args>\nlocal_ref<jobject> slowCall(jmethodID method_id, alias_ref<jobject> self, Args... args) {\n    static auto invoke = findClassStatic(\"java/lang/reflect/Method\")\n      ->getMethod<jobject(jobject, JArrayClass<jobject>::javaobject)>(\"invoke\");\n    // TODO(xxxxxxx): Provide fbjni interface to ToReflectedMethod.\n    auto reflected = adopt_local(Environment::current()->ToReflectedMethod(self->getClass().get(), method_id, JNI_FALSE));\n    FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n    if (!reflected) throw std::runtime_error(\"Unable to get reflected java.lang.reflect.Method\");\n    auto argsArray = makeArgsArray(args...);\n    // No need to check for exceptions since invoke is itself a JMethod that will do that for us.\n    return invoke(reflected, self.get(), argsArray.get());\n}\n\n\n// JField<T> ///////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\ninline JField<T>::JField(jfieldID field) noexcept\n  : field_id_{field}\n{}\n\ntemplate<typename T>\ninline JField<T>::operator bool() const noexcept {\n  return field_id_ != nullptr;\n}\n\ntemplate<typename T>\ninline jfieldID JField<T>::getId() const noexcept {\n  return field_id_;\n}\n\n#pragma push_macro(\"DEFINE_FIELD_PRIMITIVE_GET_SET\")\n#undef DEFINE_FIELD_PRIMITIVE_GET_SET\n#define DEFINE_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD)                 \\\ntemplate<>                                                           \\\ninline TYPE JField<TYPE>::get(jobject object) const noexcept {       \\\n  const auto env = internal::getEnv();                               \\\n  return env->Get ## METHOD ## Field(object, field_id_);             \\\n}                                                                    \\\n                                                                     \\\ntemplate<>                                                           \\\ninline void JField<TYPE>::set(jobject object, TYPE value) noexcept { \\\n  const auto env = internal::getEnv();                               \\\n  env->Set ## METHOD ## Field(object, field_id_, value);             \\\n}\n\nDEFINE_FIELD_PRIMITIVE_GET_SET(jboolean, Boolean)\nDEFINE_FIELD_PRIMITIVE_GET_SET(jbyte, Byte)\nDEFINE_FIELD_PRIMITIVE_GET_SET(jchar, Char)\nDEFINE_FIELD_PRIMITIVE_GET_SET(jshort, Short)\nDEFINE_FIELD_PRIMITIVE_GET_SET(jint, Int)\nDEFINE_FIELD_PRIMITIVE_GET_SET(jlong, Long)\nDEFINE_FIELD_PRIMITIVE_GET_SET(jfloat, Float)\nDEFINE_FIELD_PRIMITIVE_GET_SET(jdouble, Double)\n#pragma pop_macro(\"DEFINE_FIELD_PRIMITIVE_GET_SET\")\n\ntemplate<typename T>\ninline T JField<T>::get(jobject object) const noexcept {\n  return static_cast<T>(internal::getEnv()->GetObjectField(object, field_id_));\n}\n\ntemplate<typename T>\ninline void JField<T>::set(jobject object, T value) noexcept {\n  internal::getEnv()->SetObjectField(object, field_id_, static_cast<jobject>(value));\n}\n\n// JStaticField<T> /////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\ninline JStaticField<T>::JStaticField(jfieldID field) noexcept\n  : field_id_{field}\n{}\n\ntemplate<typename T>\ninline JStaticField<T>::operator bool() const noexcept {\n  return field_id_ != nullptr;\n}\n\ntemplate<typename T>\ninline jfieldID JStaticField<T>::getId() const noexcept {\n  return field_id_;\n}\n\n#pragma push_macro(\"DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET\")\n#undef DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET\n#define DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(TYPE, METHOD)                \\\ntemplate<>                                                                 \\\ninline TYPE JStaticField<TYPE>::get(jclass jcls) const noexcept {          \\\n  const auto env = internal::getEnv();                                     \\\n  return env->GetStatic ## METHOD ## Field(jcls, field_id_);               \\\n}                                                                          \\\n                                                                           \\\ntemplate<>                                                                 \\\ninline void JStaticField<TYPE>::set(jclass jcls, TYPE value) noexcept {    \\\n  const auto env = internal::getEnv();                                     \\\n  env->SetStatic ## METHOD ## Field(jcls, field_id_, value);               \\\n}\n\nDEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jboolean, Boolean)\nDEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jbyte, Byte)\nDEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jchar, Char)\nDEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jshort, Short)\nDEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jint, Int)\nDEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jlong, Long)\nDEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jfloat, Float)\nDEFINE_STATIC_FIELD_PRIMITIVE_GET_SET(jdouble, Double)\n#pragma pop_macro(\"DEFINE_STATIC_FIELD_PRIMITIVE_GET_SET\")\n\ntemplate<typename T>\ninline T JStaticField<T>::get(jclass jcls) const noexcept {\n  const auto env = internal::getEnv();\n  return static_cast<T>(env->GetStaticObjectField(jcls, field_id_));\n}\n\ntemplate<typename T>\ninline void JStaticField<T>::set(jclass jcls, T value) noexcept {\n  internal::getEnv()->SetStaticObjectField(jcls, field_id_, value);\n}\n\n\n// jmethod_traits //////////////////////////////////////////////////////////////////////////////////\n\n// TODO(T6608405) Adapt this to implement a register natives method that requires no descriptor\nnamespace internal {\n\ntemplate<typename Head>\ninline std::string JavaDescriptor() {\n  return jtype_traits<Head>::descriptor();\n}\n\ntemplate<typename Head, typename Elem, typename... Tail>\ninline std::string JavaDescriptor() {\n  return JavaDescriptor<Head>() + JavaDescriptor<Elem, Tail...>();\n}\n\ntemplate<typename R, typename Arg1, typename... Args>\ninline std::string JMethodDescriptor() {\n  return \"(\" + JavaDescriptor<Arg1, Args...>() + \")\" + JavaDescriptor<R>();\n}\n\ntemplate<typename R>\ninline std::string JMethodDescriptor() {\n  return \"()\" + JavaDescriptor<R>();\n}\n\n} // internal\n\ntemplate<typename R, typename... Args>\ninline std::string jmethod_traits<R(Args...)>::descriptor() {\n  return internal::JMethodDescriptor<R, Args...>();\n}\n\ntemplate<typename R, typename... Args>\ninline std::string jmethod_traits<R(Args...)>::constructor_descriptor() {\n  return internal::JMethodDescriptor<void, Args...>();\n}\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/Meta.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n/** @file meta.h\n *\n * Provides wrappers for meta data such as methods and fields.\n */\n\n#pragma once\n\n#include <type_traits>\n#include <string>\n\n#include <jni.h>\n\n#include \"References-forward.h\"\n\n#ifdef __ANDROID__\n# include <android/log.h>\n# define XLOG_TAG \"fb-jni\"\n# define XLOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, XLOG_TAG, __VA_ARGS__)\n# define XLOGD(...) __android_log_print(ANDROID_LOG_DEBUG, XLOG_TAG, __VA_ARGS__)\n# define XLOGI(...) __android_log_print(ANDROID_LOG_INFO, XLOG_TAG, __VA_ARGS__)\n# define XLOGW(...) __android_log_print(ANDROID_LOG_WARN, XLOG_TAG, __VA_ARGS__)\n# define XLOGE(...) __android_log_print(ANDROID_LOG_ERROR, XLOG_TAG, __VA_ARGS__)\n# define XLOGWTF(...) __android_log_print(ANDROID_LOG_FATAL, XLOG_TAG, __VA_ARGS__)\n#endif\n\nnamespace facebook {\nnamespace jni {\n\n// This will get the reflected Java Method from the method_id, get it's invoke\n// method, and call the method via that. This shouldn't ever be needed, but\n// Android 6.0 crashes when calling a method on a java.lang.Proxy via jni.\ntemplate <typename... Args>\nlocal_ref<jobject> slowCall(jmethodID method_id, alias_ref<jobject> self, Args... args);\n\nclass JObject;\n\n\n/// Wrapper of a jmethodID. Provides a common base for JMethod specializations\nclass JMethodBase {\n public:\n  /// Verify that the method is valid\n  explicit operator bool() const noexcept;\n\n  /// Access the wrapped id\n  jmethodID getId() const noexcept;\n\n protected:\n  /// Create a wrapper of a method id\n  explicit JMethodBase(jmethodID method_id = nullptr) noexcept;\n\n private:\n  jmethodID method_id_;\n};\n\n\n/// Representation of a jmethodID\ntemplate<typename F>\nclass JMethod;\n\n/// @cond INTERNAL\n#pragma push_macro(\"DEFINE_PRIMITIVE_METHOD_CLASS\")\n\n#undef DEFINE_PRIMITIVE_METHOD_CLASS\n\n// Defining JMethod specializations based on return value\n#define DEFINE_PRIMITIVE_METHOD_CLASS(TYPE)                                      \\\ntemplate<typename... Args>                                                       \\\nclass JMethod<TYPE(Args...)> : public JMethodBase {                              \\\n public:                                                                         \\\n  static_assert(std::is_void<TYPE>::value || IsJniPrimitive<TYPE>(),             \\\n      \"TYPE must be primitive or void\");                                         \\\n                                                                                 \\\n  using JMethodBase::JMethodBase;                                                \\\n  JMethod() noexcept {};                                                         \\\n  JMethod(const JMethod& other) noexcept = default;                              \\\n                                                                                 \\\n  TYPE operator()(alias_ref<jobject> self, Args... args);                        \\\n                                                                                 \\\n  friend class JClass;                                                           \\\n}\n\nDEFINE_PRIMITIVE_METHOD_CLASS(void);\nDEFINE_PRIMITIVE_METHOD_CLASS(jboolean);\nDEFINE_PRIMITIVE_METHOD_CLASS(jbyte);\nDEFINE_PRIMITIVE_METHOD_CLASS(jchar);\nDEFINE_PRIMITIVE_METHOD_CLASS(jshort);\nDEFINE_PRIMITIVE_METHOD_CLASS(jint);\nDEFINE_PRIMITIVE_METHOD_CLASS(jlong);\nDEFINE_PRIMITIVE_METHOD_CLASS(jfloat);\nDEFINE_PRIMITIVE_METHOD_CLASS(jdouble);\n\n#pragma pop_macro(\"DEFINE_PRIMITIVE_METHOD_CLASS\")\n/// @endcond\n\n\n/// Convenience type representing constructors\n/// These should only be used with JClass::getConstructor and JClass::newObject.\ntemplate<typename F>\nstruct JConstructor : private JMethod<F> {\n  using JMethod<F>::JMethod;\n private:\n  JConstructor(const JMethod<F>& other) : JMethod<F>(other.getId()) {}\n  friend class JClass;\n};\n\n/// Representation of a jStaticMethodID\ntemplate<typename F>\nclass JStaticMethod;\n\n/// @cond INTERNAL\n#pragma push_macro(\"DEFINE_PRIMITIVE_STATIC_METHOD_CLASS\")\n\n#undef DEFINE_PRIMITIVE_STATIC_METHOD_CLASS\n\n// Defining JStaticMethod specializations based on return value\n#define DEFINE_PRIMITIVE_STATIC_METHOD_CLASS(TYPE)                          \\\ntemplate<typename... Args>                                                  \\\nclass JStaticMethod<TYPE(Args...)> : public JMethodBase {                   \\\n  static_assert(std::is_void<TYPE>::value || IsJniPrimitive<TYPE>(),        \\\n      \"T must be a JNI primitive or void\");                                 \\\n                                                                            \\\n public:                                                                    \\\n  using JMethodBase::JMethodBase;                                           \\\n  JStaticMethod() noexcept {};                                              \\\n  JStaticMethod(const JStaticMethod& other) noexcept = default;             \\\n                                                                            \\\n  TYPE operator()(alias_ref<jclass> cls, Args... args);                     \\\n                                                                            \\\n  friend class JClass;                                                      \\\n}\n\nDEFINE_PRIMITIVE_STATIC_METHOD_CLASS(void);\nDEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jboolean);\nDEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jbyte);\nDEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jchar);\nDEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jshort);\nDEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jint);\nDEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jlong);\nDEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jfloat);\nDEFINE_PRIMITIVE_STATIC_METHOD_CLASS(jdouble);\n\n#pragma pop_macro(\"DEFINE_PRIMITIVE_STATIC_METHOD_CLASS\")\n/// @endcond\n\n\n/// Representation of a jNonvirtualMethodID\ntemplate<typename F>\nclass JNonvirtualMethod;\n\n/// @cond INTERNAL\n#pragma push_macro(\"DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS\")\n\n#undef DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS\n\n// Defining JNonvirtualMethod specializations based on return value\n#define DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(TYPE)                     \\\ntemplate<typename... Args>                                                  \\\nclass JNonvirtualMethod<TYPE(Args...)> : public JMethodBase {               \\\n  static_assert(std::is_void<TYPE>::value || IsJniPrimitive<TYPE>(),        \\\n      \"T must be a JNI primitive or void\");                                 \\\n                                                                            \\\n public:                                                                    \\\n  using JMethodBase::JMethodBase;                                           \\\n  JNonvirtualMethod() noexcept {};                                          \\\n  JNonvirtualMethod(const JNonvirtualMethod& other) noexcept = default;     \\\n                                                                            \\\n  TYPE operator()(alias_ref<jobject> self, alias_ref<jclass> cls, Args... args);       \\\n                                                                            \\\n  friend class JClass;                                                      \\\n}\n\nDEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(void);\nDEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jboolean);\nDEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jbyte);\nDEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jchar);\nDEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jshort);\nDEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jint);\nDEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jlong);\nDEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jfloat);\nDEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS(jdouble);\n\n#pragma pop_macro(\"DEFINE_PRIMITIVE_NON_VIRTUAL_METHOD_CLASS\")\n/// @endcond\n\n\n/**\n * JField represents typed fields and simplifies their access. Note that object types return\n * raw pointers which generally should promptly get a wrap_local treatment.\n */\ntemplate<typename T>\nclass JField {\n  static_assert(IsJniScalar<T>(), \"T must be a JNI scalar\");\n\n public:\n  /// Wraps an existing field id\n  explicit JField(jfieldID field = nullptr) noexcept;\n\n  /// Verify that the id is valid\n  explicit operator bool() const noexcept;\n\n  /// Access the wrapped id\n  jfieldID getId() const noexcept;\n\n private:\n  jfieldID field_id_;\n\n  /// Get field value\n  /// @pre object != nullptr\n  T get(jobject object) const noexcept;\n\n  /// Set field value\n  /// @pre object != nullptr\n  void set(jobject object, T value) noexcept;\n\n  friend class JObject;\n};\n\n\n/**\n * JStaticField represents typed fields and simplifies their access. Note that object types\n * return raw pointers which generally should promptly get a wrap_local treatment.\n */\ntemplate<typename T>\nclass JStaticField {\n  static_assert(IsJniScalar<T>(), \"T must be a JNI scalar\");\n\n public:\n  /// Wraps an existing field id\n  explicit JStaticField(jfieldID field = nullptr) noexcept;\n\n  /// Verify that the id is valid\n  explicit operator bool() const noexcept;\n\n  /// Access the wrapped id\n  jfieldID getId() const noexcept;\n\n private:\n  jfieldID field_id_;\n\n  /// Get field value\n  /// @pre object != nullptr\n  T get(jclass jcls) const noexcept;\n\n  /// Set field value\n  /// @pre object != nullptr\n  void set(jclass jcls, T value) noexcept;\n\n  friend class JClass;\n  friend class JObject;\n};\n\n\n/// Template magic to provide @ref jmethod_traits\ntemplate<typename R, typename... Args>\nstruct jmethod_traits<R(Args...)> {\n  static std::string descriptor();\n  static std::string constructor_descriptor();\n};\n\n\n// jtype_traits ////////////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\nstruct jtype_traits {\nprivate:\n  using Repr = ReprType<T>;\npublic:\n  // The jni type signature (described at\n  // http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html).\n  static std::string descriptor() {\n    std::string descriptor;\n    if (Repr::kJavaDescriptor == nullptr) {\n      descriptor = Repr::get_instantiated_java_descriptor();\n    } else {\n      descriptor = Repr::kJavaDescriptor;\n    }\n    return descriptor;\n  }\n\n  // The signature used for class lookups. See\n  // http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#getName().\n  static std::string base_name() {\n    if (Repr::kJavaDescriptor != nullptr) {\n      std::string base_name = Repr::kJavaDescriptor;\n      return base_name.substr(1, base_name.size() - 2);\n    }\n    return Repr::get_instantiated_base_name();\n  }\n};\n\n#pragma push_macro(\"DEFINE_FIELD_AND_ARRAY_TRAIT\")\n#undef DEFINE_FIELD_AND_ARRAY_TRAIT\n\n#define DEFINE_FIELD_AND_ARRAY_TRAIT(TYPE, DSC)                     \\\ntemplate<>                                                          \\\nstruct jtype_traits<TYPE> {                                         \\\n  static std::string descriptor() { return std::string{#DSC}; }     \\\n  static std::string base_name() { return descriptor(); }           \\\n  using array_type = TYPE ## Array;                                 \\\n};                                                                  \\\ntemplate<>                                                          \\\nstruct jtype_traits<TYPE ## Array> {                                \\\n  static std::string descriptor() { return std::string{\"[\" #DSC}; } \\\n  static std::string base_name() { return descriptor(); }           \\\n  using entry_type = TYPE;                                          \\\n};\n\n// There is no voidArray, handle that without the macro.\ntemplate<>\nstruct jtype_traits<void> {\n  static std::string descriptor() { return std::string{\"V\"}; };\n};\n\nDEFINE_FIELD_AND_ARRAY_TRAIT(jboolean, Z)\nDEFINE_FIELD_AND_ARRAY_TRAIT(jbyte,    B)\nDEFINE_FIELD_AND_ARRAY_TRAIT(jchar,    C)\nDEFINE_FIELD_AND_ARRAY_TRAIT(jshort,   S)\nDEFINE_FIELD_AND_ARRAY_TRAIT(jint,     I)\nDEFINE_FIELD_AND_ARRAY_TRAIT(jlong,    J)\nDEFINE_FIELD_AND_ARRAY_TRAIT(jfloat,   F)\nDEFINE_FIELD_AND_ARRAY_TRAIT(jdouble,  D)\n\n#pragma pop_macro(\"DEFINE_FIELD_AND_ARRAY_TRAIT\")\n\n\ntemplate <typename T>\nstruct jmethod_traits_from_cxx;\n\n}}\n\n#include \"Meta-inl.h\"\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/MetaConvert.h",
    "content": "// Copyright 2004-present Facebook. All Rights Reserved.\n\n#pragma once\n\n#include <jni.h>\n\n#include \"Common.h\"\n#include \"References.h\"\n\nnamespace facebook {\nnamespace jni {\n\nnamespace detail {\n\n// In order to avoid potentially filling the jni locals table,\n// temporary objects (right now, this is just jstrings) need to be\n// released. This is done by returning a holder which autoconverts to\n// jstring.\ntemplate <typename T>\ninline T callToJni(T&& t) {\n  return t;\n}\n\ntemplate <typename T>\ninline JniType<T> callToJni(local_ref<T>&& sref) {\n  return sref.get();\n}\n\n// Normally, pass through types unmolested.\ntemplate <typename T, typename Enabled = void>\nstruct Convert {\n  typedef T jniType;\n  static jniType fromJni(jniType t) {\n    return t;\n  }\n  static jniType toJniRet(jniType t) {\n    return t;\n  }\n  static jniType toCall(jniType t) {\n    return t;\n  }\n};\n\n// This is needed for return conversion\ntemplate <>\nstruct Convert<void> {\n  typedef void jniType;\n};\n\n// jboolean is an unsigned char, not a bool. Allow it to work either way.\ntemplate<>\nstruct Convert<bool> {\n  typedef jboolean jniType;\n  static bool fromJni(jniType t) {\n    return t;\n  }\n  static jniType toJniRet(bool t) {\n    return t;\n  }\n  static jniType toCall(bool t) {\n    return t;\n  }\n};\n\n// convert to alias_ref<T> from T\ntemplate <typename T>\nstruct Convert<alias_ref<T>> {\n  typedef JniType<T> jniType;\n  static alias_ref<jniType> fromJni(jniType t) {\n    return wrap_alias(t);\n  }\n  static jniType toJniRet(alias_ref<jniType> t) {\n    return t.get();\n  }\n  static jniType toCall(alias_ref<jniType> t) {\n    return t.get();\n  }\n};\n\n// convert return from local_ref<T>\ntemplate <typename T>\nstruct Convert<local_ref<T>> {\n  typedef JniType<T> jniType;\n  // No automatic synthesis of local_ref\n  static jniType toJniRet(local_ref<jniType> t) {\n    return t.release();\n  }\n  static jniType toCall(local_ref<jniType> t) {\n    return t.get();\n  }\n};\n\n// convert return from global_ref<T>\ntemplate <typename T>\nstruct Convert<global_ref<T>> {\n  typedef JniType<T> jniType;\n  // No automatic synthesis of global_ref\n  static jniType toJniRet(global_ref<jniType>&& t) {\n    // If this gets called, ownership the global_ref was passed in here.  (It's\n    // probably a copy of a persistent global_ref made when a function was\n    // declared to return a global_ref, but it could moved out or otherwise not\n    // referenced elsewhere.  Doesn't matter.)  Either way, the only safe way\n    // to return it is to make a local_ref, release it, and return the\n    // underlying local jobject.\n    auto ret = make_local(t);\n    return ret.release();\n  }\n  static jniType toJniRet(const global_ref<jniType>& t) {\n    // If this gets called, the function was declared to return const&.  We\n    // have a ref to a global_ref whose lifetime will exceed this call, so we\n    // can just get the underlying jobject and return it to java without\n    // needing to make a local_ref.\n    return t.get();\n  }\n  static jniType toCall(global_ref<jniType> t) {\n    return t.get();\n  }\n};\n\ntemplate <typename T> struct jni_sig_from_cxx_t;\ntemplate <typename R, typename... Args>\nstruct jni_sig_from_cxx_t<R(Args...)> {\n  using JniRet = typename Convert<typename std::decay<R>::type>::jniType;\n  using JniSig = JniRet(typename Convert<typename std::decay<Args>::type>::jniType...);\n};\n\ntemplate <typename T>\nusing jni_sig_from_cxx = typename jni_sig_from_cxx_t<T>::JniSig;\n\n} // namespace detail\n\ntemplate <typename R, typename... Args>\nstruct jmethod_traits_from_cxx<R(Args...)> : jmethod_traits<detail::jni_sig_from_cxx<R(Args...)>> {\n};\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/NativeRunnable.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include \"CoreClasses.h\"\n#include \"Hybrid.h\"\n#include \"Registration.h\"\n\n#include <functional>\n\nnamespace facebook {\nnamespace jni {\n\nstruct JRunnable : public JavaClass<JRunnable> {\n  static auto constexpr kJavaDescriptor = \"Ljava/lang/Runnable;\";\n};\n\nstruct JNativeRunnable : public HybridClass<JNativeRunnable, JRunnable> {\n public:\n  static auto constexpr kJavaDescriptor = \"Lcom/facebook/jni/NativeRunnable;\";\n\n  JNativeRunnable(std::function<void()>&& runnable) : runnable_(std::move(runnable)) {}\n\n  static void OnLoad() {\n    registerHybrid({\n        makeNativeMethod(\"run\", JNativeRunnable::run),\n      });\n  }\n\n  void run() {\n    runnable_();\n  }\n\n private:\n  std::function<void()> runnable_;\n};\n\n\n} // namespace jni\n} // namespace facebook\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/ReferenceAllocators-inl.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <cassert>\n#include <new>\n#include <atomic>\n\nnamespace facebook {\nnamespace jni {\n\n/// @cond INTERNAL\nnamespace internal {\n\n// Statistics mostly provided for test (only updated if FBJNI_DEBUG_REFS is defined)\nstruct ReferenceStats {\n  std::atomic_uint locals_created, globals_created, weaks_created,\n                   locals_deleted, globals_deleted, weaks_deleted;\n\n  void reset() noexcept;\n};\n\nextern ReferenceStats g_reference_stats;\n}\n/// @endcond\n\n\n// LocalReferenceAllocator /////////////////////////////////////////////////////////////////////////\n\ninline jobject LocalReferenceAllocator::newReference(jobject original) const {\n  internal::dbglog(\"Local new: %p\", original);\n  #ifdef FBJNI_DEBUG_REFS\n    ++internal::g_reference_stats.locals_created;\n  #endif\n  auto ref = internal::getEnv()->NewLocalRef(original);\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n  return ref;\n}\n\ninline void LocalReferenceAllocator::deleteReference(jobject reference) const noexcept {\n  internal::dbglog(\"Local release: %p\", reference);\n\n  if (reference) {\n    #ifdef FBJNI_DEBUG_REFS\n      ++internal::g_reference_stats.locals_deleted;\n    #endif\n    assert(verifyReference(reference));\n    internal::getEnv()->DeleteLocalRef(reference);\n  }\n}\n\ninline bool LocalReferenceAllocator::verifyReference(jobject reference) const noexcept {\n  if (!reference || !internal::doesGetObjectRefTypeWork()) {\n    return true;\n  }\n  return internal::getEnv()->GetObjectRefType(reference) == JNILocalRefType;\n}\n\n\n// GlobalReferenceAllocator ////////////////////////////////////////////////////////////////////////\n\ninline jobject GlobalReferenceAllocator::newReference(jobject original) const {\n  internal::dbglog(\"Global new: %p\", original);\n  #ifdef FBJNI_DEBUG_REFS\n    ++internal::g_reference_stats.globals_created;\n  #endif\n  auto ref = internal::getEnv()->NewGlobalRef(original);\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n  return ref;\n}\n\ninline void GlobalReferenceAllocator::deleteReference(jobject reference) const noexcept {\n  internal::dbglog(\"Global release: %p\", reference);\n\n  if (reference) {\n    #ifdef FBJNI_DEBUG_REFS\n      ++internal::g_reference_stats.globals_deleted;\n    #endif\n    assert(verifyReference(reference));\n    internal::getEnv()->DeleteGlobalRef(reference);\n  }\n}\n\ninline bool GlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {\n  if (!reference || !internal::doesGetObjectRefTypeWork()) {\n    return true;\n  }\n  return internal::getEnv()->GetObjectRefType(reference) == JNIGlobalRefType;\n}\n\n\n// WeakGlobalReferenceAllocator ////////////////////////////////////////////////////////////////////\n\ninline jobject WeakGlobalReferenceAllocator::newReference(jobject original) const {\n  internal::dbglog(\"Weak global new: %p\", original);\n  #ifdef FBJNI_DEBUG_REFS\n    ++internal::g_reference_stats.weaks_created;\n  #endif\n  auto ref = internal::getEnv()->NewWeakGlobalRef(original);\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n  return ref;\n}\n\ninline void WeakGlobalReferenceAllocator::deleteReference(jobject reference) const noexcept {\n  internal::dbglog(\"Weak Global release: %p\", reference);\n\n  if (reference) {\n    #ifdef FBJNI_DEBUG_REFS\n      ++internal::g_reference_stats.weaks_deleted;\n    #endif\n    assert(verifyReference(reference));\n    internal::getEnv()->DeleteWeakGlobalRef(reference);\n  }\n}\n\ninline bool WeakGlobalReferenceAllocator::verifyReference(jobject reference) const noexcept {\n  if (!reference || !internal::doesGetObjectRefTypeWork()) {\n    return true;\n  }\n  return internal::getEnv()->GetObjectRefType(reference) == JNIWeakGlobalRefType;\n}\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/ReferenceAllocators.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n/**\n * @file ReferenceAllocators.h\n *\n * Reference allocators are used to create and delete various classes of JNI references (local,\n * global, and weak global).\n */\n\n#pragma once\n\n#include <fb/visibility.h>\n\n#include \"Common.h\"\n\nnamespace facebook { namespace jni {\n\n/// Allocator that handles local references\nclass FBEXPORT LocalReferenceAllocator {\n public:\n  jobject newReference(jobject original) const;\n  void deleteReference(jobject reference) const noexcept;\n  bool verifyReference(jobject reference) const noexcept;\n};\n\n/// Allocator that handles global references\nclass FBEXPORT GlobalReferenceAllocator {\n public:\n  jobject newReference(jobject original) const;\n  void deleteReference(jobject reference) const noexcept;\n  bool verifyReference(jobject reference) const noexcept;\n};\n\n/// Allocator that handles weak global references\nclass FBEXPORT WeakGlobalReferenceAllocator {\n public:\n  jobject newReference(jobject original) const;\n  void deleteReference(jobject reference) const noexcept;\n  bool verifyReference(jobject reference) const noexcept;\n};\n\n/// @cond INTERNAL\nnamespace internal {\n\n/**\n * @return true iff env->GetObjectRefType is expected to work properly.\n */\nFBEXPORT bool doesGetObjectRefTypeWork();\n\n}\n/// @endcond\n\n}}\n\n#include \"ReferenceAllocators-inl.h\"\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/References-forward.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include \"ReferenceAllocators.h\"\n\nnamespace facebook {\nnamespace jni {\n\ntemplate<typename T, typename Enable = void>\nclass JObjectWrapper;\n\nnamespace detail {\nstruct JObjectBase {\n  jobject get() const noexcept;\n  void set(jobject reference) noexcept;\n  jobject this_;\n};\n\n// RefReprType maps a type to the representation used by fbjni smart references.\ntemplate <typename T, typename Enable = void>\nstruct RefReprType;\n\ntemplate <typename T>\nstruct JavaObjectType;\n\ntemplate <typename T>\nstruct ReprAccess;\n}\n\n// Given T, either a jobject-like type or a JavaClass-derived type, ReprType<T>\n// is the corresponding JavaClass-derived type and JniType<T> is the\n// jobject-like type.\ntemplate <typename T>\nusing ReprType = typename detail::RefReprType<T>::type;\n\ntemplate <typename T>\nusing JniType = typename detail::JavaObjectType<T>::type;\n\ntemplate<typename T, typename Alloc>\nclass base_owned_ref;\n\ntemplate<typename T, typename Alloc>\nclass basic_strong_ref;\n\ntemplate<typename T>\nclass weak_ref;\n\ntemplate<typename T>\nclass alias_ref;\n\n/// A smart unique reference owning a local JNI reference\ntemplate<typename T>\nusing local_ref = basic_strong_ref<T, LocalReferenceAllocator>;\n\n/// A smart unique reference owning a global JNI reference\ntemplate<typename T>\nusing global_ref = basic_strong_ref<T, GlobalReferenceAllocator>;\n\n}} // namespace facebook::jni\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/References-inl.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <new>\n#include \"CoreClasses.h\"\n\nnamespace facebook {\nnamespace jni {\n\ntemplate<typename T>\ninline enable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref) {\n  return ref;\n}\n\ntemplate<typename T>\ninline JniType<T> getPlainJniReference(alias_ref<T> ref) {\n  return ref.get();\n}\n\ntemplate<typename T, typename A>\ninline JniType<T> getPlainJniReference(const base_owned_ref<T, A>& ref) {\n  return ref.get();\n}\n\n\nnamespace detail {\ntemplate <typename Repr>\nstruct ReprAccess {\n  using javaobject = JniType<Repr>;\n  static void set(Repr& repr, javaobject obj) noexcept {\n    repr.JObjectBase::set(obj);\n  }\n  static javaobject get(const Repr& repr) {\n    return static_cast<javaobject>(repr.JObject::get());\n  }\n};\n\nnamespace {\ntemplate <typename Repr>\nvoid StaticAssertValidRepr() noexcept {\n  static_assert(std::is_base_of<JObject, Repr>::value,\n      \"A smart ref representation must be derived from JObject.\");\n  static_assert(IsPlainJniReference<JniType<Repr>>(), \"T must be a JNI reference\");\n  static_assert(sizeof(Repr) == sizeof(JObjectBase), \"\");\n  static_assert(alignof(Repr) == alignof(JObjectBase), \"\");\n}\n}\n\ntemplate <typename Repr>\nReprStorage<Repr>::ReprStorage(JniType<Repr> obj) noexcept {\n  StaticAssertValidRepr<Repr>();\n  set(obj);\n}\n\ntemplate <typename Repr>\nvoid ReprStorage<Repr>::set(JniType<Repr> obj) noexcept {\n  new (&storage_) Repr;\n  ReprAccess<Repr>::set(get(), obj);\n}\n\ntemplate <typename Repr>\nRepr& ReprStorage<Repr>::get() noexcept {\n  return *reinterpret_cast<Repr*>(&storage_);\n}\n\ntemplate <typename Repr>\nconst Repr& ReprStorage<Repr>::get() const noexcept {\n  return *reinterpret_cast<const Repr*>(&storage_);\n}\n\ntemplate <typename Repr>\nJniType<Repr> ReprStorage<Repr>::jobj() const noexcept {\n  ReprAccess<Repr>::get(get());\n  return ReprAccess<Repr>::get(get());\n}\n\ntemplate <typename Repr>\nvoid ReprStorage<Repr>::swap(ReprStorage& other) noexcept {\n  StaticAssertValidRepr<Repr>();\n  using std::swap;\n  swap(get(), other.get());\n}\n\ninline void JObjectBase::set(jobject reference) noexcept {\n  this_ = reference;\n}\n\ninline jobject JObjectBase::get() const noexcept {\n  return this_;\n}\n\ntemplate<typename T, typename Alloc>\nenable_if_t<IsNonWeakReference<T>(), plain_jni_reference_t<T>> make_ref(const T& reference) {\n  auto old_reference = getPlainJniReference(reference);\n  if (!old_reference) {\n    return nullptr;\n  }\n\n  auto ref = Alloc{}.newReference(old_reference);\n  if (!ref) {\n    // Note that we end up here if we pass a weak ref that refers to a collected object.\n    // Thus, it's hard to come up with a reason why this function should be used with\n    // weak references.\n    throw std::bad_alloc{};\n  }\n\n  return static_cast<plain_jni_reference_t<T>>(ref);\n}\n\n} // namespace detail\n\ntemplate<typename T>\ninline local_ref<T> adopt_local(T ref) noexcept {\n  static_assert(IsPlainJniReference<T>(), \"T must be a plain jni reference\");\n  return local_ref<T>{ref};\n}\n\ntemplate<typename T>\ninline global_ref<T> adopt_global(T ref) noexcept {\n  static_assert(IsPlainJniReference<T>(), \"T must be a plain jni reference\");\n  return global_ref<T>{ref};\n}\n\ntemplate<typename T>\ninline weak_ref<T> adopt_weak_global(T ref) noexcept {\n  static_assert(IsPlainJniReference<T>(), \"T must be a plain jni reference\");\n  return weak_ref<T>{ref};\n}\n\n\ntemplate<typename T>\ninline enable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept {\n  return alias_ref<T>(ref);\n}\n\n\ntemplate<typename T>\nenable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;\n\n\ntemplate<typename T>\nenable_if_t<IsNonWeakReference<T>(), local_ref<plain_jni_reference_t<T>>>\nmake_local(const T& ref) {\n  return adopt_local(detail::make_ref<T, LocalReferenceAllocator>(ref));\n}\n\ntemplate<typename T>\nenable_if_t<IsNonWeakReference<T>(), global_ref<plain_jni_reference_t<T>>>\nmake_global(const T& ref) {\n  return adopt_global(detail::make_ref<T, GlobalReferenceAllocator>(ref));\n}\n\ntemplate<typename T>\nenable_if_t<IsNonWeakReference<T>(), weak_ref<plain_jni_reference_t<T>>>\nmake_weak(const T& ref) {\n  return adopt_weak_global(detail::make_ref<T, WeakGlobalReferenceAllocator>(ref));\n}\n\ntemplate<typename T1, typename T2>\ninline enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>\noperator==(const T1& a, const T2& b) {\n  return isSameObject(getPlainJniReference(a), getPlainJniReference(b));\n}\n\ntemplate<typename T1, typename T2>\ninline enable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>\noperator!=(const T1& a, const T2& b) {\n  return !(a == b);\n}\n\n\n// base_owned_ref ///////////////////////////////////////////////////////////////////////\n\ntemplate<typename T, typename Alloc>\ninline base_owned_ref<T, Alloc>::base_owned_ref() noexcept\n  : base_owned_ref(nullptr)\n{}\n\ntemplate<typename T, typename Alloc>\ninline base_owned_ref<T, Alloc>::base_owned_ref(std::nullptr_t t) noexcept\n  : base_owned_ref(static_cast<javaobject>(nullptr))\n{}\n\ntemplate<typename T, typename Alloc>\ninline base_owned_ref<T, Alloc>::base_owned_ref(const base_owned_ref& other)\n  : storage_{static_cast<javaobject>(Alloc{}.newReference(other.get()))}\n{}\n\ntemplate<typename T, typename Alloc>\ntemplate<typename U>\ninline base_owned_ref<T, Alloc>::base_owned_ref(const base_owned_ref<U, Alloc>& other)\n  : storage_{static_cast<javaobject>(Alloc{}.newReference(other.get()))}\n{\n  static_assert(std::is_convertible<JniType<U>, javaobject>::value, \"\");\n}\n\ntemplate<typename T, typename Alloc>\ninline facebook::jni::base_owned_ref<T, Alloc>::base_owned_ref(\n    javaobject reference) noexcept\n  : storage_(reference) {\n  assert(Alloc{}.verifyReference(reference));\n  internal::dbglog(\"New wrapped ref=%p this=%p\", get(), this);\n}\n\ntemplate<typename T, typename Alloc>\ninline base_owned_ref<T, Alloc>::base_owned_ref(\n    base_owned_ref<T, Alloc>&& other) noexcept\n  : storage_(other.get()) {\n  internal::dbglog(\"New move from ref=%p other=%p\", other.get(), &other);\n  internal::dbglog(\"New move to ref=%p this=%p\", get(), this);\n  // JObject is a simple type and does not support move semantics so we explicitly\n  // clear other\n  other.set(nullptr);\n}\n\ntemplate<typename T, typename Alloc>\ntemplate<typename U>\nbase_owned_ref<T, Alloc>::base_owned_ref(base_owned_ref<U, Alloc>&& other) noexcept\n  : storage_(other.get()) {\n  internal::dbglog(\"New move from ref=%p other=%p\", other.get(), &other);\n  internal::dbglog(\"New move to ref=%p this=%p\", get(), this);\n  // JObject is a simple type and does not support move semantics so we explicitly\n  // clear other\n  other.set(nullptr);\n}\n\ntemplate<typename T, typename Alloc>\ninline base_owned_ref<T, Alloc>::~base_owned_ref() noexcept {\n  reset();\n  internal::dbglog(\"Ref destruct ref=%p this=%p\", get(), this);\n}\n\ntemplate<typename T, typename Alloc>\ninline auto base_owned_ref<T, Alloc>::release() noexcept -> javaobject {\n  auto value = get();\n  internal::dbglog(\"Ref release ref=%p this=%p\", value, this);\n  set(nullptr);\n  return value;\n}\n\ntemplate<typename T, typename Alloc>\ninline void base_owned_ref<T,Alloc>::reset() noexcept {\n  reset(nullptr);\n}\n\ntemplate<typename T, typename Alloc>\ninline void base_owned_ref<T,Alloc>::reset(javaobject reference) noexcept {\n  if (get()) {\n    assert(Alloc{}.verifyReference(reference));\n    Alloc{}.deleteReference(get());\n  }\n  set(reference);\n}\n\ntemplate<typename T, typename Alloc>\ninline auto base_owned_ref<T, Alloc>::get() const noexcept -> javaobject {\n  return storage_.jobj();\n}\n\ntemplate<typename T, typename Alloc>\ninline void base_owned_ref<T, Alloc>::set(javaobject ref) noexcept {\n  storage_.set(ref);\n}\n\n\n// weak_ref ///////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\ninline weak_ref<T>& weak_ref<T>::operator=(\n    const weak_ref& other) {\n  auto otherCopy = other;\n  swap(*this, otherCopy);\n  return *this;\n}\n\ntemplate<typename T>\ninline weak_ref<T>& weak_ref<T>::operator=(\n    weak_ref<T>&& other) noexcept {\n  internal::dbglog(\"Op= move ref=%p this=%p oref=%p other=%p\",\n      get(), this, other.get(), &other);\n  reset(other.release());\n  return *this;\n}\n\ntemplate<typename T>\nlocal_ref<T> weak_ref<T>::lockLocal() const {\n  return adopt_local(\n      static_cast<javaobject>(LocalReferenceAllocator{}.newReference(get())));\n}\n\ntemplate<typename T>\nglobal_ref<T> weak_ref<T>::lockGlobal() const {\n  return adopt_global(\n      static_cast<javaobject>(GlobalReferenceAllocator{}.newReference(get())));\n}\n\ntemplate<typename T>\ninline void swap(\n    weak_ref<T>& a,\n    weak_ref<T>& b) noexcept {\n  internal::dbglog(\"Ref swap a.ref=%p a=%p b.ref=%p b=%p\",\n      a.get(), &a, b.get(), &b);\n  a.storage_.swap(b.storage_);\n}\n\n\n// basic_strong_ref ////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T, typename Alloc>\ninline basic_strong_ref<T, Alloc>& basic_strong_ref<T, Alloc>::operator=(\n    const basic_strong_ref& other) {\n  auto otherCopy = other;\n  swap(*this, otherCopy);\n  return *this;\n}\n\ntemplate<typename T, typename Alloc>\ninline basic_strong_ref<T, Alloc>& basic_strong_ref<T, Alloc>::operator=(\n    basic_strong_ref<T, Alloc>&& other) noexcept {\n  internal::dbglog(\"Op= move ref=%p this=%p oref=%p other=%p\",\n      get(), this, other.get(), &other);\n  reset(other.release());\n  return *this;\n}\n\ntemplate<typename T, typename Alloc>\ninline alias_ref<T> basic_strong_ref<T, Alloc>::releaseAlias() noexcept {\n  return wrap_alias(release());\n}\n\ntemplate<typename T, typename Alloc>\ninline basic_strong_ref<T, Alloc>::operator bool() const noexcept {\n  return get() != nullptr;\n}\n\ntemplate<typename T, typename Alloc>\ninline auto basic_strong_ref<T, Alloc>::operator->() noexcept -> Repr* {\n  return &storage_.get();\n}\n\ntemplate<typename T, typename Alloc>\ninline auto basic_strong_ref<T, Alloc>::operator->() const noexcept -> const Repr* {\n  return &storage_.get();\n}\n\ntemplate<typename T, typename Alloc>\ninline auto basic_strong_ref<T, Alloc>::operator*() noexcept -> Repr& {\n  return storage_.get();\n}\n\ntemplate<typename T, typename Alloc>\ninline auto basic_strong_ref<T, Alloc>::operator*() const noexcept -> const Repr& {\n  return storage_.get();\n}\n\ntemplate<typename T, typename Alloc>\ninline void swap(\n    basic_strong_ref<T, Alloc>& a,\n    basic_strong_ref<T, Alloc>& b) noexcept {\n  internal::dbglog(\"Ref swap a.ref=%p a=%p b.ref=%p b=%p\",\n      a.get(), &a, b.get(), &b);\n  using std::swap;\n  a.storage_.swap(b.storage_);\n}\n\n\n// alias_ref //////////////////////////////////////////////////////////////////////////////\n\ntemplate<typename T>\ninline alias_ref<T>::alias_ref() noexcept\n  : storage_{nullptr}\n{}\n\ntemplate<typename T>\ninline alias_ref<T>::alias_ref(std::nullptr_t) noexcept\n  : storage_{nullptr}\n{}\n\ntemplate<typename T>\ninline alias_ref<T>::alias_ref(const alias_ref& other) noexcept\n  : storage_{other.get()}\n{}\n\ntemplate<typename T>\ninline alias_ref<T>::alias_ref(javaobject ref) noexcept\n  : storage_(ref) {\n  assert(\n      LocalReferenceAllocator{}.verifyReference(ref) ||\n      GlobalReferenceAllocator{}.verifyReference(ref));\n}\n\ntemplate<typename T>\ntemplate<typename TOther, typename /* for SFINAE */>\ninline alias_ref<T>::alias_ref(alias_ref<TOther> other) noexcept\n  : storage_{other.get()}\n{}\n\ntemplate<typename T>\ntemplate<typename TOther, typename AOther, typename /* for SFINAE */>\ninline alias_ref<T>::alias_ref(const basic_strong_ref<TOther, AOther>& other) noexcept\n  : storage_{other.get()}\n{}\n\ntemplate<typename T>\ninline alias_ref<T>& alias_ref<T>::operator=(alias_ref other) noexcept {\n  swap(*this, other);\n  return *this;\n}\n\ntemplate<typename T>\ninline alias_ref<T>::operator bool() const noexcept {\n  return get() != nullptr;\n}\n\ntemplate<typename T>\ninline auto facebook::jni::alias_ref<T>::get() const noexcept -> javaobject {\n  return storage_.jobj();\n}\n\ntemplate<typename T>\ninline auto alias_ref<T>::operator->() noexcept -> Repr* {\n  return &(**this);\n}\n\ntemplate<typename T>\ninline auto alias_ref<T>::operator->() const noexcept -> const Repr* {\n  return &(**this);\n}\n\ntemplate<typename T>\ninline auto alias_ref<T>::operator*() noexcept -> Repr& {\n  return storage_.get();\n}\n\ntemplate<typename T>\ninline auto alias_ref<T>::operator*() const noexcept -> const Repr& {\n  return storage_.get();\n}\n\ntemplate<typename T>\ninline void alias_ref<T>::set(javaobject ref) noexcept {\n  storage_.set(ref);\n}\n\ntemplate<typename T>\ninline void swap(alias_ref<T>& a, alias_ref<T>& b) noexcept {\n  a.storage_.swap(b.storage_);\n}\n\n// Could reduce code duplication by using a pointer-to-function\n// template argument.  I'm not sure whether that would make the code\n// more maintainable (DRY), or less (too clever/confusing.).\ntemplate<typename T, typename U>\nenable_if_t<IsPlainJniReference<T>(), local_ref<T>>\nstatic_ref_cast(const local_ref<U>& ref) noexcept\n{\n  T p = static_cast<T>(ref.get());\n  return make_local(p);\n}\n\ntemplate<typename T, typename U>\nenable_if_t<IsPlainJniReference<T>(), global_ref<T>>\nstatic_ref_cast(const global_ref<U>& ref) noexcept\n{\n  T p = static_cast<T>(ref.get());\n  return make_global(p);\n}\n\ntemplate<typename T, typename U>\nenable_if_t<IsPlainJniReference<T>(), alias_ref<T>>\nstatic_ref_cast(const alias_ref<U>& ref) noexcept\n{\n  T p = static_cast<T>(ref.get());\n  return wrap_alias(p);\n}\n\ntemplate<typename T, typename RefType>\nauto dynamic_ref_cast(const RefType& ref) ->\nenable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))>\n{\n  if (!ref) {\n    return decltype(static_ref_cast<T>(ref))();\n  }\n\n  static alias_ref<jclass> target_class = findClassStatic(jtype_traits<T>::base_name().c_str());\n  if (!target_class) {\n    throwNewJavaException(\"java/lang/ClassCastException\",\n                          \"Could not find class %s.\",\n                          jtype_traits<T>::base_name().c_str());\n\n  }\n\n  local_ref<jclass> source_class = ref->getClass();\n\n  if (!target_class->isAssignableFrom(source_class)) {\n    throwNewJavaException(\"java/lang/ClassCastException\",\n                          \"Tried to cast from %s to %s.\",\n                          source_class->toString().c_str(),\n                          jtype_traits<T>::base_name().c_str());\n  }\n\n  return static_ref_cast<T>(ref);\n}\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/References.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n\n/** @file References.h\n *\n * Functionality similar to smart pointers, but for references into the VM. Four main reference\n * types are provided: local_ref, global_ref, weak_ref, and alias_ref. All are generic\n * templates that and refer to objects in the jobject hierarchy. The type of the referred objects\n * are specified using the template parameter. All reference types except alias_ref own their\n * underlying reference, just as a std smart pointer owns the underlying raw pointer. In the context\n * of std smart pointers, these references behave like unique_ptr, and have basically the same\n * interface. Thus, when the reference is destructed, the plain JNI reference, i.e. the underlying\n * JNI reference (like the parameters passed directly to JNI functions), is released. The alias\n * references provides no ownership and is a simple wrapper for plain JNI references.\n *\n * All but the weak references provides access to the underlying object using dereferencing, and a\n * get() method. It is also possible to convert these references to booleans to test for nullity.\n * To access the underlying object of a weak reference, the reference must either be released, or\n * the weak reference can be used to create a local or global reference.\n *\n * An owning reference is created either by moving the reference from an existing owned reference,\n * by copying an existing owned reference (which creates a new underlying reference), by using the\n * default constructor which initialize the reference to nullptr, or by using a helper function. The\n * helper function exist in two flavors: make_XXX or adopt_XXX.\n *\n * Adopting takes a plain JNI reference and wrap it in an owned reference. It takes ownership of the\n * plain JNI reference so be sure that no one else owns the reference when you adopt it, and make\n * sure that you know what kind of reference it is.\n *\n * New owned references can be created from existing plain JNI references, alias references, local\n * references, and global references (i.e. non-weak references) using the make_local, make_global,\n * and make_weak functions.\n *\n * Alias references can be implicitly initialized using global, local and plain JNI references using\n * the wrap_alias function. Here, we don't assume ownership of the passed-in reference, but rather\n * create a separate reference that we do own, leaving the passed-in reference to its fate.\n *\n * Similar rules apply for assignment. An owned reference can be copy or move assigned using a smart\n * reference of the same type. In the case of copy assignment a new reference is created. Alias\n * reference can also be assigned new values, but since they are simple wrappers of plain JNI\n * references there is no move semantics involved.\n *\n * Alias references are special in that they do not own the object and can therefore safely be\n * converted to and from its corresponding plain JNI reference. They are useful as parameters of\n * functions that do not affect the lifetime of a reference. Usage can be compared with using plain\n * JNI pointers as parameters where a function does not take ownership of the underlying object.\n *\n * The local, global, and alias references makes it possible to access methods in the underlying\n * objects. A core set of classes are implemented in CoreClasses.h, and user defined wrappers are\n * supported (see example below). The wrappers also supports inheritance so a wrapper can inherit\n * from another wrapper to gain access to its functionality. As an example the jstring wrapper\n * inherits from the jobject wrapper, so does the jclass wrapper. That means that you can for\n * example call the toString() method using the jclass wrapper, or any other class that inherits\n * from the jobject wrapper.\n *\n * Note that the wrappers are parameterized on the static type of your (jobject) pointer, thus if\n * you have a jobject that refers to a Java String you will need to cast it to jstring to get the\n * jstring wrapper. This also mean that if you make a down cast that is invalid there will be no one\n * stopping you and the wrappers currently does not detect this which can cause crashes. Thus, cast\n * wisely.\n *\n * @include WrapperSample.cpp\n */\n\n#pragma once\n\n#include <cassert>\n#include <cstddef>\n#include <type_traits>\n\n#include <jni.h>\n\n#include <fb/visibility.h>\n\n#include \"ReferenceAllocators.h\"\n#include \"TypeTraits.h\"\n#include \"References-forward.h\"\n\nnamespace facebook {\nnamespace jni {\n\n/// Convenience function to wrap an existing local reference\ntemplate<typename T>\nlocal_ref<T> adopt_local(T ref) noexcept;\n\n/// Convenience function to wrap an existing global reference\ntemplate<typename T>\nglobal_ref<T> adopt_global(T ref) noexcept;\n\n/// Convenience function to wrap an existing weak reference\ntemplate<typename T>\nweak_ref<T> adopt_weak_global(T ref) noexcept;\n\n\n/// Swaps two owning references of the same type\ntemplate<typename T>\nvoid swap(weak_ref<T>& a, weak_ref<T>& b) noexcept;\n\n/// Swaps two owning references of the same type\ntemplate<typename T, typename Alloc>\nvoid swap(basic_strong_ref<T, Alloc>& a, basic_strong_ref<T, Alloc>& b) noexcept;\n\n/**\n * Retrieve the plain reference from a plain reference.\n */\ntemplate<typename T>\nenable_if_t<IsPlainJniReference<T>(), T> getPlainJniReference(T ref);\n\n/**\n * Retrieve the plain reference from an alias reference.\n */\ntemplate<typename T>\nJniType<T> getPlainJniReference(alias_ref<T> ref);\n\n/**\n * Retrieve the plain JNI reference from any reference owned reference.\n */\ntemplate<typename T, typename Alloc>\nJniType<T> getPlainJniReference(const base_owned_ref<T, Alloc>& ref);\n\nclass JObject;\nclass JClass;\n\nnamespace detail {\n\ntemplate <typename T, typename Enable = void>\nstruct HasJniRefRepr : std::false_type {};\n\ntemplate <typename T>\nstruct HasJniRefRepr<T, typename std::enable_if<!std::is_same<typename T::JniRefRepr, void>::value, void>::type> : std::true_type {\n  using type = typename T::JniRefRepr;\n};\n\ntemplate <typename T>\nstruct RefReprType<T*> {\n  using type = typename std::conditional<HasJniRefRepr<T>::value, typename HasJniRefRepr<T>::type, JObjectWrapper<T*>>::type;\n  static_assert(std::is_base_of<JObject, type>::value,\n      \"Repr type missing JObject base.\");\n  static_assert(std::is_same<type, typename RefReprType<type>::type>::value,\n      \"RefReprType<T> not idempotent\");\n};\n\ntemplate <typename T>\nstruct RefReprType<T, typename std::enable_if<std::is_base_of<JObject, T>::value, void>::type> {\n  using type = T;\n  static_assert(std::is_base_of<JObject, type>::value,\n      \"Repr type missing JObject base.\");\n  static_assert(std::is_same<type, typename RefReprType<type>::type>::value,\n      \"RefReprType<T> not idempotent\");\n};\n\ntemplate <typename T>\nstruct JavaObjectType {\n  using type = typename RefReprType<T>::type::javaobject;\n  static_assert(IsPlainJniReference<type>(),\n      \"JavaObjectType<T> not a plain jni reference\");\n  static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,\n      \"JavaObjectType<T> not idempotent\");\n};\n\ntemplate <typename T>\nstruct JavaObjectType<JObjectWrapper<T>> {\n  using type = T;\n  static_assert(IsPlainJniReference<type>(),\n      \"JavaObjectType<T> not a plain jni reference\");\n  static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,\n      \"JavaObjectType<T> not idempotent\");\n};\n\ntemplate <typename T>\nstruct JavaObjectType<T*> {\n  using type = T*;\n  static_assert(IsPlainJniReference<type>(),\n      \"JavaObjectType<T> not a plain jni reference\");\n  static_assert(std::is_same<type, typename JavaObjectType<type>::type>::value,\n      \"JavaObjectType<T> not idempotent\");\n};\n\ntemplate <typename Repr>\nstruct ReprStorage {\n  explicit ReprStorage(JniType<Repr> obj) noexcept;\n\n  void set(JniType<Repr> obj) noexcept;\n\n  Repr& get() noexcept;\n  const Repr& get() const noexcept;\n  JniType<Repr> jobj() const noexcept;\n\n  void swap(ReprStorage& other) noexcept;\n private:\n  ReprStorage() = delete;\n  ReprStorage(const ReprStorage&) = delete;\n  ReprStorage(ReprStorage&&) = delete;\n  ReprStorage& operator=(const ReprStorage&) = delete;\n  ReprStorage& operator=(ReprStorage&&) = delete;\n\n  using Storage = typename std::aligned_storage<sizeof(JObjectBase), alignof(JObjectBase)>::type;\n  Storage storage_;\n};\n\n} // namespace detail\n\n/**\n * Create a new local reference from an existing reference\n *\n * @param ref a plain JNI, alias, or strong reference\n * @return an owned local reference (referring to null if the input does)\n * @throws std::bad_alloc if the JNI reference could not be created\n */\ntemplate<typename T>\nenable_if_t<IsNonWeakReference<T>(), local_ref<plain_jni_reference_t<T>>>\nmake_local(const T& r);\n\n/**\n * Create a new global reference from an existing reference\n *\n * @param ref a plain JNI, alias, or strong reference\n * @return an owned global reference (referring to null if the input does)\n * @throws std::bad_alloc if the JNI reference could not be created\n */\ntemplate<typename T>\nenable_if_t<IsNonWeakReference<T>(), global_ref<plain_jni_reference_t<T>>>\nmake_global(const T& r);\n\n/**\n * Create a new weak global reference from an existing reference\n *\n * @param ref a plain JNI, alias, or strong reference\n * @return an owned weak global reference (referring to null if the input does)\n * @throws std::bad_alloc if the returned reference is null\n */\ntemplate<typename T>\nenable_if_t<IsNonWeakReference<T>(), weak_ref<plain_jni_reference_t<T>>>\nmake_weak(const T& r);\n\n/**\n * Compare two references to see if they refer to the same object\n */\ntemplate<typename T1, typename T2>\nenable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>\noperator==(const T1& a, const T2& b);\n\n/**\n * Compare two references to see if they don't refer to the same object\n */\ntemplate<typename T1, typename T2>\nenable_if_t<IsNonWeakReference<T1>() && IsNonWeakReference<T2>(), bool>\noperator!=(const T1& a, const T2& b);\n\ntemplate<typename T, typename Alloc>\nclass base_owned_ref {\n public:\n  using javaobject = JniType<T>;\n\n  /**\n   * Release the ownership and set the reference to null. Thus no deleter is invoked.\n   * @return Returns the reference\n   */\n  javaobject release() noexcept;\n\n  /**\n   * Reset the reference to refer to nullptr.\n   */\n  void reset() noexcept;\n\n protected:\n  using Repr = ReprType<T>;\n  detail::ReprStorage<Repr> storage_;\n\n  javaobject get() const noexcept;\n  void set(javaobject ref) noexcept;\n\n  /*\n   * Wrap an existing reference and transfers its ownership to the newly created unique reference.\n   * NB! Does not create a new reference\n   */\n  explicit base_owned_ref(javaobject reference) noexcept;\n\n  /// Create a null reference\n  base_owned_ref() noexcept;\n\n  /// Create a null reference\n  explicit base_owned_ref(std::nullptr_t) noexcept;\n\n  /// Copy constructor (note creates a new reference)\n  base_owned_ref(const base_owned_ref& other);\n  template<typename U>\n  base_owned_ref(const base_owned_ref<U, Alloc>& other);\n\n  /// Transfers ownership of an underlying reference from one unique reference to another\n  base_owned_ref(base_owned_ref&& other) noexcept;\n  template<typename U>\n  base_owned_ref(base_owned_ref<U, Alloc>&& other) noexcept;\n\n  /// The delete the underlying reference if applicable\n  ~base_owned_ref() noexcept;\n\n\n  /// Assignment operator (note creates a new reference)\n  base_owned_ref& operator=(const base_owned_ref& other);\n\n  /// Assignment by moving a reference thus not creating a new reference\n  base_owned_ref& operator=(base_owned_ref&& rhs) noexcept;\n\n  void reset(javaobject reference) noexcept;\n\n  friend javaobject jni::getPlainJniReference<>(const base_owned_ref& ref);\n\n  template<typename U, typename UAlloc>\n  friend class base_owned_ref;\n};\n\n\n/**\n * A smart reference that owns its underlying JNI reference. The class provides basic\n * functionality to handle a reference but gives no access to it unless the reference is\n * released, thus no longer owned. The API is stolen with pride from unique_ptr and the\n * semantics should be basically the same. This class should not be used directly, instead use\n * @ref weak_ref\n */\ntemplate<typename T>\nclass weak_ref : public base_owned_ref<T, WeakGlobalReferenceAllocator> {\n public:\n  using javaobject = JniType<T>;\n\n  using Allocator = WeakGlobalReferenceAllocator;\n\n  // This inherits non-default, non-copy, non-move ctors.\n  using base_owned_ref<T, Allocator>::base_owned_ref;\n\n  /// Create a null reference\n  weak_ref() noexcept\n    : base_owned_ref<T, Allocator>{} {}\n\n  /// Create a null reference\n  /* implicit */ weak_ref(std::nullptr_t) noexcept\n    : base_owned_ref<T, Allocator>{nullptr} {}\n\n  /// Copy constructor (note creates a new reference)\n  weak_ref(const weak_ref& other)\n    : base_owned_ref<T, Allocator>{other} {}\n\n  // This needs to be explicit to change its visibility.\n  template<typename U>\n  weak_ref(const weak_ref<U>& other)\n    : base_owned_ref<T, Allocator>{other} {}\n\n  /// Transfers ownership of an underlying reference from one unique reference to another\n  weak_ref(weak_ref&& other) noexcept\n    : base_owned_ref<T, Allocator>{std::move(other)} {}\n\n\n  /// Assignment operator (note creates a new reference)\n  weak_ref& operator=(const weak_ref& other);\n\n  /// Assignment by moving a reference thus not creating a new reference\n  weak_ref& operator=(weak_ref&& rhs) noexcept;\n\n  // Creates an owned local reference to the referred object or to null if the object is reclaimed\n  local_ref<T> lockLocal() const;\n\n  // Creates an owned global reference to the referred object or to null if the object is reclaimed\n  global_ref<T> lockGlobal() const;\n\n private:\n  // get/release/reset on weak_ref are not exposed to users.\n  using base_owned_ref<T, Allocator>::get;\n  using base_owned_ref<T, Allocator>::release;\n  using base_owned_ref<T, Allocator>::reset;\n  /*\n   * Wrap an existing reference and transfers its ownership to the newly created unique reference.\n   * NB! Does not create a new reference\n   */\n  explicit weak_ref(javaobject reference) noexcept\n    : base_owned_ref<T, Allocator>{reference} {}\n\n  template<typename T2> friend class weak_ref;\n  friend weak_ref<javaobject> adopt_weak_global<javaobject>(javaobject ref) noexcept;\n  friend void swap<T>(weak_ref& a, weak_ref& b) noexcept;\n};\n\n\n/**\n * A class representing owned strong references to Java objects. This class\n * should not be used directly, instead use @ref local_ref, or @ref global_ref.\n */\ntemplate<typename T, typename Alloc>\nclass basic_strong_ref : public base_owned_ref<T, Alloc> {\n  using typename base_owned_ref<T, Alloc>::Repr;\n public:\n  using javaobject = JniType<T>;\n\n  using Allocator = Alloc;\n\n  // This inherits non-default, non-copy, non-move ctors.\n  using base_owned_ref<T, Alloc>::base_owned_ref;\n  using base_owned_ref<T, Alloc>::release;\n  using base_owned_ref<T, Alloc>::reset;\n\n  /// Create a null reference\n  basic_strong_ref() noexcept\n    : base_owned_ref<T, Alloc>{} {}\n\n  /// Create a null reference\n  /* implicit */ basic_strong_ref(std::nullptr_t) noexcept\n    : base_owned_ref<T, Alloc>{nullptr} {}\n\n  /// Copy constructor (note creates a new reference)\n  basic_strong_ref(const basic_strong_ref& other)\n    : base_owned_ref<T, Alloc>{other} {}\n\n  // This needs to be explicit to change its visibility.\n  template<typename U>\n  basic_strong_ref(const basic_strong_ref<U, Alloc>& other)\n    : base_owned_ref<T, Alloc>{other} {}\n\n  /// Transfers ownership of an underlying reference from one unique reference to another\n  basic_strong_ref(basic_strong_ref&& other) noexcept\n    : base_owned_ref<T, Alloc>{std::move(other)} {}\n\n  /// Assignment operator (note creates a new reference)\n  basic_strong_ref& operator=(const basic_strong_ref& other);\n\n  /// Assignment by moving a reference thus not creating a new reference\n  basic_strong_ref& operator=(basic_strong_ref&& rhs) noexcept;\n\n  /// Get the plain JNI reference\n  using base_owned_ref<T, Allocator>::get;\n\n  /// Release the ownership of the reference and return the wrapped reference in an alias\n  alias_ref<T> releaseAlias() noexcept;\n\n  /// Checks if the reference points to a non-null object\n  explicit operator bool() const noexcept;\n\n  /// Access the functionality provided by the object wrappers\n  Repr* operator->() noexcept;\n\n  /// Access the functionality provided by the object wrappers\n  const Repr* operator->() const noexcept;\n\n  /// Provide a reference to the underlying wrapper (be sure that it is non-null before invoking)\n  Repr& operator*() noexcept;\n\n  /// Provide a const reference to the underlying wrapper (be sure that it is non-null\n  /// before invoking)\n  const Repr& operator*() const noexcept;\n\n private:\n\n  using base_owned_ref<T, Alloc>::storage_;\n\n  /*\n   * Wrap an existing reference and transfers its ownership to the newly created unique reference.\n   * NB! Does not create a new reference\n   */\n  explicit basic_strong_ref(javaobject reference) noexcept\n    : base_owned_ref<T, Alloc>{reference} {}\n\n\n  friend local_ref<T> adopt_local<T>(T ref) noexcept;\n  friend global_ref<T> adopt_global<T>(T ref) noexcept;\n  friend void swap<T, Alloc>(basic_strong_ref& a, basic_strong_ref& b) noexcept;\n};\n\n\ntemplate<typename T>\nenable_if_t<IsPlainJniReference<T>(), alias_ref<T>> wrap_alias(T ref) noexcept;\n\n/// Swaps to alias reference of the same type\ntemplate<typename T>\nvoid swap(alias_ref<T>& a, alias_ref<T>& b) noexcept;\n\n/**\n * A non-owning variant of the smart references (a dumb reference). These references still provide\n * access to the functionality of the @ref JObjectWrapper specializations including exception\n * handling and ease of use. Use this representation when you don't want to claim ownership of the\n * underlying reference (compare to using raw pointers instead of smart pointers.) For symmetry use\n * @ref alias_ref instead of this class.\n */\ntemplate<typename T>\nclass alias_ref {\n  using Repr = ReprType<T>;\n\n public:\n  using javaobject = JniType<T>;\n\n  /// Create a null reference\n  alias_ref() noexcept;\n\n  /// Create a null reference\n  /* implicit */ alias_ref(std::nullptr_t) noexcept;\n\n  /// Copy constructor\n  alias_ref(const alias_ref& other) noexcept;\n\n  /// Wrap an existing plain JNI reference\n  /* implicit */ alias_ref(javaobject ref) noexcept;\n\n  /// Wrap an existing smart reference of any type convertible to T\n  template<\n    typename TOther,\n    typename = enable_if_t<\n      IsConvertible<JniType<TOther>, javaobject>(), T>\n    >\n  alias_ref(alias_ref<TOther> other) noexcept;\n\n  /// Wrap an existing alias reference of a type convertible to T\n  template<\n    typename TOther,\n    typename AOther,\n    typename = enable_if_t<\n      IsConvertible<JniType<TOther>, javaobject>(), T>\n    >\n  alias_ref(const basic_strong_ref<TOther, AOther>& other) noexcept;\n\n  /// Assignment operator\n  alias_ref& operator=(alias_ref other) noexcept;\n\n  /// Checks if the reference points to a non-null object\n  explicit operator bool() const noexcept;\n\n  /// Converts back to a plain JNI reference\n  javaobject get() const noexcept;\n\n  /// Access the functionality provided by the object wrappers\n  Repr* operator->() noexcept;\n\n  /// Access the functionality provided by the object wrappers\n  const Repr* operator->() const noexcept;\n\n  /// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)\n  Repr& operator*() noexcept;\n\n  /// Provide a guaranteed non-null reference (be sure that it is non-null before invoking)\n  const Repr& operator*() const noexcept;\n\n private:\n  void set(javaobject ref) noexcept;\n\n  detail::ReprStorage<Repr> storage_;\n\n  friend void swap<T>(alias_ref& a, alias_ref& b) noexcept;\n};\n\n\n/**\n * RAII object to create a local JNI frame, using PushLocalFrame/PopLocalFrame.\n *\n * This is useful when you have a call which is initiated from C++-land, and therefore\n * doesn't automatically get a local JNI frame managed for you by the JNI framework.\n */\nclass FBEXPORT JniLocalScope {\npublic:\n  JniLocalScope(JNIEnv* p_env, jint capacity);\n  ~JniLocalScope();\n\nprivate:\n  JNIEnv* env_;\n  bool hasFrame_;\n};\n\ntemplate<typename T, typename U>\nenable_if_t<IsPlainJniReference<T>(), local_ref<T>>\nstatic_ref_cast(const local_ref<U>& ref) noexcept;\n\ntemplate<typename T, typename U>\nenable_if_t<IsPlainJniReference<T>(), global_ref<T>>\nstatic_ref_cast(const global_ref<U>& ref) noexcept;\n\ntemplate<typename T, typename U>\nenable_if_t<IsPlainJniReference<T>(), alias_ref<T>>\nstatic_ref_cast(const alias_ref<U>& ref) noexcept;\n\ntemplate<typename T, typename RefType>\nauto dynamic_ref_cast(const RefType& ref) ->\nenable_if_t<IsPlainJniReference<T>(), decltype(static_ref_cast<T>(ref))> ;\n\n}}\n\n#include \"References-inl.h\"\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/Registration-inl.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include \"Exceptions.h\"\n#include \"Hybrid.h\"\n\nnamespace facebook {\nnamespace jni {\n\nnamespace detail {\n\n#ifdef __i386__\n// X86 ABI forces 16 byte stack allignment on calls. Unfortunately\n// sometimes Dalvik chooses not to obey the ABI:\n// - https://code.google.com/p/android/issues/detail?id=61012\n// - https://android.googlesource.com/platform/ndk/+/81696d2%5E!/\n// Therefore, we tell the compiler to re-align the stack on entry\n// to our JNI functions.\n#define JNI_ENTRY_POINT __attribute__((force_align_arg_pointer))\n#else\n#define JNI_ENTRY_POINT\n#endif\n\ntemplate <typename R>\nstruct CreateDefault {\n  static R create() {\n    return R{};\n  }\n};\n\ntemplate <>\nstruct CreateDefault<void> {\n  static void create() {}\n};\n\ntemplate <typename R>\nusing Converter = Convert<typename std::decay<R>::type>;\n\ntemplate <typename F, F func, typename R, typename... Args>\nstruct WrapForVoidReturn {\n  static typename Converter<R>::jniType call(Args&&... args) {\n    return Converter<R>::toJniRet(func(std::forward<Args>(args)...));\n  }\n};\n\ntemplate <typename F, F func, typename... Args>\nstruct WrapForVoidReturn<F, func, void, Args...> {\n  static void call(Args&&... args) {\n    func(std::forward<Args>(args)...);\n  }\n};\n\n// registration wrapper for legacy JNI-style functions\ntemplate<typename F, F func, typename C, typename R, typename... Args>\nstruct BareJniWrapper {\n  JNI_ENTRY_POINT static R call(JNIEnv* env, jobject obj, Args... args) {\n    ThreadScope ts(env, internal::CacheEnvTag{});\n    try {\n      return (*func)(env, static_cast<JniType<C>>(obj), args...);\n    } catch (...) {\n      translatePendingCppExceptionToJavaException();\n      return CreateDefault<R>::create();\n    }\n  }\n};\n\n// registration wrappers for functions, with autoconversion of arguments.\ntemplate<typename F, F func, typename C, typename R, typename... Args>\nstruct FunctionWrapper {\n  using jniRet = typename Converter<R>::jniType;\n  JNI_ENTRY_POINT static jniRet call(JNIEnv* env, jobject obj, typename Converter<Args>::jniType... args) {\n    ThreadScope ts(env, internal::CacheEnvTag{});\n    try {\n      return WrapForVoidReturn<F, func, R, JniType<C>, Args...>::call(\n          static_cast<JniType<C>>(obj), Converter<Args>::fromJni(args)...);\n    } catch (...) {\n      translatePendingCppExceptionToJavaException();\n      return CreateDefault<jniRet>::create();\n    }\n  }\n};\n\n// registration wrappers for non-static methods, with autoconvertion of arguments.\ntemplate<typename M, M method, typename C, typename R, typename... Args>\nstruct MethodWrapper {\n  using jhybrid = typename C::jhybridobject;\n  static R dispatch(alias_ref<jhybrid> ref, Args&&... args) {\n    try {\n      // This is usually a noop, but if the hybrid object is a\n      // base class of other classes which register JNI methods,\n      // this will get the right type for the registered method.\n      auto cobj = static_cast<C*>(ref->cthis());\n      return (cobj->*method)(std::forward<Args>(args)...);\n    } catch (const std::exception& ex) {\n      C::mapException(ex);\n      throw;\n    }\n  }\n\n  JNI_ENTRY_POINT static typename Converter<R>::jniType call(\n      JNIEnv* env, jobject obj, typename Converter<Args>::jniType... args) {\n    return FunctionWrapper<R(*)(alias_ref<jhybrid>, Args&&...), dispatch, jhybrid, R, Args...>::call(env, obj, args...);\n  }\n};\n\ntemplate<typename F, F func, typename C, typename R, typename... Args>\ninline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(JNIEnv*, C, Args... args)) {\n  // This intentionally erases the real type; JNI will do it anyway\n  return reinterpret_cast<NativeMethodWrapper*>(&(BareJniWrapper<F, func, C, R, Args...>::call));\n}\n\ntemplate<typename F, F func, typename C, typename R, typename... Args>\ninline NativeMethodWrapper* exceptionWrapJNIMethod(R (*)(alias_ref<C>, Args... args)) {\n  // This intentionally erases the real type; JNI will do it anyway\n  return reinterpret_cast<NativeMethodWrapper*>(&(FunctionWrapper<F, func, C, R, Args...>::call));\n}\n\ntemplate<typename M, M method, typename C, typename R, typename... Args>\ninline NativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args)) {\n  // This intentionally erases the real type; JNI will do it anyway\n  return reinterpret_cast<NativeMethodWrapper*>(&(MethodWrapper<M, method, C, R, Args...>::call));\n}\n\ntemplate<typename R, typename C, typename... Args>\ninline std::string makeDescriptor(R (*)(JNIEnv*, C, Args... args)) {\n  return jmethod_traits<R(Args...)>::descriptor();\n}\n\ntemplate<typename R, typename C, typename... Args>\ninline std::string makeDescriptor(R (*)(alias_ref<C>, Args... args)) {\n  return jmethod_traits_from_cxx<R(Args...)>::descriptor();\n}\n\ntemplate<typename R, typename C, typename... Args>\ninline std::string makeDescriptor(R (C::*)(Args... args)) {\n  return jmethod_traits_from_cxx<R(Args...)>::descriptor();\n}\n\n}\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/Registration.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <jni.h>\n#include \"References.h\"\n\nnamespace facebook {\nnamespace jni {\n\nnamespace detail {\n\n// This uses the real JNI function as a non-type template parameter to\n// cause a (static member) function to exist with the same signature,\n// but with try/catch exception translation.\ntemplate<typename F, F func, typename C, typename R, typename... Args>\nNativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(JNIEnv*, jobject, Args... args));\n\n// Automatically wrap object argument, and don't take env explicitly.\ntemplate<typename F, F func, typename C, typename R, typename... Args>\nNativeMethodWrapper* exceptionWrapJNIMethod(R (*func0)(alias_ref<C>, Args... args));\n\n// Extract C++ instance from object, and invoke given method on it,\ntemplate<typename M, M method, typename C, typename R, typename... Args>\nNativeMethodWrapper* exceptionWrapJNIMethod(R (C::*method0)(Args... args));\n\n// This uses deduction to figure out the descriptor name if the types\n// are primitive or have JObjectWrapper specializations.\ntemplate<typename R, typename C, typename... Args>\nstd::string makeDescriptor(R (*func)(JNIEnv*, C, Args... args));\n\n// This uses deduction to figure out the descriptor name if the types\n// are primitive or have JObjectWrapper specializations.\ntemplate<typename R, typename C, typename... Args>\nstd::string makeDescriptor(R (*func)(alias_ref<C>, Args... args));\n\n// This uses deduction to figure out the descriptor name if the types\n// are primitive or have JObjectWrapper specializations.\ntemplate<typename R, typename C, typename... Args>\nstd::string makeDescriptor(R (C::*method0)(Args... args));\n\n}\n\n// We have to use macros here, because the func needs to be used\n// as both a decltype expression argument and as a non-type template\n// parameter, since C++ provides no way for translateException\n// to deduce the type of its non-type template parameter.\n// The empty string in the macros below ensures that name\n// is always a string literal (because that syntax is only\n// valid when name is a string literal).\n#define makeNativeMethod2(name, func)                                   \\\n  { name \"\", ::facebook::jni::detail::makeDescriptor(&func),            \\\n      ::facebook::jni::detail::exceptionWrapJNIMethod<decltype(&func), &func>(&func) }\n\n#define makeNativeMethod3(name, desc, func)                             \\\n  { name \"\", desc,                                                      \\\n      ::facebook::jni::detail::exceptionWrapJNIMethod<decltype(&func), &func>(&func) }\n\n// Variadic template hacks to get macros with different numbers of\n// arguments. Usage instructions are in CoreClasses.h.\n#define makeNativeMethodN(a, b, c, count, ...) makeNativeMethod ## count\n#define makeNativeMethod(...) makeNativeMethodN(__VA_ARGS__, 3, 2)(__VA_ARGS__)\n\n}}\n\n#include \"Registration-inl.h\"\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni/TypeTraits.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <type_traits>\n\n#include \"References-forward.h\"\n\nnamespace facebook {\nnamespace jni {\n\n/// Generic std::enable_if helper\ntemplate<bool B, typename T>\nusing enable_if_t = typename std::enable_if<B, T>::type;\n\n/// Generic std::is_convertible helper\ntemplate<typename From, typename To>\nconstexpr bool IsConvertible() {\n  return std::is_convertible<From, To>::value;\n}\n\ntemplate<template<typename...> class TT, typename T>\nstruct is_instantiation_of : std::false_type {};\n\ntemplate<template<typename...> class TT, typename... Ts>\nstruct is_instantiation_of<TT, TT<Ts...>> : std::true_type {};\n\ntemplate<template<typename...> class TT, typename... Ts>\nconstexpr bool IsInstantiationOf() {\n  return is_instantiation_of<TT, Ts...>::value;\n}\n\n/// Metafunction to determine whether a type is a JNI reference or not\ntemplate<typename T>\nstruct is_plain_jni_reference :\n  std::integral_constant<bool,\n      std::is_pointer<T>::value &&\n      std::is_base_of<\n        typename std::remove_pointer<jobject>::type,\n        typename std::remove_pointer<T>::type>::value> {};\n\n/// Helper to simplify use of is_plain_jni_reference\ntemplate<typename T>\nconstexpr bool IsPlainJniReference() {\n  return is_plain_jni_reference<T>::value;\n}\n\n/// Metafunction to determine whether a type is a primitive JNI type or not\ntemplate<typename T>\nstruct is_jni_primitive :\n  std::integral_constant<bool,\n    std::is_same<jboolean, T>::value ||\n    std::is_same<jbyte, T>::value ||\n    std::is_same<jchar, T>::value ||\n    std::is_same<jshort, T>::value ||\n    std::is_same<jint, T>::value ||\n    std::is_same<jlong, T>::value ||\n    std::is_same<jfloat, T>::value ||\n    std::is_same<jdouble, T>::value> {};\n\n/// Helper to simplify use of is_jni_primitive\ntemplate<typename T>\nconstexpr bool IsJniPrimitive() {\n  return is_jni_primitive<T>::value;\n}\n\n/// Metafunction to determine whether a type is a JNI array of primitives or not\ntemplate <typename T>\nstruct is_jni_primitive_array :\n  std::integral_constant<bool,\n    std::is_same<jbooleanArray, T>::value ||\n    std::is_same<jbyteArray, T>::value ||\n    std::is_same<jcharArray, T>::value ||\n    std::is_same<jshortArray, T>::value ||\n    std::is_same<jintArray, T>::value ||\n    std::is_same<jlongArray, T>::value ||\n    std::is_same<jfloatArray, T>::value ||\n    std::is_same<jdoubleArray, T>::value> {};\n\n/// Helper to simplify use of is_jni_primitive_array\ntemplate <typename T>\nconstexpr bool IsJniPrimitiveArray() {\n  return is_jni_primitive_array<T>::value;\n}\n\n/// Metafunction to determine if a type is a scalar (primitive or reference) JNI type\ntemplate<typename T>\nstruct is_jni_scalar :\n  std::integral_constant<bool,\n    is_plain_jni_reference<T>::value ||\n    is_jni_primitive<T>::value> {};\n\n/// Helper to simplify use of is_jni_scalar\ntemplate<typename T>\nconstexpr bool IsJniScalar() {\n  return is_jni_scalar<T>::value;\n}\n\n// Metafunction to determine if a type is a JNI type\ntemplate<typename T>\nstruct is_jni_type :\n  std::integral_constant<bool,\n    is_jni_scalar<T>::value ||\n    std::is_void<T>::value> {};\n\n/// Helper to simplify use of is_jni_type\ntemplate<typename T>\nconstexpr bool IsJniType() {\n  return is_jni_type<T>::value;\n}\n\ntemplate<typename T>\nstruct is_non_weak_reference :\n  std::integral_constant<bool,\n    IsPlainJniReference<T>() ||\n    IsInstantiationOf<basic_strong_ref, T>() ||\n    IsInstantiationOf<alias_ref, T>()> {};\n\ntemplate<typename T>\nconstexpr bool IsNonWeakReference() {\n  return is_non_weak_reference<T>::value;\n}\n\ntemplate<typename T>\nstruct is_any_reference :\n  std::integral_constant<bool,\n    IsPlainJniReference<T>() ||\n    IsInstantiationOf<weak_ref, T>() ||\n    IsInstantiationOf<basic_strong_ref, T>() ||\n    IsInstantiationOf<alias_ref, T>()> {};\n\ntemplate<typename T>\nconstexpr bool IsAnyReference() {\n  return is_any_reference<T>::value;\n}\n\ntemplate<typename T>\nstruct reference_traits {\n  using plain_jni_reference_t = JniType<T>;\n  static_assert(IsPlainJniReference<plain_jni_reference_t>(), \"Need a plain JNI reference\");\n};\n\ntemplate<template <typename...> class R, typename T, typename... A>\nstruct reference_traits<R<T, A...>> {\n  using plain_jni_reference_t = JniType<T>;\n  static_assert(IsPlainJniReference<plain_jni_reference_t>(), \"Need a plain JNI reference\");\n};\n\ntemplate<typename T>\nusing plain_jni_reference_t = typename reference_traits<T>::plain_jni_reference_t;\n\n} // namespace jni\n} // namespace facebook\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/fbjni.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <jni.h>\n\n#include <fb/Environment.h>\n#include <fb/ALog.h>\n#include <fb/fbjni/Common.h>\n#include <fb/fbjni/Exceptions.h>\n#include <fb/fbjni/ReferenceAllocators.h>\n#include <fb/fbjni/References.h>\n#include <fb/fbjni/Meta.h>\n#include <fb/fbjni/CoreClasses.h>\n#include <fb/fbjni/Iterator.h>\n#include <fb/fbjni/Hybrid.h>\n#include <fb/fbjni/Registration.h>\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/log.h",
    "content": "/*\n * Copyright (C) 2005 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/*\n * FB Wrapper for logging functions.\n *\n * The android logging API uses the macro \"LOG()\" for its logic, which means\n * that it conflicts with random other places that use LOG for their own\n * purposes and doesn't work right half the places you include it\n *\n * FBLOG uses exactly the same semantics (FBLOGD for debug etc) but because of\n * the FB prefix it's strictly better. FBLOGV also gets stripped out based on\n * whether NDEBUG is set, but can be overridden by FBLOG_NDEBUG\n *\n * Most of the rest is a copy of <cutils/log.h> with minor changes.\n */\n\n//\n// C/C++ logging functions.  See the logging documentation for API details.\n//\n// We'd like these to be available from C code (in case we import some from\n// somewhere), so this has a C interface.\n//\n// The output will be correct when the log file is shared between multiple\n// threads and/or multiple processes so long as the operating system\n// supports O_APPEND.  These calls have mutex-protected data structures\n// and so are NOT reentrant.  Do not use LOG in a signal handler.\n//\n#pragma once\n\n#include <fb/visibility.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifdef ANDROID\n#include <android/log.h>\n#else\n// These declarations are needed for our internal use even on non-Android\n// builds.\n// (they are borrowed from <android/log.h>)\n\n/*\n * Android log priority values, in ascending priority order.\n */\ntypedef enum android_LogPriority {\n  ANDROID_LOG_UNKNOWN = 0,\n  ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */\n  ANDROID_LOG_VERBOSE,\n  ANDROID_LOG_DEBUG,\n  ANDROID_LOG_INFO,\n  ANDROID_LOG_WARN,\n  ANDROID_LOG_ERROR,\n  ANDROID_LOG_FATAL,\n  ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */\n} android_LogPriority;\n\n/*\n * Send a simple string to the log.\n */\nint __android_log_write(int prio, const char *tag, const char *text);\n\n/*\n * Send a formatted string to the log, used like printf(fmt,...)\n */\nint __android_log_print(int prio, const char *tag, const char *fmt, ...)\n#if defined(__GNUC__)\n    __attribute__((format(printf, 3, 4)))\n#endif\n    ;\n\n#endif\n\n// ---------------------------------------------------------------------\n\n/*\n * Normally we strip FBLOGV (VERBOSE messages) from release builds.\n * You can modify this (for example with \"#define FBLOG_NDEBUG 0\"\n * at the top of your source file) to change that behavior.\n */\n#ifndef FBLOG_NDEBUG\n#ifdef NDEBUG\n#define FBLOG_NDEBUG 1\n#else\n#define FBLOG_NDEBUG 0\n#endif\n#endif\n\n/*\n * This is the local tag used for the following simplified\n * logging macros.  You can change this preprocessor definition\n * before using the other macros to change the tag.\n */\n#ifndef LOG_TAG\n#define LOG_TAG NULL\n#endif\n\n// ---------------------------------------------------------------------\n\n/*\n * Simplified macro to send a verbose log message using the current LOG_TAG.\n */\n#ifndef FBLOGV\n#if FBLOG_NDEBUG\n#define FBLOGV(...) ((void)0)\n#else\n#define FBLOGV(...) ((void)FBLOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))\n#endif\n#endif\n\n#define CONDITION(cond) (__builtin_expect((cond) != 0, 0))\n\n#ifndef FBLOGV_IF\n#if FBLOG_NDEBUG\n#define FBLOGV_IF(cond, ...) ((void)0)\n#else\n#define FBLOGV_IF(cond, ...)                                            \\\n  ((CONDITION(cond)) ? ((void)FBLOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \\\n                     : (void)0)\n#endif\n#endif\n\n/*\n * Simplified macro to send a debug log message using the current LOG_TAG.\n */\n#ifndef FBLOGD\n#define FBLOGD(...) ((void)FBLOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))\n#endif\n\n#ifndef FBLOGD_IF\n#define FBLOGD_IF(cond, ...) \\\n  ((CONDITION(cond)) ? ((void)FBLOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) : (void)0)\n#endif\n\n/*\n * Simplified macro to send an info log message using the current LOG_TAG.\n */\n#ifndef FBLOGI\n#define FBLOGI(...) ((void)FBLOG(LOG_INFO, LOG_TAG, __VA_ARGS__))\n#endif\n\n#ifndef FBLOGI_IF\n#define FBLOGI_IF(cond, ...) \\\n  ((CONDITION(cond)) ? ((void)FBLOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) : (void)0)\n#endif\n\n/*\n * Simplified macro to send a warning log message using the current LOG_TAG.\n */\n#ifndef FBLOGW\n#define FBLOGW(...) ((void)FBLOG(LOG_WARN, LOG_TAG, __VA_ARGS__))\n#endif\n\n#ifndef FBLOGW_IF\n#define FBLOGW_IF(cond, ...) \\\n  ((CONDITION(cond)) ? ((void)FBLOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) : (void)0)\n#endif\n\n/*\n * Simplified macro to send an error log message using the current LOG_TAG.\n */\n#ifndef FBLOGE\n#define FBLOGE(...) ((void)FBLOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))\n#endif\n\n#ifndef FBLOGE_IF\n#define FBLOGE_IF(cond, ...) \\\n  ((CONDITION(cond)) ? ((void)FBLOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) : (void)0)\n#endif\n\n// ---------------------------------------------------------------------\n\n/*\n * Conditional based on whether the current LOG_TAG is enabled at\n * verbose priority.\n */\n#ifndef IF_FBLOGV\n#if FBLOG_NDEBUG\n#define IF_FBLOGV() if (false)\n#else\n#define IF_FBLOGV() IF_FBLOG(LOG_VERBOSE, LOG_TAG)\n#endif\n#endif\n\n/*\n * Conditional based on whether the current LOG_TAG is enabled at\n * debug priority.\n */\n#ifndef IF_FBLOGD\n#define IF_FBLOGD() IF_FBLOG(LOG_DEBUG, LOG_TAG)\n#endif\n\n/*\n * Conditional based on whether the current LOG_TAG is enabled at\n * info priority.\n */\n#ifndef IF_FBLOGI\n#define IF_FBLOGI() IF_FBLOG(LOG_INFO, LOG_TAG)\n#endif\n\n/*\n * Conditional based on whether the current LOG_TAG is enabled at\n * warn priority.\n */\n#ifndef IF_FBLOGW\n#define IF_FBLOGW() IF_FBLOG(LOG_WARN, LOG_TAG)\n#endif\n\n/*\n * Conditional based on whether the current LOG_TAG is enabled at\n * error priority.\n */\n#ifndef IF_FBLOGE\n#define IF_FBLOGE() IF_FBLOG(LOG_ERROR, LOG_TAG)\n#endif\n\n// ---------------------------------------------------------------------\n\n/*\n * Log a fatal error.  If the given condition fails, this stops program\n * execution like a normal assertion, but also generating the given message.\n * It is NOT stripped from release builds.  Note that the condition test\n * is -inverted- from the normal assert() semantics.\n */\n#define FBLOG_ALWAYS_FATAL_IF(cond, ...)                                   \\\n  ((CONDITION(cond)) ? ((void)fb_printAssert(#cond, LOG_TAG, __VA_ARGS__)) \\\n                     : (void)0)\n\n#define FBLOG_ALWAYS_FATAL(...) \\\n  (((void)fb_printAssert(NULL, LOG_TAG, __VA_ARGS__)))\n\n/*\n * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that\n * are stripped out of release builds.\n */\n#if FBLOG_NDEBUG\n\n#define FBLOG_FATAL_IF(cond, ...) ((void)0)\n#define FBLOG_FATAL(...) ((void)0)\n\n#else\n\n#define FBLOG_FATAL_IF(cond, ...) FBLOG_ALWAYS_FATAL_IF(cond, __VA_ARGS__)\n#define FBLOG_FATAL(...) FBLOG_ALWAYS_FATAL(__VA_ARGS__)\n\n#endif\n\n/*\n * Assertion that generates a log message when the assertion fails.\n * Stripped out of release builds.  Uses the current LOG_TAG.\n */\n#define FBLOG_ASSERT(cond, ...) FBLOG_FATAL_IF(!(cond), __VA_ARGS__)\n//#define LOG_ASSERT(cond) LOG_FATAL_IF(!(cond), \"Assertion failed: \" #cond)\n\n// ---------------------------------------------------------------------\n\n/*\n * Basic log message macro.\n *\n * Example:\n *  FBLOG(LOG_WARN, NULL, \"Failed with error %d\", errno);\n *\n * The second argument may be NULL or \"\" to indicate the \"global\" tag.\n */\n#ifndef FBLOG\n#define FBLOG(priority, tag, ...) \\\n  FBLOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)\n#endif\n\n#ifndef FBLOG_BY_DELIMS\n#define FBLOG_BY_DELIMS(priority, tag, delims, msg, ...) \\\n  logPrintByDelims(ANDROID_##priority, tag, delims, msg, ##__VA_ARGS__)\n#endif\n\n/*\n * Log macro that allows you to specify a number for the priority.\n */\n#ifndef FBLOG_PRI\n#define FBLOG_PRI(priority, tag, ...) fb_printLog(priority, tag, __VA_ARGS__)\n#endif\n\n/*\n * Log macro that allows you to pass in a varargs (\"args\" is a va_list).\n */\n#ifndef FBLOG_PRI_VA\n#define FBLOG_PRI_VA(priority, tag, fmt, args) \\\n  fb_vprintLog(priority, NULL, tag, fmt, args)\n#endif\n\n/*\n * Conditional given a desired logging priority and tag.\n */\n#ifndef IF_FBLOG\n#define IF_FBLOG(priority, tag) if (fb_testLog(ANDROID_##priority, tag))\n#endif\n\ntypedef void (*LogHandler)(int priority, const char* tag, const char* message);\nFBEXPORT void setLogHandler(LogHandler logHandler);\n\n/*\n * ===========================================================================\n *\n * The stuff in the rest of this file should not be used directly.\n */\nFBEXPORT int fb_printLog(int prio, const char* tag, const char* fmt, ...)\n#if defined(__GNUC__)\n    __attribute__((format(printf, 3, 4)))\n#endif\n    ;\n\n#define fb_vprintLog(prio, cond, tag, fmt...) \\\n  __android_log_vprint(prio, tag, fmt)\n\n#define fb_printAssert(cond, tag, fmt...) __android_log_assert(cond, tag, fmt)\n\n#define fb_writeLog(prio, tag, text) __android_log_write(prio, tag, text)\n\n#define fb_bWriteLog(tag, payload, len) __android_log_bwrite(tag, payload, len)\n#define fb_btWriteLog(tag, type, payload, len) \\\n  __android_log_btwrite(tag, type, payload, len)\n\n#define fb_testLog(prio, tag) (1)\n\n/*\n * FB extensions\n */\nvoid logPrintByDelims(int priority, const char* tag, const char* delims,\n                      const char* msg, ...);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/lyra.h",
    "content": "// Copyright 2004-present Facebook. All Rights Reserved.\n\n#pragma once\n\n#include <iomanip>\n#include <iostream>\n#include <string>\n#include <vector>\n\n#include <fb/visibility.h>\n\nnamespace facebook {\nnamespace lyra {\n\nconstexpr size_t kDefaultLimit = 64;\n\nusing InstructionPointer = const void*;\n\nclass FBEXPORT StackTraceElement {\n public:\n  StackTraceElement(InstructionPointer absoluteProgramCounter,\n                    InstructionPointer libraryBase,\n                    InstructionPointer functionAddress, std::string libraryName,\n                    std::string functionName)\n      : absoluteProgramCounter_{absoluteProgramCounter},\n        libraryBase_{libraryBase},\n        functionAddress_{functionAddress},\n        libraryName_{std::move(libraryName)},\n        functionName_{std::move(functionName)} {}\n\n  InstructionPointer libraryBase() const noexcept { return libraryBase_; }\n\n  InstructionPointer functionAddress() const noexcept {\n    return functionAddress_;\n  }\n\n  InstructionPointer absoluteProgramCounter() const noexcept {\n    return absoluteProgramCounter_;\n  }\n\n  const std::string& libraryName() const noexcept { return libraryName_; }\n\n  const std::string& functionName() const noexcept { return functionName_; }\n\n  /**\n   * The offset of the program counter to the base of the library (i.e. the\n   * address that addr2line takes as input>\n   */\n  std::ptrdiff_t libraryOffset() const noexcept {\n    auto absoluteLibrary = static_cast<const char*>(libraryBase_);\n    auto absoluteabsoluteProgramCounter =\n        static_cast<const char*>(absoluteProgramCounter_);\n    return absoluteabsoluteProgramCounter - absoluteLibrary;\n  }\n\n  /**\n   * The offset within the current function\n   */\n  int functionOffset() const noexcept {\n    auto absoluteSymbol = static_cast<const char*>(functionAddress_);\n    auto absoluteabsoluteProgramCounter =\n        static_cast<const char*>(absoluteProgramCounter_);\n    return absoluteabsoluteProgramCounter - absoluteSymbol;\n  }\n\n private:\n  const InstructionPointer absoluteProgramCounter_;\n  const InstructionPointer libraryBase_;\n  const InstructionPointer functionAddress_;\n  const std::string libraryName_;\n  const std::string functionName_;\n};\n\n/**\n * Populate the vector with the current stack trace\n *\n * Note that this trace needs to be symbolicated to get the library offset even\n * if it is to be symbolicated off-line.\n *\n * Beware of a bug on some platforms, which makes the trace loop until the\n * buffer is full when it reaches a noexpr function. It seems to be fixed in\n * newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846\n *\n * @param stackTrace The vector that will receive the stack trace. Before\n * filling the vector it will be cleared. The vector will never grow so the\n * number of frames captured is limited by the capacity of it.\n *\n * @param skip The number of frames to skip before capturing the trace\n */\nFBEXPORT void getStackTrace(std::vector<InstructionPointer>& stackTrace,\n                            size_t skip = 0);\n\n/**\n * Creates a vector and populates it with the current stack trace\n *\n * Note that this trace needs to be symbolicated to get the library offset even\n * if it is to be symbolicated off-line.\n *\n * Beware of a bug on some platforms, which makes the trace loop until the\n * buffer is full when it reaches a noexpr function. It seems to be fixed in\n * newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846\n *\n * @param skip The number of frames to skip before capturing the trace\n *\n * @limit The maximum number of frames captured\n */\nFBEXPORT inline std::vector<InstructionPointer> getStackTrace(\n    size_t skip = 0,\n    size_t limit = kDefaultLimit) {\n  auto stackTrace = std::vector<InstructionPointer>{};\n  stackTrace.reserve(limit);\n  getStackTrace(stackTrace, skip + 1);\n  return stackTrace;\n}\n\n/**\n * Symbolicates a stack trace into a given vector\n *\n * @param symbols The vector to receive the output. The vector is cleared and\n * enough room to keep the frames are reserved.\n *\n * @param stackTrace The input stack trace\n */\nFBEXPORT void getStackTraceSymbols(std::vector<StackTraceElement>& symbols,\n                                   const std::vector<InstructionPointer>& trace);\n\n/**\n * Symbolicates a stack trace into a new vector\n *\n * @param stackTrace The input stack trace\n */\nFBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols(\n    const std::vector<InstructionPointer>& trace) {\n  auto symbols = std::vector<StackTraceElement>{};\n  getStackTraceSymbols(symbols, trace);\n  return symbols;\n}\n\n\n/**\n * Captures and symbolicates a stack trace\n *\n * Beware of a bug on some platforms, which makes the trace loop until the\n * buffer is full when it reaches a noexpr function. It seems to be fixed in\n * newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846\n *\n * @param skip The number of frames before capturing the trace\n *\n * @param limit The maximum number of frames captured\n */\nFBEXPORT inline std::vector<StackTraceElement> getStackTraceSymbols(\n    size_t skip = 0,\n    size_t limit = kDefaultLimit) {\n  return getStackTraceSymbols(getStackTrace(skip + 1, limit));\n}\n\n/**\n * Formatting a stack trace element\n */\nFBEXPORT std::ostream& operator<<(std::ostream& out, const StackTraceElement& elm);\n\n/**\n * Formatting a stack trace\n */\nFBEXPORT std::ostream& operator<<(std::ostream& out,\n                                  const std::vector<StackTraceElement>& trace);\n}\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/noncopyable.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\nnamespace facebook {\n\nstruct noncopyable {\n  noncopyable(const noncopyable&) = delete;\n  noncopyable& operator=(const noncopyable&) = delete;\nprotected:\n  noncopyable() = default;\n};\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/nonmovable.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\nnamespace facebook {\n\nstruct nonmovable {\n  nonmovable(nonmovable&&) = delete;\n  nonmovable& operator=(nonmovable&&) = delete;\nprotected:\n  nonmovable() = default;\n};\n\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/fb/visibility.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#define FBEXPORT __attribute__((visibility(\"default\")))\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/jni/Countable.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <jni.h>\n\n#include <fb/Countable.h>\n#include <fb/RefPtr.h>\n#include <fb/visibility.h>\n\nnamespace facebook {\nnamespace jni {\n\nFBEXPORT const RefPtr<Countable>& countableFromJava(JNIEnv* env, jobject obj);\n\ntemplate <typename T> RefPtr<T> extractRefPtr(JNIEnv* env, jobject obj) {\n  return static_cast<RefPtr<T>>(countableFromJava(env, obj));\n}\n\ntemplate <typename T> RefPtr<T> extractPossiblyNullRefPtr(JNIEnv* env, jobject obj) {\n  return obj ? extractRefPtr<T>(env, obj) : nullptr;\n}\n\nFBEXPORT void setCountableForJava(JNIEnv* env, jobject obj, RefPtr<Countable>&& countable);\n\nvoid CountableOnLoad(JNIEnv* env);\n\n} }\n\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/jni/GlobalReference.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <memory>\n#include <type_traits>\n\n#include <jni.h>\n\n#include <fb/Environment.h>\n\nnamespace facebook { namespace jni {\n\ntemplate<typename T>\nclass GlobalReference {\n  static_assert(std::is_convertible<T, jobject>::value,\n                \"GlobalReference<T> instantiated with type that is not \"\n                \"convertible to jobject\");\n\n public:\n  explicit GlobalReference(T globalReference) :\n    reference_(globalReference? Environment::current()->NewGlobalRef(globalReference) : nullptr) {\n  }\n\n  ~GlobalReference() {\n    reset();\n  }\n\n  GlobalReference() :\n    reference_(nullptr) {\n  }\n\n  // enable move constructor and assignment\n  GlobalReference(GlobalReference&& rhs) :\n    reference_(std::move(rhs.reference_)) {\n    rhs.reference_ = nullptr;\n  }\n\n  GlobalReference& operator=(GlobalReference&& rhs) {\n    if (this != &rhs) {\n      reset();\n      reference_ = std::move(rhs.reference_);\n      rhs.reference_ = nullptr;\n    }\n    return *this;\n  }\n\n  GlobalReference(const GlobalReference<T>& rhs) :\n    reference_{} {\n    reset(rhs.get());\n  }\n\n  GlobalReference& operator=(const GlobalReference<T>& rhs) {\n    if (this == &rhs) {\n      return *this;\n    }\n    reset(rhs.get());\n    return *this;\n  }\n\n  explicit operator bool() const {\n    return (reference_ != nullptr);\n  }\n\n  T get() const {\n    return reinterpret_cast<T>(reference_);\n  }\n\n  void reset(T globalReference = nullptr) {\n    if (reference_) {\n      Environment::current()->DeleteGlobalRef(reference_);\n    }\n    if (globalReference) {\n      reference_ = Environment::current()->NewGlobalRef(globalReference);\n    } else {\n      reference_ = nullptr;\n    }\n  }\n\n private:\n  jobject reference_;\n};\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/jni/JniTerminateHandler.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <fb/visibility.h>\n\nnamespace facebook {\nnamespace jni {\n\nvoid FBEXPORT installTerminateHandler();\n}};\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/jni/LocalReference.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <memory>\n#include <type_traits>\n\n#include <jni.h>\n\n#include <fb/Environment.h>\n\nnamespace facebook {\nnamespace jni {\n\ntemplate<class T>\nstruct LocalReferenceDeleter {\n  static_assert(std::is_convertible<T, jobject>::value,\n    \"LocalReferenceDeleter<T> instantiated with type that is not convertible to jobject\");\n  void operator()(T localReference) {\n    if (localReference != nullptr) {\n      Environment::current()->DeleteLocalRef(localReference);\n    }\n  } \n };\n\ntemplate<class T>\nusing LocalReference =\n  std::unique_ptr<typename std::remove_pointer<T>::type, LocalReferenceDeleter<T>>;\n\n} }\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/jni/LocalString.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <string>\n\n#include <jni.h>\n\n#include <fb/visibility.h>\n\nnamespace facebook {\nnamespace jni {\n\nnamespace detail {\n\nvoid utf8ToModifiedUTF8(const uint8_t* bytes, size_t len, uint8_t* modified, size_t modifiedLength);\nsize_t modifiedLength(const std::string& str);\nsize_t modifiedLength(const uint8_t* str, size_t* length);\nstd::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len) noexcept;\nstd::string utf16toUTF8(const uint16_t* utf16Bytes, size_t len) noexcept;\n\n}\n\n// JNI represents strings encoded with modified version of UTF-8.  The difference between UTF-8 and\n// Modified UTF-8 is that the latter support only 1-byte, 2-byte, and 3-byte formats. Supplementary\n// character (4 bytes in unicode) needs to be represented in the form of surrogate pairs. To create\n// a Modified UTF-8 surrogate pair that Dalvik would understand we take 4-byte unicode character,\n// encode it with UTF-16 which gives us two 2 byte chars (surrogate pair) and then we encode each\n// pair as UTF-8. This result in 2 x 3 byte characters.  To convert modified UTF-8 to standard\n// UTF-8, this mus tbe reversed.\n//\n// The second difference is that Modified UTF-8 is encoding NUL byte in 2-byte format.\n//\n// In order to avoid complex error handling, only a minimum of validity checking is done to avoid\n// crashing.  If the input is invalid, the output may be invalid as well.\n//\n// Relevant links:\n//  - http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html\n//  - https://docs.oracle.com/javase/6/docs/api/java/io/DataInput.html#modified-utf-8\n\nclass FBEXPORT LocalString {\npublic:\n  // Assumes UTF8 encoding and make a required convertion to modified UTF-8 when the string\n  // contains unicode supplementary characters.\n  explicit LocalString(const std::string& str);\n  explicit LocalString(const char* str);\n  jstring string() const {\n    return m_string;\n  }\n  ~LocalString();\nprivate:\n  jstring m_string;\n};\n\n// JString to UTF16 extractor using RAII idiom\nclass JStringUtf16Extractor {\npublic:\n  JStringUtf16Extractor(JNIEnv* env, jstring javaString)\n  : env_(env)\n  , javaString_(javaString)\n  , length_(0)\n  , utf16String_(nullptr) {\n    if (env_ && javaString_) {\n      length_ = env_->GetStringLength(javaString_);\n      utf16String_ = env_->GetStringCritical(javaString_, nullptr);\n    }\n  }\n\n  ~JStringUtf16Extractor() {\n    if (utf16String_) {\n      env_->ReleaseStringCritical(javaString_, utf16String_);\n    }\n  }\n\n  const jsize length() const {\n    return length_;\n  }\n\n  const jchar* chars() const {\n    return utf16String_;\n  }\n\nprivate:\n  JNIEnv* env_;\n  jstring javaString_;\n  jsize length_;\n  const jchar* utf16String_;\n};\n\n// The string from JNI is converted to standard UTF-8 if the string contains supplementary\n// characters.\nFBEXPORT std::string fromJString(JNIEnv* env, jstring str);\n\n} }\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/jni/Registration.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n#include <jni.h>\n#include <initializer_list>\n#include <fb/assert.h>\n\nnamespace facebook {\nnamespace jni {\n\nstatic inline void registerNatives(JNIEnv* env, jclass cls, std::initializer_list<JNINativeMethod> methods) {\n  auto result = env->RegisterNatives(cls, methods.begin(), methods.size());\n  FBASSERT(result == 0);\n}\n\nstatic inline void registerNatives(JNIEnv* env, const char* cls, std::initializer_list<JNINativeMethod> list) {\n  registerNatives(env, env->FindClass(cls), list);\n}\n\n} }\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/jni/WeakReference.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n#include <string>\n#include <jni.h>\n#include <fb/noncopyable.h>\n#include <fb/Countable.h>\n#include <fb/visibility.h>\n\n\nnamespace facebook {\nnamespace jni {\n\nclass FBEXPORT WeakReference : public Countable {\npublic:\n  typedef RefPtr<WeakReference> Ptr;\n  WeakReference(jobject strongRef);\n  ~WeakReference();\n  jweak weakRef() {\n    return m_weakReference;\n  }\n\nprivate:\n  jweak m_weakReference;\n};\n\n// This class is intended to take a weak reference and turn it into a strong\n// local reference. Consequently, it should only be allocated on the stack.\nclass FBEXPORT ResolvedWeakReference : public noncopyable {\npublic:\n  ResolvedWeakReference(jobject weakRef);\n  ResolvedWeakReference(const RefPtr<WeakReference>& weakRef);\n  ~ResolvedWeakReference();\n\n  operator jobject () {\n    return m_strongReference;\n  }\n\n  explicit operator bool () {\n    return m_strongReference != nullptr;\n  }\n\nprivate:\n  jobject m_strongReference;\n};\n\n} }\n\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/include/jni/jni_helpers.h",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#pragma once\n\n#include <jni.h>\n\n#include <fb/visibility.h>\n\nnamespace facebook {\n\n/**\n * Instructs the JNI environment to throw an exception.\n *\n * @param pEnv JNI environment\n * @param szClassName class name to throw\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\nFBEXPORT jint throwException(JNIEnv* pEnv, const char* szClassName, const char* szFmt, va_list va_args);\n\n/**\n * Instructs the JNI environment to throw a NoClassDefFoundError.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\nFBEXPORT jint throwNoClassDefError(JNIEnv* pEnv, const char* szFmt, ...);\n\n/**\n * Instructs the JNI environment to throw a RuntimeException.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\nFBEXPORT jint throwRuntimeException(JNIEnv* pEnv, const char* szFmt, ...);\n\n/**\n * Instructs the JNI environment to throw a IllegalArgumentException.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\nFBEXPORT jint throwIllegalArgumentException(JNIEnv* pEnv, const char* szFmt, ...);\n\n/**\n * Instructs the JNI environment to throw a IllegalStateException.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\nFBEXPORT jint throwIllegalStateException(JNIEnv* pEnv, const char* szFmt, ...);\n\n/**\n * Instructs the JNI environment to throw an IOException.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\nFBEXPORT jint throwIOException(JNIEnv* pEnv, const char* szFmt, ...);\n\n/**\n * Instructs the JNI environment to throw an AssertionError.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\nFBEXPORT jint throwAssertionError(JNIEnv* pEnv, const char* szFmt, ...);\n\n/**\n * Instructs the JNI environment to throw an OutOfMemoryError.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\nFBEXPORT jint throwOutOfMemoryError(JNIEnv* pEnv, const char* szFmt, ...);\n\n/**\n * Finds the specified class. If it's not found, instructs the JNI environment to throw an\n * exception.\n *\n * @param pEnv JNI environment\n * @param szClassName the classname to find in JNI format (e.g. \"java/lang/String\")\n * @return the class or NULL if not found (in which case a pending exception will be queued). This\n *     returns a global reference (JNIEnv::NewGlobalRef).\n */\nFBEXPORT jclass findClassOrThrow(JNIEnv *pEnv, const char* szClassName);\n\n/**\n * Finds the specified field of the specified class. If it's not found, instructs the JNI\n * environment to throw an exception.\n *\n * @param pEnv JNI environment\n * @param clazz the class to lookup the field in\n * @param szFieldName the name of the field to find\n * @param szSig the signature of the field\n * @return the field or NULL if not found (in which case a pending exception will be queued)\n */\nFBEXPORT jfieldID getFieldIdOrThrow(JNIEnv* pEnv, jclass clazz, const char* szFieldName, const char* szSig);\n\n/**\n * Finds the specified method of the specified class. If it's not found, instructs the JNI\n * environment to throw an exception.\n *\n * @param pEnv JNI environment\n * @param clazz the class to lookup the method in\n * @param szMethodName the name of the method to find\n * @param szSig the signature of the method\n * @return the method or NULL if not found (in which case a pending exception will be queued)\n */\nFBEXPORT jmethodID getMethodIdOrThrow(\n    JNIEnv* pEnv,\n    jclass clazz,\n    const char* szMethodName,\n    const char* szSig);\n\n} // namespace facebook\n\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/ByteBuffer.cpp",
    "content": "/*\n * Copyright (c) 2016-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include <fb/fbjni/ByteBuffer.h>\n\n#include <stdexcept>\n\n#include <fb/fbjni/References.h>\n\nnamespace facebook {\nnamespace jni {\n\nnamespace {\nlocal_ref<JByteBuffer> createEmpty() {\n  static auto cls = JByteBuffer::javaClassStatic();\n  static auto meth = cls->getStaticMethod<JByteBuffer::javaobject(int)>(\"allocateDirect\");\n  return meth(cls, 0);\n}\n}\n\nlocal_ref<JByteBuffer> JByteBuffer::wrapBytes(uint8_t* data, size_t size) {\n  // env->NewDirectByteBuffer requires that size is positive. Android's\n  // dalvik returns an invalid result and Android's art aborts if size == 0.\n  // Workaround this by using a slow path through Java in that case.\n  if (!size) {\n    return createEmpty();\n  }\n  auto res = adopt_local(static_cast<javaobject>(Environment::current()->NewDirectByteBuffer(data, size)));\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n  if (!res) {\n    throw std::runtime_error(\"Direct byte buffers are unsupported.\");\n  }\n  return res;\n}\n\nuint8_t* JByteBuffer::getDirectBytes() const {\n  if (!self()) {\n    throwNewJavaException(\"java/lang/NullPointerException\", \"java.lang.NullPointerException\");\n  }\n  void* bytes = Environment::current()->GetDirectBufferAddress(self());\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n  if (!bytes) {\n    throw std::runtime_error(\n        isDirect() ?\n          \"Attempt to get direct bytes of non-direct byte buffer.\" :\n          \"Error getting direct bytes of byte buffer.\");\n  }\n  return static_cast<uint8_t*>(bytes);\n}\n\nsize_t JByteBuffer::getDirectSize() const {\n  if (!self()) {\n    throwNewJavaException(\"java/lang/NullPointerException\", \"java.lang.NullPointerException\");\n  }\n  int size = Environment::current()->GetDirectBufferCapacity(self());\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n  if (size < 0) {\n    throw std::runtime_error(\n        isDirect() ?\n          \"Attempt to get direct size of non-direct byte buffer.\" :\n          \"Error getting direct size of byte buffer.\");\n  }\n  return static_cast<size_t>(size);\n}\n\nbool JByteBuffer::isDirect() const {\n  static auto meth = javaClassStatic()->getMethod<jboolean()>(\"isDirect\");\n  return meth(self());\n}\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/Countable.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include <cstdint>\n#include <jni/Countable.h>\n#include <fb/Environment.h>\n#include <jni/Registration.h>\n\nnamespace facebook {\nnamespace jni {\n\nstatic jfieldID gCountableNativePtr;\n\nstatic RefPtr<Countable>* rawCountableFromJava(JNIEnv* env, jobject obj) {\n  FBASSERT(obj);\n  return reinterpret_cast<RefPtr<Countable>*>(env->GetLongField(obj, gCountableNativePtr));\n}\n\nconst RefPtr<Countable>& countableFromJava(JNIEnv* env, jobject obj) {\n  FBASSERT(obj);\n  return *rawCountableFromJava(env, obj);\n}\n\nvoid setCountableForJava(JNIEnv* env, jobject obj, RefPtr<Countable>&& countable) {\n  int oldValue = env->GetLongField(obj, gCountableNativePtr);\n  FBASSERTMSGF(oldValue == 0, \"Cannot reinitialize object; expected nullptr, got %x\", oldValue);\n\n  FBASSERT(countable);\n  uintptr_t fieldValue = (uintptr_t) new RefPtr<Countable>(std::move(countable));\n  env->SetLongField(obj, gCountableNativePtr, fieldValue);\n}\n\n/**\n * NB: THREAD SAFETY (this comment also exists at Countable.java)\n *\n * This method deletes the corresponding native object on whatever thread the method is called\n * on. In the common case when this is called by Countable#finalize(), this will be called on the\n * system finalizer thread. If you manually call dispose on the Java object, the native object \n * will be deleted synchronously on that thread.\n */\nvoid dispose(JNIEnv* env, jobject obj) {\n  // Grab the pointer\n  RefPtr<Countable>* countable = rawCountableFromJava(env, obj);\n  if (!countable) {\n    // That was easy.\n    return;\n  }\n\n  // Clear out the old value to avoid double-frees\n  env->SetLongField(obj, gCountableNativePtr, 0);\n\n  delete countable;\n}\n\nvoid CountableOnLoad(JNIEnv* env) {\n  jclass countable = env->FindClass(\"com/facebook/jni/Countable\");\n  gCountableNativePtr = env->GetFieldID(countable, \"mInstance\", \"J\");\n  registerNatives(env, countable, {\n    { \"dispose\", \"()V\", (void*) dispose },\n  });\n}\n\n} }\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/Environment.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include <fb/log.h>\n#include <fb/ThreadLocal.h>\n#include <fb/Environment.h>\n#include <fb/fbjni/CoreClasses.h>\n#include <fb/fbjni/NativeRunnable.h>\n\n#include <functional>\n\nnamespace facebook {\nnamespace jni {\n\nnamespace {\n\nThreadLocal<ThreadScope>& scopeStorage() {\n  // We don't want the ThreadLocal to delete the ThreadScopes.\n  static ThreadLocal<ThreadScope> scope([] (void*) {});\n  return scope;\n}\n\nThreadScope* currentScope() {\n  return scopeStorage().get();\n}\n\nJavaVM* g_vm = nullptr;\n\nstruct EnvironmentInitializer {\n  EnvironmentInitializer(JavaVM* vm) {\n      FBASSERT(!g_vm);\n      FBASSERT(vm);\n      g_vm = vm;\n  }\n};\n\nint getEnv(JNIEnv** env) {\n  FBASSERT(g_vm);\n  // g_vm->GetEnv() might not clear the env* in failure cases.\n  *env = nullptr;\n  return g_vm->GetEnv((void**)env, JNI_VERSION_1_6);\n}\n\nJNIEnv* attachCurrentThread() {\n  JavaVMAttachArgs args{JNI_VERSION_1_6, nullptr, nullptr};\n  JNIEnv* env = nullptr;\n  auto result = g_vm->AttachCurrentThread(&env, &args);\n  FBASSERT(result == JNI_OK);\n  return env;\n}\n}\n\n/* static */\nvoid Environment::initialize(JavaVM* vm) {\n  static EnvironmentInitializer init(vm);\n}\n\n/* static */\nJNIEnv* Environment::current() {\n  auto scope = currentScope();\n  if (scope && scope->env_) {\n    return scope->env_;\n  }\n\n  JNIEnv* env;\n  if (getEnv(&env) != JNI_OK) {\n    // If there's a ThreadScope in the stack, we should be attached and able to\n    // retrieve a JNIEnv*.\n    FBASSERT(!scope);\n\n    // TODO(cjhopman): this should probably be a hard failure, too.\n    FBLOGE(\"Unable to retrieve jni environment. Is the thread attached?\");\n  }\n  return env;\n}\n\n/* static */\nvoid Environment::detachCurrentThread() {\n  FBASSERT(g_vm);\n  // The thread shouldn't be detached while a ThreadScope is in the stack.\n  FBASSERT(!currentScope());\n  g_vm->DetachCurrentThread();\n}\n\n/* static */\nJNIEnv* Environment::ensureCurrentThreadIsAttached() {\n  auto scope = currentScope();\n  if (scope && scope->env_) {\n    return scope->env_;\n  }\n\n  JNIEnv* env;\n  // We should be able to just get the JNIEnv* by just calling\n  // AttachCurrentThread, but the spec is unclear (and using getEnv is probably\n  // generally more reliable).\n  auto result = getEnv(&env);\n  // We don't know how to deal with anything other than JNI_OK or JNI_DETACHED.\n  FBASSERT(result == JNI_OK || result == JNI_EDETACHED);\n  if (result == JNI_EDETACHED) {\n    // The thread should not be detached while a ThreadScope is in the stack.\n    FBASSERT(!scope);\n    env = attachCurrentThread();\n  }\n  FBASSERT(env);\n  return env;\n}\n\nThreadScope::ThreadScope() : ThreadScope(nullptr, internal::CacheEnvTag{}) {}\n\nThreadScope::ThreadScope(JNIEnv* env, internal::CacheEnvTag)\n    : previous_(nullptr), env_(nullptr), attachedWithThisScope_(false) {\n  auto& storage = scopeStorage();\n  previous_ = storage.get();\n  storage.reset(this);\n\n  if (previous_ && previous_->env_) {\n    FBASSERT(!env || env == previous_->env_);\n    env = previous_->env_;\n  }\n\n  env_ = env;\n  if (env_) {\n    return;\n  }\n\n  // Check if the thread is attached by someone else.\n  auto result = getEnv(&env);\n  if (result == JNI_OK) {\n    return;\n  }\n\n  // We don't know how to deal with anything other than JNI_OK or JNI_DETACHED.\n  FBASSERT(result == JNI_EDETACHED);\n\n  // If there's already a ThreadScope on the stack, then the thread should be attached.\n  FBASSERT(!previous_);\n  attachCurrentThread();\n  attachedWithThisScope_ = true;\n}\n\nThreadScope::~ThreadScope() {\n  auto& storage = scopeStorage();\n  // ThreadScopes should be destroyed in the reverse order they are created\n  // (that is, just put them on the stack).\n  FBASSERT(this == storage.get());\n  storage.reset(previous_);\n  if (attachedWithThisScope_) {\n    Environment::detachCurrentThread();\n  }\n}\n\nnamespace {\nstruct JThreadScopeSupport : JavaClass<JThreadScopeSupport> {\n  static auto constexpr kJavaDescriptor = \"Lcom/facebook/jni/ThreadScopeSupport;\";\n\n  // These reinterpret_casts are a totally dangerous pattern. Don't use them. Use HybridData instead.\n  static void runStdFunction(std::function<void()>&& func) {\n    static auto method = javaClassStatic()->getStaticMethod<void(jlong)>(\"runStdFunction\");\n    method(javaClassStatic(), reinterpret_cast<jlong>(&func));\n  }\n\n  static void runStdFunctionImpl(alias_ref<JClass>, jlong ptr) {\n    (*reinterpret_cast<std::function<void()>*>(ptr))();\n  }\n\n  static void OnLoad() {\n    // We need the javaClassStatic so that the class lookup is cached and that\n    // runStdFunction can be called from a ThreadScope-attached thread.\n    javaClassStatic()->registerNatives({\n        makeNativeMethod(\"runStdFunctionImpl\", runStdFunctionImpl),\n      });\n  }\n};\n}\n\n/* static */\nvoid ThreadScope::OnLoad() {\n  // These classes are required for ScopeWithClassLoader. Ensure they are looked up when loading.\n  JThreadScopeSupport::OnLoad();\n}\n\n/* static */\nvoid ThreadScope::WithClassLoader(std::function<void()>&& runnable) {\n  // TODO(cjhopman): If the classloader is already available in this scope, we\n  // shouldn't have to jump through java. It should be enough to check if the\n  // attach state env* is set.\n  ThreadScope ts;\n  JThreadScopeSupport::runStdFunction(std::move(runnable));\n}\n\n} }\n\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/Exceptions.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include <fb/fbjni/CoreClasses.h>\n\n#include <fb/assert.h>\n#include <fb/log.h>\n\n#ifdef USE_LYRA\n#include <fb/lyra.h>\n#include <fb/lyra_exceptions.h>\n#endif\n\n#include <alloca.h>\n#include <cstdlib>\n#include <ios>\n#include <stdexcept>\n#include <stdio.h>\n#include <string>\n#include <system_error>\n\n#include <jni.h>\n\nnamespace facebook {\nnamespace jni {\n\nnamespace {\nclass JRuntimeException : public JavaClass<JRuntimeException, JThrowable> {\n public:\n  static auto constexpr kJavaDescriptor = \"Ljava/lang/RuntimeException;\";\n\n  static local_ref<JRuntimeException> create(const char* str) {\n    return newInstance(make_jstring(str));\n  }\n\n  static local_ref<JRuntimeException> create() {\n    return newInstance();\n  }\n};\n\nclass JIOException : public JavaClass<JIOException, JThrowable> {\n public:\n  static auto constexpr kJavaDescriptor = \"Ljava/io/IOException;\";\n\n  static local_ref<JIOException> create(const char* str) {\n    return newInstance(make_jstring(str));\n  }\n};\n\nclass JOutOfMemoryError : public JavaClass<JOutOfMemoryError, JThrowable> {\n public:\n  static auto constexpr kJavaDescriptor = \"Ljava/lang/OutOfMemoryError;\";\n\n  static local_ref<JOutOfMemoryError> create(const char* str) {\n    return newInstance(make_jstring(str));\n  }\n};\n\nclass JArrayIndexOutOfBoundsException : public JavaClass<JArrayIndexOutOfBoundsException, JThrowable> {\n public:\n  static auto constexpr kJavaDescriptor = \"Ljava/lang/ArrayIndexOutOfBoundsException;\";\n\n  static local_ref<JArrayIndexOutOfBoundsException> create(const char* str) {\n    return newInstance(make_jstring(str));\n  }\n};\n\nclass JUnknownCppException : public JavaClass<JUnknownCppException, JThrowable> {\n public:\n  static auto constexpr kJavaDescriptor = \"Lcom/facebook/jni/UnknownCppException;\";\n\n  static local_ref<JUnknownCppException> create() {\n    return newInstance();\n  }\n\n  static local_ref<JUnknownCppException> create(const char* str) {\n    return newInstance(make_jstring(str));\n  }\n};\n\nclass JCppSystemErrorException : public JavaClass<JCppSystemErrorException, JThrowable> {\n public:\n  static auto constexpr kJavaDescriptor = \"Lcom/facebook/jni/CppSystemErrorException;\";\n\n  static local_ref<JCppSystemErrorException> create(const std::system_error& e) {\n    return newInstance(make_jstring(e.what()), e.code().value());\n  }\n};\n\n// Exception throwing & translating functions //////////////////////////////////////////////////////\n\n// Functions that throw Java exceptions\n\nvoid setJavaExceptionAndAbortOnFailure(alias_ref<JThrowable> throwable) {\n  auto env = Environment::current();\n  if (throwable) {\n    env->Throw(throwable.get());\n  }\n  if (env->ExceptionCheck() != JNI_TRUE) {\n    std::abort();\n  }\n}\n\n}\n\n// Functions that throw C++ exceptions\n\n// TODO(T6618159) Inject the c++ stack into the exception's stack trace. One\n// issue: when a java exception is created, it captures the full java stack\n// across jni boundaries. lyra will only capture the c++ stack to the jni\n// boundary. So, as we pass the java exception up to c++, we need to capture\n// the c++ stack and then insert it into the correct place in the java stack\n// trace. Then, as the exception propagates across the boundaries, we will\n// slowly fill in the c++ parts of the trace.\nvoid throwPendingJniExceptionAsCppException() {\n  JNIEnv* env = Environment::current();\n  if (env->ExceptionCheck() == JNI_FALSE) {\n    return;\n  }\n\n  auto throwable = adopt_local(env->ExceptionOccurred());\n  if (!throwable) {\n    throw std::runtime_error(\"Unable to get pending JNI exception.\");\n  }\n  env->ExceptionClear();\n\n  throw JniException(throwable);\n}\n\nvoid throwCppExceptionIf(bool condition) {\n  if (!condition) {\n    return;\n  }\n\n  auto env = Environment::current();\n  if (env->ExceptionCheck() == JNI_TRUE) {\n    throwPendingJniExceptionAsCppException();\n    return;\n  }\n\n  throw JniException();\n}\n\nvoid throwNewJavaException(jthrowable throwable) {\n  throw JniException(wrap_alias(throwable));\n}\n\nvoid throwNewJavaException(const char* throwableName, const char* msg) {\n  // If anything of the fbjni calls fail, an exception of a suitable\n  // form will be thrown, which is what we want.\n  auto throwableClass = findClassLocal(throwableName);\n  auto throwable = throwableClass->newObject(\n    throwableClass->getConstructor<jthrowable(jstring)>(),\n    make_jstring(msg).release());\n  throwNewJavaException(throwable.get());\n}\n\n// jthrowable //////////////////////////////////////////////////////////////////////////////////////\n\nlocal_ref<JThrowable> JThrowable::initCause(alias_ref<JThrowable> cause) {\n  static auto meth = javaClassStatic()->getMethod<javaobject(alias_ref<javaobject>)>(\"initCause\");\n  return meth(self(), cause);\n}\n\nauto JThrowable::getStackTrace() -> local_ref<JStackTrace> {\n  static auto meth = javaClassStatic()->getMethod<JStackTrace::javaobject()>(\"getStackTrace\");\n  return meth(self());\n}\n\nvoid JThrowable::setStackTrace(alias_ref<JStackTrace> stack) {\n  static auto meth = javaClassStatic()->getMethod<void(alias_ref<JStackTrace>)>(\"setStackTrace\");\n  return meth(self(), stack);\n}\n\nauto JStackTraceElement::create(\n    const std::string& declaringClass, const std::string& methodName, const std::string& file, int line)\n    -> local_ref<javaobject> {\n  return newInstance(declaringClass, methodName, file, line);\n}\n\nstd::string JStackTraceElement::getClassName() const {\n  static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>(\"getClassName\");\n  return meth(self())->toStdString();\n}\n\nstd::string JStackTraceElement::getMethodName() const {\n  static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>(\"getMethodName\");\n  return meth(self())->toStdString();\n}\n\nstd::string JStackTraceElement::getFileName() const {\n  static auto meth = javaClassStatic()->getMethod<local_ref<JString>()>(\"getFileName\");\n  return meth(self())->toStdString();\n}\n\nint JStackTraceElement::getLineNumber() const {\n  static auto meth = javaClassStatic()->getMethod<jint()>(\"getLineNumber\");\n  return meth(self());\n}\n\n// Translate C++ to Java Exception\n\nnamespace {\n\n// For each exception in the chain of the exception_ptr argument, func\n// will be called with that exception (in reverse order, i.e. innermost first).\n#ifndef FBJNI_NO_EXCEPTION_PTR\nvoid denest(const std::function<void(std::exception_ptr)>& func, std::exception_ptr ptr) {\n  FBASSERT(ptr);\n  try {\n    std::rethrow_exception(ptr);\n  } catch (const std::nested_exception& e) {\n    denest(func, e.nested_ptr());\n  } catch (...) {\n    // ignored.\n  }\n  func(ptr);\n  }\n#endif\n\n} // namespace\n\n\n#ifdef USE_LYRA\nlocal_ref<JStackTraceElement> createJStackTraceElement(const lyra::StackTraceElement& cpp) {\n  return JStackTraceElement::create(\n      \"|lyra|{\" + cpp.libraryName() + \"}\", cpp.functionName(), cpp.buildId(), cpp.libraryOffset());\n}\n#endif\n\n#ifndef FBJNI_NO_EXCEPTION_PTR\nvoid addCppStacktraceToJavaException(alias_ref<JThrowable> java, std::exception_ptr cpp) {\n#ifdef USE_LYRA\n  auto cppStack = lyra::getStackTraceSymbols(\n                    (cpp == nullptr) ?\n                      lyra::getStackTrace()\n                      : lyra::getExceptionTrace(cpp));\n\n  auto javaStack = java->getStackTrace();\n  auto newStack = JThrowable::JStackTrace::newArray(javaStack->size() + cppStack.size());\n  size_t i = 0;\n  for (size_t j = 0; j < cppStack.size(); j++, i++) {\n    (*newStack)[i] = createJStackTraceElement(cppStack[j]);\n  }\n  for (size_t j = 0; j < javaStack->size(); j++, i++) {\n    (*newStack)[i] = (*javaStack)[j];\n  }\n  java->setStackTrace(newStack);\n#endif\n}\n\nlocal_ref<JThrowable> convertCppExceptionToJavaException(std::exception_ptr ptr) {\n  FBASSERT(ptr);\n  local_ref<JThrowable> current;\n  bool addCppStack = true;\n  try {\n    std::rethrow_exception(ptr);\n    addCppStack = false;\n  } catch (const JniException& ex) {\n    current = ex.getThrowable();\n  } catch (const std::ios_base::failure& ex) {\n    current = JIOException::create(ex.what());\n  } catch (const std::bad_alloc& ex) {\n    current = JOutOfMemoryError::create(ex.what());\n  } catch (const std::out_of_range& ex) {\n    current = JArrayIndexOutOfBoundsException::create(ex.what());\n  } catch (const std::system_error& ex) {\n    current = JCppSystemErrorException::create(ex);\n  } catch (const std::runtime_error& ex) {\n    current = JRuntimeException::create(ex.what());\n  } catch (const std::exception& ex) {\n    current = JCppException::create(ex.what());\n  } catch (const char* msg) {\n    current = JUnknownCppException::create(msg);\n  } catch (...) {\n    current = JUnknownCppException::create();\n  }\n\n  if (addCppStack) {\n    addCppStacktraceToJavaException(current, ptr);\n  }\n  return current;\n  }\n#endif\n\nlocal_ref<JThrowable> getJavaExceptionForCppBackTrace() {\n  return getJavaExceptionForCppBackTrace(nullptr);\n}\n\nlocal_ref<JThrowable> getJavaExceptionForCppBackTrace(const char* msg) {\n  local_ref<JThrowable> current =\n      msg ? JUnknownCppException::create(msg) : JUnknownCppException::create();\n#ifndef FBJNI_NO_EXCEPTION_PTR\n  addCppStacktraceToJavaException(current, nullptr);\n#endif\n  return current;\n}\n\n\n#ifndef FBJNI_NO_EXCEPTION_PTR\nlocal_ref<JThrowable> getJavaExceptionForCppException(std::exception_ptr ptr) {\n  FBASSERT(ptr);\n  local_ref<JThrowable> previous;\n  auto func = [&previous] (std::exception_ptr ptr) {\n    auto current = convertCppExceptionToJavaException(ptr);\n    if (previous) {\n      current->initCause(previous);\n    }\n    previous = current;\n  };\n  denest(func, ptr);\n  return previous;\n}\n#endif\n\nvoid translatePendingCppExceptionToJavaException() {\n  try {\n#ifndef FBJNI_NO_EXCEPTION_PTR\n    auto exc = getJavaExceptionForCppException(std::current_exception());\n#else\n    auto exc = JUnknownCppException::create();\n#endif\n    setJavaExceptionAndAbortOnFailure(exc);\n  } catch (...) {\n#ifdef USE_LYRA\n    FBLOGE(\"Unexpected error in translatePendingCppExceptionToJavaException(): %s\",\n        lyra::toString(std::current_exception()).c_str());\n#endif\n    std::terminate();\n  }\n}\n\n// JniException ////////////////////////////////////////////////////////////////////////////////////\n\nconst std::string JniException::kExceptionMessageFailure_ = \"Unable to get exception message.\";\n\nJniException::JniException() : JniException(JRuntimeException::create()) { }\n\nJniException::JniException(alias_ref<jthrowable> throwable) : isMessageExtracted_(false) {\n  throwable_ = make_global(throwable);\n}\n\nJniException::JniException(JniException &&rhs)\n    : throwable_(std::move(rhs.throwable_)),\n      what_(std::move(rhs.what_)),\n      isMessageExtracted_(rhs.isMessageExtracted_) {\n}\n\nJniException::JniException(const JniException &rhs)\n    : what_(rhs.what_), isMessageExtracted_(rhs.isMessageExtracted_) {\n  throwable_ = make_global(rhs.throwable_);\n}\n\nJniException::~JniException() {\n  try {\n    ThreadScope ts;\n    throwable_.reset();\n  } catch (...) {\n    FBLOGE(\"Exception in ~JniException()\");\n    std::terminate();\n  }\n}\n\nlocal_ref<JThrowable> JniException::getThrowable() const noexcept {\n  return make_local(throwable_);\n}\n\n// TODO 6900503: consider making this thread-safe.\nvoid JniException::populateWhat() const noexcept {\n  try {\n    ThreadScope ts;\n    what_ = throwable_->toString();\n    isMessageExtracted_ = true;\n  } catch(...) {\n    what_ = kExceptionMessageFailure_;\n  }\n}\n\nconst char* JniException::what() const noexcept {\n  if (!isMessageExtracted_) {\n    populateWhat();\n  }\n  return what_.c_str();\n}\n\nvoid JniException::setJavaException() const noexcept {\n  setJavaExceptionAndAbortOnFailure(throwable_);\n}\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/Hybrid.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include \"fb/fbjni.h\"\n\n\nnamespace facebook {\nnamespace jni {\n\nnamespace detail {\n\nlocal_ref<HybridData> HybridData::create() {\n  return newInstance();\n}\n\n}\n\nnamespace {\nvoid deleteNative(alias_ref<jclass>, jlong ptr) {\n  delete reinterpret_cast<detail::BaseHybridClass*>(ptr);\n}\n}\n\nvoid HybridDataOnLoad() {\n  registerNatives(\"com/facebook/jni/HybridData$Destructor\", {\n      makeNativeMethod(\"deleteNative\", deleteNative),\n  });\n}\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/LocalString.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include <jni/LocalString.h>\n#include <fb/Environment.h>\n#include <fb/assert.h>\n\n#include <vector>\n\nnamespace facebook {\nnamespace jni {\n\nnamespace {\n\nconst uint16_t kUtf8OneByteBoundary       = 0x80;\nconst uint16_t kUtf8TwoBytesBoundary      = 0x800;\nconst uint16_t kUtf16HighSubLowBoundary   = 0xD800;\nconst uint16_t kUtf16HighSubHighBoundary  = 0xDC00;\nconst uint16_t kUtf16LowSubHighBoundary   = 0xE000;\n\ninline void encode3ByteUTF8(char32_t code, uint8_t* out) {\n  FBASSERTMSGF((code & 0xffff0000) == 0, \"3 byte utf-8 encodings only valid for up to 16 bits\");\n\n  out[0] = 0xE0 | (code >> 12);\n  out[1] = 0x80 | ((code >> 6) & 0x3F);\n  out[2] = 0x80 | (code & 0x3F);\n}\n\ninline char32_t decode3ByteUTF8(const uint8_t* in) {\n  return (((in[0] & 0x0f) << 12) |\n          ((in[1] & 0x3f) << 6) |\n          ( in[2] & 0x3f));\n}\n\ninline void encode4ByteUTF8(char32_t code, std::string& out, size_t offset) {\n  FBASSERTMSGF((code & 0xfff80000) == 0, \"4 byte utf-8 encodings only valid for up to 21 bits\");\n\n  out[offset] =     (char) (0xF0 | (code >> 18));\n  out[offset + 1] = (char) (0x80 | ((code >> 12) & 0x3F));\n  out[offset + 2] = (char) (0x80 | ((code >> 6) & 0x3F));\n  out[offset + 3] = (char) (0x80 | (code & 0x3F));\n}\n\ntemplate <typename T>\ninline bool isFourByteUTF8Encoding(const T* utf8) {\n  return ((*utf8 & 0xF8) == 0xF0);\n}\n\n}\n\nnamespace detail {\n\nsize_t modifiedLength(const std::string& str) {\n  // Scan for supplementary characters\n  size_t j = 0;\n  for (size_t i = 0; i < str.size(); ) {\n    if (str[i] == 0) {\n      i += 1;\n      j += 2;\n    } else if (i + 4 > str.size() ||\n               !isFourByteUTF8Encoding(&(str[i]))) {\n      // See the code in utf8ToModifiedUTF8 for what's happening here.\n      i += 1;\n      j += 1;\n    } else {\n      i += 4;\n      j += 6;\n    }\n  }\n\n  return j;\n}\n\n// returns modified utf8 length; *length is set to strlen(str)\nsize_t modifiedLength(const uint8_t* str, size_t* length) {\n  // NUL-terminated: Scan for length and supplementary characters\n  size_t i = 0;\n  size_t j = 0;\n  if (str != nullptr) {\n    while (str[i] != 0) {\n      if (str[i + 1] == 0 ||\n          str[i + 2] == 0 ||\n          str[i + 3] == 0 ||\n          !isFourByteUTF8Encoding(&(str[i]))) {\n        i += 1;\n        j += 1;\n      } else {\n        i += 4;\n        j += 6;\n      }\n    }\n  }\n\n  *length = i;\n  return j;\n}\n\nvoid utf8ToModifiedUTF8(const uint8_t* utf8, size_t len, uint8_t* modified, size_t modifiedBufLen)\n{\n  size_t j = 0;\n  for (size_t i = 0; i < len; ) {\n    FBASSERTMSGF(j < modifiedBufLen, \"output buffer is too short\");\n    if (utf8[i] == 0) {\n      FBASSERTMSGF(j + 1 < modifiedBufLen, \"output buffer is too short\");\n      modified[j] = 0xc0;\n      modified[j + 1] = 0x80;\n      i += 1;\n      j += 2;\n      continue;\n    }\n\n    if (i + 4 > len ||\n        !isFourByteUTF8Encoding(utf8 + i)) {\n      // If the input is too short for this to be a four-byte\n      // encoding, or it isn't one for real, just copy it on through.\n      modified[j] = utf8[i];\n      i++;\n      j++;\n      continue;\n    }\n\n    // Convert 4 bytes of input to 2 * 3 bytes of output\n    char32_t code = (((utf8[i]     & 0x07) << 18) |\n                     ((utf8[i + 1] & 0x3f) << 12) |\n                     ((utf8[i + 2] & 0x3f) << 6) |\n                     ( utf8[i + 3] & 0x3f));\n    char32_t first;\n    char32_t second;\n\n    if (code > 0x10ffff) {\n      // These could be valid utf-8, but cannot be represented as modified UTF-8, due to the 20-bit\n      // limit on that representation.  Encode two replacement characters, so the expected output\n      // length lines up.\n      const char32_t kUnicodeReplacementChar = 0xfffd;\n      first = kUnicodeReplacementChar;\n      second = kUnicodeReplacementChar;\n    } else {\n      // split into surrogate pair\n      first = ((code - 0x010000) >> 10) | 0xd800;\n      second = ((code - 0x010000) & 0x3ff) | 0xdc00;\n    }\n\n    // encode each as a 3 byte surrogate value\n    FBASSERTMSGF(j + 5 < modifiedBufLen, \"output buffer is too short\");\n    encode3ByteUTF8(first, modified + j);\n    encode3ByteUTF8(second, modified + j + 3);\n    i += 4;\n    j += 6;\n  }\n\n  FBASSERTMSGF(j < modifiedBufLen, \"output buffer is too short\");\n  modified[j++] = '\\0';\n}\n\nstd::string modifiedUTF8ToUTF8(const uint8_t* modified, size_t len) noexcept {\n  // Converting from modified utf8 to utf8 will always shrink, so this will always be sufficient\n  std::string utf8(len, 0);\n  size_t j = 0;\n  for (size_t i = 0; i < len; ) {\n    // surrogate pair: 1101 10xx  xxxx xxxx  1101 11xx  xxxx xxxx\n    // encoded pair: 1110 1101  1010 xxxx  10xx xxxx  1110 1101  1011 xxxx  10xx xxxx\n\n    if (len >= i + 6 &&\n        modified[i] == 0xed &&\n        (modified[i + 1] & 0xf0) == 0xa0 &&\n        modified[i + 3] == 0xed &&\n        (modified[i + 4] & 0xf0) == 0xb0) {\n      // Valid surrogate pair\n      char32_t pair1 = decode3ByteUTF8(modified + i);\n      char32_t pair2 = decode3ByteUTF8(modified + i + 3);\n      char32_t ch = 0x10000 + (((pair1 & 0x3ff) << 10) |\n                               ( pair2 & 0x3ff));\n      encode4ByteUTF8(ch, utf8, j);\n      i += 6;\n      j += 4;\n      continue;\n    } else if (len >= i + 2 &&\n               modified[i] == 0xc0 &&\n               modified[i + 1] == 0x80) {\n      utf8[j] = 0;\n      i += 2;\n      j += 1;\n      continue;\n    }\n\n    // copy one byte.  This might be a one, two, or three-byte encoding.  It might be an invalid\n    // encoding of some sort, but garbage in garbage out is ok.\n\n    utf8[j] = (char) modified[i];\n    i++;\n    j++;\n  }\n\n  utf8.resize(j);\n\n  return utf8;\n}\n\n// Calculate how many bytes are needed to convert an UTF16 string into UTF8\n// UTF16 string\nsize_t utf16toUTF8Length(const uint16_t* utf16String, size_t utf16StringLen) {\n  if (!utf16String || utf16StringLen == 0) {\n    return 0;\n  }\n\n  uint32_t utf8StringLen = 0;\n  auto utf16StringEnd = utf16String + utf16StringLen;\n  auto idx16 = utf16String;\n  while (idx16 < utf16StringEnd) {\n    auto ch = *idx16++;\n    if (ch < kUtf8OneByteBoundary) {\n      utf8StringLen++;\n    } else if (ch < kUtf8TwoBytesBoundary) {\n      utf8StringLen += 2;\n    } else if (\n        (ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&\n        (idx16 < utf16StringEnd) &&\n        (*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {\n      utf8StringLen += 4;\n      idx16++;\n    } else {\n      utf8StringLen += 3;\n    }\n  }\n\n  return utf8StringLen;\n}\n\nstd::string utf16toUTF8(const uint16_t* utf16String, size_t utf16StringLen) noexcept {\n  if (!utf16String || utf16StringLen <= 0) {\n    return \"\";\n  }\n\n  std::string utf8String(utf16toUTF8Length(utf16String, utf16StringLen), '\\0');\n  auto idx8 = utf8String.begin();\n  auto idx16 = utf16String;\n  auto utf16StringEnd = utf16String + utf16StringLen;\n  while (idx16 < utf16StringEnd) {\n    auto ch = *idx16++;\n    if (ch < kUtf8OneByteBoundary) {\n      *idx8++ = (ch & 0x7F);\n    } else if (ch < kUtf8TwoBytesBoundary) {\n      *idx8++ = 0b11000000 | (ch >> 6);\n      *idx8++ = 0b10000000 | (ch & 0x3F);\n    } else if (\n        (ch >= kUtf16HighSubLowBoundary) && (ch < kUtf16HighSubHighBoundary) &&\n        (idx16 < utf16StringEnd) &&\n        (*idx16 >= kUtf16HighSubHighBoundary) && (*idx16 < kUtf16LowSubHighBoundary)) {\n      auto ch2 = *idx16++;\n      uint8_t trunc_byte = (((ch >> 6) & 0x0F) + 1);\n      *idx8++ = 0b11110000 | (trunc_byte >> 2);\n      *idx8++ = 0b10000000 | ((trunc_byte & 0x03) << 4) | ((ch >> 2) & 0x0F);\n      *idx8++ = 0b10000000 | ((ch & 0x03) << 4) | ((ch2 >> 6) & 0x0F);\n      *idx8++ = 0b10000000 | (ch2 & 0x3F);\n    } else {\n      *idx8++ = 0b11100000 | (ch >> 12);\n      *idx8++ = 0b10000000 | ((ch >> 6) & 0x3F);\n      *idx8++ = 0b10000000 | (ch & 0x3F);\n    }\n  }\n\n  return utf8String;\n}\n\n}\n\nLocalString::LocalString(const std::string& str)\n{\n  size_t modlen = detail::modifiedLength(str);\n  if (modlen == str.size()) {\n    // no supplementary characters, build jstring from input buffer\n    m_string = Environment::current()->NewStringUTF(str.data());\n    return;\n  }\n  auto modified = std::vector<char>(modlen + 1); // allocate extra byte for \\0\n  detail::utf8ToModifiedUTF8(\n    reinterpret_cast<const uint8_t*>(str.data()), str.size(),\n    reinterpret_cast<uint8_t*>(modified.data()), modified.size());\n  m_string = Environment::current()->NewStringUTF(modified.data());\n}\n\nLocalString::LocalString(const char* str)\n{\n  size_t len;\n  size_t modlen = detail::modifiedLength(reinterpret_cast<const uint8_t*>(str), &len);\n  if (modlen == len) {\n    // no supplementary characters, build jstring from input buffer\n    m_string = Environment::current()->NewStringUTF(str);\n    return;\n  }\n  auto modified = std::vector<char>(modlen + 1); // allocate extra byte for \\0\n  detail::utf8ToModifiedUTF8(\n    reinterpret_cast<const uint8_t*>(str), len,\n    reinterpret_cast<uint8_t*>(modified.data()), modified.size());\n  m_string = Environment::current()->NewStringUTF(modified.data());\n}\n\nLocalString::~LocalString() {\n  Environment::current()->DeleteLocalRef(m_string);\n}\n\nstd::string fromJString(JNIEnv* env, jstring str) {\n  auto utf16String = JStringUtf16Extractor(env, str);\n  return detail::utf16toUTF8(utf16String.chars(), utf16String.length());\n}\n\n} }\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/OnLoad.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include <jni/Countable.h>\n#include <fb/Environment.h>\n#include <fb/fbjni.h>\n#include <fb/fbjni/NativeRunnable.h>\n\nusing namespace facebook::jni;\n\nvoid initialize_fbjni() {\n  CountableOnLoad(Environment::current());\n  HybridDataOnLoad();\n  JNativeRunnable::OnLoad();\n  ThreadScope::OnLoad();\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/References.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include <fb/fbjni/References.h>\n\nnamespace facebook {\nnamespace jni {\n\nJniLocalScope::JniLocalScope(JNIEnv* env, jint capacity)\n    : env_(env) {\n  hasFrame_ = false;\n  auto pushResult = env->PushLocalFrame(capacity);\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(pushResult < 0);\n  hasFrame_ = true;\n}\n\nJniLocalScope::~JniLocalScope() {\n  if (hasFrame_) {\n    env_->PopLocalFrame(nullptr);\n  }\n}\n\nnamespace internal {\n\n// Default implementation always returns true.\n// Platform-specific sources can override this.\nbool doesGetObjectRefTypeWork() __attribute__ ((weak));\nbool doesGetObjectRefTypeWork() {\n  return true;\n}\n\n}\n\n}\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/WeakReference.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include <fb/Environment.h>\n#include <jni/WeakReference.h>\n\nnamespace facebook {\nnamespace jni {\n\nWeakReference::WeakReference(jobject strongRef) :\n  m_weakReference(Environment::current()->NewWeakGlobalRef(strongRef))\n{\n}\n\nWeakReference::~WeakReference() {\n  auto env = Environment::current();\n  FBASSERTMSGF(env, \"Attempt to delete jni::WeakReference from non-JNI thread\");\n  env->DeleteWeakGlobalRef(m_weakReference);\n}\n\nResolvedWeakReference::ResolvedWeakReference(jobject weakRef) :\n  m_strongReference(Environment::current()->NewLocalRef(weakRef))\n{\n}\n\nResolvedWeakReference::ResolvedWeakReference(const RefPtr<WeakReference>& weakRef) :\n  m_strongReference(Environment::current()->NewLocalRef(weakRef->weakRef()))\n{\n}\n\nResolvedWeakReference::~ResolvedWeakReference() {\n  if (m_strongReference)\n    Environment::current()->DeleteLocalRef(m_strongReference);\n}\n\n} }\n\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/android/CpuCapabilities.cpp",
    "content": "// Copyright 2004-present Facebook. All Rights Reserved.\n\n#include <fb/CpuCapabilities.h>\n#include <cpu-features.h>\n#include <fb/Environment.h>\n#include <glog/logging.h>\n#include <jni/Registration.h>\n\nnamespace facebook { namespace jni {\n\n// =========================================\n// returns true if this device supports NEON calls, false otherwise\njboolean nativeDeviceSupportsNeon(JNIEnv* env, jobject obj) {\n  if (android_getCpuFamily() != ANDROID_CPU_FAMILY_ARM) {\n    VLOG(2) << \"NEON disabled, not an ARM CPU\";\n    return false;\n  }\n  uint64_t cpufeatures = android_getCpuFeatures();\n  if ((cpufeatures & ANDROID_CPU_ARM_FEATURE_ARMv7) == 0) {\n    VLOG(2) << \"NEON disabled, not an ARMv7 CPU\";\n    return false;\n  }\n  if ((cpufeatures & ANDROID_CPU_ARM_FEATURE_NEON) == 0) {\n    VLOG(2) << \"NEON disabled, not supported\";\n    return false;\n  }\n\n  VLOG(2) << \"NEON supported and enabled\";\n  return true;\n}\n\n// =========================================\n// returns true if this device supports VFP_FP16, false otherwise.\njboolean nativeDeviceSupportsVFPFP16(JNIEnv *env, jobject obj) {\n  uint64_t cpufeatures = android_getCpuFeatures();\n  if ((cpufeatures & ANDROID_CPU_ARM_FEATURE_VFP_FP16) == 0) {\n    VLOG(2) << \"VPF_FP16 disabled, not supported\";\n    return false;\n  }\n  VLOG(2) << \"VFP_FP16 supported and enabled\";\n  return true;\n}\n\n// =========================================\n// returns true if this device is x86 based, false otherwise\njboolean nativeDeviceSupportsX86(JNIEnv* env, jobject obj) {\n  return (android_getCpuFamily() == ANDROID_CPU_FAMILY_X86);\n}\n\n// =========================================\n// register native methods\nvoid initialize_cpucapabilities() {\n  facebook::jni::registerNatives(\n      Environment::current(),\n      \"com/facebook/jni/CpuCapabilitiesJni\",\n      {\n        { \"nativeDeviceSupportsNeon\",\n          \"()Z\",\n          (void*) nativeDeviceSupportsNeon },\n        { \"nativeDeviceSupportsVFPFP16\",\n          \"()Z\",\n          (void*) nativeDeviceSupportsVFPFP16 },\n        { \"nativeDeviceSupportsX86\",\n          \"()Z\",\n          (void*) nativeDeviceSupportsX86 },\n      }\n  );\n}\n\n} } // namespace facebook::jni\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/android/ReferenceChecking.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#ifndef __ANDROID__\n#error \"This file should only be compiled for Android.\"\n#endif\n\n#include <fb/fbjni/References.h>\n#include <fb/fbjni/CoreClasses.h>\n\nnamespace facebook {\nnamespace jni {\nnamespace internal {\n\nstatic int32_t getApiLevel() {\n  auto cls = findClassLocal(\"android/os/Build$VERSION\");\n  auto fld = cls->getStaticField<int32_t>(\"SDK_INT\");\n  if (fld) {\n    return cls->getStaticFieldValue(fld);\n  }\n  return 0;\n}\n\nbool doesGetObjectRefTypeWork() {\n  static auto level = getApiLevel();\n  return level >= 14;\n}\n\n}\n}\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/fbjni.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include <fb/fbjni.h>\n\n#include <mutex>\n#include <vector>\n#include <jni/LocalString.h>\n#include <fb/log.h>\n\nnamespace facebook {\nnamespace jni {\n\njint initialize(JavaVM* vm, std::function<void()>&& init_fn) noexcept {\n  static std::once_flag flag{};\n  // TODO (t7832883): DTRT when we have exception pointers\n  static auto error_msg = std::string{\"Failed to initialize fbjni\"};\n  static auto error_occured = false;\n\n  std::call_once(flag, [vm] {\n    try {\n      Environment::initialize(vm);\n    } catch (std::exception& ex) {\n      error_occured = true;\n      try {\n        error_msg = std::string{\"Failed to initialize fbjni: \"} + ex.what();\n      } catch (...) {\n        // Ignore, we already have a fall back message\n      }\n    } catch (...) {\n      error_occured = true;\n    }\n  });\n\n  try {\n    if (error_occured) {\n      throw std::runtime_error(error_msg);\n    }\n\n    init_fn();\n  } catch (const std::exception& e) {\n    FBLOGE(\"error %s\", e.what());\n    translatePendingCppExceptionToJavaException();\n  } catch (...) {\n    translatePendingCppExceptionToJavaException();\n    // So Java will handle the translated exception, fall through and\n    // return a good version number.\n  }\n  return JNI_VERSION_1_6;\n}\n\nalias_ref<JClass> findClassStatic(const char* name) {\n  const auto env = internal::getEnv();\n  if (!env) {\n    throw std::runtime_error(\"Unable to retrieve JNIEnv*.\");\n  }\n  local_ref<jclass> cls = adopt_local(env->FindClass(name));\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);\n  auto leaking_ref = (jclass)env->NewGlobalRef(cls.get());\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(!leaking_ref);\n  return wrap_alias(leaking_ref);\n}\n\nlocal_ref<JClass> findClassLocal(const char* name) {\n  const auto env = internal::getEnv();\n  if (!env) {\n    throw std::runtime_error(\"Unable to retrieve JNIEnv*.\");\n  }\n  auto cls = env->FindClass(name);\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(!cls);\n  return adopt_local(cls);\n}\n\n\n// jstring /////////////////////////////////////////////////////////////////////////////////////////\n\nstd::string JString::toStdString() const {\n  const auto env = internal::getEnv();\n  auto utf16String = JStringUtf16Extractor(env, self());\n  return detail::utf16toUTF8(utf16String.chars(), utf16String.length());\n}\n\nlocal_ref<JString> make_jstring(const char* utf8) {\n  if (!utf8) {\n    return {};\n  }\n  const auto env = internal::getEnv();\n  size_t len;\n  size_t modlen = detail::modifiedLength(reinterpret_cast<const uint8_t*>(utf8), &len);\n  jstring result;\n  if (modlen == len) {\n    // The only difference between utf8 and modifiedUTF8 is in encoding 4-byte UTF8 chars\n    // and '\\0' that is encoded on 2 bytes.\n    //\n    // Since modifiedUTF8-encoded string can be no shorter than it's UTF8 conterpart we\n    // know that if those two strings are of the same length we don't need to do any\n    // conversion -> no 4-byte chars nor '\\0'.\n    result = env->NewStringUTF(utf8);\n  } else {\n    auto modified = std::vector<char>(modlen + 1); // allocate extra byte for \\0\n    detail::utf8ToModifiedUTF8(\n      reinterpret_cast<const uint8_t*>(utf8), len,\n      reinterpret_cast<uint8_t*>(modified.data()), modified.size());\n    result = env->NewStringUTF(modified.data());\n  }\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();\n  return adopt_local(result);\n}\n\n\n// JniPrimitiveArrayFunctions //////////////////////////////////////////////////////////////////////\n\n#pragma push_macro(\"DEFINE_PRIMITIVE_METHODS\")\n#undef DEFINE_PRIMITIVE_METHODS\n#define DEFINE_PRIMITIVE_METHODS(TYPE, NAME, SMALLNAME)                        \\\n                                                                               \\\ntemplate<>                                                                     \\\nFBEXPORT                                                                       \\\nTYPE* JPrimitiveArray<TYPE ## Array>::getElements(jboolean* isCopy) {          \\\n  auto env = internal::getEnv();                                               \\\n  TYPE* res =  env->Get ## NAME ## ArrayElements(self(), isCopy);              \\\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();                                      \\\n  return res;                                                                  \\\n}                                                                              \\\n                                                                               \\\ntemplate<>                                                                     \\\nFBEXPORT                                                                       \\\nvoid JPrimitiveArray<TYPE ## Array>::releaseElements(                          \\\n    TYPE* elements, jint mode) {                                               \\\n  auto env = internal::getEnv();                                               \\\n  env->Release ## NAME ## ArrayElements(self(), elements, mode);               \\\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();                                      \\\n}                                                                              \\\n                                                                               \\\ntemplate<>                                                                     \\\nFBEXPORT                                                                       \\\nvoid JPrimitiveArray<TYPE ## Array>::getRegion(                                \\\n    jsize start, jsize length, TYPE* buf) {                                    \\\n  auto env = internal::getEnv();                                               \\\n  env->Get ## NAME ## ArrayRegion(self(), start, length, buf);                 \\\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();                                      \\\n}                                                                              \\\n                                                                               \\\ntemplate<>                                                                     \\\nFBEXPORT                                                                       \\\nvoid JPrimitiveArray<TYPE ## Array>::setRegion(                                \\\n    jsize start, jsize length, const TYPE* elements) {                         \\\n  auto env = internal::getEnv();                                               \\\n  env->Set ## NAME ## ArrayRegion(self(), start, length, elements);            \\\n  FACEBOOK_JNI_THROW_PENDING_EXCEPTION();                                      \\\n}                                                                              \\\n                                                                               \\\nFBEXPORT                                                                       \\\nlocal_ref<TYPE ## Array> make_ ## SMALLNAME ## _array(jsize size) {            \\\n  auto array = internal::getEnv()->New ## NAME ## Array(size);                 \\\n  FACEBOOK_JNI_THROW_EXCEPTION_IF(!array);                                     \\\n  return adopt_local(array);                                                   \\\n}                                                                              \\\n                                                                               \\\ntemplate<>                                                                     \\\nFBEXPORT                                                                       \\\nlocal_ref<TYPE ## Array> JArray ## NAME::newArray(size_t count) {              \\\n  return make_ ## SMALLNAME ## _array(count);                                  \\\n}                                                                              \\\n                                                                               \\\n\nDEFINE_PRIMITIVE_METHODS(jboolean, Boolean, boolean)\nDEFINE_PRIMITIVE_METHODS(jbyte, Byte, byte)\nDEFINE_PRIMITIVE_METHODS(jchar, Char, char)\nDEFINE_PRIMITIVE_METHODS(jshort, Short, short)\nDEFINE_PRIMITIVE_METHODS(jint, Int, int)\nDEFINE_PRIMITIVE_METHODS(jlong, Long, long)\nDEFINE_PRIMITIVE_METHODS(jfloat, Float, float)\nDEFINE_PRIMITIVE_METHODS(jdouble, Double, double)\n#pragma pop_macro(\"DEFINE_PRIMITIVE_METHODS\")\n\n// Internal debug /////////////////////////////////////////////////////////////////////////////////\n\nnamespace internal {\n\nFBEXPORT ReferenceStats g_reference_stats;\n\nFBEXPORT void facebook::jni::internal::ReferenceStats::reset() noexcept {\n  locals_deleted = globals_deleted = weaks_deleted = 0;\n}\n\n}\n\n}}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/java/BUCK",
    "content": "include_defs(\"//ReactAndroid/DEFS\")\n\njava_library(\n    name = \"java\",\n    srcs = glob([\"**/*.java\"]),\n    visibility = [\"PUBLIC\"],\n    deps = [\n        \"//java/com/facebook/proguard/annotations:annotations\",\n    ],\n)\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/java/CppException.java",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\npackage fb.jni.java;\n\nimport com.facebook.proguard.annotations.DoNotStrip;\n\n@DoNotStrip\npublic class CppException extends RuntimeException {\n  @DoNotStrip\n  public CppException(String message) {\n    super(message);\n  }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/java/CppSystemErrorException.java",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\npackage fb.jni.java;\n\nimport com.facebook.proguard.annotations.DoNotStrip;\n\n@DoNotStrip\npublic class CppSystemErrorException extends CppException {\n  int errorCode;\n\n  @DoNotStrip\n  public CppSystemErrorException(String message, int errorCode) {\n    super(message);\n    this.errorCode = errorCode;\n  }\n\n  public int getErrorCode() {\n    return errorCode;\n  }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/java/UnknownCppException.java",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\npackage fb.jni.java;\n\nimport com.facebook.proguard.annotations.DoNotStrip;\n\n@DoNotStrip\npublic class UnknownCppException extends CppException {\n  @DoNotStrip\n  public UnknownCppException() {\n    super(\"Unknown\");\n  }\n\n  @DoNotStrip\n  public UnknownCppException(String message) {\n    super(message);\n  }\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/jni/jni_helpers.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include <jni.h>\n#include <stddef.h>\n#include <cstdio>\n\n#include <jni/jni_helpers.h>\n\n#define MSG_SIZE 1024\n\nnamespace facebook {\n\n/**\n * Instructs the JNI environment to throw an exception.\n *\n * @param pEnv JNI environment\n * @param szClassName class name to throw\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\njint throwException(JNIEnv* pEnv, const char* szClassName, const char* szFmt, va_list va_args) {\n  char szMsg[MSG_SIZE];\n  vsnprintf(szMsg, MSG_SIZE, szFmt, va_args);\n  jclass exClass = pEnv->FindClass(szClassName);\n  return pEnv->ThrowNew(exClass, szMsg);\n}\n\n/**\n * Instructs the JNI environment to throw a NoClassDefFoundError.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\njint throwNoClassDefError(JNIEnv* pEnv, const char* szFmt, ...) {\n  va_list va_args;\n  va_start(va_args, szFmt);\n  jint ret = throwException(pEnv, \"java/lang/NoClassDefFoundError\", szFmt, va_args);\n  va_end(va_args);\n  return ret;\n}\n\n/**\n * Instructs the JNI environment to throw a RuntimeException.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\njint throwRuntimeException(JNIEnv* pEnv, const char* szFmt, ...) {\n  va_list va_args;\n  va_start(va_args, szFmt);\n  jint ret = throwException(pEnv, \"java/lang/RuntimeException\", szFmt, va_args);\n  va_end(va_args);\n  return ret;\n}\n\n/**\n * Instructs the JNI environment to throw an IllegalArgumentException.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\njint throwIllegalArgumentException(JNIEnv* pEnv, const char* szFmt, ...) {\n  va_list va_args;\n  va_start(va_args, szFmt);\n  jint ret = throwException(pEnv, \"java/lang/IllegalArgumentException\", szFmt, va_args);\n  va_end(va_args);\n  return ret;\n}\n\n/**\n * Instructs the JNI environment to throw an IllegalStateException.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\njint throwIllegalStateException(JNIEnv* pEnv, const char* szFmt, ...) {\n  va_list va_args;\n  va_start(va_args, szFmt);\n  jint ret = throwException(pEnv, \"java/lang/IllegalStateException\", szFmt, va_args);\n  va_end(va_args);\n  return ret;\n}\n\n/**\n * Instructs the JNI environment to throw an OutOfMemoryError.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\njint throwOutOfMemoryError(JNIEnv* pEnv, const char* szFmt, ...) {\n  va_list va_args;\n  va_start(va_args, szFmt);\n  jint ret = throwException(pEnv, \"java/lang/OutOfMemoryError\", szFmt, va_args);\n  va_end(va_args);\n  return ret;\n}\n\n/**\n * Instructs the JNI environment to throw an AssertionError.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\njint throwAssertionError(JNIEnv* pEnv, const char* szFmt, ...) {\n  va_list va_args;\n  va_start(va_args, szFmt);\n  jint ret = throwException(pEnv, \"java/lang/AssertionError\", szFmt, va_args);\n  va_end(va_args);\n  return ret;\n}\n\n/**\n * Instructs the JNI environment to throw an IOException.\n *\n * @param pEnv JNI environment\n * @param szFmt sprintf-style format string\n * @param ... sprintf-style args\n * @return 0 on success; a negative value on failure\n */\njint throwIOException(JNIEnv* pEnv, const char* szFmt, ...) {\n  va_list va_args;\n  va_start(va_args, szFmt);\n  jint ret = throwException(pEnv, \"java/io/IOException\", szFmt, va_args);\n  va_end(va_args);\n  return ret;\n}\n\n/**\n * Finds the specified class. If it's not found, instructs the JNI environment to throw an\n * exception.\n *\n * @param pEnv JNI environment\n * @param szClassName the classname to find in JNI format (e.g. \"java/lang/String\")\n * @return the class or NULL if not found (in which case a pending exception will be queued). This\n *     returns a global reference (JNIEnv::NewGlobalRef).\n */\njclass findClassOrThrow(JNIEnv* pEnv, const char* szClassName) {\n  jclass clazz = pEnv->FindClass(szClassName);\n  if (!clazz) {\n    return NULL;\n  }\n  return (jclass) pEnv->NewGlobalRef(clazz);\n}\n\n/**\n * Finds the specified field of the specified class. If it's not found, instructs the JNI\n * environment to throw an exception.\n *\n * @param pEnv JNI environment\n * @param clazz the class to lookup the field in\n * @param szFieldName the name of the field to find\n * @param szSig the signature of the field\n * @return the field or NULL if not found (in which case a pending exception will be queued)\n */\njfieldID getFieldIdOrThrow(JNIEnv* pEnv, jclass clazz, const char* szFieldName, const char* szSig) {\n  return pEnv->GetFieldID(clazz, szFieldName, szSig);\n}\n\n/**\n * Finds the specified method of the specified class. If it's not found, instructs the JNI\n * environment to throw an exception.\n *\n * @param pEnv JNI environment\n * @param clazz the class to lookup the method in\n * @param szMethodName the name of the method to find\n * @param szSig the signature of the method\n * @return the method or NULL if not found (in which case a pending exception will be queued)\n */\njmethodID getMethodIdOrThrow(\n    JNIEnv* pEnv,\n    jclass clazz,\n    const char* szMethodName,\n    const char* szSig) {\n  return pEnv->GetMethodID(clazz, szMethodName, szSig);\n}\n\n} // namespace facebook\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/log.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include <fb/log.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <string.h>\n\n#define LOG_BUFFER_SIZE 4096\nstatic LogHandler gLogHandler;\n\nvoid setLogHandler(LogHandler logHandler) {\n  gLogHandler = logHandler;\n}\n\nint fb_printLog(int prio, const char *tag,  const char *fmt, ...) {\n  char logBuffer[LOG_BUFFER_SIZE];\n\n  va_list va_args;\n  va_start(va_args, fmt);\n  int result = vsnprintf(logBuffer, sizeof(logBuffer), fmt, va_args);\n  va_end(va_args);\n  if (gLogHandler != NULL) {\n      gLogHandler(prio, tag, logBuffer);\n  }\n  __android_log_write(prio, tag, logBuffer);\n  return result;\n}\n\nvoid logPrintByDelims(int priority, const char* tag, const char* delims,\n        const char* msg, ...)\n{\n    va_list ap;\n    char buf[32768];\n    char* context;\n    char* tok;\n\n    va_start(ap, msg);\n    vsnprintf(buf, sizeof(buf), msg, ap);\n    va_end(ap);\n\n    tok = strtok_r(buf, delims, &context);\n\n    if (!tok) {\n        return;\n    }\n\n    do {\n        __android_log_write(priority, tag, tok);\n    } while ((tok = strtok_r(NULL, delims, &context)));\n}\n\n#ifndef ANDROID\n\n// Implementations of the basic android logging functions for non-android platforms.\n\nstatic char logTagChar(int prio) {\n  switch (prio) {\n    default:\n    case ANDROID_LOG_UNKNOWN:\n    case ANDROID_LOG_DEFAULT:\n    case ANDROID_LOG_SILENT:\n      return ' ';\n    case ANDROID_LOG_VERBOSE:\n      return 'V';\n    case ANDROID_LOG_DEBUG:\n      return 'D';\n    case ANDROID_LOG_INFO:\n      return 'I';\n    case ANDROID_LOG_WARN:\n      return 'W';\n    case ANDROID_LOG_ERROR:\n      return 'E';\n    case ANDROID_LOG_FATAL:\n      return 'F';\n  }\n}\n\nint __android_log_write(int prio, const char *tag, const char *text) {\n  return fprintf(stderr, \"[%c/%.16s] %s\\n\", logTagChar(prio), tag, text);\n}\n\nint __android_log_print(int prio, const char *tag,  const char *fmt, ...) {\n  va_list ap;\n  va_start(ap, fmt);\n\n  int res = fprintf(stderr, \"[%c/%.16s] \", logTagChar(prio), tag);\n  res += vfprintf(stderr, \"%s\\n\", ap);\n\n  va_end(ap);\n  return res;\n}\n\n#endif\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/lyra/lyra.cpp",
    "content": "// Copyright 2004-present Facebook. All Rights Reserved.\n\n#include <fb/lyra.h>\n\n#include <ios>\n#include <memory>\n#include <vector>\n\n#include <dlfcn.h>\n#include <unwind.h>\n\nusing namespace std;\n\nnamespace facebook {\nnamespace lyra {\n\nnamespace {\n\nclass IosFlagsSaver {\n  ios_base& ios_;\n  ios_base::fmtflags flags_;\n\n public:\n  IosFlagsSaver(ios_base& ios)\n  : ios_(ios),\n    flags_(ios.flags())\n  {}\n\n  ~IosFlagsSaver() {\n    ios_.flags(flags_);\n  }\n};\n\nstruct BacktraceState {\n  size_t skip;\n  vector<InstructionPointer>& stackTrace;\n};\n\n_Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) {\n  BacktraceState* state = reinterpret_cast<BacktraceState*>(arg);\n  auto absoluteProgramCounter =\n      reinterpret_cast<InstructionPointer>(_Unwind_GetIP(context));\n\n  if (state->skip > 0) {\n    --state->skip;\n    return _URC_NO_REASON;\n  }\n\n  if (state->stackTrace.size() == state->stackTrace.capacity()) {\n    return _URC_END_OF_STACK;\n  }\n\n  state->stackTrace.push_back(absoluteProgramCounter);\n\n  return _URC_NO_REASON;\n}\n\nvoid captureBacktrace(size_t skip, vector<InstructionPointer>& stackTrace) {\n  // Beware of a bug on some platforms, which makes the trace loop until the\n  // buffer is full when it reaches a noexcept function. It seems to be fixed in\n  // newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846\n  // TODO(t10738439): Investigate workaround for the stack trace bug\n  BacktraceState state = {skip, stackTrace};\n  _Unwind_Backtrace(unwindCallback, &state);\n}\n}\n\nvoid getStackTrace(vector<InstructionPointer>& stackTrace, size_t skip) {\n  stackTrace.clear();\n  captureBacktrace(skip + 1, stackTrace);\n}\n\n// TODO(t10737622): Improve on-device symbolification\nvoid getStackTraceSymbols(vector<StackTraceElement>& symbols,\n                          const vector<InstructionPointer>& trace) {\n  symbols.clear();\n  symbols.reserve(trace.size());\n\n  for (size_t i = 0; i < trace.size(); ++i) {\n    Dl_info info;\n    if (dladdr(trace[i], &info)) {\n      symbols.emplace_back(trace[i], info.dli_fbase, info.dli_saddr,\n                           info.dli_fname ? info.dli_fname : \"\",\n                           info.dli_sname ? info.dli_sname : \"\");\n    }\n  }\n}\n\nostream& operator<<(ostream& out, const StackTraceElement& elm) {\n  IosFlagsSaver flags{out};\n\n  // TODO(t10748683): Add build id to the output\n  out << \"{dso=\" << elm.libraryName() << \" offset=\" << hex\n      << showbase << elm.libraryOffset();\n\n  if (!elm.functionName().empty()) {\n    out << \" func=\" << elm.functionName() << \"()+\" << elm.functionOffset();\n  }\n\n  out << \" build-id=\" << hex << setw(8) << 0\n      << \"}\";\n\n  return out;\n}\n\n// TODO(t10737667): The implement a tool that parse the stack trace and\n// symbolicate it\nostream& operator<<(ostream& out, const vector<StackTraceElement>& trace) {\n  IosFlagsSaver flags{out};\n\n  auto i = 0;\n  out << \"Backtrace:\\n\";\n  for (auto& elm : trace) {\n    out << \"    #\" << dec << setfill('0') << setw(2) << i++ << \" \" << elm << '\\n';\n  }\n\n  return out;\n}\n}\n}\n"
  },
  {
    "path": "VirtualApp/lib/src/main/jni/fb/onload.cpp",
    "content": "/*\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\n#include <jni.h>\n#ifndef DISABLE_CPUCAP\n#include <fb/CpuCapabilities.h>\n#endif\n#include <fb/fbjni.h>\n\nusing namespace facebook::jni;\n\nvoid initialize_xplatinit();\nvoid initialize_fbjni();\n\nJNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {\n  return facebook::jni::initialize(vm, [] {\n    initialize_fbjni();\n#ifndef DISABLE_XPLAT\n    initialize_xplatinit();\n#endif\n#ifndef DISABLE_CPUCAP\n    initialize_cpucapabilities();\n#endif\n  });\n}\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/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": "VirtualApp/lib/src/main/res/layout/resolve_list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<!--\r\n/* //device/apps/common/res/any/layout/resolve_list_item.xml\r\n**\r\n** Copyright 2006, The Android Open Source Project\r\n**\r\n** Licensed under the Apache License, Version 2.0 (the \"License\");\r\n** you may not use this file except in compliance with the License.\r\n** You may obtain a copy of the License at\r\n**\r\n**     http://www.apache.org/licenses/LICENSE-2.0\r\n**\r\n** Unless required by applicable law or agreed to in writing, software\r\n** distributed under the License is distributed on an \"AS IS\" BASIS,\r\n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n** See the License for the specific language governing permissions and\r\n** limitations under the License.\r\n*/\r\n-->\r\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n              android:layout_width=\"match_parent\"\r\n              android:layout_height=\"wrap_content\"\r\n              android:orientation=\"horizontal\"\r\n              android:paddingBottom=\"4dp\"\r\n              android:paddingTop=\"4dp\">\r\n\r\n    <!-- Activity icon when presenting dialog\r\n         Size will be filled in by ResolverActivity -->\r\n    <ImageView\r\n        android:id=\"@+id/icon\"\r\n        android:layout_width=\"24dp\"\r\n        android:layout_height=\"24dp\"\r\n        android:layout_marginLeft=\"8dp\"\r\n        android:layout_gravity=\"start|center_vertical\"\r\n        android:layout_marginBottom=\"12dp\"\r\n        android:layout_marginTop=\"12dp\"\r\n        android:scaleType=\"fitCenter\"/>\r\n\r\n    <LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\r\n                  android:layout_width=\"wrap_content\"\r\n                  android:layout_height=\"wrap_content\"\r\n                  android:layout_marginLeft=\"8dp\"\r\n                  android:layout_gravity=\"start|center_vertical\"\r\n                  android:gravity=\"start|center_vertical\"\r\n                  android:orientation=\"vertical\">\r\n        <!-- Activity name -->\r\n        <TextView\r\n            android:id=\"@+id/text1\"\r\n            android:layout_width=\"wrap_content\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:ellipsize=\"marquee\"\r\n            android:maxLines=\"1\"\r\n            android:minLines=\"1\"/>\r\n        <!-- Extended activity info to distinguish between duplicate activity names -->\r\n        <TextView\r\n            android:id=\"@+id/text2\"\r\n            android:layout_width=\"wrap_content\"\r\n            android:layout_height=\"wrap_content\"\r\n            android:ellipsize=\"marquee\"\r\n            android:maxLines=\"1\"\r\n            android:minLines=\"1\"\r\n            android:textAppearance=\"?android:attr/textAppearanceSmall\"/>\r\n    </LinearLayout>\r\n</LinearLayout>\r\n\r\n"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/lib/src/main/res/values/integer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<resources>\r\n    <integer name=\"config_maxResolverActivityColumns\">8</integer>\r\n</resources>"
  },
  {
    "path": "VirtualApp/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": "VirtualApp/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": "VirtualApp/settings.gradle",
    "content": "include ':lib', ':app'\n"
  },
  {
    "path": "doc/VADev.md",
    "content": "<h1><p align=\"center\">VA基础开发文档</p></h1> \n\n本文档主要介绍2部分。  \n第一部分是VA的源码结构介绍，这部分是为了让开发者能快速了解掌握VA源码框架。  \n第二部分是VA的基础SDK使用说明。 \n其他更多的开发文档见：[VA私有库Wiki](https://github.com/asLody/VirtualApp-Priv/wiki)  \nVA产品说明：[文档](../README.md)\n</br>\n\n**下面开始第一部分，VA源码结构介绍：**\n\n## 1. VA源码目录介绍 ##\n下图是VA源码根目录：  \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/1.png)  \n可以看到VA一共有4个源码目录，各个目录介绍如下：\n\n目录名称 | 作用\n---- | ---\napp | VA Demo主包源码所在目录\napp-ext | VA Demo插件包源码所在目录\nlib | VA库源码所在目录\nlib-ext | VA插件库源码所在目录\n<br/>\n\n## 2. VA编译配置文件介绍 ##\nVA的编译配置文件是VAConfig.gradle：  \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/2_1.jpg)  \n\n配置解释：\n\n配置名称 | 作用\n---- | ---\nPACKAGE_NAME | 用于配置VA主包的包名\nEXT_PACKAGE_NAME | 用于配置VA插件包的包名\nVA_MAIN_PACKAGE_32BIT | 用于配置VA主包是32位还是64位，true为32位，false为64位\nVA_ACCESS_PERMISSION_NAME | 用于配置VA中4大组建的权限名称\nVA_AUTHORITY_PREFIX | 用于配置VA主包中ContentProvider的authorities\nVA_EXT_AUTHORITY_PREFIX | 用于配置VA插件包中ContentProvider的authorities\nVA_VERSION | 用于配置VA库版本，开发者一般不需要关心\nVA_VERSION_CODE | 用于配置VA库版本代码，开发者一般不需要关心\n<br/>\n\n## 3. VA核心代码解释 ##\n1. `com.lody.virtual.client`包下的代码运行在VAPP Client进程中，主要用于VA Framework中的APP Hook部分，完成对各个Service的HOOK处理  \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/3_1.png)  \n2. `com.lody.virtual.server`包下的代码运行在VA Server进程中，代码主要用于VA Framework中的APP Server部分，实现处理APP安装以及其他不给Android系统处理的APP请求  \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/3_2.png)\n3. `mirror`包下的代码主要用于对系统隐藏类的引用，属于工具类，减少大量反射代码的编写  \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/3_3.png)\n4. `cpp`包下的代码进行在VAPP Client进程中，主要用于VA Native部分，实现IO重定向和jni函数HOOK。其中：  \n\t- `substrate`中实现了针对arm32和arm64的hook  \n\t- `vfs.cpp`中实现了VA的虚拟文件系统，用于控制APP文件访问限制  \n\t- `syscall_hook.cpp`中实现了对IO的Hook  \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/3_4.png)  \n5. `DelegateApplicationExt.java`运行在VA Host Plugin进程中，用于VA插件包，实现了对主包代码的加载执行  \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/3_5.png)  \n\n</br></br>\n**下面开始第二部分，VA SDK使用介绍：**\n\n## 1. VA工程接入 ##\n### 用Android Studio打开VirtualApp-Priv项目\n\n可见多个模块:\n* app\n* app-ext\n* lib\n* lib-ext\n\n其中**lib**和**lib-ext**属于VirtualApp`核心库`以及`扩展库`，**app**和**app-ext**则属于`示例app`。\n\n\n\n### 创建自己的App\n新建一个application类型的module，并添加lib模块为依赖\n```gradle\nimplementation project(':lib')\n```\n\n### 根据需求修改VAConfig.gradle：\n```gradle\next {\n    VA_MAIN_PACKAGE_32BIT = true  // 主包为32位\n    VA_ACCESS_PERMISSION_NAME = \"io.busniess.va.permission.SAFE_ACCESS\"  // VirtualApp组件用到的权限名称\n    VA_AUTHORITY_PREFIX = \"io.busniess.va\"  // VirtualApp中ContentProvider用到的authority，不能与其他app重复\n    VA_EXT_AUTHORITY_PREFIX = \"io.busniess.va.ext\"  // VirtualApp扩展包中ContentProvider用到的authority，不能与其他app重复\n    // ...\n}\n```\n\n### 在AndroidManifest.xml添加所需的权限\n```xml\n<uses-permission android:name=\"${VA_ACCESS_PERMISSION_NAME}\" />\n```\n权限名称必须与**VAConfig.gradle**中所声明的保持一致，可以在**build.gradle**中添加**Placeholder**来防止出错。\n\n``` gradle\nandroid {\n    // ...\n    manifestPlaceholders = [\n                VA_ACCESS_PERMISSION_NAME: rootProject.ext.VA_ACCESS_PERMISSION_NAME,\n    ]\n}\n```\n\n### 创建一个Application\n\n#### 复写attachBaseContext方法，添加引导VirtualApp的代码：\n\n```java\n    @Override\n    protected void attachBaseContext(Context base) {\n        super.attachBaseContext(base);\n        try {\n            VirtualCore.get().startup(base, mConfig);\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n    }\n\n```\n\n#### 这里传入了一个VirtualApp的一个配置 mConfig\n```java\nprivate SettingConfig mConfig = new SettingConfig() {\n        @Override\n        public String getMainPackageName() {\n            // 主包的包名\n            return BuildConfig.APPLICATION_ID;\n        }\n\n        @Override\n        public String getExtPackageName() {\n            // 扩展包包名\n            return BuildConfig.EXT_PACKAGE_NAME;\n        }\n\n        @Override\n        public boolean isEnableIORedirect() {\n            // 是否启用IO重定向，建议开启\n            return true;\n        }\n\n        @Override\n        public Intent onHandleLauncherIntent(Intent originIntent) {\n            // 回到桌面的 Intent 拦截操作，这里把回到桌面的动作改成回到主包的BackHomeActivity页面\n            Intent intent = new Intent();\n            ComponentName component = new ComponentName(getMainPackageName(), BackHomeActivity.class.getName());\n            intent.setComponent(component);\n            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n            return intent;\n        }\n\n        @Override\n        public boolean isUseRealDataDir(String packageName) {\n            // data路径模拟真实路径格式，需要启用IO重定向。部分加固会校该验路径格式\n            return false;\n        }\n\n        @Override\n        public boolean isOutsidePackage(String packageName) {\n            // 是否是外部app。 设置外部 app 对内部app看见\n            return false;\n        }\n\n        @Override\n        public boolean isAllowCreateShortcut() {\n            // 是否允许创建桌面快捷图标。建议关闭（false），自己实现桌面快捷方式\n            return false;\n        }\n\n        @Override\n        public boolean isHostIntent(Intent intent) {\n            // 是否由VirtualApp处理的Intent\n            return intent.getData() != null && \"market\".equals(intent.getData().getScheme());\n        }\n\n        @Override\n        public boolean isUseRealApkPath(String packageName) {\n            // 安装apk路径模拟真实路径，需要启用IO重定向。部分加固会校验该路径格式\n            return false;\n        }\n\n        @Override\n        public boolean isEnableVirtualSdcardAndroidData() {\n            // 启用外置存储下的 `Android/data` 目录的重定向\n            // 需要重定向支持\n            // Android 11 之后必须启用！！\n            return BuildCompat.isR();\n        }\n\n        @Override\n        public String getVirtualSdcardAndroidDataName() {\n            // 设置外置存储下的 `Android/data` 目录的重定向路径\n            // /sdcard/Android/data/com.example.test/ ==>> /sdcard/{VirtualSdcardAndroidDataName}/{user_id}/Android/data/com.example.test/\n            return \"Android_va\";\n        }\n\n        @Override\n        public FakeWifiStatus getFakeWifiStatus() {\n            // 修改wifi信息。 null 则不修改\n            return null;\n        }\n\n        @Override\n        public boolean isHideForegroundNotification() {\n            // 隐藏前台消息，不建议隐藏\n            return false;\n        }\n\n        @Override\n        public boolean isOutsideAction(String action) {\n            // 外部 Intent 的 action 事件响应\n            return MediaStore.ACTION_IMAGE_CAPTURE.equals(action)\n                || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)\n                || Intent.ACTION_PICK.equals(action);\n        }\n\n        @Override\n        public boolean isDisableDrawOverlays(String packageName) {\n            // 禁用 VAPP 的顶层覆盖（浮窗）。\n            return false;\n        }\n    };\n```\n\n### 复写onCreate，添加初始化VirtualApp的代码：\n```java\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        VirtualCore virtualCore = VirtualCore.get();\n        virtualCore.initialize(new VirtualCore.VirtualInitializer() {\n            @Override\n            public void onMainProcess() {\n                // 主进程回调\n            }\n\n            @Override\n            public void onVirtualProcess() {\n                // 虚拟App进程回调\n            }\n\n            @Override\n            public void onServerProcess() {\n                // 服务端进程回调\n            }\n\n            @Override\n            public void onChildProcess() {\n                // 其他子进程回调\n            }\n        });\n    }\n\n```\n\n由于VirtualApp会启动多个进程，所以Application会进入N次，不同的进程会走到VirtualInitializer不同的回调，可以在这里根据进程类型添加额外的初始化代码。\n\n## 2. 安装APP ##\n## API:\n```java\nVirtualCore.java\n\n public VAppInstallerResult installPackage(Uri uri, VAppInstallerParams params);\n```\n## 参数Uri是什么?\nUri决定了**需要安装的apk**的来源，目前支持 package 和 file 协议。\n### Package Uri 示例:\n```java\nUri packageUri = Uri.parse(\"package:com.hello.world\");\n```\n### File Uri 示例:\n```java\nFile apkFile = new File(\"/sdcard/test.apk\"); \nUri packageUri = Uri.fromFile(apkFile);\n```\n\n## 两种Uri安装app有何区别?\n**package协议** 安装app，只需要传入包名，不需要具体的APK路径，所以以这种协议安装的app，**相当于双开**。\n\napp会随外部版本的升级而自动升级，随外部版本的卸载而自动卸载。`PackageSetting` 中的 `dynamic` 为 `true`。\n\n**file协议** 则是内部安装，apk会被复制到容器内部，与外部版本完全独立. `PackageSetting` 中的 `dynamic` 为 `false`。\n\n## 安装参数 VAppInstallerParams\n\n### 安装标志 installFlags\n\nFLAG | 说明\n--- | ---\nFLAG_INSTALL_OVERRIDE_NO_CHECK | 允许覆盖安装\nFLAG_INSTALL_OVERRIDE_FORBIDDEN | 禁止覆盖安装\nFLAG_INSTALL_OVERRIDE_DONT_KILL_APP | 覆盖安装不kill已经启动的APP\n\n### 安装模式 mode\n\nFLAG | 说明\n--- | ---\nMODE_FULL_INSTALL | 完整安装\nMODE_INHERIT_EXISTING | 已安装的的安装模式。预留\n\n预留参数，暂时未使用。目前不管设置哪种都一样。\n\n### cpuAbiOverride\n\n指定app的abi。特殊需求下，可以强制指定app在指定abi下运行。不指定的情况下默认根据`系统规则`来决定运行的abi。\n\n可选参数：\n* armeabi\n* armeabi-v7a\n* arm64-v8a\n\n### 双开app实例代码：\n```java\nVAppInstallerParams params = new VAppInstallerParams(VAppInstallerParams.FLAG_INSTALL_OVERRIDE_NO_CHECK);\nVAppInstallerResult result = VirtualCore.get().installPackage(Uri.parse(\"package:com.tencent.mobileqq\"), params);\nif (result.status == VAppInstallerResult.STATUS_SUCCESS) {\n    Log.e(\"test\", \"install apk success.\");\n}\n```\n\n### 从sd卡安装apk实例代码：\n```java\nVAppInstallerParams params = new VAppInstallerParams(VAppInstallerParams.FLAG_INSTALL_OVERRIDE_NO_CHECK);\nVAppInstallerResult result = VirtualCore.get().installPackage(Uri.fromFile(new File(\"/sdcard/test.apk\")), params);\nif (result.status == VAppInstallerResult.STATUS_SUCCESS) {\n    Log.e(\"test\", \"install apk success.\");\n}\n```\n\n### 安装Split apk\n先安装base包，然后再安装所有split包即可。\n```java\nFile dir = new File(\"/sdcard/YouTube_XAPK_Unzip/\");\nVAppInstallerParams params = new VAppInstallerParams(VAppInstallerParams.FLAG_INSTALL_OVERRIDE_NO_CHECK);\nVAppInstallerResult result = VirtualCore.get().installPackage(\n        Uri.fromFile(new File(dir,\"com.google.android.youtube.apk\")), params);\nfor (File file : dir.listFiles()) {\n    String name = file.getName();\n    if (name.startsWith(\"config.\") && name.endsWith(\".apk\")) {\n        result = VirtualCore.get().installPackage(\n            Uri.fromFile(file), params);\n    }\n}\n\n```\n\n\n\n\n## 3. 启动及管理Application ##\n# 启动App\n\n```java\n// class VActivityManager\npublic boolean launchApp(final int userId, String packageName)\n````\n实例代码：\n```java\nVActivityManager.get().launchApp(0, \"com.tencent.mobileqq\");\n```\n\n# 杀死App\n```java\n// class VActivityManager\npublic void killAppByPkg(String pkg, int userId)\npublic void killAllApps()\n```\n实例代码：\n```java\n// 杀死userid为0的QQ程序进程\nVActivityManager.get().killAppByPkg(\"com.tencent.mobileqq\", 0);\n\n```\n\n```java\n// 杀死所有App进程\nVActivityManager.get().killAllApps();\n```\n\n# 卸载App\n```java\n// class VirtualCore\npublic boolean uninstallPackageAsUser(String pkgName, int userId)\npublic boolean uninstallPackage(String pkgName)\n```\n实例代码：\n```java\n// 卸载userid为0的QQ程序\nVirtualCore.get().uninstallPackageAsUser(\"com.tencent.mobileqq\", 0);\n// 卸载所有user下安装的QQ程序\nVirtualCore.get().uninstallPackage(\"com.tencent.mobileqq\");\n```\n\n# 查询已安装的App\n```java\n// class VirtualCore\npublic List<InstalledAppInfo> getInstalledApps(int flags)\n```\n\n## 4. Java Hook使用 ##\nVirtualApp中实现了一套Xposed接口,用户只要会使用Xposed就可以做到原本需要系统内置Xposed才能做到的事情.\n但是用户也需要明白,VA中Xposed的作用域是VA这个APP中的,不能越权控制系统或其他外部App.\n\n\nVA中提供了一个App创建启动的回调接口`com.lody.virtual.client.core.AppCallback`,接口如下:\n```java\npublic interface AppCallback {\n    void beforeStartApplication(String packageName, String processName, Context context);\n\n    void beforeApplicationCreate(String packageName, String processName, Application application);\n\n    void afterApplicationCreate(String packageName, String processName, Application application);\n}\n```\n\n> 接口说明:\n\n名称 | 说明\n---- | ---\nbeforeStartApplication | APP启动之前,创建之后\nbeforeApplicationCreate | APP被创建之前,Application已经准备完毕,Application.OnCreate未执行\nafterApplicationCreate | APP被创建之后,Application.OnCreate已被执行\n\n<br/>\n\n>参数说明:\n\n名称 | 说明\n---- | ---\npackageName | VAPP的包名\nprocessName | VAPP的进程名\ncontext | VAPP的Application context\napplication | VAPP的Application\n\n<br/>\n\n> 注: APP的创建指的是`Application`被创建.\n\n接口有了,接下来就是怎么使用了.查看[`VirualApp进程说明`](VirualApp进程说明.md),可以知道,\n我们只需要在`VAPP进程`回调里(`onVirtualProcess`) 设置App回调 `AppCallback` 就可以达到目的.\n\n> 宿主Application代码,参考[io/busniess/va/App.java](https://github.com/asLody/VirtualApp-Priv/blob/v2.1/VirtualApp/app/src/main/java/io/busniess/va/App.java)\n\n```java\n@Override\n    public void onCreate() {\n        super.onCreate();\n        VirtualCore virtualCore = VirtualCore.get();\n        virtualCore.initialize(new VirtualCore.VirtualInitializer() {\n            @Override\n            public void onVirtualProcess() {\n                // 设置VAPP启动回调\n                virtualCore.setAppCallback(new MyComponentDelegate());\n            }\n        });\n    }\n```\n\n> [MyComponentDelegate](https://github.com/asLody/VirtualApp-Priv/blob/v2.1/VirtualApp/app/src/main/java/io/busniess/va/delegate/MyComponentDelegate.java)类代码\n\n```java\npublic class MyComponentDelegate implements AppCallback {\n\n    @Override\n    public void beforeStartApplication(String packageName, String processName, Context context) {\n    }\n\n    @Override\n    public void beforeApplicationCreate(String packageName, String processName, Application application) {\n\n        XposedHelpers.findAndHookMethod(\"android.app.ContextImpl\", ClassLoader.getSystemClassLoader(), \"getOpPackageName\", new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) {\n                VLog.printStackTrace(\"getOpPackageName\");\n                param.setResult(VirtualCore.get().getHostPkg());\n            }\n        });\n    }\n\n    @Override\n    public void afterApplicationCreate(String packageName, String processName, Application application) {\n    }\n}\n```\n\n上面示例中,已经添加了一个Xposed的使用案例.Xposed的入口是一个`IXposedHookLoadPackage`的实例,他提供了一个`void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam)`的接口,有一个`XC_LoadPackage.LoadPackageParam`的参数.这里我们虽然不能完全一一对用,但是也完全够用了.`loadPackageParam.classsload`可以用`context.getClassLoader()`或者`application.getClassLoader()`都是可以的.后续`XposedHelpers`,`XposedBridge`原来怎么用,这里也一样使用.\n\n\n\n## 5. Native Hook使用 ##\n对于ARM 32和ARM 64的Hook，只需要引入头文件```CydiaSubstrate.h```即可,Hook API:  \n```MSHookFunction(Type_ *symbol, Type_ *replace, Type_ **result)```  \n>参数说明:\n\n名称 | 说明\n---- | ---\nsymbol | 要Hook的地址  \nreplace | 你自定义的hook函数\nresult | 被hook函数的备份\n<br/>\n参考```syscall_hook.cpp```代码\n\n```cpp\nauto is_accessible_str = \"__dl__ZN19android_namespace_t13is_accessibleERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE\";\nvoid *is_accessible_addr = getSym(linker_path, is_accessible_str);\nif (is_accessible_addr) {\n    MSHookFunction(is_accessible_addr, (void *) new_is_accessible,(void **)     &orig_is_accessible);\n}\n```\n\n在`MSHookFunction`内部会自动判断当前是ARM32还是ARM64：\n\n\n```cpp\n_extern void MSHookFunction(void *symbol, void *replace, void **result) {\n    if (*result != nullptr) {\n        return;\n    }\n    // ALOGE(\"[MSHookFunction] symbol(%p) replace(%p) result(%p)\", symbol, replace, *result);\n#ifdef __aarch64__\n    A64HookFunction(symbol, replace, result);\n#else\n    SubstrateHookFunction(NULL, symbol, replace, result);\n#endif\n}\n```\n\n\n</br>\n</br>\n\n[其他更多的开发指导请见VA私有库Wiki](https://github.com/asLody/VirtualApp-Priv/wiki)\n\n</br>\n</br>\n\n"
  },
  {
    "path": "doc/VADev_eng.md",
    "content": "<h1><p align=\"center\">VA Basic Development Document</p></h1> \n\nThis document mainly introduces 2 parts.  \nThe first part is the introduction of VA source code structure, this part is to allow developers to quickly understand to master the VA source code framework.    \nThe second part is a description of VA's basic SDK. For more development documents, see: VA Private Library Wiki.   \nFor more development documents, see：[VA Private Library Wiki](https://github.com/asLody/VirtualApp-Priv/wiki)  \nVA Product Description：[Document](../README_eng.md)\n</br>\n\n**The following is the first part, the introduction of the VA source code structure：**\n\n## 1. Introduction of VA source code directory ##\nThe following figure is the root of the VA source code：  \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/1.png)  \nYou can see that VA has a total of four source code directories, and each directory is described as follows：\n\nDirectory Name | Function\n---- | ---\napp | The directory where the VA Demo master package source code is located\napp-ext | The directory where the source code of VA Demo plug-in package is located\nlib | The directory where the VA library source code is located\nlib-ext | The directory where the source code of VA Plugin Library is located\n<br/>\n\n## 2. Introduction of VA compilation configuration profile ##\nVA compilation configuration profile isVAConfig.gradle：  \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/2_1.jpg)  \n\nConfiguration explanation：\n\nConfiguration Name | Function\n---- | ---\nPACKAGE_NAME | Used to configure the package name of the VA main package\nEXT_PACKAGE_NAME | Used to configure the package name of the VA plug-in package\nVA_MAIN_PACKAGE_32BIT | Used to configure whether the VA main package is 32-bit or 64-bit, true is 32-bit, false is 64-bit\nVA_ACCESS_PERMISSION_NAME | Used to configure the permission names of the 4 major components in VA\nVA_AUTHORITY_PREFIX | Used to configure the authorities of ContentProvider in the VA main packag\nVA_EXT_AUTHORITY_PREFIX | Used to configure the authorities of the ContentProvider in the VA plug-in package\nVA_VERSION | Used to configure the VA library version, developers generally do not need to care\nVA_VERSION_CODE | Used to configure the VA library version code, developers generally do not need to care\n<br/>\n\n## 3. VA core code explanation ##\n1. The code under the`com.lody.virtual.client`package runs in the VAPP Client process and is mainly used in the APP Hook part of the VA Framework to complete the HOOK processing for each service.  \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/3_1.png)  \n2. The code under the`com.lody.virtual.server`package runs in the VA Server process. The code is mainly used in the APP Server part of the VA Framework to handle APP installation and other APP requests that are not handled by the Android system.  \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/3_2.png)  \n3.The code under the `mirror`package is mainly used for references to the system's hidden classes, and belongs to the tool class, reducing a lot of reflection code's writing.     \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/3_3.png)  \n4.The code under the `cpp`package is carried out in the VAPP Client process and is mainly used in the VA Native part. Implement IO redirection and jni function HOOK. Among them：  \n\t- `substrate`implements hook for arm32 and arm64  \n\t- `vfs.cpp`implements VA's virtual file system for controlling APP file access restrictions \n\t- `syscall_hook.cpp`implements Hook for IO  \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/3_4.png)  \n5.`DelegateApplicationExt.java`runs in the VA Host Plugin process，used in  the VA plug-in package,  implementing the loading and execution to the main package code.   \n![](https://cdn.jsdelivr.net/gh/xxxyanchenxxx/temp@1.0/doc/3_5.png)  \n\n</br></br>\n**The following is the second part, the introduction of using VA SDK：**\n\n## 1. VA Project Integration ##\n### Open VirtualApp-Priv project with Android Studio\n\nMultiple modules can be seen:\n* app\n* app-ext\n* lib\n* lib-ext\n\nAmong them, **lib** and **lib-ext** belong to the VirtualApp`core library `and `extensions`，while **app** and **app-ext** belong to the`sample app`.  \n\n\n\n### Create your own App\nCreate a module of type application, and add the lib module as a dependency\n```gradle\nimplementation project(':lib')\n```\n\n### Modify VAConfig.gradle according to demand：\n```gradle\next {\n    VA_MAIN_PACKAGE_32BIT = true  // The main package is 32-bit\n    VA_ACCESS_PERMISSION_NAME = \"io.busniess.va.permission.SAFE_ACCESS\"  // The name of the permission used by the VirtualApp component\n    VA_AUTHORITY_PREFIX = \"io.busniess.va\"  // The authority used by ContentProvider in VirtualApp cannot be duplicated with other Apps.  \n    VA_EXT_AUTHORITY_PREFIX = \"io.busniess.va.ext\"  // The authority used by the ContentProvider in the VirtualApp extension package cannot be duplicated with other Apps.  \n    // ...\n}\n```\n\n### Add the required permissions in AndroidManifest.xml\n```xml\n<uses-permission android:name=\"${VA_ACCESS_PERMISSION_NAME}\" />\n```\nPermission's name must be consistent with those declared in **VAConfig.gradle**, and adding **Placeholder** in **build.gradle** to prevent errors.  \n``` gradle\nandroid {\n    // ...\n    manifestPlaceholders = [\n                VA_ACCESS_PERMISSION_NAME: rootProject.ext.VA_ACCESS_PERMISSION_NAME,\n    ]\n}\n```\n\n### Create an Application\n\n#### Override the attachBaseContext method and add the code to bootstrap the VirtualApp：\n\n```java\n    @Override\n    protected void attachBaseContext(Context base) {\n        super.attachBaseContext(base);\n        try {\n            VirtualCore.get().startup(base, mConfig);\n        } catch (Throwable e) {\n            e.printStackTrace();\n        }\n    }\n\n```\n\n#### Here, a configuration of VirtualApp is passed in mConfig\n```java\nprivate SettingConfig mConfig = new SettingConfig() {\n        @Override\n        public String getMainPackageName() {\n            // Name of the main package\n            return BuildConfig.APPLICATION_ID;\n        }\n\n        @Override\n        public String getExtPackageName() {\n            // Name of extension package\n            return BuildConfig.EXT_PACKAGE_NAME;\n        }\n\n        @Override\n        public boolean isEnableIORedirect() {\n            // Whether to enable IO redirection, it is recommended to enable\n            return true;\n        }\n\n        @Override\n        public Intent onHandleLauncherIntent(Intent originIntent) {\n            // Back to the desktop of the Intent interception operation. Here change the action that back to the desktop to return to BackHomeActivity page of the main package.    \n            Intent intent = new Intent();\n            ComponentName component = new ComponentName(getMainPackageName(), BackHomeActivity.class.getName());\n            intent.setComponent(component);\n            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n            return intent;\n        }\n\n        @Override\n        public boolean isUseRealDataDir(String packageName) {\n            // The data path simulates the real path format and requires IO redirection to be enabled. Some of the hardening will check the path format.    \n            return false;\n        }\n\n        @Override\n        public boolean isOutsidePackage(String packageName) {\n            // Whether is an external App. Set the external App to be visible to the internal App.  \n            return false;\n        }\n\n        @Override\n        public boolean isAllowCreateShortcut() {\n            // Whether allow to create desktop shortcut icons. It is recommended to turn off (false) and implement desktop shortcuts by yourself.  \n            return false;\n        }\n\n        @Override\n        public boolean isHostIntent(Intent intent) {\n            // Whether the Intent is handled by VirtualApp.\n            return intent.getData() != null && \"market\".equals(intent.getData().getScheme());\n        }\n\n        @Override\n        public boolean isUseRealApkPath(String packageName) {\n            // The installation apk path simulates the real path and requires IO redirection to be enabled. Some hardening will check the path format.    \n            return false;\n        }\n\n        @Override\n        public boolean isEnableVirtualSdcardAndroidData() {\n            // Enable redirection of `Android/data` directory under external storage.    \n            // Require redirection support.  \n            // Must be enabled after Android 11！！  \n            return BuildCompat.isR();\n        }\n\n        @Override\n        public String getVirtualSdcardAndroidDataName() {\n            // Set the redirect path for  `Android/data` directory under external storage.  \n            // /sdcard/Android/data/com.example.test/ ==>> /sdcard/{VirtualSdcardAndroidDataName}/{user_id}/Android/data/com.example.test/\n            return \"Android_va\";\n        }\n\n        @Override\n        public FakeWifiStatus getFakeWifiStatus() {\n            // Modify the wifi information. null is not modified.  \n            return null;\n        }\n\n        @Override\n        public boolean isHideForegroundNotification() {\n            // Hide foreground messages, not recommended to hide.  \n            return false;\n        }\n\n        @Override\n        public boolean isOutsideAction(String action) {\n            // Action event response of external Intent.  \n            return MediaStore.ACTION_IMAGE_CAPTURE.equals(action)\n                || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)\n                || Intent.ACTION_PICK.equals(action);\n        }\n\n        @Override\n        public boolean isDisableDrawOverlays(String packageName) {\n            // Disable top-level overlay (floating window) for VAPP.  \n            return false;\n        }\n    };\n```\n\n### Override onCreate and add the code of initialize the VirtualApp：\n```java\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        VirtualCore virtualCore = VirtualCore.get();\n        virtualCore.initialize(new VirtualCore.VirtualInitializer() {\n            @Override\n            public void onMainProcess() {\n                // Main process callback\n            }\n\n            @Override\n            public void onVirtualProcess() {\n                // Virtual App process callback\n            }\n\n            @Override\n            public void onServerProcess() {\n                // Server-side process callback\n            }\n\n            @Override\n            public void onChildProcess() {\n                // Other sub-process callback\n            }\n        });\n    }\n\n```\n\nSince VirtualApp will start multiple processes, Application will enter N times, and different processes will go to different callbacks of VirtualInitializer, where additional initialization code can be added depending on the process type.  \n\n## 2. Install the APP ##\n## API:\n```java\nVirtualCore.java\n\n public VAppInstallerResult installPackage(Uri uri, VAppInstallerParams params);\n```\n## What is the parameter Uri?\nUri determines the source of **the apk that need to be installed**,and currently supports both package and file protocols.    \n### Package Uri Example:\n```java\nUri packageUri = Uri.parse(\"package:com.hello.world\");\n```\n### File Uri Example:\n```java\nFile apkFile = new File(\"/sdcard/test.apk\"); \nUri packageUri = Uri.fromFile(apkFile);\n```\n\n## What is the difference between the two types of Uri installation apps?\n**package agreement**  To install the app, you only need to pass in the package name, not the specific APK path, so the app installed with this protocol **is equivalent to double space**.  \n\nThe APP is automatically upgraded as external versions are upgraded and uninstalled as external versions are uninstalled. `dynamic` in `PackageSetting` is `true`。\n\n**file agreement** It is an internal installation, apk is copied inside the container, completely independent from the external version. `dynamic` in `PackageSetting` is `false`。\n\n## Installation Parameters VAppInstallerParams\n\n### Installation Flags installFlags\n\nFLAG | Instruction\n--- | ---\nFLAG_INSTALL_OVERRIDE_NO_CHECK | Allow overlay installation\nFLAG_INSTALL_OVERRIDE_FORBIDDEN | Prohibit overlay installation\nFLAG_INSTALL_OVERRIDE_DONT_KILL_APP | Overwrite installation, and not kill the already launched APP\n\n### Installation Mode mode\n\nFLAG | Instruction\n--- | ---\nMODE_FULL_INSTALL | Complete installation\nMODE_INHERIT_EXISTING | The installed installation mode of the installed. Reserve.  \n\nReserve parameters, not used for now. Currently the same no matter which one is set.  \n\n### cpuAbiOverride\n\nSpecify the abi of the App. You can force the App to run under the specified abi in special need. If you don't specify, the default abi is determined by `system rules`.  \nOptional parameters：\n* armeabi\n* armeabi-v7a\n* arm64-v8a\n\n### Double space App example code：\n```java\nVAppInstallerParams params = new VAppInstallerParams(VAppInstallerParams.FLAG_INSTALL_OVERRIDE_NO_CHECK);\nVAppInstallerResult result = VirtualCore.get().installPackage(Uri.parse(\"package:com.tencent.mobileqq\"), params);\nif (result.status == VAppInstallerResult.STATUS_SUCCESS) {\n    Log.e(\"test\", \"install apk success.\");\n}\n```\n\n### Install apk from sd card example code：\n```java\nVAppInstallerParams params = new VAppInstallerParams(VAppInstallerParams.FLAG_INSTALL_OVERRIDE_NO_CHECK);\nVAppInstallerResult result = VirtualCore.get().installPackage(Uri.fromFile(new File(\"/sdcard/test.apk\")), params);\nif (result.status == VAppInstallerResult.STATUS_SUCCESS) {\n    Log.e(\"test\", \"install apk success.\");\n}\n```\n\n### Install Split apk\nJust install the base package firstly, and then install all the split packages.  \n```java\nFile dir = new File(\"/sdcard/YouTube_XAPK_Unzip/\");\nVAppInstallerParams params = new VAppInstallerParams(VAppInstallerParams.FLAG_INSTALL_OVERRIDE_NO_CHECK);\nVAppInstallerResult result = VirtualCore.get().installPackage(\n        Uri.fromFile(new File(dir,\"com.google.android.youtube.apk\")), params);\nfor (File file : dir.listFiles()) {\n    String name = file.getName();\n    if (name.startsWith(\"config.\") && name.endsWith(\".apk\")) {\n        result = VirtualCore.get().installPackage(\n            Uri.fromFile(file), params);\n    }\n}\n\n```\n\n\n\n\n## 3. Launch and manage Application ##\n# Launch App\n\n```java\n// class VActivityManager\npublic boolean launchApp(final int userId, String packageName)\n````\nExample code：\n```java\nVActivityManager.get().launchApp(0, \"com.tencent.mobileqq\");\n```\n\n# Kill App\n```java\n// class VActivityManager\npublic void killAppByPkg(String pkg, int userId)\npublic void killAllApps()\n```\nExample code：\n```java\n// Kill the QQ program process with userid 0\nVActivityManager.get().killAppByPkg(\"com.tencent.mobileqq\", 0);\n\n```\n\n```java\n// Kill all App processes\nVActivityManager.get().killAllApps();\n```\n\n# Uninstall App\n```java\n// class VirtualCore\npublic boolean uninstallPackageAsUser(String pkgName, int userId)\npublic boolean uninstallPackage(String pkgName)\n```\nExample code：\n```java\n// Uninstall the QQ program with userid 0\nVirtualCore.get().uninstallPackageAsUser(\"com.tencent.mobileqq\", 0);\n// Uninstall the QQ programs installed under all user\nVirtualCore.get().uninstallPackage(\"com.tencent.mobileqq\");\n```\n\n# Check the installed Apps\n```java\n// class VirtualCore\npublic List<InstalledAppInfo> getInstalledApps(int flags)\n```\n\n## 4. Java Hook Usage ##\nVirtualApp implements a set of Xposed interface. Users who can use Xposed can do things that originally need the system built-in Xposed to do.  \nHowever, users also need to understand that the scope of Xposed in VA is within the VA app. Cannot overstep the authority to control system or other external apps.  \n\n\nVA provides a callback interface of App creation and launch `com.lody.virtual.client.core.AppCallback`, the interfaces are as follows:\n```java\npublic interface AppCallback {\n    void beforeStartApplication(String packageName, String processName, Context context);\n\n    void beforeApplicationCreate(String packageName, String processName, Application application);\n\n    void afterApplicationCreate(String packageName, String processName, Application application);\n}\n```\n\n> Interface Instruction:\n\nName | Instruction\n---- | ---\nbeforeStartApplication | Before APP launch, after creation\nbeforeApplicationCreate | Before the APP is created, application has already prepared, Application.OnCreate is not executed.\nafterApplicationCreate | After the APP is created, Application.OnCreate is executed.\n\n<br/>\n\n>Parameter Instruction:\n\nName | Instruction\n---- | ---\npackageName | Name of VAPP\nprocessName | Process name of VAPP\ncontext | Application context of VAPP\napplication | Application of VAPP\n\n<br/>\n\n> Note: APP creation means that`Application`is created.\n\nThe interface is there, and the next step is how to use it. View[`VirualApp Process Instruction`](VirualApp Process Instruction.md), we kan see  \nWe just need to put in the`VAPP process`callback(`onVirtualProcess`) set App callback `AppCallback` and then achieve the purpose.\n\n> Host Application code, please refer [io/busniess/va/App.java](https://github.com/asLody/VirtualApp-Priv/blob/v2.1/VirtualApp/app/src/main/java/io/busniess/va/App.java)\n\n```java\n@Override\n    public void onCreate() {\n        super.onCreate();\n        VirtualCore virtualCore = VirtualCore.get();\n        virtualCore.initialize(new VirtualCore.VirtualInitializer() {\n            @Override\n            public void onVirtualProcess() {\n                // Set VAPP launch callback\n                virtualCore.setAppCallback(new MyComponentDelegate());\n            }\n        });\n    }\n```\n\n> [MyComponentDelegate](https://github.com/asLody/VirtualApp-Priv/blob/v2.1/VirtualApp/app/src/main/java/io/busniess/va/delegate/MyComponentDelegate.java)Class Code\n\n```java\npublic class MyComponentDelegate implements AppCallback {\n\n    @Override\n    public void beforeStartApplication(String packageName, String processName, Context context) {\n    }\n\n    @Override\n    public void beforeApplicationCreate(String packageName, String processName, Application application) {\n\n        XposedHelpers.findAndHookMethod(\"android.app.ContextImpl\", ClassLoader.getSystemClassLoader(), \"getOpPackageName\", new XC_MethodHook() {\n            @Override\n            protected void beforeHookedMethod(MethodHookParam param) {\n                VLog.printStackTrace(\"getOpPackageName\");\n                param.setResult(VirtualCore.get().getHostPkg());\n            }\n        });\n    }\n\n    @Override\n    public void afterApplicationCreate(String packageName, String processName, Application application) {\n    }\n}\n```\n\nIn the above example, a use case for Xposed has been added. The entry point of Xposed is an example of `IXposedHookLoadPackage`, it provides an interface of `void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam)`. There is one parameter of `XC_LoadPackage.LoadPackageParam`. Here we can't use it exactly one by one, but it's enough. `loadPackageParam.classsload`can use  `context.getClassLoader()` or `application.getClassLoader()` both are ok. Afterwards `XposedHelpers`, the same way how to use`XposedBridge`, is also used here.  \n\n\n\n## 5. Native Hook Usage ##\nFor ARM 32 and ARM 64 Hooks, only the header files ```CydiaSubstrate.h``` need to be introduced, i.e. the Hook API:  \n```MSHookFunction(Type_ *symbol, Type_ *replace, Type_ **result)```  \n>Parameter Instruction:\n\nName | Instruction\n---- | ---\nsymbol | Address to Hook  \nreplace | Your custom Hook function\nresult | Backups of hooked function\n<br/>\nRefer ```syscall_hook.cpp``` code\n```cpp\nauto is_accessible_str = \"__dl__ZN19android_namespace_t13is_accessibleERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE\";\nvoid *is_accessible_addr = getSym(linker_path, is_accessible_str);\nif (is_accessible_addr) {\n    MSHookFunction(is_accessible_addr, (void *) new_is_accessible,(void **)     &orig_is_accessible);\n}\n```\n\nWithin `MSHookFunction`, it automatically determines whether the current is ARM32 or ARM64：\n\n\n```cpp\n_extern void MSHookFunction(void *symbol, void *replace, void **result) {\n    if (*result != nullptr) {\n        return;\n    }\n    // ALOGE(\"[MSHookFunction] symbol(%p) replace(%p) result(%p)\", symbol, replace, *result);\n#ifdef __aarch64__\n    A64HookFunction(symbol, replace, result);\n#else\n    SubstrateHookFunction(NULL, symbol, replace, result);\n#endif\n}\n```\n\n\n</br>\n</br>\n\n[Additional development guidance can be found on the VA Private Library Wiki](https://github.com/asLody/VirtualApp-Priv/wiki)\n\n</br>\n</br>\n"
  }
]