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凭据进入「准备工作设置」阶段,数分钟后提示「无法设置设备」,需重置设备。
核心疑问
- 当前代码是否正确?
- 载荷中的参数是否正确?部分资料显示应使用
PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM而非android.app.extra.PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM? - 是否还需要其他参数?
- 有资料称需用keytool生成校验和,但DPC版本随时更新,是否需在C#中动态生成?
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
相关产品推荐
相关产品推荐

