简介

Safety Detect提供系统完整性检测(SysIntegrity)、应用安全检测(AppsCheck)、恶意URL检测(URLCheck)、虚假用户检测(UserDetect)、恶意Wi-Fi检测(WifiDetect),可帮助保护应用程序免受安全威胁。

SysIntegrity API:系统完整性检测API,开发者可通过该API评估其App所运行的设备环境是否安全(如设备是否被root)。

AppsCheck API:应用安全检测API,开发者可通过该API获取恶意应用列表。

URLCheck API:恶意URL检测API,开发者可通过该API来确定特定URL的威胁类型。

UserDetect API:虚假用户检测API,开发者可通过该API判断当前App的交互对象是否为虚假用户。

WifiDetect API:恶意Wi-Fi检测API,开发者可通过该API检测当前尝试连接的Wi-Fi是否安全。

您将建立什么

在这个Codelab中,您将使用已经创建好的Demo Project实现对华为系统完整性检测应用安全检测、恶意URL检测、虚假用户检测、恶意Wi-Fi检测的API调用,通过Demo Project你可以体验到:

系统完整性检测:

应用安全检测:

恶意URL检测:

虚假用户检测:

恶意Wi-Fi检测:

您将会学到什么

硬件要求

软件要求

集成HUAWEI HMS Core能力,需要完成以下准备工作:

具体操作,请按照《HUAWEI HMS Core集成准备》中详细说明来完成。

打开服务开关

在项目的应用信息页面,选择API管理页签。

打开"Safety Detect"服务开关

添加当前应用的AGC配置文件

  1. 登录AppGallery Connect 网站,单击我的项目
  2. 在项目列表中找到您的项目,在项目中单击需要集成HMS Core SDK的应用。
  3. 项目设置页面的应用区域,单击agconnect-services.json下载配置文件。
  4. 将下载的"agconnect-services.json"文件移至Android Studio开发工程app的根目录下。

添加SDK依赖

1.打开应用级的build.gradle文件。

2.在"dependencies"中添加编译依赖。

dependencies { // Add the following line implementation 'com.huawei.hms:safetydetect:5.0.3.300' }

配置混淆脚本

如果开发者使用了AndResGuard,需要在混淆配置文件中加入AndResGuard允许清单。

"R.string.hms*", "R.string.connect_server_fail_prompt_toast", "R.string.getting_message_fail_prompt_toast", "R.string.no_available_network_prompt_toast", "R.string.third_app_*", "R.string.upsdk_*", "R.layout.hms*", "R.layout.upsdk_*", "R.drawable.upsdk*", "R.color.upsdk*", "R.dimen.upsdk*", "R.style.upsdk*"

系统完整性检测

调用SysIntefrity API主要分为四个步骤:

  1. 获取nonce。
  2. 请求 Safety Detect SysIntegrity。
  3. 在您的服务器中验证检测结果(签名,证书链,域名)。

获取nonce

在调用Safety Detect SysIntegrity API时,您必须传入一个nonce值。在检测结果中会包含这个nonce值,您可以通过校验这个nonce来确定返回结果能够对应您的请求,并且没有被重放攻击。

一个 nonce 值只能被使用一次,nonce 值必须为 16至66字节之间。推荐的做法是从发送到您的服务器的数据中派生 nonce 值。

请求Safety Detect SysIntegrity

Safety Detect SysIntegrity接口有两个参数,第一个是上一小节我们获取的nonce值,第二个是APP ID。您可以通过如下方式获取APP ID,将APP ID作为第二个参数传入接口:

  1. 登录AppGallery Connect网站,点击"我的项目"。
  2. 在项目列表中找到您的项目,在项目中点击需要配置签名证书指纹的应用。
  3. 在"项目设置 > 常规"页面的"应用"区域,可以查看应用的APP ID。

Java语言:

private void invokeSysIntegrity() { SafetyDetectClient mClient = SafetyDetect.getClient(Mainactivity.this); // TODO(developer): Change the nonce generation to include your own, used once value, // ideally from your remote server. byte[] nonce = new byte[24]; try { SecureRandom random; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { random = SecureRandom.getInstanceStrong(); } else { random = SecureRandom.getInstance("SHA1PRNG"); } random.nextBytes(nonce); } catch (NoSuchAlgorithmException e) { Log.e(TAG, e.getMessage()); } // TODO(developer): Change your app ID. You can obtain your app ID in AppGallery Connect. Task task = mClient.sysIntegrity(nonce,"3*******"); task.addOnSuccessListener(new OnSuccessListener<SysIntegrityResp>() { @Override public void onSuccess(SysIntegrityResp response) { // Indicates communication with the service was successful. // Use response.getResult() to get the result data. String jwsStr = response.getResult(); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { // An error occurred while communicating with the service. if (e instanceof ApiException) { // An error with the HMS API contains some // additional details. ApiException apiException = (ApiException) e; // You can retrieve the status code using // the apiException.getStatusCode() method. Log.e(TAG, "Error: " + SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getMessage()); } else { // A different, unknown type of error occurred. Log.e(TAG, "ERROR:" + e.getMessage()); } } }); }

Kotlin语言:

private fun invokeSysIntegrity() { // TODO(developer): Change the nonce generation to include your own value. val nonce = ByteArray(24) try { val random: SecureRandom = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { SecureRandom.getInstanceStrong() } else { SecureRandom.getInstance("SHA1PRNG") } random.nextBytes(nonce) } catch (e: NoSuchAlgorithmException) { Log.e(TAG, e.message) } // TODO(developer): Change your app ID. You can obtain your app ID in AppGallery Connect. SafetyDetect.getClient(activity) .sysIntegrity(nonce, APP_ID) .addOnSuccessListener { response -> // Indicates communication with the service was successful. // Use response.getResult() to get the result data. val jwsStr = response.result // Process the result data here val jwsSplit = jwsStr.split(".").toTypedArray() val jwsPayloadStr = jwsSplit[1] val payloadDetail = String(Base64.decode(jwsPayloadStr.toByteArray(StandardCharsets.UTF_8), Base64.URL_SAFE), StandardCharsets.UTF_8) try { val jsonObject = JSONObject(payloadDetail) val basicIntegrity = jsonObject.getBoolean("basicIntegrity") fg_button_sys_integrity_go.setBackgroundResource(if (basicIntegrity) R.drawable.btn_round_green else R.drawable.btn_round_red) fg_button_sys_integrity_go.setText(R.string.rerun) val isBasicIntegrity = basicIntegrity.toString() val basicIntegrityResult = "Basic Integrity: $isBasicIntegrity" fg_payloadBasicIntegrity.text = basicIntegrityResult if (!basicIntegrity) { val advice = "Advice: " + jsonObject.getString("advice") fg_payloadAdvice.text = advice } } catch (e: JSONException) { val errorMsg = e.message Log.e(TAG, errorMsg ?: "unknown error") } } .addOnFailureListener { e -> // There was an error communicating with the service. val errorMsg: String? errorMsg = if (e is ApiException) { // An error with the HMS API contains some additional details. val apiException = e as ApiException SafetyDetectStatusCodes.getStatusCodeString(apiException.statusCode) + ": " + apiException.message // You can use the apiException.getStatusCode() method to get the status code. } else { // unknown type of error has occurred. e.message } Log.e(TAG, errorMsg) Toast.makeText(activity?.applicationContext, errorMsg, Toast.LENGTH_SHORT).show() fg_button_sys_integrity_go.setBackgroundResource(R.drawable.btn_round_yellow) fg_button_sys_integrity_go.setText(R.string.rerun) } }

Safety-detect-App-sample

服务端

建议对JwsResult的处理流程如下,其中校验证书和签名的部分,需要放在您App的服务器端进行。

具体校验逻辑请获取示例代码,请下载

safety-detect-server-sample

测试

安装测试APK,点击接口调用

应用安全检测

示例代码为您演示如何完成应用安全检测API调用并获取检测结果。

客户端

您可直接调用Apps Check getMaliciousApps获取恶意应用列表
Java语言:

private void invokeGetMaliciousApps() { SafetyDetectClient appsCheckClient = SafetyDetect.getClient(MainActivity.this); Task task = appsCheckClient.getMaliciousAppsList(); task.addOnSuccessListener(new OnSuccessListener<MaliciousAppsListResp>() { @Override public void onSuccess(MaliciousAppsListResp maliciousAppsListResp) { // Indicates communication with the service was successful. // Use resp.getMaliciousApps() to get malicious apps data. List<MaliciousAppsData> appsDataList = maliciousAppsListResp.getMaliciousAppsList(); // Indicates get malicious apps was successful. if (maliciousAppsListResp.getRtnCode() == CommonCode.OK) { if (appsDataList.isEmpty()) { // Indicates there are no known malicious apps. Log.i(TAG, "There are no known potentially malicious apps installed."); } else { Log.i(TAG, "Potentially malicious apps are installed!"); for (MaliciousAppsData maliciousApp : appsDataList) { Log.i(TAG, "Information about a malicious app:"); // Use getApkPackageName() to get APK name of malicious app. Log.i(TAG, "APK: " + maliciousApp.getApkPackageName()); // Use getApkSha256() to get APK sha256 of malicious app. Log.i(TAG, "SHA-256: " + maliciousApp.getApkSha256()); // Use getApkCategory() to get category of malicious app. // Categories are defined in AppsCheckConstants Log.i(TAG, "Category: " + maliciousApp.getApkCategory()); } } } else { Log.e(TAG, "getMaliciousAppsList fialed: " + maliciousAppsListResp.getErrorReason()); } } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { // An error occurred while communicating with the service. if (e instanceof ApiException) { // An error with the HMS API contains some // additional details. ApiException apiException = (ApiException) e; // You can retrieve the status code using the apiException.getStatusCode() method. Log.e(TAG, "Error: " + SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getStatusMessage()); } else { // A different, unknown type of error occurred. Log.e(TAG, "ERROR: " + e.getMessage()); } } }); }

Kotlin语言:

private fun getMaliciousApps() { SafetyDetect.getClient(activity) .maliciousAppsList .addOnSuccessListener { maliciousAppsListResp -> val appsDataList: List<MaliciousAppsData> = maliciousAppsListResp.maliciousAppsList if (maliciousAppsListResp.rtnCode == CommonCode.OK) { if (appsDataList.isEmpty()) { val text = "No known potentially malicious apps are installed." Toast.makeText(activity!!.applicationContext, text, Toast.LENGTH_SHORT).show() } else { for (maliciousApp in appsDataList) { Log.e(TAG, "Information about a malicious app:") Log.e(TAG, " APK: " + maliciousApp.apkPackageName) Log.e(TAG, " SHA-256: " + maliciousApp.apkSha256) Log.e(TAG, " Category: " + maliciousApp.apkCategory) } val maliciousAppAdapter: ListAdapter = MaliciousAppsDataListAdapter(appsDataList, requireContext()) fg_list_app.adapter = maliciousAppAdapter } } else { val msg = ("Get malicious apps list failed! Message: " + maliciousAppsListResp.errorReason) Log.e(com.huawei.hms.safetydetect.sample.SafetyDetectAppsCheckAPIFragment.TAG, msg) Toast.makeText(activity!!.applicationContext, msg, Toast.LENGTH_SHORT).show() } } .addOnFailureListener { e -> // There was an error communicating with the service. val errorMsg: String? errorMsg = if (e is ApiException) { // An error with the HMS API contains some additional details. val apiException = e as ApiException SafetyDetectStatusCodes.getStatusCodeString(apiException.statusCode) + ": " + apiException.message // You can use the apiException.getStatusCode() method to get the status code. } else { // Unknown type of error has occurred. e.message } val msg = "Get malicious apps list failed! Message: $errorMsg" Log.e(com.huawei.hms.safetydetect.sample.SafetyDetectAppsCheckAPIFragment.TAG, msg) Toast.makeText(activity!!.applicationContext, msg, Toast.LENGTH_SHORT).show() } }

测试

点击"GET HARMFUL APPS LIST":

恶意URL检测

调用URLCheck API主要分为三个步骤:

  1. 初始化URLCheck API。
  2. 请求网址检测。
  3. 关闭网址检测会话。

初始化URLCheck API

在使用URLCheck API前,必须调用initUrlCheck()方法进行接口初始化,并且需要等待初始化完成后再进行接下来的接口调用,示例代码如下:

Java语言:

SafetyDetectClient client = SafetyDetect.getClient(getActivity()); client.initUrlCheck();

Kotlin语言:

client = SafetyDetect.getClient(activity) client.initUrlCheck()

请求网址检测

指定关注的威胁类型,您可以将关注的威胁类型作为网址检测API的参数。其中,UrlCheckThreat类中的常量包含了当前支持的威胁类型:

public class UrlCheckThreat { //此类型URL被标记为包含潜在有害应用的页面的URL(篡改首页、网页挂马、恶意应用下载链接等) public static final int MALWARE = 1; // 这种类型的URL被标记为钓鱼、欺诈网站 public static final int PHISHING = 3; }

发起网址检测请求,待检测的URL包含协议、主机、路径,不包含查询参数。调用API示例代码如下:

Java语言:

String url = "https://developer.huawei.com/consumer/cn/"; SafetyDetect.getClient(this).urlCheck(url, appId, UrlCheckThreat.MALWARE, UrlCheckThreat.PHISHING).addOnSuccessListener(this, new OnSuccessListener<UrlCheckResponse >(){ @Override public void onSuccess(UrlCheckResponse urlResponse) { if (urlResponse.getUrlCheckResponse().isEmpty()) { // 无威胁 } else { // 存在威胁! } } }).addOnFailureListener(this, new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // 与服务通信发生错误. if (e instanceof ApiException) { // HMS发生错误的状态码及对应的错误详情. ApiException apiException = (ApiException) e; Log.d(TAG, "Error: " + CommonStatusCodes.getStatusCodeString(apiException.getStatusCode())); // 注意:如果状态码是SafetyDetectStatusCode.CHECK_WITHOUT_INIT, // 这意味着您未调用initUrlCheck()或者调用未完成就发起了网址检测请求, // 或者在初始化过程中发生了内部错误需要重新进行初始化,需要重新调用initUrlCheck() } else { // 发生未知类型的异常. Log.d(TAG, "Error: " + e.getMessage()); } } });

Kotlin语言:

val url = "https://developer.huawei.com/consumer/cn/" client.urlCheck( url, APP_ID, // Specify url threat type UrlCheckThreat.MALWARE, UrlCheckThreat.PHISHING ).addOnSuccessListener { val list = it.urlCheckResponse if (list.isEmpty()) { // 无威胁 } else { // 存在威胁! } }.addOnFailureListener { // 与服务通信发生错误. if (it is ApiException) { // HMS发生错误的状态码及对应的错误详情. val apiException = it Log.d( TAG, "Error: " + CommonStatusCodes.getStatusCodeString(apiException.statusCode) ) // 注意:如果状态码是SafetyDetectStatusCode.CHECK_WITHOUT_INIT, // 这意味着您未调用initUrlCheck()或者调用未完成就发起了网址检测请求, // 或者在初始化过程中发生了内部错误需要重新进行初始化,需要重新调用initUrlCheck() } else { // 发生未知类型的异常. Log.d(TAG, "Error: " + it.message) } }

获取网址检测的响应,调用返回对象URLCheckResponse的getUrlCheckResponse()方法,返回List<UrlCheckThreat>,包含检测到的URL威胁类型。若该列表为空,则表示未检测到威胁,否则,可调用UrlCheckThreat中的getUrlCheckResult()取得具体的威胁代码。示例代码如下:

Java语言:

final EditText testRes = getActivity().findViewById(R.id.fg_call_urlResult); List<UrlCheckThreat> list = urlCheckResponse.getUrlCheckResponse(); if (list.isEmpty()) { testRes.setText("ok"); } else{ for (UrlCheckThreat threat : list) { int type = threat.getUrlCheckResult(); } }

Kotlin语言:

val list = it.urlCheckResponse if (list.isEmpty()) { fg_call_urlResult.text = Editable.Factory.getInstance().newEditable("ok") } else { for (threat in list) { val type = threat.urlCheckResult } }

关闭网址检测会话

如果您的应用不再使用或长时间不再调用网址检测接口,请调用shutdownUrlCheck()方法关闭网址检测会话,释放资源。

SafetyDetect.getClient(this).shutdownUrlCheck();

测试

安装测试APK,以此点击接口调用

虚假用户检测

示例代码分为两个部分:客户端部分展示API调用并获取检测结果;服务端部分展示如何获取虚假用户检测最终结果。

调用UserDetect API

调用UserDetect API分为两步:

  1. 发起检测请求:调用UserDetect API的userDetection()方法以发起虚假用户检测请求,API会返回一个response token。
  2. 获取检测结果:使用步骤1获取response token调用云端verify获取检测结果:

初始化Bot行为检测

UserDetect提供了行为检测能力,如果您希望您的虚假用户检测功能具备该能力,可以通过调用initUserDetect()接口初始化虚假用户检测,示例代码如下:
Java语言:

// Replace with your activity or context as a parameter. SafetyDetectClient client = SafetyDetect.getClient(MainActivity.this); client.initUserDetect().addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void v) { // Indicates communication with the service was successful. } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { // There was an error communicating with the service. } });

Kotlin语言:

private fun initUserDetect() { // Replace with your activity or context as a parameter. val client = SafetyDetect.getClient(this@MainActivity) client.initUserDetect().addOnSuccessListener { // Indicates communication with the service was successful. }.addOnFailureListener { // There was an error communicating with the service. } }

发起检测请求

您需要通过调用userDetection发起检测。通常在用户点击UI组件(如一个按钮)后触发调用该方法。调用userDetection方法时,您需要做以下事情:

  1. 传入您申请的appId作为该方法的参数。
  2. 分别添加OnSuccessListener和OnFailureListener实例作为监听器。
  3. 重写onSuccess、onFailure,处理对应的结果。

调用该方法的示例代码如下所示:

Java语言:

public void onClick(View v) { SafetyDetectClient client = SafetyDetect.getClient(getActivity()); String appId = "your_app_id"; client.userDetection(appId) .addOnSuccessListener(new OnSuccessListener<UserDetectResponse>() { @Override public void onSuccess(UserDetectResponse userDetectResponse) { // Indicates communication with the service was successful. String responseToken = userDetectResponse.getResponseToken(); if (!responseToken.isEmpty()) { // Send the response token to your app server, and call the cloud API of HMS Core on your server to obtain the fake user detection result. } } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { // There was an error communicating with the service. String errorMsg; if (e instanceof ApiException) { // An error with the HMS API contains some additional details. // You can use the apiException.getStatusCode() method to get the status code. ApiException apiException = (ApiException) e; errorMsg = SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getMessage(); } else { // Unknown type of error has occurred. errorMsg = e.getMessage(); } Log.i(TAG, "User detection fail. Error info: " + errorMsg); } }); }

Kotlin语言:

override fun onClick(v: View?) { val client = SafetyDetect.getClient(activity) val appId = "your_app_id" client.userDetection(appId) .addOnSuccessListener { userDetectResponse -> // Indicates communication with the service was successful. val responseToken = userDetectResponse.responseToken if (responseToken.isNotEmpty()) { // Send the response token to your app server, and call the cloud API of HMS Core on your server to obtain the fake user detection result. } } .addOnFailureListener { // There was an error communicating with the service. val errorMsg: String? = if (it is ApiException) { // An error with the HMS API contains some additional details. // You can use the apiException.getStatusCode() method to get the status code. (SafetyDetectStatusCodes.getStatusCodeString(it.statusCode) + ": " + it.message) } else { // Unknown type of error has occurred. it.message } Log.i(TAG, "User detection fail. Error info: $errorMsg") } }

获取检测结果

服务端主要分为两步:

  1. 获取accessToken。
  2. 调用云侧API获取检测结果。

具体步骤如下:

获取accessToken
详细请参见开放平台鉴权相关内容描述
调用云侧API获取检测结果:调用云侧获取结果。消息请求示例如下所示。

POST https://hirms.cloud.huawei.com/rms/v1/userRisks/verify?appId=123456 HTTP/1.1 Content-Type: application/json;charset=utf-8 {"accessToken":"AAWWHI94sgUR2RU5_P1ZptUiwLq7W8XWJO2LxaAPuXw4_HOJFXnBlN-q5_3bwlxVW_SHeDPx_s5bWW-9DjtWZsvcm9CwXe1FHJg0u-D2pcQPcb3sTxDTJeiwEb9WBPl_9w","response":"1_55d74c04eab36a0a018bb7a879a6f49f072b023690cba936" }

更多关于请求接口的详细信息可以参照API参考

App端关闭虚假用户检测:

您可以通过调用shutdownUserDetect()接口关闭虚假用户检测功能,释放资源,示例代码如下:
Java语言:

// Replace with your activity or context as a parameter. SafetyDetectClient client = SafetyDetect.getClient(MainActivity.this); client.shutdownUserDetect().addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void v) { // Indicates communication with the service was successful. } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { // There was an error communicating with the service. } });

Kotlin语言:

private fun shutdownUserDetect() { // Replace with your activity or context as a parameter. val client = SafetyDetect.getClient(this@MainActivity) client.shutdownUserDetect() .addOnSuccessListener { // Indicates communication with the service was successful. }.addOnFailureListener { // There was an error communicating with the service. } }

测试

安装测试APK,以此点击接口调用

恶意Wi-Fi检测

示例代码为您演示如何完成恶意Wi-Fi检测API调用并获取检测结果。

客户端

您可直接调用SafetyDetectClient的getWifiDetectStatus()获取恶意Wi-Fi检测结果。
Java语言:

private void invokeGetWifiDetectStatus() { Log.i(TAG, "Start to getWifiDetectStatus!"); SafetyDetectClient wifidetectClient = SafetyDetect.getClient(getApplicationContext()); Task task = wifidetectClient.getWifiDetectStatus(); task.addOnSuccessListener(new OnSuccessListener<WifiDetectResponse>() { @Override public void onSuccess(WifiDetectResponse wifiDetectResponse) { int wifiDetectStatus = wifiDetectResponse.getWifiDetectStatus(); Log.i(TAG, "\n-1 :获取WiFi状态失败\n" + "0 :未连接WIFI\n" + "1 :当前连接的WIFI安全\n" + "2 :当前连接的WIFI不安全."); Log.i(TAG, "wifiDetectStatus is: " + wifiDetectStatus); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { if (e instanceof ApiException) { ApiException apiException = (ApiException) e; Log.e(TAG, "Error: " + apiException.getStatusCode() + ":" + SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getStatusMessage()); } else { Log.e(TAG, "ERROR! " + e.getMessage()); } } }); }

Kotlin语言:

private val wifiDetectStatus: Unit private get() { SafetyDetect.getClient(activity) .wifiDetectStatus .addOnSuccessListener { wifiDetectResponse -> val wifiDetectStatus = wifiDetectResponse.wifiDetectStatus val wifiDetectView = "WifiDetect status: $wifiDetectStatus" fg_wifidetecttextView.text = wifiDetectView } .addOnFailureListener { e -> // There was an error communicating with the service. val errorMsg: String? errorMsg = if (e is ApiException) { // An error with the HMS API contains some additional details. val apiException = e (SafetyDetectStatusCodes.getStatusCodeString(apiException.statusCode) + ": " + apiException.message) // You can use the apiException.getStatusCode() method to get the status code. } else { // Unknown type of error has occurred. e.message } val msg = "Get wifiDetect status failed! Message: $errorMsg" Log.e(TAG, msg) fg_wifidetecttextView.text = msg } }

测试

点击"GET WIFIDETECT STATUS":

干得好,您已经成功完成了Codelab并学到了:

您可以阅读下面链接,了解更多相关的信息。

相关文档

您可以在Github上下载源码: https://github.com/HMS-Core/hms-safetydetect-demo-android

已复制代码