[Huawei Developers] Basavaraj.navi:
Introduction​

In this article, we will learn how to integrate Huawei Safety Detect in Pygmy collection finance application.

What is Safety detect?

Safety Detect builds robust security capabilities, including system integrity check (SysIntegrity), app security check (AppsCheck), malicious URL check (URLCheck), fake user detection (UserDetect), and malicious Wi-Fi detection (WifiDetect), into your app, effectively protecting it against security threats.

Why do we need to use safety detect?

Mobile applications capture almost 90% of people’s time on mobile devices, while the rest of the time is spent browsing the web. So basically now a day’s mobile usage is more than the web. Since all the users are using smart mobile phones for daily needs like, reading news, email, online shopping, booking taxi, and wallets to pay, educational and Finance & Banking apps etc. Even for banking transaction there was time people use to stand in the queue to deposit or withdraw, but now everything can be done in mobile application with just 2 to 3 clicks. Since everything happening over the phone definitely we should provide security to mobile apps.

Now let’s see what all are the security features provided by Huawei Safety detect kit.

SysIntegrity

AppsCheck

URLCheck

UserDetect

WifiDetect

Integration of Safety detect

1. Configure application on the AGC.

2. Client application development process.

How to integrate Huawei Safety Detect in Android finance application?

To achieve this you need to follow couple of steps as follows.

1. Configure application on the AGC.

2. Client application development process.

Configure application on the AGC

Follow the steps.

Step 1: We need to register as a developeraccount in AppGallery Connect. If you are already developer ignore this step.

Step 2: Create an app by referring to Creating a Project and Creating an App in the Project

Step 3: Set the data storage location based on current location.

Step 4: Enabling Analytics Kit. Project setting > Manage API > Enable analytics kit toggle button.

Step 5: Generating a Signing Certificate Fingerprint.

Step 6: Configuring the Signing Certificate Fingerprint.

Step 7: Download your agconnect-services.json file, paste it into the app root directory.

Client application development process

Follow the steps.

Step 1: Create Android application in the Android studio (Any IDE which is your favorite)

Step 2: Add the App level gradle dependencies. Choose inside project Android > app > build.gradle.

apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'
dependencies {
implementation 'com.huawei.hms:safetydetect:5.2.0.300'
}

Root level gradle dependencies.

maven { url 'https://developer.huawei.com/repo/' }
classpath 'com.huawei.agconnect:agcp:1.4.1.300'

Step 3: Build Application.

SysIntegrity: Checks whether the device is secure( e.g. whether it is rooted)

import android.app.AlertDialog;
import android.content.Context;
import android.util.Base64;
import android.util.Log;

import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hmf.tasks.Task;
import com.huawei.hms.common.ApiException;
import com.huawei.hms.support.api.entity.safetydetect.SysIntegrityResp;
import com.huawei.hms.support.api.safetydetect.SafetyDetect;
import com.huawei.hms.support.api.safetydetect.SafetyDetectClient;
import com.huawei.hms.support.api.safetydetect.SafetyDetectStatusCodes;

import org.json.JSONException;
import org.json.JSONObject;

import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

public class SysIntegrity {

private static final String TAG= "Safety SysIntegrity";
Context context;
String APP_ID;
private SafetyDetectClient client;

public SysIntegrity(SafetyDetectClient client,Context context, String APP_ID) {
this.context = context;
this.APP_ID = APP_ID;
this.client=client;
}
public void invoke() {
// 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 = client.sysIntegrity(nonce, APP_ID);
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();
String[] jwsSplit = jwsStr.split("\\.");
String jwsPayloadStr = jwsSplit[1];
String payloadDetail = new String(Base64.decode(jwsPayloadStr.getBytes(StandardCharsets.UTF_8), Base64.URL_SAFE), StandardCharsets.UTF_8);
try {
final JSONObject jsonObject = new JSONObject(payloadDetail);
final boolean basicIntegrity = jsonObject.getBoolean("basicIntegrity");
String isBasicIntegrity = String.valueOf(basicIntegrity);
String basicIntegrityResult = "Basic Integrity: " + isBasicIntegrity;
showAlert(basicIntegrityResult);
Log.i(TAG, basicIntegrityResult);
if (!basicIntegrity) {
String advice = "Advice: " + jsonObject.getString("advice");
Log.i("Advice log", advice);
}
} catch (JSONException e) {
String errorMsg = e.getMessage();
showAlert(errorMsg + "unknown error");
Log.e(TAG, errorMsg != null ? errorMsg : "unknown error");
}
}
}).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.
showAlert("Error: " + SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getMessage());
Log.e(TAG, "Error: " + SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getMessage());
} else {
// A different, unknown type of error occurred.
showAlert("ERROR:"+e.getMessage());
Log.e(TAG, "ERROR:" + e.getMessage());
}
}
});
}
public void showAlert(String message){
AlertDialog alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setTitle("SysIntegrity");
alertDialog.setMessage(message);
alertDialog.show();
}
}

AppsCheck: Determinate malicious apps and provides you with a list of malicious apps.

import android.app.AlertDialog;
import android.content.Context;
import android.util.Log;

import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hmf.tasks.Task;
import com.huawei.hms.common.ApiException;
import com.huawei.hms.support.api.entity.core.CommonCode;
import com.huawei.hms.support.api.entity.safetydetect.MaliciousAppsData;
import com.huawei.hms.support.api.entity.safetydetect.MaliciousAppsListResp;
import com.huawei.hms.support.api.safetydetect.SafetyDetect;
import com.huawei.hms.support.api.safetydetect.SafetyDetectClient;
import com.huawei.hms.support.api.safetydetect.SafetyDetectStatusCodes;

import java.util.List;

public class AppsCheck {

private static final String TAG= "Safety AppsCheck";
Context context;
String APP_ID;
private SafetyDetectClient client;

public AppsCheck(SafetyDetectClient client,Context context, String APP_ID) {
this.context = context;
this.APP_ID = APP_ID;
this.client=client;
}
public void invokeGetMaliciousApps() {
Task task = client.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.
showAlert("There are no known potentially malicious apps installed.");
Log.i(TAG, "There are no known potentially malicious apps installed.");
} else {
showAlert("Potentially malicious apps are installed!");
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 {
showAlert("getMaliciousAppsList fialed: " + maliciousAppsListResp.getErrorReason());
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.
showAlert("Error: " + SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getStatusMessage());
Log.e(TAG, "Error: " + SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getStatusMessage());
} else {
// A different, unknown type of error occurred.
Log.e(TAG, "ERROR: " + e.getMessage());
}
}
});
}
public void showAlert(String message){
AlertDialog alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setTitle("AppsCheck");
alertDialog.setMessage(message);
alertDialog.show();
}
}

URLCheck: Provide malicious URL detection capabilities.

import android.app.AlertDialog;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;

import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hms.common.ApiException;
import com.huawei.hms.support.api.entity.safetydetect.UrlCheckResponse;
import com.huawei.hms.support.api.entity.safetydetect.UrlCheckThreat;
import com.huawei.hms.support.api.safetydetect.SafetyDetect;
import com.huawei.hms.support.api.safetydetect.SafetyDetectClient;
import com.huawei.hms.support.api.safetydetect.SafetyDetectStatusCodes;

import java.util.List;

public class URLCheck {

private static final String TAG= "Safety URLCheck";
Context context;
String APP_ID;
private SafetyDetectClient client;

public URLCheck(SafetyDetectClient client, Context context, String APP_ID) {
this.client = client;
this.context = context;
this.APP_ID = APP_ID;
}

public void callUrlCheckApi() {
client.urlCheck("https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/SafetyDetectWiFiDetectAPIDevelopment", APP_ID,
// Specify url threat type
UrlCheckThreat.MALWARE,
UrlCheckThreat.PHISHING)
.addOnSuccessListener(new OnSuccessListener<UrlCheckResponse>() {
/**
* Called after successfully communicating with the SafetyDetect API.
* The #onSuccess callback receives an
* {@link com.huawei.hms.support.api.entity.safetydetect.UrlCheckResponse} that contains a
* list of UrlCheckThreat that contains the threat type of the Url.
*/
@Override
public void onSuccess(UrlCheckResponse urlCheckResponse) {
// Indicates communication with the service was successful.
// Identify any detected threats.
// Call getUrlCheckResponse method of UrlCheckResponse then you can get List<UrlCheckThreat> .
// If List<UrlCheckThreat> is empty , that means no threats found , else that means threats found.
List<UrlCheckThreat> list = urlCheckResponse.getUrlCheckResponse();
if (list.isEmpty()) {
// No threats found.
showAlert("No Threats found!");
Log.i(TAG,"No Threats found!");
} else {
// Threats found!
showAlert("Threats found!");
Log.i(TAG,"Threats found!");
}
}

})
.addOnFailureListener(new OnFailureListener() {
/**
* Called when an error occurred when communicating with the SafetyDetect API.
*/
@Override
public void onFailure(Exception e) {
// An error with the Huawei Mobile Service API contains some additional details.
String errorMsg;
if (e instanceof ApiException) {
ApiException apiException = (ApiException) e;
errorMsg = "Error: " +
SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " +
e.getMessage();

// You can use the apiException.getStatusCode() method to get the status code.
// Note: If the status code is SafetyDetectStatusCodes.CHECK_WITHOUT_INIT, you need to call initUrlCheck().
} else {
// Unknown type of error has occurred.
errorMsg = e.getMessage();
}
showAlert(errorMsg);
Log.d(TAG, errorMsg);
Toast.makeText(context.getApplicationContext(), errorMsg, Toast.LENGTH_SHORT).show();
}
});

}
public void showAlert(String message){
AlertDialog alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setTitle("URLCheck");
alertDialog.setMessage(message);
alertDialog.show();
}
}

UserDetect: Determinate fake users and bots.

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hms.common.ApiException;
import com.huawei.hms.support.api.entity.safetydetect.UserDetectResponse;
import com.huawei.hms.support.api.safetydetect.SafetyDetect;
import com.huawei.hms.support.api.safetydetect.SafetyDetectStatusCodes;

import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutionException;

public class UserDetect {
private static final String TAG= "Safety User Detect";
Context context;
String APP_ID;

public UserDetect(Context context, String APP_ID) {
this.APP_ID=APP_ID;
this.context=context;
}

public void detect() {
Log.i(TAG, "User detection start.");
SafetyDetect.getClient(context)
.userDetection(APP_ID)
.addOnSuccessListener(new OnSuccessListener<UserDetectResponse>() {
/**
* Called after successfully communicating with the SafetyDetect API.
* The #onSuccess callback receives an
* {@link UserDetectResponse} that contains a
* responseToken that can be used to get user detect result.
*/
@Override
public void onSuccess(UserDetectResponse userDetectResponse) {
// Indicates communication with the service was successful.
Log.i(TAG, "User detection succeed, response = " + userDetectResponse);
boolean verifySucceed = verify(userDetectResponse.getResponseToken());
if (verifySucceed) {
Toast.makeText(context.getApplicationContext(),
"User detection succeed and verify succeed",
Toast.LENGTH_SHORT)
.show();
} else {
Toast.makeText(context.getApplicationContext(),
"User detection succeed but verify fail,"
+ "please replace verify url with your's server address",
Toast.LENGTH_SHORT)
.show();
}
}
})
.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.
ApiException apiException = (ApiException) e;
errorMsg = SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode())
+ ": " + apiException.getMessage();
// You can use the apiException.getStatusCode() method to get the status code.
} else {
// Unknown type of error has occurred.
errorMsg = e.getMessage();
}
Log.i(TAG, "User detection fail. Error info: " + errorMsg);
Toast.makeText(context.getApplicationContext(), errorMsg, Toast.LENGTH_SHORT).show();
}
});
}

/**
* Send responseToken to your server to get the result of user detect.
*/
private static boolean verify(final String responseToken) {
try {
return new AsyncTask<String, Void, Boolean>() {
@Override
protected Boolean doInBackground(String... strings) {
String input = strings[0];
JSONObject jsonObject = new JSONObject();
try {
// TODO(developer): Replace the baseUrl with your own server address,better not hard code.
String baseUrl = "https://www.example.com/userdetect/verify";
jsonObject.put("response", input);
String result = sendPost(baseUrl, jsonObject);
JSONObject resultJson = new JSONObject(result);
boolean success = resultJson.getBoolean("success");
// if success is true that means the user is real human instead of a robot.
Log.i(TAG, "verify: result = " + success);
return success;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}.execute(responseToken).get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
return false;
}
}

/**
* post the response token to yur own server.
*/
private static String sendPost(String baseUrl, JSONObject postDataParams) throws Exception {
URL url = new URL(baseUrl);

HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(20000);
conn.setConnectTimeout(20000);
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Accept", "application/json");

try (OutputStream os = conn.getOutputStream(); BufferedWriter writer =
new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))) {
writer.write(postDataParams.toString());
writer.flush();
}

int responseCode = conn.getResponseCode(); // To Check for 200
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = in.readLine()) != null) {
sb.append(line);
break;
}
in.close();
return sb.toString();
}
return null;
}
}

WifiDetect: Check Wifi to be connected is secure.

1: Failed to obtain the Wi-Fi status.

0: No Wi-Fi is connected.

1: The connected Wi-Fi is secure.

2: The connected Wi-Fi is insecure.

import android.app.AlertDialog;
import android.content.Context;
import android.util.Log;

import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hmf.tasks.Task;
import com.huawei.hms.common.ApiException;
import com.huawei.hms.support.api.entity.safetydetect.WifiDetectResponse;
import com.huawei.hms.support.api.safetydetect.SafetyDetectClient;
import com.huawei.hms.support.api.safetydetect.SafetyDetectStatusCodes;

public class WifiCheck {

private SafetyDetectClient client;
private static final String TAG= "Safety WIFICheck";
Context context;
String APP_ID;

public WifiCheck(SafetyDetectClient client, Context context, String APP_ID) {
this.client = client;
this.context = context;
this.APP_ID = APP_ID;
}

public void invokeGetWifiDetectStatus() {
Log.i(TAG, "Start to getWifiDetectStatus!");
Task task = client.getWifiDetectStatus();
task.addOnSuccessListener(new OnSuccessListener<WifiDetectResponse>() {
@Override
public void onSuccess(WifiDetectResponse wifiDetectResponse) {
int wifiDetectStatus = wifiDetectResponse.getWifiDetectStatus();
showAlert("\n-1: Failed to obtain the Wi-Fi status. \n" + "0: No Wi-Fi is connected. \n" + "1: The connected Wi-Fi is secure. \n" + "2: The connected Wi-Fi is insecure." + "wifiDetectStatus is: " + wifiDetectStatus);
Log.i(TAG, "\n-1: Failed to obtain the Wi-Fi status. \n" + "0: No Wi-Fi is connected. \n" + "1: The connected Wi-Fi is secure. \n" + "2: The connected Wi-Fi is insecure.");
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());
showAlert("Error: " + apiException.getStatusCode() + ":"
+ SafetyDetectStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": "
+ apiException.getStatusMessage());
} else {
Log.e(TAG, "ERROR! " + e.getMessage());
showAlert("ERROR! " + e.getMessage());
}
}
});
}
public void showAlert(String message){
AlertDialog alertDialog = new AlertDialog.Builder(context).create();
alertDialog.setTitle("WifiCheck");
alertDialog.setMessage(message);
alertDialog.show();
}
}

Now call the required things

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.buttonSysIntegrity:
sysIntegrity= new SysIntegrity(client,MainActivity.this,APP_ID);
sysIntegrity.invoke();
break;
case R.id.buttonAppsCheck:
appsCheck=new AppsCheck(client,MainActivity.this,APP_ID);
appsCheck.invokeGetMaliciousApps();
break;
case R.id.buttonURLCheck:
urlCheck=new URLCheck(client,MainActivity.this,APP_ID);
urlCheck.callUrlCheckApi();
break;
case R.id.buttonUserDetect:
userDetect=new UserDetect(MainActivity.this,APP_ID);
userDetect.detect();
break;
case R.id.buttonWifiCheck:
wifiCheck=new WifiCheck(client,MainActivity.this,APP_ID);
wifiCheck.invokeGetWifiDetectStatus();
break;
default:
break;
}
}

Result​

Tips and Tricks​

Download latest HMS Flutter plugin.

Check dependencies downloaded properly.

Latest HMS Core APK is required.

Set minSDK 19 or later.

WifiDetect function available only in Chinese mainland.

UserDetect function not available in Chinese mainland.

Conclusion​

In this article, we have learnt integration of Huawei safety detect and types of security provided by Huawei safety detect kit. Nowadays everything is happening over the phone, so as developer it is our responsibility to provide security to application. Otherwise users do not use the application. Thank Huawei for giving such great Safety detect kit to build secure application.

Reference​

Safety detect