Android 应用程序的最佳实践

本文提供的最佳实践可提高 Citrix Endpoint Management 和适用于 Android 设备的移动应用程序之间的兼容性。

MDX 应用程序 SDK 和封装

如果您的应用程序使用 MDX 应用程序 SDK,则必须使用匹配的 MDX Toolkit 版本进行封装。如果这两个组件的版本不匹配,可能会导致运行不正常。

为了防止发生此不匹配情况,请使用“高级”或“一般”应用程序类型封装应用程序。让您可以提供预先封装应用程序。因此,您的客户不需要封装应用程序,从而避免使用不匹配的 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 或引发异常。
  • 定向到非托管应用程序的意向失败。
  • 如果从主线程中使用,则文件和数据库访问可能会失败。有关详细信息,请参阅本文后面的“确保数据加密兼容性”和“加密用户熵”部分。

当遇到故障时,应用程序应正常处理问题,而不是崩溃。

挂接限制

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 写数据,则应让其首先写这些数据,然后您在此 API 完成操作后加密数据。需要执行下列几个步骤:

    1. 指定将保持未加密状态的区域。必须为您的客户记录此内容,因为 Citrix Endpoint Management 管理员必须创建加密排除项策略。
    2. 要进行解密,只需将文件从普通(已加密)位置复制到已解密的位置即可。请注意,必须执行字节复制操作,而不是文件移动操作。
    3. 要进行加密,请以相反方向执行以上过程。从未加密的位置复制到已加密的位置。
    4. 当不再需要未加密的文件时,删除此文件。
  • 不支持对已加密的文件执行内存映射。如果调用一个将执行内存映射的 API,此 API 会失败。应该对错误进行处理。如果可能,应避免直接和间接使用内存映射。第三方 SqlCipher 库是一个关于间接使用内存映射的典型示例。

    如果无法避免内存映射,管理员必须指定加密排除项策略以忽略相关文件。必须为您的客户记录此策略。

  • 加密功能会显著增加开销。请务必优化文件 I/O,以防止性能下降。例如,如果重复读取和写入相同的信息,则您可能希望实施应用程序级别的缓存。

  • 数据库仅仅是一些文件,所以它们也将进行加密。此处也可能会发生性能问题。标准数据库缓存大小为 2000 页(或 8 MB)。如果数据库很大,您可能会增大此大小。

    由于内存映射的限制,SQLite WAL 模式不受支持。

加密用户熵

一个用于加密功能的 Citrix Endpoint Management 选项,要求最终用户输入 PIN,然后才能生成加密密钥。此选项称为用户熵。它可导致应用程序出现特殊问题。

具体而言,在用户输入 PIN 之前,将无法执行任何文件或数据库访问。如果在可显示 PIN UI 之前运行的某个位置中存在此 I/O 操作,则此操作始终会失败。存在一些限制条件:

  • 不要在主线程中执行文件和数据库操作。例如,如果尝试通过应用程序对象的 onCreate() 方法读取文件,则此尝试总是会失败。
  • 即使不存在应用程序活动,后台操作(如服务或内容提供程序)也可能会运行。这些后台组件无法显示 PIN 用户界面,并且因此无法执行文件或数据库访问。请注意,一旦有活动在应用程序中运行,将会允许在后台执行 I/O 操作。

针对因用户熵功能而使加密密钥不可用的情况,提供了几种相应的失败处理机制:

  • 如果主线程在尚无可用 PIN 的情况下访问数据库,则应用程序将被终止。
  • 如果非主线程在尚无可用 PIN 的情况下访问数据库,则该线程将被阻止,直到输入 PIN。
  • 对于在尚无可用 PIN 的情况下启动非数据库访问,打开操作将失败。将在 C 级别返回一个 EACCES 错误。在 Java 中,将引发异常。

为确保您的应用程序中不存在此问题,请在已启用用户熵功能的情况下进行测试。可使用 Citrix Endpoint Management 客户端属性“Encrypt secrets using Passcode”(使用通行码加密机密)添加用户熵。可以在 Citrix Endpoint Management 控制台的配置 > 设置 > 更多 > 客户端属性下配置默已禁用的客户端属性。

网络连接和 Micro VPN

管理员可以使用多个 Citrix Endpoint Management 策略选项执行网络操作。“网络访问”策略可阻止、允许或重定向应用程序的网络活动。

重要:

MDX Toolkit 版本 18.12.0 包含了合并或替换较旧的策略的新策略。 “网络访问”策略结合了“网络访问”、“首选 VPN 模式”和“允许 VPN 模式切换”。“排除列表”策略替换拆分通道排除列表。“要求 Micro VPN 会话”策略替换“要求联机会话”。有关详细信息,请参阅早期版本中的新增功能

这些选项如下所示:

  • 使用以前的设置:默认值为您已在更早版本的策略中设置的值。如果更改了此选项,则您不应还原为使用以前的设置。另请注意,在用户将应用程序升级到版本 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 错误。
  • 如果未进行修改但已重新签名的程序不运行:
    1. 使用 JAR 解压缩 APK 的内容:

      jar xvf {some.apk}

    2. 删除 META-INF 文件夹:

      rm -rf META-INF

    3. 使用 JAR 将内容重新压缩到新 APK:

      jar cvf {/tmp/new.apk} *

    4. 使用 JARSIGNER 签名新 APK:

      jarsigner -keystore {some.keystore} -storepass {keystorepassword} -keypass {keypassword} {/tmp/new.apk} {keyalias}

    5. 如果应用程序仍不运行,则无法使用不同于原始 APK 的签名证书封装应用程序。

  • 如果经过反编译或重新编译的 apk 不运行:
    1. 使用 APKTOOL 进行反编译和重新编译:

      apktool d {some.apk} -o {some.directory}

      apktool b {some.directory} -o {new.apk}

    2. 使用 JARSIGNER 签名 APK,如上所述。

    3. 如果应用程序仍不运行,则意味着这是一个第三方 APKTOOL 缺陷。

  • 如果应用程序封装程序不工作:
    1. 尝试删除 APKTOOL 框架并重新封装。
      • Mac/Linux:rm -rf ~/Library/apktool/framework
      • Windows:del /q /s C:\Users\{username}\apktool\framework
    2. 将封装程序正使用的 APKTOOL 与您在上一步骤中成功用于反编译和重新编译操作的 APKTOOL 进行比较。
      • 如果是同一个 APKTOOL 版本,则表明封装程序中存在错误。
      • 如果是不同的 APKTOOL 版本,则可能在集成到 MDX Toolkit 实用程序的 APKTOOL 中存在错误。
        1. 解压缩 ManagedAppUtility.jar 的内容。
        2. 用您已在上一步骤中成功用于封装应用程序的 APKTOOL.jar 的内容进行覆盖。
        3. 将内容重新压缩到新 ManagedAppUtility.jar。
        4. 封装应用程序,以确认嵌入式 APKTOOL 中的缺陷。
  • 运行封装的应用程序,捕获日志信息。
    1. 使用 grep 调查在应用程序中发生的活动。

      执行应用程序的活动:grep “MDX-Activity”

      跟踪应用程序的 MDX 锁定:grep “MDX-Locked”

      同时查看两个日志:egrep “MDX-Act MDX-Loc”
    2. 如果存在“应用程序不响应”错误,则使用 ADB 拉取 ANR 跟踪信息。

  • 如果在与多个应用程序交互(例如使用打开方式)时出现问题:
    1. 验证各应用程序的加密策略和安全组是否相同。
    2. 尝试其他应用程序。在测试的应用程序中可能存在缺陷。
    3. 从所有相关应用程序捕获日志。请注意,Secure Hub 可以捆绑并通过电子发送来自各个应用程序的日志。从“我的应用程序”屏幕中,向右滑动到“支持”屏幕。然后,单击屏幕底部的“需要帮助”按钮。

除了上面提到的工具,以下工具也可能有帮助:

  • AAPT,用于转储有关应用程序的信息。

    aapt dump badging {some.apk}

  • 在设备上执行 DUMPSYS 命令。

    adb shell dumpsys 2>&1 | tee {dumpsys.out}

  • DEX2JAR,用于将类重新编译成伪 Java。

    dex2jar {some.apk}

    转换来自 Dual-Dex 封装的应用程序的类:

    apktool d {some.apk} -o {some.dir}

    dex2jar {some.dir}/assets/secondary-1.dex

  • JD-GUI,用于查看伪 Java 代码。

  • BAKSMALI,用于反编译来自 Dual-Dex 封装的应用程序中的应用程序类。

    • 反编译已封装的 APK:

      apktool d {some.apk} -o {some.dir}

    • 反编译未在以上调用中反编译的应用程序的类:

      baksmali {some.dir}/assets/secondary-1.dex -o {some.dir}/smali

Android 应用程序的最佳实践