Mengonfigurasi Kontrol Update Terpadu dengan AMAPI SDK

Panduan ini menjelaskan cara menggunakan Android Management API (AMAPI) SDK di Pengontrol Kebijakan Perangkat (DPC) kustom untuk mengonfigurasi Kontrol Update Terpadu (UUC) di perangkat milik perusahaan.

Dengan menggunakan AMAPI SDK, DPC kustom Anda mengirimkan kebijakan UUC langsung ke aplikasi Android Device Policy (ADP) di perangkat. ADP mengevaluasi aturan kebijakan berdasarkan status perangkat saat ini dan menginstruksikan Google Play dan klien OTA OEM tentang cara menangani update yang tertunda.

Sebelum memulai, baca Unified Update Controls untuk mempelajari cara kerja aturan, target, predikat, dan hasil, serta batasan yang berlaku untuk semua integrasi UUC.

Prasyarat

Sebelum mengintegrasikan UUC ke DPC kustom, verifikasi persyaratan berikut:

Mengintegrasikan dengan AMAPI SDK di DPC kustom Anda

Untuk mengonfigurasi kebijakan UUC di perangkat, DPC kustom Anda harus terintegrasi dengan AMAPI SDK. Untuk mengetahui petunjuk tentang cara menambahkan library dan mengonfigurasi manifes aplikasi, lihat panduan integrasi AMAPI SDK.

Langkah-langkah untuk mengonfigurasi Kontrol Update Terpadu

Untuk mengonfigurasi kebijakan UUC dari DPC kustom, pastikan lingkungan klien di perangkat sudah siap dan yang terbaru sebelum mengirim permintaan kebijakan ke ADP:

  1. Memverifikasi lingkungan klien
  2. Menyiapkan lingkungan klien
  3. Menetapkan, mengambil, dan menghapus kebijakan update

Memverifikasi lingkungan klien

Contoh berikut menunjukkan cara memanggil getEnvironment (di Kotlin) atau getEnvironmentAsync (di Java) dengan Role.RoleType.DEVICE_POLICY_CONTROLLER untuk memeriksa status aplikasi ADP. Jika lingkungannya adalah READY dan UP_TO_DATE, DPC kustom Anda dapat melanjutkan untuk mengonfigurasi kebijakan UUC menggunakan PolicyClient. Jika tidak, panggil prepareEnvironment untuk menginstal atau mengupdate aplikasi ADP (lihat Menyiapkan lingkungan klien).

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 {
        ImmutableList roles = 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);
    }
}

Menyiapkan lingkungan klien

Jika lingkungan di perangkat belum siap atau belum diupdate, panggil prepareEnvironment sebelum menggunakan PolicyClient.

Saat dipanggil dari DPC kustom dengan Role.RoleType.DEVICE_POLICY_CONTROLLER dan nama komponen DeviceAdminReceiver DPC Anda diteruskan ke setAdmin(), prepareEnvironment akan otomatis menginstal aplikasi ADP (jika belum diinstal) dan mengupdate aplikasi ADP dan Google Play Store tanpa interaksi pengguna. Untuk mengetahui informasi selengkapnya tentang persiapan lingkungan dalam alur DPC kustom, lihat Persiapan lingkungan dan pendaftaran pengguna.

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 {
        ImmutableList roles = 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);
    }
}

Menetapkan, mengambil, dan menghapus kebijakan update

Setelah lingkungan siap, gunakan PolicyClient (yang dibuat dengan PolicyClientFactory.create(context)) untuk menerapkan Policy yang berisi aturan UnifiedUpdatePolicy, memeriksa kebijakan aktif, atau menghapus kebijakan UUC dari perangkat.

Rekomendasi penggunaan PolicyClient

Membuat instance PolicyClient akan membuat saluran komunikasi dengan aplikasi ADP. Untuk mengoptimalkan performa, sebaiknya buat satu instance PolicyClient menggunakan PolicyClientFactory.create() dan simpan dalam cache untuk digunakan kembali di seluruh aplikasi Anda. Hindari membuat instance PolicyClient baru saat instance lain aktif, karena tindakan ini dapat memaksa saluran komunikasi yang ada ditutup, yang menimbulkan overhead dan dapat menyebabkan kebocoran resource.

Menetapkan kebijakan update

Untuk menerapkan aturan UUC, buat Policy.UnifiedUpdatePolicy yang berisi daftar objek Rule, lampirkan ke Policy di dalam UpdatePolicyRequest, dan panggil updatePolicy (di Kotlin) atau updatePolicyFuture (di 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;

ListenableFuture setUucPolicy(
        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);
    }
}

Mengambil kebijakan update aktif

Untuk memeriksa kebijakan UUC aktif di perangkat, buat GetPolicyRequest dan panggil getPolicy (di Kotlin) atau getPolicyFuture (di Java). Gunakan hasUnifiedUpdatePolicy() pada Policy yang ditampilkan untuk memeriksa apakah kebijakan UUC telah dikonfigurasi:

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();
        ListenableFuture getActivePolicy =
                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);
    }
}

Menghapus kebijakan update

Untuk menghapus UnifiedUpdatePolicy dari perangkat tanpa menghapus kolom lain di Policy, ambil Policy saat ini menggunakan getPolicy, panggil currentPolicy.toBuilder().clearUnifiedUpdatePolicy().build(), dan terapkan Policy yang diperbarui:

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;

ListenableFuture clearUucPolicy(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);
    }
}

Prioritas SystemUpdatePolicy AOSP

Dalam DPC kustom, Compatibility Definition Document (CDD) Android mengharuskan perangkat mematuhi SystemUpdatePolicy AOSP yang ditetapkan melalui DevicePolicyManager.setSystemUpdatePolicy.

Akibatnya, perilaku update OTA OEM bergantung pada apakah SystemUpdatePolicy AOSP aktif di perangkat:

  • Kebijakan UUC dan SystemUpdatePolicy AOSP ditetapkan: SystemUpdatePolicy AOSP diprioritaskan untuk update OTA OEM, dan target UUC OS_UPDATE_WITH_API_LEVEL_CHANGE dan OS_UPDATE_WITHOUT_API_LEVEL_CHANGE diabaikan. Hapus SystemUpdatePolicy AOSP di DPC kustom Anda jika Anda ingin UUC mengelola update OTA OEM.
  • Hanya kebijakan UUC yang ditetapkan (AOSP SystemUpdatePolicy dihapus): Agen OTA OEM menerapkan kebijakan UUC untuk update OTA OEM di perangkat yang didukung.
  • Perangkat yang belum mendukung UUC untuk update OTA OEM: Di perangkat yang dukungan OTA OEM-nya memerlukan Android 17 (API level 37), Anda dapat menggunakan UUC untuk mengontrol update sistem Google Play (GOOGLE_PLAY_SYSTEM_UPDATE) dan layanan sistem Google (GOOGLE_SYSTEM_SERVICES dan SINGLE_APP) sambil terus menggunakan SystemUpdatePolicy AOSP untuk update OTA OEM.

Anda dapat memeriksa apakah SystemUpdatePolicy AOSP aktif di perangkat menggunakan 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;
}

Contoh konfigurasi kebijakan

Contoh berikut menunjukkan cara membuat aturan UUC di Kotlin dan Java serta menerapkannya menggunakan helper setUucPolicy yang ditentukan di Menetapkan, mengambil, dan menghapus kebijakan update. Untuk mengetahui arti setiap kolom, lihat Memahami kebijakan UUC. Skenario yang sama tersedia sebagai JSON di Unified Update Controls.

Memblokir update OS utama selama jam kerja

Aturan berikut memblokir update OS utama (OS_UPDATE_WITH_API_LEVEL_CHANGE) dari pukul 09.00 hingga 17.00, Senin hingga Jumat:

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));

Memaksa update OS berdasarkan usia update

Aturan berikut memaksa update OS utama (OS_UPDATE_WITH_API_LEVEL_CHANGE) dengan batas atas usia update 7 hari:

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));

Memblokir update untuk satu aplikasi layanan sistem Google

Untuk mengontrol paket tertentu yang merupakan bagian dari layanan sistem Google—seperti Android System WebView (com.google.android.webview)—tetapkan jenis target ke SINGLE_APP dan berikan objek SingleApp dengan nama paket:

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));

Menggabungkan beberapa aturan di semua target update

Karena aturan dievaluasi secara berurutan, Anda dapat menggabungkan beberapa aturan untuk memblokir update selama periode tertentu (seperti jam kerja atau sebelum tanggal kalender target) dan memaksa update di luar periode tersebut.

Contoh berikut menerapkan dua aturan berurutan di keempat target kategori (OS_UPDATE_WITH_API_LEVEL_CHANGE, OS_UPDATE_WITHOUT_API_LEVEL_CHANGE, GOOGLE_SYSTEM_SERVICES, dan GOOGLE_PLAY_SYSTEM_UPDATE):

  1. Blokir semua update selama jam kerja (Senin–Jumat, pukul 09.00 hingga 17.00).
  2. Di luar jam kerja, paksa update yang telah tersedia hingga 7 hari.

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.
List allTargets = 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));

Membatasi update hingga tanggal kalender

Anda dapat menggunakan setDateLowerBound dan setDateUpperBound di Predicate.Builder untuk membatasi atau mengizinkan update dalam rentang tanggal kalender tertentu di zona waktu lokal perangkat:

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));

Menangani error

Jika panggilan PolicyClient gagal, DPC kustom Anda akan menerima pengecualian (yang ditampilkan langsung oleh metode suspend Kotlin getPolicy dan updatePolicy, atau digabungkan sebagai penyebab ExecutionException di getPolicyFuture dan updatePolicyFuture). Pengecualian umum meliputi:

  • ApiLevelException: Perangkat menjalankan versi Android yang lebih rendah dari Android 13 (API level 33).
  • DeviceNotManagedException: Perangkat tidak dikelola sebagai perangkat yang dikelola sepenuhnya (Pemilik Perangkat) atau perangkat milik perusahaan dengan profil kerja (COPE).
  • AndroidDevicePolicyAppNotInstalledException, AndroidDevicePolicyAppDisabledException, atau AndroidDevicePolicyAppNotUpdatedException: Aplikasi ADP tidak diinstal, dinonaktifkan, atau menjalankan versi lama yang tidak mendukung PolicyClient. Panggil prepareEnvironment untuk menginstal atau mengupdate aplikasi ADP.
  • InvalidArgumentException: Permintaan salah format atau berisi aturan kebijakan yang tidak valid (misalnya, menghilangkan aturan id atau menggunakan id duplikat, menetapkan menit atau detik pada batas waktu, menentukan durasi usia update yang bukan kelipatan 24 jam, menyertakan target duplikat, atau menetapkan OutcomeType.DEFAULT secara eksplisit dalam aturan).
  • SecurityException: Aplikasi panggilan tidak diizinkan untuk mengelola kebijakan perangkat.
  • TimeoutException (java.util.concurrent.TimeoutException), InternalException, atau subclass lain dari AmapiSdkException: Terjadi IPC sementara atau error internal saat berkomunikasi dengan aplikasi ADP. Coba lagi panggilan menggunakan strategi backoff eksponensial.

Strategi percobaan ulang

Untuk pengecualian yang disebabkan oleh kondisi sementara (seperti TimeoutException atau InternalException), terapkan strategi percobaan ulang dengan backoff eksponensial:

  1. Setelah kegagalan pertama, tunggu penundaan awal selama 5 detik sebelum mencoba lagi.
  2. Tingkatkan penundaan secara eksponensial pada setiap percobaan berikutnya (10 detik, 20 detik, dan seterusnya) hingga jumlah percobaan maksimum.