本实战宝典可帮助开发者和系统集成商增强其 解决方案请按照我们的方法指南查找适用于专用设备的解决方案 行为本实战宝典最适合已拥有专门 设备应用 - 如果您刚刚开始使用,请阅读专用设备 概览。
自定义主屏幕应用
如果您正在开发一款应用来取代 Android Home,这些食谱会非常有用 屏幕和启动器。
成为主屏幕应用
您可以将应用设为设备的主屏幕应用,以便其启动 自动同步。您还可以启用首页 按钮,可将已列入许可名单的应用锁定在前台 任务模式。
所有主屏幕应用都会处理 CATEGORY_HOME
intent 类别,这样做
是系统识别主屏幕应用的方式。若要成为默认主屏幕应用,请设置一个
作为首选的“主屏幕 intent”处理程序
DevicePolicyManager.addPersistentPreferredActivity()
如以下示例中所示:
Kotlin
// Create an intent filter to specify the Home category. val filter = IntentFilter(Intent.ACTION_MAIN) filter.addCategory(Intent.CATEGORY_HOME) filter.addCategory(Intent.CATEGORY_DEFAULT) // Set the activity as the preferred option for the device. val activity = ComponentName(context, KioskModeActivity::class.java) val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager dpm.addPersistentPreferredActivity(adminName, filter, activity)
Java
// Create an intent filter to specify the Home category. IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN); filter.addCategory(Intent.CATEGORY_HOME); filter.addCategory(Intent.CATEGORY_DEFAULT); // Set the activity as the preferred option for the device. ComponentName activity = new ComponentName(context, KioskModeActivity.class); DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); dpm.addPersistentPreferredActivity(adminName, filter, activity);
您仍然需要声明 intent 过滤器 应用清单文件中,如以下 XML 代码段所示:
<activity
android:name=".KioskModeActivity"
android:label="@string/kiosk_mode"
android:launchMode="singleInstance"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
通常情况下,您不希望启动器应用显示在“概览”屏幕中。
不过,您无需将 excludeFromRecents
添加到
activity 声明,因为 Android 启动器会隐藏最初启动的
(当系统在锁定任务模式下运行时)。
显示单独的任务
对于以下情况,可以将 FLAG_ACTIVITY_NEW_TASK
作为一个实用标志
因为每个新任务在
概览屏幕。如需详细了解“概览”屏幕中的任务,请阅读最近使用的应用
屏幕。
公用自助服务终端
这些食谱非常适合公共场所中的无人值守设备 可帮助许多专用设备用户专注于自己的任务。
锁定设备
为了确保设备用于其预期用途,您可以添加 表 1 中列出的用户限制。
用户限制 | 说明 |
---|---|
DISALLOW_FACTORY_RESET |
阻止设备用户将设备重置为出厂默认设置。 完全受管设备的管理员和主要用户可以设置此项 限制。 |
DISALLOW_SAFE_BOOT |
阻止设备用户启动设备 安全模式 系统将不会自动启动您的应用拥有完全访问权限的管理员 且主要用户可以设置此限制。 |
DISALLOW_MOUNT_PHYSICAL_MEDIA |
阻止设备用户装载他们可能使用的任何存储卷 连接到设备。完全受管设备的管理员和主要用户 可以设置此限制 |
DISALLOW_ADJUST_VOLUME |
将设备静音,并阻止设备用户更改声音 音量和振动设置检查您的自助服务终端不需要音频功能 。全托管式管理员 主要用户、次要用户和工作资料均可设置此项 限制。 |
DISALLOW_ADD_USER |
阻止设备用户添加新用户(如次要用户或 权限受限制的用户系统会自动将此用户限制添加到 但可能已被清除拥有完全访问权限的管理员 且主要用户可以设置此限制。 |
以下代码段展示了如何设置限制:
Kotlin
// If the system is running in lock task mode, set the user restrictions // for a kiosk after launching the activity. arrayOf( UserManager.DISALLOW_FACTORY_RESET, UserManager.DISALLOW_SAFE_BOOT, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, UserManager.DISALLOW_ADJUST_VOLUME, UserManager.DISALLOW_ADD_USER).forEach { dpm.addUserRestriction(adminName, it) }
Java
// If the system is running in lock task mode, set the user restrictions // for a kiosk after launching the activity. String[] restrictions = { UserManager.DISALLOW_FACTORY_RESET, UserManager.DISALLOW_SAFE_BOOT, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, UserManager.DISALLOW_ADJUST_VOLUME, UserManager.DISALLOW_ADD_USER}; for (String restriction: restrictions) dpm.addUserRestriction(adminName, restriction);
当您的应用处于管理员模式时,您不妨取消这些限制,
确保 IT 管理员仍可使用这些功能进行设备维护清除
限制、调用
DevicePolicyManager.clearUserRestriction()
。
不显示错误对话框
在某些环境中,例如零售演示或公共信息
您可能不想向用户显示错误对话框。在 Android 9.0 (API
级别 28)或更高版本,您可以禁止在 Chrome 操作系统中针对
添加
DISALLOW_SYSTEM_ERROR_DIALOGS
位用户
限制。系统会重启无响应的应用,就像设备用户关闭了应用一样
从对话框中打开应用。以下示例展示如何执行此操作:
Kotlin
override fun onEnabled(context: Context, intent: Intent) { val dpm = getManager(context) val adminName = getWho(context) dpm.addUserRestriction(adminName, UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS) }
Java
public void onEnabled(Context context, Intent intent) { DevicePolicyManager dpm = getManager(context); ComponentName adminName = getWho(context); dpm.addUserRestriction(adminName, UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS); }
如果主要或次要用户的管理员设置了此限制,系统会 隐藏该用户的错误对话框。如果一个全托管式 设备设置此限制,则系统会禁止所有用户显示对话框。
使屏幕保持开启状态
如果您要构建自助服务终端,可以停止访问
休眠。将
将 FLAG_KEEP_SCREEN_ON
布局标志添加到应用的
窗口,如以下示例所示:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Keep the screen on and bright while this kiosk activity is running. window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Keep the screen on and bright while this kiosk activity is running. getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); }
您可能需要检查设备是否已插入交流电源、USB 或无线电源
充电器。注册接收电池变化广播并使用 BatteryManager
以了解充电状态。您甚至可以向 IT 人员发送远程提醒
管理员。如需分步说明,请参阅
监控电池电量和充电
状态。
您还可以将 STAY_ON_WHILE_PLUGGED_IN
全局设置,以使设备在连接到电源时保持唤醒状态。
Android 6.0(API 级别 23)或更高版本中的完全托管设备的管理员可以
调用 DevicePolicyManager.setGlobalSetting()
,如下所示
如下例中所示:
Kotlin
val pluggedInto = BatteryManager.BATTERY_PLUGGED_AC or BatteryManager.BATTERY_PLUGGED_USB or BatteryManager.BATTERY_PLUGGED_WIRELESS dpm.setGlobalSetting(adminName, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, pluggedInto.toString())
Java
int pluggedInto = BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB | BatteryManager.BATTERY_PLUGGED_WIRELESS; dpm.setGlobalSetting( adminName, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, String.valueOf(pluggedInto));
应用软件包
本部分包含可帮助您高效将应用安装到专用设备的方案。
缓存应用软件包
如果共用设备的用户共用一组通用应用, 尽量避免下载应用简化用户 在具有一组固定用户的共享设备上进行配置,例如 轮班员工,在 Android 9.0(API 级别 28)或更高版本中,您可以缓存应用 多用户会话所需的软件包 (APK)。
安装缓存的 APK(已安装在设备上)的整个过程如下: 两个阶段:
- 完全受管设备(或受托人)的管理员组件,请参阅 下文)用于设置要保留在设备上的 APK 列表。
- 关联的次要用户(或其受托人)的管理员组件可以 代表用户安装缓存的 APK。全托管式服务的管理员 主要用户或关联的工作资料(或其资料) 受托人)也可以根据需要安装缓存的应用。
要设置在设备上保留的 APK 列表,管理员调用
DevicePolicyManager.setKeepUninstalledPackages()
。
此方法不会检查 APK 是否已安装在设备上。
您希望在用户需要某个应用程序之前就安装这个应用程序。要获取
您可以调用
DevicePolicyManager.getKeepUninstalledPackages()
。
在您调用 setKeepUninstalledPackages()
并进行更改后,或次要
当用户被删除时,系统会删除不再需要的所有缓存 APK。
要安装缓存的 APK,请调用
DevicePolicyManager.installExistingPackage()
。
此方法只能安装系统已经缓存的应用,即您的
必须首先在 Chrome 或 Chrome 中
然后才能调用此方法。
以下示例展示了如何在 全托管式设备和次要用户:
Kotlin
// Set the package to keep. This method assumes that the package is already // installed on the device by managed Google Play. val cachedAppPackageName = "com.example.android.myapp" dpm.setKeepUninstalledPackages(adminName, listOf(cachedAppPackageName)) // ... // The admin of a secondary user installs the app. val success = dpm.installExistingPackage(adminName, cachedAppPackageName)
Java
// Set the package to keep. This method assumes that the package is already // installed on the device by managed Google Play. String cachedAppPackageName = "com.example.android.myapp"; List<String> packages = new ArrayList<String>(); packages.add(cachedAppPackageName); dpm.setKeepUninstalledPackages(adminName, packages); // ... // The admin of a secondary user installs the app. boolean success = dpm.installExistingPackage(adminName, cachedAppPackageName);
将应用委托给他人
您可以委托其他应用来管理应用缓存。为此,您可能需要
分离您解决方案的功能,或者让 IT 管理员能够
自己的应用。受委托应用将获得与管理员相同的权限
组件。例如,次要用户管理员的应用委托可以调用
installExistingPackage()
,但无法呼叫 setKeepUninstalledPackages()
。
进行委托通话
DevicePolicyManager.setDelegatedScopes()
并包含
DELEGATION_KEEP_UNINSTALLED_PACKAGES
。以下示例展示了如何创建另一个应用
委托人:
Kotlin
var delegatePackageName = "com.example.tools.kept_app_assist" // Check that the package is installed before delegating. try { context.packageManager.getPackageInfo(delegatePackageName, 0) dpm.setDelegatedScopes( adminName, delegatePackageName, listOf(DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES)) } catch (e: PackageManager.NameNotFoundException) { // The delegate app isn't installed. Send a report to the IT admin ... }
Java
String delegatePackageName = "com.example.tools.kept_app_assist"; // Check that the package is installed before delegating. try { context.getPackageManager().getPackageInfo(delegatePackageName, 0); dpm.setDelegatedScopes( adminName, delegatePackageName, Arrays.asList(DevicePolicyManager.DELEGATION_KEEP_UNINSTALLED_PACKAGES)); } catch (PackageManager.NameNotFoundException e) { // The delegate app isn't installed. Send a report to the IT admin ... }
如果一切顺利,受委托应用会收到
ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED
广播并成为代理。应用可以调用本指南中的方法
就像是设备所有者或资料所有者一样。调用时
DevicePolicyManager
方法,委托可以为管理员传递 null
组件参数。
安装应用软件包
有时,将本地缓存的自定义应用安装到
设备。例如,专用设备经常部署到
带宽受限的环境或无互联网连接的区域您的
专用设备解决方案时,应考虑客户的带宽。您的
使用
PackageInstaller
类。
虽然任何应用都可以安装 APK,但完全受管设备上的管理员可以安装 APK 无需用户互动即可安装(或卸载)软件包。管理员可能负责管理 关联的次要用户或关联的工作资料。更新后 完成安装时,系统会发布一条通知,告诉所有设备用户 看到的内容。通知会告知设备用户该应用已安装(或者 由其管理员更新)。
Android 版本 | 用于安装和卸载的管理组件 |
---|---|
Android 9.0(API 级别 28)或更高版本 | 关联的次要用户和工作资料(均为全代管式服务) 设备 |
Android 6.0(API 级别 23)或更高版本 | 全托管式设备 |
如何将一个或多个 APK 副本分发到专用设备, 取决于设备之间的距离以及设备之间的距离 都是来自另一个。您的解决方案需要遵循安全性最佳实践 然后再将 APK 安装到专用设备上。
您可以使用 PackageInstaller.Session
创建一个会话,将一个会话加入队列
一个或多个 APK 供安装。在以下示例中,我们收到的状态是
反馈,但您可以使用
服务或广播接收器:
Kotlin
// First, create a package installer session. val packageInstaller = context.packageManager.packageInstaller val params = PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL) val sessionId = packageInstaller.createSession(params) val session = packageInstaller.openSession(sessionId) // Add the APK binary to the session. The APK is included in our app binary // and is read from res/raw but file storage is a more typical location. // The I/O streams can't be open when installation begins. session.openWrite("apk", 0, -1).use { output -> getContext().resources.openRawResource(R.raw.app).use { input -> input.copyTo(output, 2048) } } // Create a status receiver to report progress of the installation. // We'll use the current activity. // Here we're requesting status feedback to our Activity but this can be a // service or broadcast receiver. val intent = Intent(context, activity.javaClass) intent.action = "com.android.example.APK_INSTALLATION_ACTION" val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0) val statusReceiver = pendingIntent.intentSender // Start the installation. Because we're an admin of a fully managed device, // there isn't any user interaction. session.commit(statusReceiver)
Java
// First, create a package installer session. PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); int sessionId = packageInstaller.createSession(params); PackageInstaller.Session session = packageInstaller.openSession(sessionId); // Add the APK binary to the session. The APK is included in our app binary // and is read from res/raw but file storage is a more typical location. try ( // These I/O streams can't be open when installation begins. OutputStream output = session.openWrite("apk", 0, -1); InputStream input = getContext().getResources().openRawResource(R.raw.app); ) { byte[] buffer = new byte[2048]; int n; while ((n = input.read(buffer)) >= 0) { output.write(buffer, 0, n); } } // Create a status receiver to report progress of the installation. // We'll use the current activity. // Here we're requesting status feedback to our Activity but this can be a // service or broadcast receiver. Intent intent = new Intent(context, getActivity().getClass()); intent.setAction("com.android.example.APK_INSTALLATION_ACTION"); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); IntentSender statusReceiver = pendingIntent.getIntentSender(); // Start the installation. Because we're an admin of a fully managed device, // there isn't any user interaction. session.commit(statusReceiver);
会话使用 intent 发送有关安装的状态反馈。查看
每个 intent 的 EXTRA_STATUS
字段来获取
状态。请注意,管理员不会收到
STATUS_PENDING_USER_ACTION
状态更新
因为无需设备用户批准安装。
如需卸载应用,您可以调用 PackageInstaller.uninstall
。
完全受管设备、用户和工作资料的管理员可以卸载软件包
无需用户互动即可运行支持的 Android 版本(请参阅
表 2)。
冻结系统更新
Android 设备接收系统和应用的无线下载 (OTA) 更新 软件开发应用。在重要时段(例如节假日或 其他繁忙时段,专用设备最多可在 90 秒内暂停 OTA 系统更新 天。如需了解详情,请参阅管理系统更新。
远程配置
借助 Android 的受管配置,IT 管理员可以 远程配置您的应用。您可能需要公开设置 许可名单、网络主机或内容网址,让应用对 IT 更加有用 管理员。
如果您的应用公开了其配置,请务必在您的 文档。详细了解如何公开应用的配置以及对 请参阅设置受管配置。
开发设置
在为专用设备开发解决方案时,有时需要 将应用设置为不带出厂设置的全代管式设备的管理员非常有用 重置。如需设置完全受管设备的管理员,请按以下步骤操作:
- 构建您的设备政策控制器 (DPC) 应用并将其安装在设备上。
- 检查确认设备上未配置任何账号。
在 Android 调试桥 (adb) shell 中运行以下命令。您 需要将示例中的
com.example.dpc/.MyDeviceAdminReceiver
替换为 应用的管理组件名称:adb shell dpm set-device-owner com.example.dpc/.MyDeviceAdminReceiver
为帮助客户部署您的解决方案,您需要查看其他注册情况 方法。我们建议您:注册二维码 专用设备
其他资源
如需详细了解专用设备,请参阅以下文档: