工作配置文件

Android 平台允许设备拥有 工作配置文件(有时称为托管配置文件)。工作配置文件由 IT 管理员控制,其可用功能与用户主配置文件的功能分开设置。这种方法使组织能够控制公司特定应用和数据在用户设备上运行的环境,同时仍允许用户使用其个人应用和配置文件。

本课程将向您展示如何修改您的应用程序,使其在具有工作配置文件的设备上可靠地运行。除了普通的应用开发最佳实践之外,您无需执行任何操作。但是,其中一些最佳实践在具有工作配置文件的设备上变得尤为重要。本文档重点介绍了您需要注意的问题。

概述

用户通常希望在企业环境中使用其个人设备。这种情况可能会给组织带来两难境地。如果用户可以使用自己的设备,则组织必须担心机密信息(例如员工电子邮件和联系人)是否存储在组织无法控制的设备上。

为了解决这种情况,Android 5.0(API 级别 21)允许组织设置工作配置文件。如果设备具有工作配置文件,则该配置文件的设置受 IT 管理员控制。IT 管理员可以选择允许该配置文件使用哪些应用,并可以控制该配置文件可用的设备功能。

如果设备具有工作配置文件,则无论应用在哪个配置文件下运行,都会对设备上运行的应用产生影响。

  • 默认情况下,大多数 Intent 不会跨配置文件传递。如果在配置文件上运行的应用触发了一个 Intent,而该配置文件上没有该 Intent 的处理程序,并且由于配置文件限制,该 Intent 不允许跨到另一个配置文件,则请求失败,应用可能会意外关闭。
  • 配置文件 IT 管理员可以限制工作配置文件上可用的系统应用。此限制也可能导致工作配置文件上某些常见 Intent 没有处理程序。

  • 由于个人资料和工作资料拥有独立的存储区域,在一个资料中有效的文件 URI 在另一个资料中可能无效。在一个资料中触发的任何意图都可能在另一个资料中处理(取决于资料设置),因此将文件 URI 附加到意图是不安全的。

防止意图失败

在具有工作资料的设备上,存在关于意图是否可以从一个资料跨到另一个资料的限制。在大多数情况下,当触发意图时,它会在触发意图的同一资料中处理。如果该资料**上**没有处理该意图的处理程序,则不会处理该意图,并且触发该意图的应用可能会意外关闭,即使另一个资料上存在处理该意图的处理程序。

资料管理员可以选择允许哪些意图从一个资料跨到另一个资料。由于 IT 管理员做出此决定,因此您无法预先知道**哪些**意图允许跨越此边界。IT 管理员设置此策略,并且可以随时更改。

在应用启动活动之前,应验证是否存在合适的解析结果。可以通过调用Intent.resolveActivity()来验证是否存在可接受的解析结果。如果没有办法解析意图,则该方法返回null。如果该方法返回非空值,则至少有一种方法可以解析意图,并且可以安全地触发意图。在这种情况下,意图可能是可解析的,因为当前资料上存在处理程序,或者因为允许意图跨到另一个资料上的处理程序。(有关解析意图的更多信息,请参阅常见意图。)

例如,如果应用需要设置计时器,则需要检查是否存在ACTION_SET_TIMER意图的有效处理程序。如果应用无法解析意图,则应采取适当的操作(例如显示错误消息)。

Kotlin

fun startTimer(message: String, seconds: Int) {

    // Build the "set timer" intent
    val timerIntent = Intent(AlarmClock.ACTION_SET_TIMER).apply {
        putExtra(AlarmClock.EXTRA_MESSAGE, message)
        putExtra(AlarmClock.EXTRA_LENGTH, seconds)
        putExtra(AlarmClock.EXTRA_SKIP_UI, true)
    }

    // Check if there's a handler for the intent
    if (timerIntent.resolveActivity(packageManager) == null) {

        // Can't resolve the intent! Fail this operation cleanly
        // (perhaps by showing an error message)

    } else {
        // Intent resolves, it's safe to fire it off
        startActivity(timerIntent)

    }
}

Java

public void startTimer(String message, int seconds) {

    // Build the "set timer" intent
    Intent timerIntent = new Intent(AlarmClock.ACTION_SET_TIMER)
            .putExtra(AlarmClock.EXTRA_MESSAGE, message)
            .putExtra(AlarmClock.EXTRA_LENGTH, seconds)
            .putExtra(AlarmClock.EXTRA_SKIP_UI, true);

    // Check if there's a handler for the intent
    if (timerIntent.resolveActivity(getPackageManager()) == null) {

        // Can't resolve the intent! Fail this operation cleanly
        // (perhaps by showing an error message)

    } else {
        // Intent resolves, it's safe to fire it off
        startActivity(timerIntent);

    }
}

跨资料共享文件

有时,应用需要向其他应用提供对其自身文件的访问权限。例如,图片库应用可能希望将其图片与图片编辑器共享。通常有两种方法可以共享文件:使用文件 URI内容 URI

文件 URI 以file:前缀开头,后跟设备存储上文件的绝对路径。但是,由于工作资料和个人资料使用单独的存储区域,因此在一个资料中有效的文件 URI 在另一个资料中可能无效。这种情况意味着,如果您将文件 URI 附加到意图,并且该意图在另一个资料中处理,则处理程序将无法访问该文件。

相反,应使用内容 URI共享文件。内容 URI 以更安全、更可共享的方式标识文件。内容 URI 包含文件路径,还包含提供文件的权限以及标识文件的 ID 号。您可以使用FileProvider为任何文件生成内容 ID。然后,您可以与其他应用(即使在另一个资料中)共享该内容 ID。接收方可以使用内容 ID 访问实际文件。

例如,以下是如何获取特定文件 URI 的内容 URI

Kotlin

// Open File object from its file URI
val fileToShare = File(fileUriToShare)

val contentUriToShare: Uri = FileProvider.getUriForFile(
        context,
        "com.example.myapp.fileprovider",
        fileToShare
)

Java

// Open File object from its file URI
File fileToShare = new File(fileUriToShare);

Uri contentUriToShare = FileProvider.getUriForFile(getContext(),
        "com.example.myapp.fileprovider", fileToShare);

调用getUriForFile()方法时,必须包含文件提供程序的权限(在此示例中为"com.example.myapp.fileprovider"),该权限在应用清单的<provider>元素中指定。有关使用内容 URI 共享文件的更多信息,请参阅共享文件

侦听通知

应用通常提供NotificationListenerService子类以接收来自系统关于通知更改的回调。具有工作资料的设备可能会影响NotificationListenerService如何与应用配合使用。

在工作资料中

无法从在工作资料中运行的应用使用NotificationListenerService。当应用在工作资料中运行时,系统会忽略应用的NotificationListenerService。但是,在个人资料中运行的应用可以侦听通知。

在个人资料中

当应用在个人资料中运行时,您可能无法获取在工作资料中运行的应用的通知。默认情况下,所有个人资料应用都会接收回调,但 IT 管理员可以将一个或多个个人资料应用列入白名单,以允许其侦听通知更改。然后,系统会阻止未列入白名单的应用。在 Android 8.0(API 级别 26)或更高版本中,管理工作资料的设备策略控制器 (DPC) 可能会阻止应用使用DevicePolicyManager方法setPermittedCrossProfileNotificationListeners()侦听工作资料的通知。应用仍然会接收有关在个人资料中发布的通知的回调。

测试应用与工作资料的兼容性

应在工作资料环境中测试应用,以捕获会导致应用在具有工作资料的设备上失败的问题。特别是,在工作资料设备上进行测试是确保应用正确处理意图的好方法:不触发无法处理的意图,不附加跨资料无效的 URI,等等。

我们提供了一个示例应用TestDPC,您可以使用它在运行 Android 5.0(API 级别 21)及更高版本的 Android 设备上设置工作资料。此应用为您提供了一种在工作资料环境中测试应用的简单方法。您还可以使用此应用配置工作资料,如下所示

  • 指定托管资料上可用的默认应用
  • 配置允许哪些意图从一个资料跨到另一个资料

如果通过 USB 线缆手动将应用安装到具有工作资料的设备上,则该应用将安装在个人资料和工作资料上。安装应用后,可以在以下条件下测试应用

  • 如果意图通常由默认应用(例如相机应用)处理,请尝试在工作资料上禁用该默认应用,并验证应用是否正确处理了此情况。
  • 如果触发意图并期望它由其他应用处理,请尝试启用和禁用该意图跨越从一个资料到另一个资料的权限。验证应用在两种情况下是否都表现正确。如果不允许意图跨资料,请验证应用在应用资料上存在合适的处理程序时以及不存在时的情况下的行为。例如,如果应用触发与地图相关的意图,请尝试以下每种情况
    • 设备允许地图意图从一个资料跨到另一个资料,并且另一个资料(应用未运行的资料)上存在合适的处理程序
    • 设备不允许地图意图跨资料,但在应用资料上存在合适的处理程序
    • 设备不允许地图意图跨资料,并且设备资料上不存在合适的地图意图处理程序
  • 如果将内容附加到意图,请验证意图在应用资料上处理时以及跨资料时是否都表现正确。

在工作资料上测试:提示和技巧

在工作资料设备上测试时,您可能会发现一些技巧很有帮助。

  • 如前所述,当您将应用侧载到工作资料设备上时,它会安装在两个资料上。如果需要,您可以从一个资料中删除应用,并将其保留在另一个资料上。
  • Android 调试桥 (adb) shell 中可用的大多数活动管理器命令都支持--user标志,该标志允许您指定要以哪个用户身份运行。通过指定用户,您可以选择是作为未管理的主用户还是工作资料运行。有关更多信息,请参阅ADB Shell 命令
  • 要查找设备上的活动用户,请使用 adb 包管理器的list users命令。输出字符串中的第一个数字是用户 ID,您可以将其与--user标志一起使用。有关更多信息,请参阅ADB Shell 命令

例如,要查找设备上的用户,您将运行以下命令

$ adb shell pm list users
UserInfo{0:Drew:13} running
UserInfo{10:Work profile:30} running

在这种情况下,主用户 (“Drew”) 的用户 ID 为 0,工作资料的用户 ID 为 10。要在工作资料中运行应用,您将使用以下命令

$ adb shell am start --user 10 \
-n "com.example.myapp/com.example.myapp.testactivity" \
-a android.intent.action.MAIN -c android.intent.category.LAUNCHER