You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Management API设备6步扫码注册失败问题咨询

Android 12/13/14 原生Google DPC「开机6次点击」部署失败问题

我们尝试通过Android Management API,利用「开机6次点击」的方式为Android 14(Android 12、13测试结果一致)设备部署原生Google DPC,但始终无法成功。

使用的二维码JSON载荷

{
    "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME": "com.google.android.apps.work.clouddpc/.receivers.CloudDeviceAdminReceiver",
    "android.app.extra.PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM": "MY_BASE64_CHECKSUM",
    "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION": "https://play.google.com/managed/downloadManagingApp?identifier=setup",
    "android.app.extra.PROVISIONING_SKIP_EDUCATION_SCREENS": "true",
    "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED": "true",
    "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE": {
        "com.google.android.apps.work.clouddpc.EXTRA_ENROLLMENT_TOKEN": "MY_ENROLLMENT_TOKEN"
    }
}

已知可用项

生成20位Enrollment Token的AnotherClass.GetEnrollment()方法确认正确,在afw#setup方式下可正常工作。

校验和生成方式

我们通过以下方式生成校验和:

  • 从链接https://play.google.com/managed/downloadManagingApp?identifier=setup下载文件并转为字节数组
  • 对文件进行Base64编码生成校验和

失败现象

扫描二维码后,设备输入WiFi凭据进入「准备工作设置」阶段,数分钟后提示「无法设置设备」,需重置设备。

核心疑问

  1. 当前代码是否正确?
  2. 载荷中的参数是否正确?部分资料显示应使用PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM而非android.app.extra.PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM?
  3. 是否还需要其他参数?
  4. 有资料称需用keytool生成校验和,但DPC版本随时更新,是否需在C#中动态生成?
  5. afw#setup方式无需位置/校验和即可安装原生DPC,为何该方式需要这些参数?(已尝试省略,仍无法成功)

次要问题

添加WiFi参数PROVISIONING_WIFI_SSID/PROVISIONING_WIFI_PASSWORD/PROVISIONING_WIFI_SECURITY_TYPE='WPA'时,设备提示「无法连接WiFi」(SSID和密码无误,WiFi正常),此为次要问题,当前优先解决原生DPC安装问题。

补充说明

无论是否包含PROVISIONING_SKIP_EDUCATION_SCREENS/android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,结果均一致。

完整代码

using Newtonsoft.Json;
using QRCoder; // the Nuget package that actually creates the QR code
using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Drawing;
using System.Net.Http;
using System.Security.Cryptography;
using System.Threading.Tasks;

public class ProvisioningData
{
    [JsonProperty("android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME")]
    public string DeviceAdminComponentName { get; set; }

    [JsonProperty("android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION")]
    public string DeviceAdminPackageDownloadLocation { get; set; }

    [JsonProperty("android.app.extra.PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM")]
    public string DeviceAdminSignatureChecksum { get; set; }

    [JsonProperty("android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE")]
    public AdminExtrasBundle AdminExtrasBundle { get; set; }

    [JsonProperty("android.app.extra.PROVISIONING_SKIP_EDUCATION_SCREENS")]
    public string SkipEducationScreens { get; set; }

    [JsonProperty("android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED")]
    public string LeaveAllSystemAppsEnabled { get; set; }

    //[JsonProperty("android.app.extra.PROVISIONING_WIFI_SSID")]
    //public string WifiSsid { get; set; }

    //[JsonProperty("android.app.extra.PROVISIONING_WIFI_PASSWORD")]
    //public string WifiPassword { get; set; }

    //[JsonProperty("android.app.extra.PROVISIONING_WIFI_SECURITY_TYPE")]
    //public string SecurityType { get; set; }
}

public class AdminExtrasBundle
{
    [JsonProperty("com.google.android.apps.work.clouddpc.EXTRA_ENROLLMENT_TOKEN")]
    public string EnrollmentToken { get; set; }
}

public class QRHelper
{
    private static byte[] DownloadFileBytes(string url)
    {
        using (HttpClient client = new HttpClient())
        {
            var response = client.GetAsync(url).Result;
            if (!response.IsSuccessStatusCode)
            {
                throw new Exception($"Failed to download file. Status code: {response.StatusCode}");
            }

            return response.Content.ReadAsByteArrayAsync().Result;
        }
    }

    private static string CalcChecksum(byte[] fileBytes)
    {
        using (SHA256 sha256 = SHA256.Create())
        {
            byte[] hash = sha256.ComputeHash(fileBytes);
            // Replaces & Trim() needed because apparently Android Management API expects the checksum to be in a specific Base64 URL-safe format.
            return Convert.ToBase64String(hash).Replace('+', '-').Replace('/', '_').TrimEnd('=');
        }
    }

    private static System.Drawing.Bitmap GenQrImage(string code)
    {
        QRCodeGenerator qrGenerator = new QRCodeGenerator();
        QRCodeData qrCodeData = qrGenerator.CreateQrCode(code, QRCodeGenerator.ECCLevel.Q);
        QRCode qrCode = new QRCode(qrCodeData);
        return qrCode.GetGraphic(20);
    }

    private static byte[] ImageToByte(Image img)
    {
        ImageConverter converter = new ImageConverter();
        return (byte[])converter.ConvertTo(img, typeof(byte[]));
    }

    private static string GetImageAsBase64String(byte[] bin)
    {
        if (bin != null)
        {
            return Convert.ToBase64String(bin);
        }
        else
        {
            return null;
        }
    }

    public static async Task<Tuple<string, string>> GetQrBitMap()
    {
        string code = await AnotherClass.GetEnrollment();
        string fileUrl = "https://play.google.com/managed/downloadManagingApp?identifier=setup";

        byte[] fileBytes = DownloadFileBytes(fileUrl);
        string checksum = CalcChecksum(fileBytes);

        string ssid = "MY_SSID";
        string pwd = "MY_PASSWORD";
        string securityType = "WPA";

        // Create provisioning data using a DTO
        var provisioningData = new ProvisioningData
        {
            DeviceAdminComponentName = "com.google.android.apps.work.clouddpc/.receivers.CloudDpcAdminReceiver",
            DeviceAdminSignatureChecksum = checksum,
            DeviceAdminPackageDownloadLocation = fileUrl,
            SkipEducationScreens = "true",
            LeaveAllSystemAppsEnabled = "true",
            //WifiSsid = ssid,
            //WifiPassword = pwd,
            //SecurityType = securityType,
            AdminExtrasBundle = new AdminExtrasBundle
            {
                EnrollmentToken = code
            }
        };

        // Serialize DTO to JSON using Newtonsoft.Json
        string jsonData = JsonConvert.SerializeObject(provisioningData, Formatting.None, new JsonSerializerSettings
        {
            ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
        });

        System.Drawing.Bitmap bmp = GenQrImage(jsonData);
        byte[] bytes = ImageToByte(bmp);
        string ret = GetImageAsBase64String(bytes);

        return Tuple.Create("data:image/bmp;base64," + ret, code);
    }
}

内容的提问来源于stack exchange,提问作者turtleboy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 15:57:03