[Huawei Developers] Freemind R:
This article is originally from HUAWEI Developer Forum

Forum link: https://forums.developer.huawei.com/forumPortal/en/home​

This is all about integration of HMS Safety Detect API in the Android app using MVVM RxAndroid.

What is HMS Safety Detect API?

Ø The Safety Detect provides system integrity check (SysIntegrity), app security check (AppsCheck), malicious URL check (URLCheck), and fake user detection (UserDetect), helping you prevent security threats to your app.

[IMG="https://miro.medium.com/max/1400/0*9kAGTJgUSq52yX6k.png"]https://miro.medium.com/max/1400/0*9kAGTJgUSq52yX6k.png[/IMG]

Let’s create a Demo Project:

HUAWEI HMS Safety Detect integration requires the following preparations

Ø Creating an AGC Application.

Ø Creating an Android Studio Project.

Ø Generating a signature certificate.

Ø Generating a signature certificate fingerprint.

Ø Configuring the signature certificate fingerprint.

Ø Adding the application package name and save the configuration file.

Ø Configure the Maven address and AGC gradle plug-in.

Ø Configure the signature file in Android Studio.

In this article, we will implement SysIntegrity API in demo project using with RxAndroid and MVVM.

Call the API and handle responses.

Verify the certificate chain, signature, and domain name on the server.

1. Open AppGallery Console:

1. We need to create an application inside console.

2. We need to enable the Safety Detect api.

Go to Console > AppGallery Connect > My apps, click your app, and go to Develop > Manage APIs.

[IMG="https://miro.medium.com/max/1400/0*4oqN8LZV4nX6BoqF.png"]https://miro.medium.com/max/1400/0*4oqN8LZV4nX6BoqF.png[/IMG]

Now enable Safety Detect Api

[IMG="https://miro.medium.com/max/1400/0*Z87vvuWJb6rSPwmD.png"]https://miro.medium.com/max/1400/0*Z87vvuWJb6rSPwmD.png[/IMG]

Download the agconnect-services.json

[IMG="https://miro.medium.com/max/1400/0*QoOgAZlUOBuwSmPq.png"]https://miro.medium.com/max/1400/0*QoOgAZlUOBuwSmPq.png[/IMG]

Move the downloaded agconnect-services.json file to the app root directory of your Android Studio project.

[IMG="https://miro.medium.com/max/976/0*MQXFlpvrYrSn41Fp.png"]https://miro.medium.com/max/976/0*MQXFlpvrYrSn41Fp.png[/IMG]

We need to add HMS SDK dependency in app:gradle file

implementation 'com.huawei.hms:safetydetect:4.0.0.300'

We need to add maven dependency inside project:gradle file

maven { url 'http://developer.huawei.com/repo/' }

We need to add two more dependencies in app:gradle file

// MVVM
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'

// RxAndroid
implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'

Enable Data Binding

dataBinding {
enabled = true
}

2. Let’s implement api :

I have created following classes.

1. SysIntegrityDataSource : Which invoke the System Integrity Api with help of RxJava.

2. SysIntegrityViewModel : Which handle the response from System Integrity api and provide LiveData for view componets.

3. SysIntegrityFragment : Which observe the livedata from viewmodel class and set values in views such as textviews and button.

Note: If you are not familiar with MVVM or RxAndroid then I would like to suggest you to please go through my following articles:

· Android MyShows App — Rxandroid MVVM LiveData ViewModel DataBinding, Networking with Retrofit, Gson & Glide — Series

· Demystifying Data Binding — Android Jetpack — Series

Let’s see the implementation of SysIntegrityDataSource.java class.

public class SysIntegrityDataSource {

private static final String APP_ID = "XXXXXXXX";
private Context context;

public SysIntegrityDataSource(Context context) {
this.context = context;
}

public Single<SysIntegrityResp> executeSystemIntegrity() {
return Single.create(this::invokeSysIntegrity);
}

private void invokeSysIntegrity(SingleEmitter<SysIntegrityResp> emitter) {
byte[] nonce = ("Sample" + System.currentTimeMillis()).getBytes();
SafetyDetect.getClient(context)
.sysIntegrity(nonce, APP_ID)
.addOnSuccessListener(emitter::onSuccess)
.addOnFailureListener(emitter::onError);
}

}

invokeSysIntegrity() : This method invoke the System Integrity api and emit the data onSuccess/OnError and past it to Single<SysIntegrityResp> observable.

executeSystemIntegrity() : This method will create Single observable and return the response from invokeSysIntegrity() method.

3. Let’s implement ViewModel :

I have created SysIntegrityViewModel.java class.

public class SysIntegrityViewModel extends AndroidViewModel {

private final CompositeDisposable disposables = new CompositeDisposable();
private SysIntegrityDataSource sysIntegrityDataSource;

private MutableLiveData<SysIntegrityResp> systemIntegrityLiveData;
private MutableLiveData<String> error;

public SysIntegrityViewModel(Application app) {
super(app);
sysIntegrityDataSource = new SysIntegrityDataSource(app.getBaseContext());
systemIntegrityLiveData = new MutableLiveData<>();
error = new MutableLiveData<>();
}

public LiveData<SysIntegrityResp> observerSystemIntegrity() {
sysIntegrityDataSource.executeSystemIntegrity()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<SysIntegrityResp>() {
@Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}

@Override
public void onSuccess(SysIntegrityResp response) {
systemIntegrityLiveData.setValue(response);
}

@Override
public void onError(Throwable e) {
error.setValue(e.getMessage());
}
});
return systemIntegrityLiveData;
}

public LiveData<String> getError() {
return error;
}

@Override
protected void onCleared() {
disposables.clear();
}

}

MutableLiveData<SysIntegrityResp> systemintegrityLiveData: This field which provide the live data and return the value from viewmodel to fragment class.

observerSysIntegrity() : Which observe RxAndroid’s Single(observable) on main thread and set the value in systemIntegrityLiveData. If we got error while observing it will post the error in MutableLiveData<String> error.

4. Let’s implement Fragment :

I have created SysIntegrityFragment.java class Which obaserve the System Integrity api’s reponse and set the values in views.

public class SysIntegrityFragment extends Fragment {

private SysIntegrityViewModel sysIntegrityViewModel;

private FragmentSysBinding sysBinding;

public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
sysBinding=DataBindingUtil.inflate(inflater, R.layout.fragment_sys, container, false);
sysIntegrityViewModel = ViewModelProviders.of(this).get(SysIntegrityViewModel.class);

sysBinding.btnSys.setOnClickListener(v->{
processView();
sysIntegrityViewModel.observerSystemIntegrity().observe(getViewLifecycleOwner(), this::setSystemIntegrity);
sysIntegrityViewModel.getError().observe(getViewLifecycleOwner(),this::showError);
});

return sysBinding.getRoot();
}

private void setSystemIntegrity(SysIntegrityResp response){
String jwsStr = response.getResult();
String[] jwsSplit = jwsStr.split("\\.");
String jwsPayloadStr = jwsSplit[1];
String payloadDetail = new String(Base64.decode(jwsPayloadStr.getBytes(), Base64.URL_SAFE));
try {
final JSONObject jsonObject = new JSONObject(payloadDetail);
final boolean basicIntegrity = jsonObject.getBoolean("basicIntegrity");
sysBinding.btnSys.setBackgroundResource(basicIntegrity ? R.drawable.btn_round_green : R.drawable.btn_round_red);
sysBinding.btnSys.setText(R.string.rerun);
String isBasicIntegrity = String.valueOf(basicIntegrity);
String basicIntegrityResult = "Basic Integrity: " + isBasicIntegrity;
sysBinding.txtBasicIntegrityTitle.setText(basicIntegrityResult);
if (!basicIntegrity) {
String advice = "Advice: " + jsonObject.getString("advice");
sysBinding.txtPayloadAdvice.setText(advice);
}
} catch (JSONException e) {
}
}

private void showError(String error){
Toast.makeText(getActivity().getApplicationContext(), error, Toast.LENGTH_SHORT).show();
sysBinding.btnSys.setBackgroundResource(R.drawable.btn_round_yellow);
sysBinding.btnSys.setText(R.string.rerun);
}

private void processView() {
sysBinding.txtBasicIntegrityTitle.setText("");
sysBinding.txtPayloadBasicIntegrity.setText("");
sysBinding.btnSys.setText(R.string.processing);
sysBinding.btnSys.setBackgroundResource(R.drawable.btn_round_processing);
}
}

We have instantiated instance of view model using ViewModel factory method.

We will consume the response on button click’s event.

If we got success response then we will display inside textviews and button otherwise we will show the error toast.

5. Let’s see the result:

Build the app and hit run button.

[IMG="https://miro.medium.com/max/782/0*gC3xgelGAKgbH17Y.png"]https://miro.medium.com/max/782/0*gC3xgelGAKgbH17Y.png[/IMG]

[IMG="https://miro.medium.com/max/786/0*88PFtf28s1u6bANB.png"]https://miro.medium.com/max/786/0*88PFtf28s1u6bANB.png[/IMG]

[IMG="https://miro.medium.com/max/738/0*OO2oq1lAUlLzSMos.png"]https://miro.medium.com/max/738/0*OO2oq1lAUlLzSMos.png[/IMG]

[IMG="https://miro.medium.com/max/1400/0*JpglmT0hPoQeIIrd.png"]https://miro.medium.com/max/1400/0*JpglmT0hPoQeIIrd.png[/IMG]

Click > RunDetection Case 1: Success Case 2: SDK Error Case 3: Integrity false (Rooted)

I hope you have learnt something new today. If you have any query regarding this article, please feel free to post any comments.

Any questions about this, you can try to acquire answers from HUAWEI Developer Forum.​