Android 应用程序的最佳实践
本文讨论的最佳实践可提高 Citrix Endpoint Management™ 与 Android 设备移动应用程序之间的兼容性。
MDX 应用程序 SDK 和包装
如果您的应用程序使用 MDX App SDK,则必须使用匹配的 MDX Toolkit 版本进行包装。这两个组件之间的版本不匹配可能会导致操作不当。
- 为防止此类不匹配,请使用 Premium 或 General 应用程序类型包装应用程序。这使您能够交付预包装的应用程序。因此,您的客户无需包装应用程序,从而避免使用不匹配的 MDX Toolkit。有关包装应用程序的详细信息,请参阅包装 Android 移动应用程序。
不要阻塞主线程
在主线程上运行时,不应使用阻塞代码。这是一项 Google 准则,但在 Citrix Endpoint Management 中更为关键。某些操作在托管应用程序中可能需要更多时间,甚至可能阻塞进一步的线程执行。
阻塞代码包括但不限于以下内容:
- 文件或数据库操作
- 网络操作
需要明确的是,所有应用程序生命周期方法(例如 onCreate)都在主线程上运行。
Google 提供了一个 StrictMode API,可以帮助检测阻塞代码。有关详细信息,请参阅此博客文章:https://android-developers.blogspot.com/2010/12/new-gingerbread-api-strictmode.html。
-
编写健壮的代码
特别是,您应该检查框架 API 的返回值或捕获异常。虽然这只是一种常见的编程最佳实践,但对于托管应用程序来说尤其重要。
如果 Citrix Endpoint Management 策略阻止了底层功能,您期望始终正常工作的各种 API 将会失败。示例包括前面描述的任何功能:
- 网络 API 失败,就像没有可用网络一样。
- 传感器 API(例如 GPS 和相机)返回 null 或抛出异常。
- 指向非托管应用程序的 Intent 失败。
- 如果从主线程使用文件和数据库访问,则可能会失败。有关详细信息,请参阅本文后面的“确保数据加密兼容性”和“加密用户熵”部分。
当您遇到故障时,您的应用程序应优雅地处理问题,而不是崩溃。
挂钩限制
MDX 通过修改 APK 中的 DEX 代码将功能注入到二进制 Android 应用程序中。存在以下几个限制:
- Citrix Endpoint Management 可能无法管理来自 4.0 之前 Android SDK 版本的已弃用框架类。请务必避免使用这些已弃用类。
- 大多数功能都注入到 Java/Android 框架 API 中。本机 (C/C++) 代码通常不受管理。一个例外是,即使对于本机代码,文件加密仍然会发生。
- 使用 JNI 访问 Java 功能的本机代码必须仅针对用户应用程序中的代码。换句话说,不要使用 JNI 直接调用 Java 或 Android 框架方法。相反,请使用代理设计模式将所需的框架类“包装”到您自己的 Java 类中。然后从本机代码调用您的类。
确保数据加密兼容性
- MDX 的主要功能之一是所有持久化数据都透明加密。您无需修改应用程序即可获得此功能,事实上,您无法直接避免它。管理员可以选择性或完全禁用加密,但应用程序不能。
- 这是 MDX 中更重要的方面之一,需要理解以下几点:
-
对于在托管进程中运行的所有 Java 和本机代码,都存在文件加密。
-
某些框架 API(例如媒体播放器和打印支持)实际上在单独的操作系统进程中运行。如果您使用此类 API,则可能会遇到问题。
- 示例:您的应用程序将文件保存到磁盘(已加密),然后将文件引用传递给媒体 API。媒体 API 尝试读取文件,但它不理解加密内容。它失败甚至导致应用程序崩溃。
- 示例:您创建一个文件句柄(启动一个加密文件)并将其提供给相机 API。相机进程直接将未加密数据写入加密文件。当您的应用程序尝试读取该数据时,数据被解密,产生垃圾。
-
处理单独进程的一种方法是在将文件交给相关 API 之前对其进行解密。或者,如果 API 写入数据,则您将让它先写入,然后在 API 完成时对其进行加密。需要几个步骤:
-
- 指定一个将保持未加密的区域。您必须为客户记录此信息,因为 Citrix Endpoint Management 管理员必须创建加密排除策略。
-
- 要解密,您只需将文件从正常(已加密)位置复制到已解密位置。请注意,您必须执行字节复制而不是文件移动操作。
-
- 要加密,请反转方向。从未加密位置复制到加密位置。
-
- 不再需要时删除未加密文件。
-
加密文件不支持内存映射。如果您调用执行内存映射的 API,它将失败。您应该处理该错误。如果可能,请避免直接和间接使用内存映射。一个值得注意的间接使用案例是第三方 SqlCipher 库。
-
如果您无法避免内存映射,则管理员必须指定一个加密排除策略,以省略相关文件。您必须为客户记录此策略。
-
加密会增加可衡量的开销。请务必优化文件 I/O 以防止性能下降。例如,如果您重复读取和写入相同的信息,您可能希望实现应用程序级缓存。
-
数据库只是文件,因此它们也已加密。性能也可能是一个问题。标准数据库缓存大小为 2000 页或 8 兆字节。如果您的数据库很大,您可能需要增加此大小。
- 由于内存映射限制,不支持 SQLite WAL 模式。
-
加密用户熵
Citrix Endpoint Management 的一个加密选项要求最终用户在生成加密密钥之前输入 PIN。此选项称为用户熵。它可能会导致应用程序出现特定问题。
具体来说,在用户输入 PIN 之前,无法执行文件或数据库访问。如果此类 I/O 操作存在于 PIN UI 显示之前运行的位置,则它将始终失败。有几个含义:
- 将文件和数据库操作保持在主线程之外。例如,尝试从应用程序对象的 onCreate() 方法读取文件将始终失败。
- 后台操作(例如服务或内容提供程序)即使没有应用程序活动也可能运行。这些后台组件无法显示 PIN UI,因此它们无法执行文件或数据库访问。请注意,一旦应用程序中运行了活动,后台操作就被允许执行 I/O 操作。
如果由于用户熵而无法获得加密密钥,则存在几种故障机制:
- 如果主线程在 PIN 可用之前访问数据库,则应用程序将被终止。
- 如果非主线程在 PIN 可用之前访问数据库,则该线程将被阻塞,直到输入 PIN。
- 对于在 PIN 可用之前启动的非数据库访问,打开操作将失败。在 C 级别,返回 EACCES 错误。在 Java 中,抛出异常。
为确保您的应用程序中不存在此问题,请在启用用户熵的情况下进行测试。Citrix Endpoint Management 客户端属性“使用密码加密机密”会添加用户熵。您可以在 Citrix Endpoint Management 控制台的 “配置”>“设置”>“更多”>“客户端属性” 下配置该客户端属性(默认情况下禁用)。
网络和微型 VPN
- Citrix Endpoint Management 为管理员提供了多个网络连接策略选项。网络访问策略可阻止、允许或重定向应用程序网络活动。
- >重要提示:
- > > MDX Toolkit 18.12.0 版本包含合并或取代旧策略的新策略。 网络访问策略合并了网络访问、首选 VPN 模式和允许 VPN 模式切换。排除列表策略取代了分割隧道排除列表。微型 VPN 会话所需策略取代了需要联机会话。有关详细信息,请参阅[早期版本中的新增功能](/zh-cn/mdx-toolkit/about-mdx-toolkit/whats-new.html#whats-new-in-earlier-releases)。
选项如下:
- **使用以前的设置**:默认使用您在早期策略中设置的值。如果更改此选项,则不应恢复为**使用以前的设置**。另请注意,新策略的更改在用户将应用升级到 18.12.0 或更高版本之前不会生效。
- **已阻止**:您的应用使用的网络 API 将失败。根据以前的指导原则,您应妥善处理此类故障。
- **无限制**:所有网络调用都直接进行,不通过隧道传输。
-
隧道式 - 完全 VPN:来自托管应用的所有流量都通过 Citrix Gateway 隧道传输。
-
限制: Citrix Endpoint Management 不支持套接字服务器。如果套接字服务器在封装的应用内部运行,则流向该套接字服务器的网络流量不会通过 Citrix Gateway 隧道传输。
移动应用开发框架支持
某些应用框架与 Citrix Endpoint Management 存在兼容性问题:
- 对于 PhoneGap,位置服务未被阻止。
-
SQLCipher 不适用于加密,因为它使用内存映射。一种解决方案是不使用 SQLCipher。第二种解决方案是使用加密排除策略将数据库文件从加密中排除。Citrix Endpoint Management 管理员必须在 Citrix Endpoint Management 控制台中配置该策略。
-
调试技巧
调试封装的应用时,请考虑以下技巧。
- 确定问题是否存在于应用的未封装版本中。如果问题在未封装时出现,请使用常规调试技术。
- 尝试关闭各种 Citrix Endpoint Management 策略。
- 这有助于定位任何不兼容性。禁用策略意味着 MDX 不再强制执行相关限制,从而使您能够像测试未封装的应用一样测试这些功能。
- 如果禁用策略解决了问题,则问题可能在于应用未检查相关 API 中的错误。
- 如果未修改但重新签名的应用无法运行:
-
- 使用 JAR 解压 APK 的内容:
[[CODE_BLOCK_0]]
-
删除 META-INF 文件夹:
[[CODE_BLOCK_1]]
-
使用 JAR 将内容重新打包为新的 APK:
[[CODE_BLOCK_2]]
-
使用 JARSIGNER 签署新的 APK:
[[CODE_BLOCK_3]]
-
如果应用仍然无法运行,则不能使用与原始 APK 不同的签名证书来封装应用。
-
- 如果反编译或重新编译的 .apk 无法运行:
-
使用 APKTOOL 反编译和重新编译:
[[CODE_BLOCK_4]]
[[CODE_BLOCK_5]]
-
如上所述,使用 JARSIGNER 签署 APK。
-
如果应用仍然无法运行,则这是第三方 APKTOOL 错误。
-
- 如果应用封装不起作用:
- 尝试删除 APKTOOL 框架并重新封装。
- Mac/Linux:rm -rf ~/Library/apktool/framework
- Windows:del /q /s C:\Users\{username}\apktool\framework
- 比较封装程序使用的 APKTOOL 与您在上一步中成功反编译和重新编译所使用的 APKTOOL。
- 如果它是相同的 APKTOOL 版本,则 Wrapper 中存在错误。
- 如果它是不同的 APKTOOL 版本,则集成到 MDX Toolkit 实用程序中的 APKTOOL 可能存在错误。
- 解压 ManagedAppUtility.jar 的内容。
- 使用您在上一步中成功封装应用所使用的 APKTOOL.jar 的内容进行覆盖。
- 将内容重新打包为新的 ManagedAppUtility.jar。
- 封装应用以确认嵌入式 APKTOOL 中的错误。
- 尝试删除 APKTOOL 框架并重新封装。
- 运行封装的应用并捕获日志信息。
-
使用 grep 调查应用中发生的情况。
要跟踪应用活动:grep “MDX-Activity”
要跟踪 MDX 锁定应用:grep “MDX-Locked”
要同时查看这两个日志:egrep “MDX-Act MDX-Loc” -
如果出现“应用无响应”错误,请使用 ADB 提取 ANR 跟踪。
-
- 如果在使用“打开方式”等与多个应用交互时出现问题:
- 验证应用之间的加密策略和安全组设置是否相同。
- 尝试其他应用。这可能是正在测试的应用之一中的错误。
- 捕获所有相关应用的日志。请注意,Secure Hub 可以捆绑来自单个应用的日志并发送电子邮件日志。从“我的应用”屏幕,向右滑动到“支持”屏幕。然后单击屏幕底部的“需要帮助”按钮。
除了上述工具之外,以下工具也可能有所帮助:
-
AAPT 用于转储应用信息。
[[CODE_BLOCK_6]]
-
设备上的 DUMPSYS 命令。
[[CODE_BLOCK_7]]
-
DEX2JAR 用于将类重新编译为伪 Java。
[[CODE_BLOCK_8]]
转换 Dual-Dex 封装应用中的类:
[[CODE_BLOCK_9]]
[[CODE_BLOCK_10]]
-
JD-GUI 用于查看伪 Java 代码。
-
BAKSMALI 用于反编译 Dual-Dex 封装应用中的应用类。
-
反编译封装的 APK:
[[CODE_BLOCK_11]]
-
反编译未通过上述调用反编译的应用类:
[[CODE_BLOCK_12]]
-