本指南介绍了如何在自定义设备政策控制器 (DPC) 中使用 Android Management API (AMAPI) SDK,在公司自有设备上配置统一更新控制 (UUC)。
使用 AMAPI SDK,您的自定义 DPC 会将 UUC 政策直接发送到设备上的 Android 设备政策 (ADP) 应用。ADP 会根据当前设备状态评估政策规则,并指示 Google Play 和 OEM OTA 客户端如何处理待处理的更新。
在开始之前,请先阅读统一更新控件,了解规则、目标、谓词和结果的工作原理,以及哪些限制适用于所有 UUC 集成。
前提条件
在将 UUC 集成到自定义 DPC 之前,请验证以下要求:
- 设备为公司自有设备,配置为全托管式设备或公司自有设备上的工作资料。
- 您的自定义 DPC 已与 AMAPI SDK 集成。
- 设备是使用 Google Play EMM API 注册的。
- 设备符合其管理模式和您要控制的每个更新目标的最低版本要求:
- 全托管式(设备所有者)设备:Android 13(API 级别 33)或更高版本。
- 由公司所有且包含工作资料 (COPE) 的设备:Android 15(API 级别 35)或更高版本。
在自定义 DPC 中与 AMAPI SDK 集成
如需在设备上配置 UUC 政策,您的自定义 DPC 必须与 AMAPI SDK 集成。如需了解如何添加库和配置应用清单,请参阅 AMAPI SDK 集成指南。
配置统一更新控件的步骤
如需通过自定义 DPC 配置 UUC 政策,请确保设备端客户端环境已准备就绪且处于最新状态,然后再向 ADP 发送政策请求:
验证客户端环境
以下示例展示了如何使用 Role.RoleType.DEVICE_POLICY_CONTROLLER 调用 getEnvironment(在 Kotlin 中)或 getEnvironmentAsync(在 Java 中),以检查 ADP 应用的状态。如果环境为 READY 和 UP_TO_DATE,您的自定义 DPC 可以继续使用 PolicyClient 配置 UUC 政策。否则,请调用 prepareEnvironment 来安装或更新 ADP 应用(请参阅准备客户端环境)。
Kotlin
import android.content.Context import android.util.Log import com.google.android.managementapi.common.model.Role import com.google.android.managementapi.environment.EnvironmentClientFactory import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.INSTALLED import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.NOT_INSTALLED import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.READY import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.Version.UP_TO_DATE import com.google.android.managementapi.environment.model.GetEnvironmentRequest private suspend fun checkEnvironmentForUuc(context: Context): Boolean { try { val roles = listOf( Role.builder() .setRoleType(Role.RoleType.DEVICE_POLICY_CONTROLLER) .build() ) val request = GetEnvironmentRequest.builder().setRoles(roles).build() val environmentClient = EnvironmentClientFactory.create(context) val environmentResponse = environmentClient.getEnvironment(request) if (environmentResponse.hasAndroidDevicePolicyEnvironment()) { val adpEnvironment = environmentResponse.androidDevicePolicyEnvironment when (adpEnvironment.state) { READY -> { return if (adpEnvironment.version == UP_TO_DATE) { // AMAPI environment state and version are ready. true } else { prepareEnvironment(context, environmentClient) } } INSTALLED -> { // Call prepareEnvironment to silently update ADP. return prepareEnvironment(context, environmentClient) } NOT_INSTALLED -> { // Call prepareEnvironment to install ADP. return prepareEnvironment(context, environmentClient) } else -> {} } } } catch (e: Exception) { Log.e(TAG, "Exception checking environment", e) } return false }
Java
import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.INSTALLED; import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.NOT_INSTALLED; import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.READY; import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.Version.UP_TO_DATE; import android.content.Context; import android.util.Log; import com.google.android.managementapi.common.model.Role; import com.google.android.managementapi.environment.EnvironmentClient; import com.google.android.managementapi.environment.EnvironmentClientFactory; import com.google.android.managementapi.environment.model.Environment; import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment; import com.google.android.managementapi.environment.model.GetEnvironmentRequest; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; private void checkEnvironmentForUuc(Context context) { try { ImmutableListroles = new ImmutableList.Builder () .add(Role.builder() .setRoleType(Role.RoleType.DEVICE_POLICY_CONTROLLER) .build()) .build(); EnvironmentClient environmentClient = EnvironmentClientFactory.create(context); GetEnvironmentRequest getRequest = GetEnvironmentRequest.builder() .setRoles(roles) .build(); ListenableFuture getEnvironmentFuture = environmentClient.getEnvironmentAsync(getRequest); Futures.addCallback(getEnvironmentFuture, new FutureCallback () { @Override public void onSuccess(Environment environment) { if (!environment.hasAndroidDevicePolicyEnvironment()) { return; } AndroidDevicePolicyEnvironment adpEnvironment = environment.getAndroidDevicePolicyEnvironment(); AndroidDevicePolicyEnvironment.State state = adpEnvironment.getState(); AndroidDevicePolicyEnvironment.Version version = adpEnvironment.getVersion(); if (state == READY && version == UP_TO_DATE) { // AMAPI environment state and version are ready. return; } else if (state == INSTALLED || state == NOT_INSTALLED) { // Call prepareEnvironment to silently install or update ADP. prepareEnvironment(context, environmentClient); } } @Override public void onFailure(Throwable t) { Log.e(TAG, "Failed to verify environment", t); } }, MoreExecutors.directExecutor()); } catch (Exception e) { Log.e(TAG, "Exception verifying environment", e); } }
准备客户端环境
如果设备上的环境尚未准备就绪或不是最新版本,请先调用 prepareEnvironment,然后再使用 PolicyClient。
当从具有 Role.RoleType.DEVICE_POLICY_CONTROLLER 的自定义 DPC 调用,并且 DPC 的 DeviceAdminReceiver 组件名称传递给 setAdmin() 时,prepareEnvironment 会静默安装 ADP 应用(如果尚未安装),并在无需用户互动的情况下更新 ADP 应用和 Google Play 商店。如需详细了解自定义 DPC 流程中的环境准备,请参阅环境准备和用户注册。
Kotlin
import android.content.ComponentName import android.content.Context import android.util.Log import com.google.android.managementapi.common.model.Role import com.google.android.managementapi.environment.EnvironmentClient import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.READY import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.Version.UP_TO_DATE import com.google.android.managementapi.environment.model.PrepareEnvironmentRequest private suspend fun prepareEnvironment( context: Context, environmentClient: EnvironmentClient ): Boolean { try { val roles = listOf( Role.builder() .setRoleType(Role.RoleType.DEVICE_POLICY_CONTROLLER) .build() ) val request = PrepareEnvironmentRequest.builder() .setRoles(roles) .setAdmin( ComponentName( context, DeviceAdminReceiver::class.java ) ) .build() val response = environmentClient.prepareEnvironment(request, null) val environment = response.environment val adpEnvironment = environment.androidDevicePolicyEnvironment val state = adpEnvironment.state val version = adpEnvironment.version return (state == READY && version == UP_TO_DATE) } catch (e: Exception) { Log.e(TAG, "Failed to prepare environment", e) } return false }
Java
import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.State.READY; import static com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment.Version.UP_TO_DATE; import android.content.ComponentName; import android.content.Context; import android.util.Log; import androidx.annotation.NonNull; import com.google.android.managementapi.common.model.Role; import com.google.android.managementapi.environment.EnvironmentClient; import com.google.android.managementapi.environment.model.Environment; import com.google.android.managementapi.environment.model.Environment.AndroidDevicePolicyEnvironment; import com.google.android.managementapi.environment.model.PrepareEnvironmentRequest; import com.google.android.managementapi.environment.model.PrepareEnvironmentResponse; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; private void prepareEnvironment(Context context, EnvironmentClient environmentClient) { try { ImmutableListroles = new ImmutableList.Builder () .add(Role.builder() .setRoleType(Role.RoleType.DEVICE_POLICY_CONTROLLER) .build()) .build(); ComponentName adminComponent = new ComponentName( context, DeviceAdminReceiver.class ); PrepareEnvironmentRequest request = PrepareEnvironmentRequest.builder() .setRoles(roles) .setAdmin(adminComponent) .build(); ListenableFuture environmentFuture = environmentClient.prepareEnvironmentAsync(request, null); Futures.addCallback(environmentFuture, new FutureCallback () { @Override public void onSuccess(PrepareEnvironmentResponse response) { Environment environment = response.getEnvironment(); AndroidDevicePolicyEnvironment adpEnvironment = environment.getAndroidDevicePolicyEnvironment(); AndroidDevicePolicyEnvironment.State state = adpEnvironment.getState(); AndroidDevicePolicyEnvironment.Version version = adpEnvironment.getVersion(); if (state == READY && version == UP_TO_DATE) { // Environment is prepared; configure UUC policy using PolicyClient. } else { Log.w( TAG, "AMAPI environment was not ready: " + state + " - " + version ); } } @Override public void onFailure(@NonNull Throwable t) { Log.e(TAG, "Failed to prepare environment", t); } }, MoreExecutors.directExecutor()); } catch (Exception e) { Log.e(TAG, "Exception preparing environment", e); } }
设置、检索和清除更新政策
环境准备就绪后,使用 PolicyClient(通过 PolicyClientFactory.create(context) 创建)应用包含 UnifiedUpdatePolicy 规则的 Policy、检查有效政策或从设备中清除 UUC 政策。
PolicyClient 使用建议
创建 PolicyClient 实例会建立与 ADP 应用的通信渠道。为了优化性能,我们建议您使用 PolicyClientFactory.create() 创建 PolicyClient 的单个实例,并将其缓存以便在整个应用中重复使用。避免在另一个 PolicyClient 实例处于活跃状态时创建新的实例,因为这样做可能会强制关闭现有通信通道,从而产生开销并可能导致资源泄露。
设置更新政策
如需应用 UUC 规则,请构建一个包含 Rule 对象列表的 Policy.UnifiedUpdatePolicy,将其附加到 UpdatePolicyRequest 内的 Policy,然后调用 updatePolicy(在 Kotlin 中)或 updatePolicyFuture(在 Java 中):
Kotlin
import android.util.Log import com.google.android.managementapi.policy.PolicyClient import com.google.android.managementapi.policy.model.Policy import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule import com.google.android.managementapi.policy.model.UpdatePolicyRequest suspend fun setUucPolicy( policyClient: PolicyClient, rules: Iterable): Boolean { // Build the UnifiedUpdatePolicy and attach it to the overall Policy. val unifiedUpdatePolicy = Policy.UnifiedUpdatePolicy.builder() .setRules(rules) .build() val policy = Policy.builder() .setUnifiedUpdatePolicy(unifiedUpdatePolicy) .build() // Create the UpdatePolicyRequest. val updateRequest = UpdatePolicyRequest.builder() .setPolicy(policy) .build() // Apply the policy using PolicyClient within a CoroutineScope. return try { val updatedPolicy = policyClient.updatePolicy(updateRequest) Log.d(TAG, "Policy updated successfully: $updatedPolicy") true } catch (e: Exception) { Log.e(TAG, "Error updating policy: ${e.message}", e) false } }
Java
import android.util.Log; import com.google.android.managementapi.policy.PolicyClient; import com.google.android.managementapi.policy.model.Policy; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule; import com.google.android.managementapi.policy.model.UpdatePolicyRequest; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import java.util.stream.Collectors; import java.util.stream.StreamSupport; ListenableFuturesetUucPolicy( PolicyClient policyClient, Iterable rules ) { // Build the UnifiedUpdatePolicy and attach it to the overall Policy. UnifiedUpdatePolicy unifiedUpdatePolicy = UnifiedUpdatePolicy.builder() .setRules( StreamSupport.stream(rules.spliterator(), false) .collect(Collectors.toList()) ) .build(); Policy policy = Policy.builder() .setUnifiedUpdatePolicy(unifiedUpdatePolicy) .build(); // Create the UpdatePolicyRequest. UpdatePolicyRequest updateRequest = UpdatePolicyRequest.builder() .setPolicy(policy) .build(); // Apply the policy using PolicyClient. try { ListenableFuture updateFuture = policyClient.updatePolicyFuture(updateRequest); return Futures.transform(updateFuture, updatedPolicy -> { Log.d(TAG, "Policy updated successfully: " + updatedPolicy); return true; }, MoreExecutors.directExecutor()); } catch (Exception e) { Log.e(TAG, "Error updating policy: " + e.getMessage(), e); return Futures.immediateFuture(false); } }
检索有效的更新政策
如需检查设备上的有效 UUC 政策,请构建 GetPolicyRequest 并调用 getPolicy(在 Kotlin 中)或 getPolicyFuture(在 Java 中)。对返回的 Policy 使用 hasUnifiedUpdatePolicy(),以检查是否配置了 UUC 政策:
Kotlin
import android.util.Log import com.google.android.managementapi.policy.PolicyClient import com.google.android.managementapi.policy.model.GetPolicyRequest import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule suspend fun getActiveUucPolicy(policyClient: PolicyClient) { try { val getPolicyRequest = GetPolicyRequest.builder().build() val activePolicy = policyClient.getPolicy(getPolicyRequest) Log.i(TAG, "Policy version: ${activePolicy.version}") if (!activePolicy.hasUnifiedUpdatePolicy()) { Log.i(TAG, "No Unified Update Policy set.") return } val activeUucPolicy = activePolicy.unifiedUpdatePolicy val rules: List= activeUucPolicy.rules.toList() rules.forEachIndexed { index, rule -> val outcome = if (rule.hasOutcome()) rule.outcome.toString() else "[no outcome]" val predicate = if (rule.hasPredicate()) rule.predicate.toString() else "[no predicate]" Log.i(TAG, "Rule [$index] targets: ${rule.targets}") Log.i(TAG, "Rule [$index] outcome: $outcome") Log.i(TAG, "Rule [$index] predicate: $predicate") } } catch (e: Exception) { Log.e(TAG, "Error retrieving policy: ${e.message}", e) } }
Java
import android.util.Log; import com.google.android.managementapi.policy.PolicyClient; import com.google.android.managementapi.policy.model.GetPolicyRequest; import com.google.android.managementapi.policy.model.Policy; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import java.util.List; import java.util.stream.Collectors; import java.util.stream.StreamSupport; void getActiveUucPolicy(PolicyClient policyClient) { try { GetPolicyRequest getPolicyRequest = GetPolicyRequest.builder().build(); ListenableFuturegetActivePolicy = policyClient.getPolicyFuture(getPolicyRequest); Futures.addCallback(getActivePolicy, new FutureCallback () { @Override public void onSuccess(Policy result) { if (!result.hasUnifiedUpdatePolicy()) { Log.i(TAG, "No Unified Update Policy set."); return; } UnifiedUpdatePolicy activeUucPolicy = result.getUnifiedUpdatePolicy(); List uucRules = StreamSupport.stream( activeUucPolicy.getRules().spliterator(), false) .collect(Collectors.toList()); for (int i = 0; i < uucRules.size(); i++) { Rule rule = uucRules.get(i); String outcome = rule.hasOutcome() ? rule.getOutcome().toString() : "[no outcome]"; String predicate = rule.hasPredicate() ? rule.getPredicate().toString() : "[no predicate]"; Log.i(TAG, "Rule [" + i + "] targets: " + rule.getTargets()); Log.i(TAG, "Rule [" + i + "] outcome: " + outcome); Log.i(TAG, "Rule [" + i + "] predicate: " + predicate); } } @Override public void onFailure(Throwable t) { Log.e(TAG, "Error retrieving policy", t); } }, MoreExecutors.directExecutor()); } catch (Exception e) { Log.e(TAG, "Exception retrieving policy", e); } }
清除更新政策
如需从设备中移除 UnifiedUpdatePolicy,而不清除 Policy 上的其他字段,请使用 getPolicy 检索当前 Policy,调用 currentPolicy.toBuilder().clearUnifiedUpdatePolicy().build(),然后应用更新后的 Policy:
Kotlin
import android.util.Log import com.google.android.managementapi.policy.PolicyClient import com.google.android.managementapi.policy.model.GetPolicyRequest import com.google.android.managementapi.policy.model.UpdatePolicyRequest suspend fun clearUucPolicy(policyClient: PolicyClient): Boolean { try { // 1. Retrieve the current policy. val currentPolicy = policyClient.getPolicy( GetPolicyRequest.builder().build() ) if (!currentPolicy.hasUnifiedUpdatePolicy()) { Log.i(TAG, "Policy does not have a UnifiedUpdatePolicy to clear.") return true } // 2. Convert to a builder and clear the UnifiedUpdatePolicy field. val policyWithUucCleared = currentPolicy.toBuilder() .clearUnifiedUpdatePolicy() .build() // 3. Apply the modified Policy object. val updateRequest = UpdatePolicyRequest.builder() .setPolicy(policyWithUucCleared) .build() val updatedPolicy = policyClient.updatePolicy(updateRequest) return !updatedPolicy.hasUnifiedUpdatePolicy() } catch (e: Exception) { Log.e(TAG, "Error clearing policy: ${e.message}", e) return false } }
Java
import android.util.Log; import com.google.android.managementapi.policy.PolicyClient; import com.google.android.managementapi.policy.model.GetPolicyRequest; import com.google.android.managementapi.policy.model.Policy; import com.google.android.managementapi.policy.model.UpdatePolicyRequest; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; ListenableFutureclearUucPolicy(PolicyClient policyClient) { try { ListenableFuture currentPolicyFuture = policyClient.getPolicyFuture(GetPolicyRequest.builder().build()); return Futures.transformAsync(currentPolicyFuture, currentPolicy -> { if (!currentPolicy.hasUnifiedUpdatePolicy()) { Log.i(TAG, "Policy does not have a UnifiedUpdatePolicy to clear."); return Futures.immediateFuture(true); } Policy policyWithUucCleared = currentPolicy.toBuilder() .clearUnifiedUpdatePolicy() .build(); UpdatePolicyRequest updateRequest = UpdatePolicyRequest.builder() .setPolicy(policyWithUucCleared) .build(); ListenableFuture updateFuture = policyClient.updatePolicyFuture(updateRequest); return Futures.transform( updateFuture, updatedPolicy -> !updatedPolicy.hasUnifiedUpdatePolicy(), MoreExecutors.directExecutor() ); }, MoreExecutors.directExecutor()); } catch (Exception e) { Log.e(TAG, "Error clearing policy: " + e.getMessage(), e); return Futures.immediateFuture(false); } }
AOSP SystemUpdatePolicy 优先级
在自定义 DPC 中,Android 兼容性定义文档 (CDD) 要求设备遵循通过 DevicePolicyManager.setSystemUpdatePolicy 设置的 AOSP SystemUpdatePolicy。
因此,OEM OTA 更新行为取决于设备上是否有 AOSP SystemUpdatePolicy 处于活动状态:
- 同时设置了 UUC 政策和 AOSP
SystemUpdatePolicy:AOSPSystemUpdatePolicy优先于 OEM OTA 更新,并且 UUC 目标OS_UPDATE_WITH_API_LEVEL_CHANGE和OS_UPDATE_WITHOUT_API_LEVEL_CHANGE会被忽略。如果您希望 UUC 管理 OEM OTA 更新,请在自定义 DPC 中清除 AOSPSystemUpdatePolicy。 - 仅设置了 UUC 政策(AOSP
SystemUpdatePolicy已清除):OEM OTA 代理会在受支持的设备上应用 UUC 政策,以进行 OEM OTA 更新。 - 尚不支持 UUC 进行 OEM OTA 更新的设备:在需要 Android 17(API 级别 37)才能支持 OEM OTA 的设备上,您可以使用 UUC 控制 Google Play 系统更新 (
GOOGLE_PLAY_SYSTEM_UPDATE) 和 Google 系统服务(GOOGLE_SYSTEM_SERVICES和SINGLE_APP),同时继续使用 AOSPSystemUpdatePolicy进行 OEM OTA 更新。
您可以使用 DevicePolicyManager 检查设备上是否有 AOSP SystemUpdatePolicy 处于活动状态:
Kotlin
import android.app.admin.DevicePolicyManager import android.content.Context fun Context.isSystemUpdatePolicyActive(): Boolean { val dpm = getSystemService(Context.DEVICE_POLICY_SERVICE) as? DevicePolicyManager return dpm?.systemUpdatePolicy != null }
Java
import android.app.admin.DevicePolicyManager; import android.content.Context; boolean isSystemUpdatePolicyActive(Context context) { DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); return dpm != null && dpm.getSystemUpdatePolicy() != null; }
政策配置示例
以下示例展示了如何在 Kotlin 和 Java 中构建 UUC 规则,并使用设置、检索和清除更新政策中定义的 setUucPolicy 辅助程序应用这些规则。如需了解每个字段的含义,请参阅了解 UUC 政策。统一更新控件中以 JSON 格式提供了相同的场景。
在工作时间内禁止重大操作系统更新
以下规则会在周一至周五的 09:00 到 17:00 期间屏蔽主要操作系统更新 (OS_UPDATE_WITH_API_LEVEL_CHANGE):
Kotlin
import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome.OutcomeType.BLOCK import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Predicate import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.OS_UPDATE_WITH_API_LEVEL_CHANGE import java.time.DayOfWeek import java.time.LocalTime // 1. Define the target (OS update with API level change). val target = Target.builder() .setTargetType(OS_UPDATE_WITH_API_LEVEL_CHANGE) .build() // 2. Define the predicate (Monday through Friday, 09:00 to 17:00). val predicate = Predicate.builder() .setDaysOfWeek( listOf( DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY ) ) .setTimeOfDayLowerBound(LocalTime.of(9, 0)) .setTimeOfDayUpperBound(LocalTime.of(17, 0)) .build() // 3. Define the outcome (BLOCK). val outcome = Outcome.builder() .setOutcomeType(BLOCK) .build() // 4. Assemble and apply the rule. val rule = Rule.builder() .setTargets(listOf(target)) .setPredicate(predicate) .setOutcome(outcome) .setId("Block Policy") .build() setUucPolicy(policyClient, listOf(rule))
Java
import static com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome.OutcomeType.BLOCK; import static com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.OS_UPDATE_WITH_API_LEVEL_CHANGE; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Predicate; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target; import java.time.DayOfWeek; import java.time.LocalTime; import java.util.List; // 1. Define the target (OS update with API level change). Target target = Target.builder() .setTargetType(OS_UPDATE_WITH_API_LEVEL_CHANGE) .build(); // 2. Define the predicate (Monday through Friday, 09:00 to 17:00). Predicate predicate = Predicate.builder() .setDaysOfWeek( List.of( DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY ) ) .setTimeOfDayLowerBound(LocalTime.of(9, 0)) .setTimeOfDayUpperBound(LocalTime.of(17, 0)) .build(); // 3. Define the outcome (BLOCK). Outcome outcome = Outcome.builder() .setOutcomeType(BLOCK) .build(); // 4. Assemble and apply the rule. Rule rule = Rule.builder() .setTargets(List.of(target)) .setPredicate(predicate) .setOutcome(outcome) .setId("Block Policy") .build(); setUucPolicy(policyClient, List.of(rule));
按更新发布时长强制执行操作系统更新
以下规则会强制执行重大操作系统更新 (OS_UPDATE_WITH_API_LEVEL_CHANGE),更新期限上限为 7 天:
Kotlin
import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome.OutcomeType.FORCE import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Predicate import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.OS_UPDATE_WITH_API_LEVEL_CHANGE import java.time.Duration val target = Target.builder() .setTargetType(OS_UPDATE_WITH_API_LEVEL_CHANGE) .build() val predicate = Predicate.builder() .setUpdateAgeUpperBound(Duration.ofDays(7)) .build() val outcome = Outcome.builder() .setOutcomeType(FORCE) .build() val rule = Rule.builder() .setTargets(listOf(target)) .setPredicate(predicate) .setOutcome(outcome) .setId("Force Policy") .build() setUucPolicy(policyClient, listOf(rule))
Java
import static com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome.OutcomeType.FORCE; import static com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.OS_UPDATE_WITH_API_LEVEL_CHANGE; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Predicate; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target; import java.time.Duration; import java.util.List; Target target = Target.builder() .setTargetType(OS_UPDATE_WITH_API_LEVEL_CHANGE) .build(); Predicate predicate = Predicate.builder() .setUpdateAgeUpperBound(Duration.ofDays(7)) .build(); Outcome outcome = Outcome.builder() .setOutcomeType(FORCE) .build(); Rule rule = Rule.builder() .setTargets(List.of(target)) .setPredicate(predicate) .setOutcome(outcome) .setId("Force Policy") .build(); setUucPolicy(policyClient, List.of(rule));
禁止更新单个 Google 系统服务应用
如需控制属于 Google 系统服务的特定软件包(例如 Android System WebView [com.google.android.webview]),请将目标类型设置为 SINGLE_APP,并提供包含软件包名称的 SingleApp 对象:
Kotlin
import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome.OutcomeType.BLOCK import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Predicate import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.SingleApp import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.SINGLE_APP import java.time.DayOfWeek import java.time.LocalTime val target = Target.builder() .setTargetType(SINGLE_APP) .setSingleApp( SingleApp.builder() .setPackageName("com.google.android.webview") .build() ) .build() val predicate = Predicate.builder() .setDaysOfWeek( listOf( DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY ) ) .setTimeOfDayLowerBound(LocalTime.of(9, 0)) .setTimeOfDayUpperBound(LocalTime.of(17, 0)) .build() val outcome = Outcome.builder() .setOutcomeType(BLOCK) .build() val rule = Rule.builder() .setTargets(listOf(target)) .setPredicate(predicate) .setOutcome(outcome) .setId("Block Webview Policy") .build() setUucPolicy(policyClient, listOf(rule))
Java
import static com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome.OutcomeType.BLOCK; import static com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.SINGLE_APP; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Predicate; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.SingleApp; import java.time.DayOfWeek; import java.time.LocalTime; import java.util.List; Target target = Target.builder() .setTargetType(SINGLE_APP) .setSingleApp( SingleApp.builder() .setPackageName("com.google.android.webview") .build() ) .build(); Predicate predicate = Predicate.builder() .setDaysOfWeek( List.of( DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY ) ) .setTimeOfDayLowerBound(LocalTime.of(9, 0)) .setTimeOfDayUpperBound(LocalTime.of(17, 0)) .build(); Outcome outcome = Outcome.builder() .setOutcomeType(BLOCK) .build(); Rule rule = Rule.builder() .setTargets(List.of(target)) .setPredicate(predicate) .setOutcome(outcome) .setId("Block Webview Policy") .build(); setUucPolicy(policyClient, List.of(rule));
跨所有更新目标组合多条规则
由于系统会按顺序评估规则,因此您可以组合使用多条规则,在特定时间段(例如营业时间或目标日历日期之前)内阻止更新,并在这些时间段之外强制执行更新。
以下示例对所有四个类别目标(OS_UPDATE_WITH_API_LEVEL_CHANGE、OS_UPDATE_WITHOUT_API_LEVEL_CHANGE、GOOGLE_SYSTEM_SERVICES 和 GOOGLE_PLAY_SYSTEM_UPDATE)应用了两个依序规则:
- 在营业时间(周一至周五,上午 9 点至下午 5 点)禁止所有更新。
- 在非营业时间,强制执行已发布最多 7 天的更新。
Kotlin
import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome.OutcomeType.BLOCK import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome.OutcomeType.FORCE import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Predicate import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.GOOGLE_PLAY_SYSTEM_UPDATE import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.GOOGLE_SYSTEM_SERVICES import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.OS_UPDATE_WITHOUT_API_LEVEL_CHANGE import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.OS_UPDATE_WITH_API_LEVEL_CHANGE import java.time.DayOfWeek import java.time.Duration import java.time.LocalTime // 1. Define the targets to reuse across both rules. val allTargets = listOf( Target.builder().setTargetType(OS_UPDATE_WITH_API_LEVEL_CHANGE).build(), Target.builder().setTargetType(OS_UPDATE_WITHOUT_API_LEVEL_CHANGE).build(), Target.builder().setTargetType(GOOGLE_SYSTEM_SERVICES).build(), Target.builder().setTargetType(GOOGLE_PLAY_SYSTEM_UPDATE).build() ) // 2. Rule 1: Block updates during business hours. val blockPredicate = Predicate.builder() .setDaysOfWeek( listOf( DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY ) ) .setTimeOfDayLowerBound(LocalTime.of(9, 0)) .setTimeOfDayUpperBound(LocalTime.of(17, 0)) .build() val blockOutcome = Outcome.builder() .setOutcomeType(BLOCK) .build() val rule1 = Rule.builder() .setTargets(allTargets) .setPredicate(blockPredicate) .setOutcome(blockOutcome) .setId("Block Policy - rule1") .build() // 3. Rule 2: Force updates within 7 days of availability. val forcePredicate = Predicate.builder() .setUpdateAgeUpperBound(Duration.ofDays(7)) .build() val forceOutcome = Outcome.builder() .setOutcomeType(FORCE) .build() val rule2 = Rule.builder() .setTargets(allTargets) .setPredicate(forcePredicate) .setOutcome(forceOutcome) .setId("Force Policy - rule2") .build() setUucPolicy(policyClient, listOf(rule1, rule2))
Java
import static com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome.OutcomeType.BLOCK; import static com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome.OutcomeType.FORCE; import static com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.GOOGLE_PLAY_SYSTEM_UPDATE; import static com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.GOOGLE_SYSTEM_SERVICES; import static com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.OS_UPDATE_WITHOUT_API_LEVEL_CHANGE; import static com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target.TargetType.OS_UPDATE_WITH_API_LEVEL_CHANGE; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Predicate; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Target; import java.time.DayOfWeek; import java.time.Duration; import java.time.LocalTime; import java.util.List; // 1. Define the targets to reuse across both rules. ListallTargets = List.of( Target.builder().setTargetType(OS_UPDATE_WITH_API_LEVEL_CHANGE).build(), Target.builder().setTargetType(OS_UPDATE_WITHOUT_API_LEVEL_CHANGE).build(), Target.builder().setTargetType(GOOGLE_SYSTEM_SERVICES).build(), Target.builder().setTargetType(GOOGLE_PLAY_SYSTEM_UPDATE).build() ); // 2. Rule 1: Block updates during business hours. Predicate blockPredicate = Predicate.builder() .setDaysOfWeek( List.of( DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY ) ) .setTimeOfDayLowerBound(LocalTime.of(9, 0)) .setTimeOfDayUpperBound(LocalTime.of(17, 0)) .build(); Outcome blockOutcome = Outcome.builder() .setOutcomeType(BLOCK) .build(); Rule rule1 = Rule.builder() .setTargets(allTargets) .setPredicate(blockPredicate) .setOutcome(blockOutcome) .setId("Block Policy - rule1") .build(); // 3. Rule 2: Force updates within 7 days of availability. Predicate forcePredicate = Predicate.builder() .setUpdateAgeUpperBound(Duration.ofDays(7)) .build(); Outcome forceOutcome = Outcome.builder() .setOutcomeType(FORCE) .build(); Rule rule2 = Rule.builder() .setTargets(allTargets) .setPredicate(forcePredicate) .setOutcome(forceOutcome) .setId("Force Policy - rule2") .build(); setUucPolicy(policyClient, List.of(rule1, rule2));
在某个日历日期之前限制更新
您可以在 Predicate.Builder 上使用 setDateLowerBound 和 setDateUpperBound,以在设备本地时区的特定日历日期范围内限制或允许更新:
Kotlin
import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome.OutcomeType.BLOCK import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Predicate import java.time.LocalDate val blockUntilDateRule = Rule.builder() .setId("block_until_date") .setTargets(allTargets) .setPredicate( Predicate.builder() .setDateUpperBound(LocalDate.of(2026, 3, 14)) .build() ) .setOutcome(Outcome.builder().setOutcomeType(BLOCK).build()) .build() setUucPolicy(policyClient, listOf(blockUntilDateRule))
Java
import static com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome.OutcomeType.BLOCK; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Outcome; import com.google.android.managementapi.policy.model.Policy.UnifiedUpdatePolicy.Rule.Predicate; import java.time.LocalDate; import java.util.List; Rule blockUntilDateRule = Rule.builder() .setId("block_until_date") .setTargets(allTargets) .setPredicate( Predicate.builder() .setDateUpperBound(LocalDate.of(2026, 3, 14)) .build() ) .setOutcome(Outcome.builder().setOutcomeType(BLOCK).build()) .build(); setUucPolicy(policyClient, List.of(blockUntilDateRule));
处理错误
如果 PolicyClient 调用失败,您的自定义 DPC 会收到异常(由 Kotlin suspend 方法 getPolicy 和 updatePolicy 直接抛出,或封装为 getPolicyFuture 和 updatePolicyFuture 中 ExecutionException 的原因)。常见异常包括:
ApiLevelException:设备搭载的 Android 版本低于 Android 13(API 级别 33)。DeviceNotManagedException:设备未作为全托管式设备(设备所有者)或设有工作资料的公司自有设备 (COPE) 进行管理。AndroidDevicePolicyAppNotInstalledException、AndroidDevicePolicyAppDisabledException或AndroidDevicePolicyAppNotUpdatedException:ADP 应用未安装、已停用或运行的是不支持PolicyClient的旧版本。调用prepareEnvironment以安装或更新 ADP 应用。InvalidArgumentException:请求格式不正确或包含无效的政策规则(例如,省略规则id或使用重复的id、在一天中的时间边界上设置分钟或秒、指定不是 24 小时倍数的更新时长、包含重复的目标,或在规则中明确设置OutcomeType.DEFAULT)。SecurityException:通话应用无权管理设备政策。TimeoutException(java.util.concurrent.TimeoutException)、InternalException或AmapiSdkException的其他子类:与 ADP 应用通信时发生暂时性 IPC 或内部错误。请使用指数退避算法策略重试调用。
重试策略
对于由暂时性条件(例如 TimeoutException 或 InternalException)引起的异常,请使用指数退避算法实现重试策略:
- 第一次失败后,先延迟 5 秒钟,然后再重试。
- 在每次后续尝试中以指数方式增加延迟时间(10 秒、20 秒等),直到达到最大尝试次数。