En esta guía, se explica cómo usar el SDK de la API de Android Management (AMAPI) en un controlador de política de dispositivo (DPC) personalizado para configurar los Controles de actualización unificados (UUC) en dispositivos de la empresa.
Con el SDK de AMAPI, tu DPC personalizado envía una política de UUC directamente a la app de Android Device Policy (ADP) en el dispositivo. ADP evalúa las reglas de la política en función del estado actual del dispositivo y le indica a Google Play y al cliente de OTA del OEM cómo controlar las actualizaciones pendientes.
Antes de comenzar, lee Controles de actualización unificados para aprender cómo funcionan las reglas, los destinos, los predicados y los resultados, y qué limitaciones se aplican a todas las integraciones de UUC.
Requisitos previos
Antes de integrar el UUC en tu DPC personalizado, verifica los siguientes requisitos:
- El dispositivo es propiedad de la empresa y está configurado como un dispositivo completamente administrado o con un perfil de trabajo en un dispositivo de propiedad de la empresa.
- Tu DPC personalizado está integrado con el SDK de AMAPI.
- El dispositivo se inscribe con la API de Google Play EMM.
- El dispositivo cumple con los requisitos mínimos de versión para su modo de administración y cada objetivo de actualización que quieras controlar:
- Dispositivos completamente administrados (propietario del dispositivo): Android 13 (nivel de API 33) o versiones posteriores
- Dispositivos de la empresa con un perfil de trabajo (COPE): Android 15 (nivel de API 35) o versiones posteriores.
Cómo realizar la integración con el SDK de AMAPI en tu DPC personalizado
Para configurar una política de UUC en un dispositivo, tu DPC personalizado debe integrarse en el SDK de AMAPI. Para obtener instrucciones sobre cómo agregar la biblioteca y configurar el manifiesto de tu app, consulta la guía de integración del SDK de AMAPI.
Pasos para configurar los controles de actualización unificados
Para configurar las políticas de UUC desde tu DPC personalizado, asegúrate de que el entorno del cliente integrado en el dispositivo esté listo y actualizado antes de enviar solicitudes de políticas a ADP:
- Verifica el entorno del cliente
- Prepara el entorno del cliente
- Establece, recupera y borra la política de actualización
Verifica el entorno del cliente
En el siguiente ejemplo, se muestra cómo llamar a getEnvironment (en Kotlin) o getEnvironmentAsync (en Java) con Role.RoleType.DEVICE_POLICY_CONTROLLER para inspeccionar el estado de la app de ADP. Si el entorno es READY y UP_TO_DATE, tu DPC personalizado puede continuar con la configuración de las políticas de UUC con PolicyClient. De lo contrario, llama a
prepareEnvironment para instalar o actualizar la app de ADP (consulta
Prepara el entorno del cliente).
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); } }
Prepara el entorno del cliente
Si el entorno del dispositivo no está listo o actualizado, llama a prepareEnvironment antes de usar PolicyClient.
Cuando se llama desde un DPC personalizado con Role.RoleType.DEVICE_POLICY_CONTROLLER y
el nombre del componente DeviceAdminReceiver de tu DPC
se pasa a setAdmin(), prepareEnvironment instala de forma silenciosa la app de ADP (si aún no está instalada)
y actualiza la app de ADP y Google Play Store sin interacción del usuario. Para obtener más
información sobre la preparación del entorno en los flujos de DPC personalizados, consulta
Preparación del entorno e inscripción de usuarios.
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); } }
Establece, recupera y borra la política de actualización
Cuando el entorno esté listo, usa PolicyClient (creado con
PolicyClientFactory.create(context)) para aplicar un Policy que contenga tus
reglas de UnifiedUpdatePolicy, inspeccionar la política activa o borrar la política de UUC
del dispositivo.
Recomendación de uso de PolicyClient
Cuando creas una instancia de PolicyClient, se establece un canal de comunicación con la
app de ADP. Para optimizar el rendimiento, te recomendamos que crees una sola instancia de
PolicyClient con PolicyClientFactory.create() y que la almacenes en caché para reutilizarla
en toda la aplicación. Evita crear una instancia de PolicyClient nueva mientras
otra esté activa, ya que esto puede forzar el cierre del canal de comunicación existente, lo que genera sobrecarga y puede provocar fugas de recursos.
Establece la política de actualización
Para aplicar las reglas de UUC, compila un Policy.UnifiedUpdatePolicy que contenga tu lista de objetos Rule, adjúntalo a un Policy dentro de un UpdatePolicyRequest y llama a updatePolicy (en Kotlin) o updatePolicyFuture (en 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); } }
Recupera la política de actualización activa
Para inspeccionar la política de UUC activa en el dispositivo, compila un GetPolicyRequest y llama a getPolicy (en Kotlin) o getPolicyFuture (en Java). Usa
hasUnifiedUpdatePolicy() en el Policy que se muestra para verificar si se configuró una política
de 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); } }
Borra la política de actualización
Para quitar el UnifiedUpdatePolicy del dispositivo sin borrar otros campos en Policy, recupera el Policy actual con getPolicy, llama a currentPolicy.toBuilder().clearUnifiedUpdatePolicy().build() y aplica el Policy actualizado:
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); } }
Prioridad de SystemUpdatePolicy del AOSP
En un DPC personalizado, el Documento de definición de compatibilidad de Android (CDD) requiere que los dispositivos respeten el SystemUpdatePolicy de AOSP establecido a través de DevicePolicyManager.setSystemUpdatePolicy.
Como resultado, el comportamiento de la actualización OTA del OEM depende de si hay un SystemUpdatePolicy de AOSP activo en el dispositivo:
- Se establecen la política de UUC y el
SystemUpdatePolicyde AOSP: ElSystemUpdatePolicyde AOSP tiene prioridad para las actualizaciones OTA de OEM, y se ignoran los objetivos de UUCOS_UPDATE_WITH_API_LEVEL_CHANGEyOS_UPDATE_WITHOUT_API_LEVEL_CHANGE. Borra elSystemUpdatePolicyde AOSP en tu DPC personalizado si quieres que UUC administre las actualizaciones OTA de OEM. - Solo se establece la política de UUC (se borra la de AOSP
SystemUpdatePolicy): El agente de OTA del OEM aplica la política de UUC para las actualizaciones OTA del OEM en los dispositivos compatibles. - Dispositivos que aún no admiten UUC para actualizaciones OTA de OEM: En los dispositivos en los que la compatibilidad con OTA de OEM requiere Android 17 (nivel de API 37), puedes usar UUC para controlar las actualizaciones del sistema de Google Play (
GOOGLE_PLAY_SYSTEM_UPDATE) y los servicios del sistema de Google (GOOGLE_SYSTEM_SERVICESySINGLE_APP) mientras sigues usandoSystemUpdatePolicyde AOSP para las actualizaciones OTA de OEM.
Puedes verificar si un SystemUpdatePolicy del AOSP está activo en el dispositivo con DevicePolicyManager:
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; }
Ejemplos de configuración de políticas
En los siguientes ejemplos, se muestra cómo compilar reglas de UUC en Kotlin y Java, y aplicarlas
con el ayudante setUucPolicy definido en
Establece, recupera y borra la política de actualización. Para conocer el significado de cada campo, consulta Información sobre las políticas de UUC. Las mismas situaciones están disponibles como JSON en Controles de actualización unificados.
Bloquea las actualizaciones importantes del SO durante el horario de atención
La siguiente regla bloquea las actualizaciones principales del SO (OS_UPDATE_WITH_API_LEVEL_CHANGE)
de 9:00 a 17:00, de lunes a viernes:
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));
Forzar actualizaciones del SO según la antigüedad de la actualización
La siguiente regla fuerza las actualizaciones principales del SO (OS_UPDATE_WITH_API_LEVEL_CHANGE)
con un límite superior de antigüedad de actualización de 7 días:
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));
Cómo bloquear las actualizaciones de una sola app de servicios del sistema de Google
Para controlar un paquete específico que forma parte de los servicios del sistema de Google, como
Android System WebView (com.google.android.webview), establece el tipo de destino en
SINGLE_APP y proporciona un objeto SingleApp con el nombre del paquete:
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));
Combina varias reglas en todos los destinos de actualización
Debido a que las reglas se evalúan en orden, puedes combinar varias reglas para bloquear las actualizaciones durante períodos específicos (como el horario de atención o antes de una fecha del calendario objetivo) y forzar las actualizaciones fuera de esos períodos.
En el siguiente ejemplo, se aplican dos reglas secuenciales en los cuatro objetivos de categoría (OS_UPDATE_WITH_API_LEVEL_CHANGE,
OS_UPDATE_WITHOUT_API_LEVEL_CHANGE, GOOGLE_SYSTEM_SERVICES y
GOOGLE_PLAY_SYSTEM_UPDATE):
- Bloquea todas las actualizaciones durante el horario de atención (de lunes a viernes, de 9:00 a.m. a 5:00 p.m.).
- Fuera del horario de atención, fuerza las actualizaciones que hayan estado disponibles durante un máximo de 7 días.
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));
Restringe las actualizaciones hasta una fecha del calendario
Puedes usar setDateLowerBound y setDateUpperBound en Predicate.Builder
para restringir o permitir actualizaciones dentro de un período específico del calendario en la
zona horaria local del dispositivo:
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));
Soluciona errores
Si falla una llamada a PolicyClient, tu DPC personalizado recibe una excepción (generada directamente por los métodos getPolicy y updatePolicy de suspend de Kotlin o unida como la causa de ExecutionException en getPolicyFuture y updatePolicyFuture). Entre las excepciones comunes, se incluyen las siguientes:
ApiLevelException: El dispositivo ejecuta una versión de Android anterior a Android 13 (nivel de API 33).DeviceNotManagedException: El dispositivo no se administra como un dispositivo completamente administrado (propietario del dispositivo) ni como un dispositivo de propiedad de la empresa con un perfil de trabajo (COPE).AndroidDevicePolicyAppNotInstalledException,AndroidDevicePolicyAppDisabledExceptionoAndroidDevicePolicyAppNotUpdatedException: La app de ADP no está instalada, está inhabilitada o ejecuta una versión anterior que no admitePolicyClient. Llama aprepareEnvironmentpara instalar o actualizar la app de ADP.InvalidArgumentException: La solicitud tiene un formato incorrecto o contiene reglas de política no válidas (por ejemplo, omitir una reglaido usar unaidduplicada, establecer minutos o segundos en un límite de hora del día, especificar una duración de la antigüedad de la actualización que no sea un múltiplo de 24 horas, incluir destinos duplicados o establecer de forma explícitaOutcomeType.DEFAULTen una regla).SecurityException: La app de llamadas no está autorizada para administrar la política del dispositivo.TimeoutException(java.util.concurrent.TimeoutException),InternalExceptiono cualquier otra subclase deAmapiSdkException: Se produjo un error interno o de IPC transitorio durante la comunicación con la app de ADP. Vuelve a intentar la llamada con una estrategia de retirada exponencial.
Estrategias de reintento
Para las excepciones causadas por una condición transitoria (como TimeoutException o
InternalException), implementa una estrategia de reintento con retirada exponencial:
- Después de la primera falla, espera un retraso inicial de 5 segundos antes de reintentar.
- Aumenta el retraso de forma exponencial en cada intento posterior (10 segundos, 20 segundos, etcétera) hasta una cantidad máxima de intentos.