Overview
In the previous post, we learned how to integrate the UserDetect API in Safety Detect into your app to identify fake users. I've been glad to receive so much positive feedback. In this post, I'll introduce SysIntegrity (system integrity check), yet another handy function in Safety Detect. Apps can face malicious attacks when they run on insecure devices, for example, rooted devices. For certain apps, such as e-commerce apps, ensuring a secure running environment during user transactions is particularly crucial. SysIntegrity enables apps to do so by helping detect device system-related risks in advance.
I encountered this function on the HUAWEI Developers website. SysIntegrity works by integrating the Safety Detect SDK into your app, and calling the SysIntegrity API in Safety Detect. It performs the check within a Trusted Execution Environment (TEE) and signs the check result with an X.509 digital certificate to ensure that it is trustworthy and tamperproof.
Now let's take a look at what it's like in practice.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Next, I'll show you how to integrate SysIntegrity.
Content
1 Preparations
1.1 Installing Android studio
1.2 Configuring App Information in AppGallery Connect
1.3 Configuring the Huawei Maven Repository Address
1.4 Adding Build Dependencies
1.5 Configuring Obfuscation Scripts
2 Code Development
2.1 Creating a SafetyDetectClient Instance and Generating a Nonce
2.2 Calling the SysIntegrity API
2.3 Verifying the Check Result on the App Server
2.4 Obtaining the System Integrity Check Result
1. Preparations
1.1 Installing Android Studio
To install Android Studio, please refer to the following:
l Android Studio Official Website
l Android Studio Installation and Configuration
1.2 Configuring App Information in AppGallery Connect
Before developing an app, you'll need to configure the app information in AppGallery Connect. For more details, please refer to Preparations.
1.3 Configuring the Huawei Maven Repository Address
Open the build.gradle file in the root directory of your Android Studio project.
Add the AppGallery Connect plug-in and the Maven repository address.
(1) Go to buildscript > repositories and configure the Maven repository address for the HMS Core SDK.
(2) Go to allprojects > repositories and configure the Maven repository address for the HMS Core SDK.
(3) If the agconnect-services.json file has been added to the app, go to buildscript > dependencies and add the AppGallery Connect plug-in configuration.
XML:
<p style="line-height: 1.5em;">apply plugin: 'com.huawei.agconnect'
</p>
Add the build dependency in the dependencies section.
XML:
<p style="line-height: 1.5em;">dependencies {
implementation 'com.huawei.hms:safetydetect:5.0.5.302'
}
</p>
1.5 Configuring Obfuscation Scripts
If you are using AndResGuard, add its trustlist to the build.gradle file in the app directory of your project. For more details about the code, please refer to Configuring Obfuscation Scripts on the HUAWEI Developers website.
2. Code Development
2.1 Creating a SafetyDetectClient Instance and Generating a Nonce
The nonce value will be contained in the check result. You can check the nonce value to verify that the returned result corresponds to your request, and does not encounter replay attacks. A nonce value should:
l Be used only once.
l Contain 16 to 66 bytes.
l Be derived from data sent to your server (Recommended).
2.2 Calling the SysIntegrity API
(1) The SysIntegrity API has two input parameters: the nonce value (obtained during the previous step) and your app ID. You can obtain the app ID from the agconnect-services.jsonfile under the app directory of your project.
Ø Sign in to AppGallery Connect and click My projects.
Ø Find your app project and click the desired app name.
Ø Go to Project Setting > General information, and view the app ID in the App information area.
Java:
<p style="line-height: 1.5em;">private void onAdapterItemClick(int position) {
// Call the SysIntegrity API to check for risks in the payment environment.
SafetyDetectUtil.detectSysIntegrity(this, new ICallBack<Boolean>() {
@Override
public void onSuccess(Boolean baseIntegrity) {
if (baseIntegrity) {
// The system integrity is not corrupted, and the user can proceed with the purchase.
buy(productInfo);
} else {
// The system integrity is corrupted. A popup is displayed to warn the user and ask the user whether to continue.
showRootTipDialog(productInfo);
}
}
…
});
}
</p>
(3) In my app, I encapsulated the SysIntegrity API in the detectSysIntegrity method of the SafetyDetectUtil.java class. The sample code is as follows:
Java:
<p style="line-height: 1.5em;">public static void detectSysIntegrity(final Activity activity, final ICallBack<? super Boolean> callBack) {
// Generate a nonce value.
byte[] nonce = ("Sample" + System.currentTimeMillis()).getBytes(StandardCharsets.UTF_8);
// Read the app_id field from the agconnect-services.json file in the app directory.
String appId = AGConnectServicesConfig.fromContext(activity).getString("client/app_id");
// Obtain the Safety Detect client, call the SysIntegrity API, and add a success event listener.
SysIntegrityRequest sysintegrityrequest = new SysIntegrityRequest();
sysintegrityrequest.setAppid(appId);
sysintegrityrequest.setNonce(nonce);
// PS256 or RS256
sysintegrityrequest.setAlg("RS256");
Task task = mClient.sysIntegrity(sysintegrityrequest);
task.addOnSuccessListener(new OnSuccessListener<SysIntegrityResp>() {
@Override
public void onSuccess(SysIntegrityResp response) {
// Call the getResult method of the SysIntegrityResp class to obtain the check result.
String jwsStr = response.getResult();
VerifyResultHandler verifyResultHandler = new VerifyResultHandler(jwsStr, callBack);
// Send the check result to your server for verification.
verifyJws(activity, jwsStr, verifyResultHandler);
}
});
}
</p>
(4) Here, I called the relevant app server API in the verifyJws method to verify the check result. The third parameter in this method is an object of the VerifyResultHandler class, which implements a callback API to process the verification result.
2.3 Verifying the Check Result on the App Server
After receiving the check result from the TSMS server, the app will send the result to the app server. The server uses the HUAWEI CBG Root CA certificate to verify the signature and certificate chain in the check result, and thereby determines whether the check result is valid.
The sample code for the app server to read the certificate and verify the JWS string is as follows:
(1) Parse the header, payload, and signature from the JWS string.
Java:
<p style="line-height: 1.5em;">public JwsVerifyResp verifyJws(JwsVerifyReq jwsVerifyReq) {
// Obtain the JWS information sent from the app to the server.
String jwsStr = jwsVerifyReq.getJws();
// Parse the JWS segments. A JWS has three fixed segments, which are separated by periods (.).
String[] jwsSplit = jwsStr.split("\\.");
try {
// Perform Base64 decoding on each segment and construct a JWS object for each decoded segment.
JWSObject jwsObject = new JWSObject(new Base64URL(jwsSplit[0]), new Base64URL(jwsSplit[1]), new Base64URL(jwsSplit[2]));
// Verify the JWS and set the verification result.
boolean result = VerifySignatureUtil.verifySignature(jwsObject);
// Construct the response body for check result verification on the app server.
JwsVerifyResp jwsVerifyResp = new JwsVerifyResp();
jwsVerifyResp.setResult(result);
} catch (ParseException | NoSuchAlgorithmException e) {
RUN_LOG.catching(e);
}
return jwsVerifyResp;
}
</p>
(2) Use the verifySignature method of the VerifySignatureUtil class to verify relevant information, including the JWS signature algorithm, certificate chain, host name in signing certificate, and JWS signature. The sample code is as follows:
Java:
<p style="line-height: 1.5em;">public static boolean verifySignature(JWSObject jws) throws NoSuchAlgorithmException {
JWSAlgorithm jwsAlgorithm = jws.getHeader().getAlgorithm();
// 1. Verify the JWS signature algorithm.
if ("RS256".equals(jwsAlgorithm.getName())) {
// Verify the certificate chain and obtain an instance of the Signature class based on the signature algorithm to verify the signature.
return verify(Signature.getInstance("SHA256withRSA"), jws);
}
return false;
}
private static boolean verify(Signature signature, JWSObject jws) {
// Extract the certificate chain information from the JWS header and convert the certificate chain into a proper type for subsequent processing.
X509Certificate[] certs = extractX509CertChain(jws);
// 2. Verify the certificate chain.
try {
verifyCertChain(certs);
} catch (Exception e) {
return false;
}
// 3. Verify the domain name in the signing certificate (leaf certificate). The domain name must be sysintegrity.platform.hicloud.com.
try {
new DefaultHostnameVerifier().verify("sysintegrity.platform.hicloud.com", certs[0]);
} catch (SSLException e) {
return false;
}
// 4. Verify the JWS signature information using the public key obtained from the signing certificate.
PublicKey pubKey = certs[0].getPublicKey();
try {
// Use the public key obtained from the signing certificate to initialize the Signature instance.
signature.initVerify(pubKey);
// Extract the input signature from the JWS and pass it to the Signature instance.
signature.update(jws.getSigningInput());
// Use the Signature instance to verify the signature information.
return signature.verify(jws.getSignature().decode());
} catch (InvalidKeyException | SignatureException e) {
return false;
}
}
</p>
(3) Call the extractX509CertChain method to extract the certificate chain from the JWS header. The sample code is as follows:
Java:
<p style="line-height: 1.5em;">private static X509Certificate[] extractX509CertChain(JWSObject jws) {
List<X509Certificate> certs = new ArrayList<>();
List<com.nimbusds.jose.util.Base64> x509CertChain = jws.getHeader().getX509CertChain();
try {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
certs.addAll(x509CertChain.stream().map(cert -> {
try {
return (X509Certificate) certFactory.generateCertificate( new ByteArrayInputStream(cert.decode()) );
} catch (CertificateException e) {
RUN_LOG.error("X5c extract failed!");
}
return null;
}).filter(Objects::nonNull).collect(Collectors.toList()));
} catch (CertificateException e) {
RUN_LOG.error("X5c extract failed!");
}
return (X509Certificate[]) certs.toArray();
}
</p>
(4) Call the verifyCertChain method to verify the certificate chain. The sample code is as follows:
Java:
<p style="line-height: 1.5em;">private static void verifyCertChain(X509Certificate[] certs) throws CertificateException, NoSuchAlgorithmException,
InvalidKeyException, NoSuchProviderException, SignatureException {
// Verify the validity period and issuing relationship of each certificate one by one.
for (int i = 0; i < certs.length - 1; ++i) {
certs[i].checkValidity();
PublicKey pubKey = certs[i + 1].getPublicKey();
certs[i].verify(pubKey);
}
// Use the preset HUAWEI CBG Root CA certificate to verify the last certificate in the certificate chain.
PublicKey caPubKey = huaweiCbgRootCaCert.getPublicKey();
certs[certs.length - 1].verify(caPubKey);
}
</p>
(5) Load the HUAWEI CBG Root CA certificate in the static code snippet of the VerifySignatureUtil class. The sample code is as follows:
Java:
<p style="line-height: 1.5em;">static {
// Load the preset HUAWEI CBG Root CA certificate.
File filepath = "~/certs/Huawei_cbg_root.cer";
try (FileInputStream in = new FileInputStream(filepath)) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
huaweiCbgRootCaCert = (X509Certificate) cf.generateCertificate(in);
} catch (IOException | CertificateException e) {
RUN_LOG.error("HUAWEI CBG root cert load failed!");
}
}
</p>
We have now verified the check result on the app server, and the verification result will be returned to the app for subsequent service processing.
2.4 Obtaining the System Integrity Check Result
(1) Once you complete the steps above, the app will obtain the reliable system integrity check result from the payload of the JWS string. Parse the system integrity check result from the callback API of the VerifyResultHandler class as follows:
Java:
<p style="line-height: 1.5em;">private static final class VerifyResultHandler implements ICallBack<Boolean> {
private final String jwsStr;
private final ICallBack<? super Boolean> callBack;
private VerifyResultHandler(String jwsStr, ICallBack<? super Boolean> callBack) {
this.jwsStr = jwsStr;
this.callBack = callBack;
}
@Override
public void onSuccess(Boolean verified) {
if (verified) {
// Extract the system integrity check result that has been successfully verified by the app server.
String payloadDetail = new String(Base64.decode(jwsStr.split("\\.")[1].getBytes(StandardCharsets.UTF_8), Base64.URL_SAFE), StandardCharsets.UTF_8);
try {
final boolean basicIntegrity = new JSONObject(payloadDetail).getBoolean("basicIntegrity");
// Call back the system integrity check result.
callBack.onSuccess(basicIntegrity);
} catch (JSONException e) {
…
}
}
…
}
}
</p>
(2) The following is an example of the system integrity check response:
XML:
<p style="line-height: 1.5em;">{
"apkCertificateDigestSha256": [
"osaUtTsdAvezjQBaW3IhN3/fsc6NQ5KwKuAQXcfrxb4="
],
"apkDigestSha256": "vFcmE0uw5s+4tFjXF9rVycxk2xR1rXiZFHuuBFzTVy8=",
"apkPackageName": "com.example.mockthirdapp",
"basicIntegrity": false,
"detail": [
"root",
"unlocked"
],
"nonce": "UjJScmEyNGZWbTV4YTJNZw==",
"timestampMs": 1604048377137,
"advice": "RESTORE_TO_FACTORY_ROM"
}
</p>
(3) If the value of the basicIntegrity field in the check result is false, it means that the system integrity is corrupted. In this case, the app can notify the user of any potential risks.
Conclusion
You can complete the integration by referring to the Preparations on HUAWEI Developers website. Also, you can download the SysIntegrity sample code for both Java and Kotlin from the site. And the sample code for four other functions in addition to SysIntegrity is also provided on the site.
Here is the sample code I wrote for my app. Feel free to take a look for yourself!
My sample code
Related
Hi, i am trying to write a application that can be used for streaming my phone to my windows desktop running a Java client to receive the images. However when i tried to create a background task following a tutorial by microsoft i am unable to access the UIElement. Does anyone know how to work around this?
the below code in the OnInvoke is able to run in a application however if i were to create it under a Task Agent project , i cant because i cant get the FrameElement.
Code:
using System.Windows;
using Microsoft.Phone.Scheduler;
using Microsoft.Phone.Shell;
using System;
namespace ScheduledTaskAgent1
{
public class ScheduledAgent : ScheduledTaskAgent
{
private static volatile bool _classInitialized;
/// <remarks>
/// ScheduledAgent constructor, initializes the UnhandledException handler
/// </remarks>
public ScheduledAgent()
{
if (!_classInitialized)
{
_classInitialized = true;
// Subscribe to the managed exception handler
Deployment.Current.Dispatcher.BeginInvoke(delegate
{
Application.Current.UnhandledException += ScheduledAgent_UnhandledException;
});
}
}
/// Code to execute on Unhandled Exceptions
private void ScheduledAgent_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
System.Diagnostics.Debugger.Break();
}
}
protected override void OnInvoke(ScheduledTask task)
{
var timer = new System.Windows.Threading.DispatcherTimer
{
Interval = System.TimeSpan.FromSeconds(10)
};
timer.Tick += (sender, args) =>
{
Microsoft.Devices.VibrateController.Default.Start(
TimeSpan.FromSeconds(0.1));
var bitmap = new System.Windows.Media.Imaging.WriteableBitmap(this.Parent, null);
var stream = new System.IO.MemoryStream();
System.Windows.Media.Imaging.Extensions.SaveJpeg(bitmap, stream,
bitmap.PixelWidth, bitmap.PixelHeight, 0, 100);
stream.Position = 0;
var mediaLib = new Microsoft.Xna.Framework.Media.MediaLibrary();
var datetime = System.DateTime.Now;
var filename =
System.String.Format("Capture-{0}-{1}-{2}-{3}-{4}-{5}",
datetime.Year % 100, datetime.Month, datetime.Day,
datetime.Hour, datetime.Minute, datetime.Second);
mediaLib.SavePicture(filename, stream);
};
timer.Start();
// Call NotifyComplete to let the system know the agent is done working.
NotifyComplete();
}
}
}
kyrogue said:
i am unable to access the UIElement
Click to expand...
Click to collapse
Hmm... Background agents don't have UIElements at all (and by the sandbox concept you can't access anything not belong to your app).
To capture WP7 screen, you should have an interop-unlock phone and use DllImport library.
Interop-unlock is *not* required to use anything in DllImport, actually - normal dev-unlock works fine.
There actually used to be an app that did what you described (stream the screen contents to a PC in real-time) but I don't think it ever got updated to work on Mango.
Prerequisite
Don't know what is AutomateIt ? Read more on this thread.
What is AutomateIt Plugin ?
Plugins are applications that by installing them, you add new actions and triggers that you can use within AutomateIt. Sounds easy, right ?
Each plugin can provide additional triggers and actions that extend the capabilities of AutomateIt, so you can focus on the functionality and take advantage of the AutomateIt platform to connect each trigger or action with existing capabilities of AutomateIt or with other plugins.
More details and a list of available plugins are available on the app website.
Getting started
Although developing a plugin is super-easy and requires only several simple steps, you are deeply encouraged to read the full documentation on the app website.
To summarize it for you, here are the steps you need to follow when developing a new plugin:
Download AutomateIt Plugin SDK
Setup your environment
Develop your plugin
Test your plugin
Setting up your environment after downloading the plugin SDK is done by adding a single dependency needs to be added to your build.gradle file:
Code:
compile 'com.automateitapp:automateit.plugin.sdk:1.0.3'
Creating a new plugin action or trigger is done by implementing a class that extends either PluginAction or PluginTrigger in your plugin project that references the AutomateItPluginLibrary.
Each of these abstract classes requires a set of simple functions to be implemented that provide metadata for the action or trigger (such as title, description, icons and parameters) and the functionality of the action or trigger. See sample code below for an action that shows a Toast message.
a complete sample app is available as a git repo that can be cloned from here.
When the plugin app is installed on a user device, it communicates with AutomateIt so that the plugin actions and triggers become available within AutomateIt.
Additional Documentation
AutomateIt Plugin Developer Guide
AutomateIt Plugin SDK Reference
Looking forward to seeing some awesome plugins !
Sample Action Code
Code:
package com.smarterapps.demoplugin.actions;
import java.util.List;
import android.content.Context;
import android.widget.Toast;
import com.smarterapps.automateitplugin.sdk.PluginDataFieldCollection;
import com.smarterapps.automateitplugin.sdk.PluginValidationResult;
import com.smarterapps.automateitplugin.sdk.RequiredApp;
import com.smarterapps.automateitplugin.sdk.fields.PluginDataFieldBoolean;
import com.smarterapps.automateitplugin.sdk.fields.PluginDataFieldString;
import com.smarterapps.demoplugin.R;
public class Action1 extends com.smarterapps.automateitplugin.sdk.PluginAction
{
private static final int FIELD_ID_TEXT_TO_SHOW = 1;
private static final int FIELD_ID_DURATION_LONG = 2;
@Override
public String getActionTitle(Context context)
{
return context.getString(R.string.action1_title);
}
@Override
public String getActionDescription(Context context)
{
return context.getString(R.string.action1_default_description);
}
@Override
public String getActionDescription(Context context, PluginDataFieldCollection data)
{
if (null != data)
{
String msgText = (String) data.getFieldById(FIELD_ID_TEXT_TO_SHOW).getValue();
boolean durationLong = (Boolean) data.getFieldById(FIELD_ID_DURATION_LONG).getValue();
int toastDurationStringId = durationLong ? R.string.duration_long: R.string.duration_short;
return context.getString(R.string.action1_description,
context.getString(toastDurationStringId),
msgText);
}
return getActionDescription(context);
}
@Override
public int getActionIconResourceId()
{
return R.drawable.ic_action1;
}
@Override
public int getActionSmallIconResourceId()
{
return R.drawable.ic_action1_small;
}
@Override
public PluginDataFieldCollection getActionFields(Context context)
{
PluginDataFieldCollection retval = new PluginDataFieldCollection();
retval.add(new PluginDataFieldBoolean(
FIELD_ID_DURATION_LONG,
context.getString(R.string.action1_field_duration_title),
context.getString(R.string.action1_field_duration_description),
true));
retval.add(new PluginDataFieldString(
FIELD_ID_TEXT_TO_SHOW,
context.getString(R.string.action1_field_msg_title),
context.getString(R.string.action1_field_msg_description),
""));
return retval;
}
@Override
public void doAction(Context context, PluginDataFieldCollection data)
{
boolean durationLong = (Boolean) data.getFieldById(FIELD_ID_DURATION_LONG).getValue();
int toastDuration = durationLong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT;
String toastMsg = (String) data.getFieldById(FIELD_ID_TEXT_TO_SHOW).getValue();
Toast.makeText(context, toastMsg, toastDuration).show();
}
@Override
public PluginValidationResult validateData(Context context, PluginDataFieldCollection data)
{
// Validate action1
String toastMsg = (String) data.getFieldById(FIELD_ID_TEXT_TO_SHOW).getValue();
if (null == toastMsg || toastMsg.trim().length() == 0)
{
return new PluginValidationResult(false, "Toast message can't be empty");
}
return PluginValidationResult.getValidResult();
}
@Override
public List<RequiredApp> getRequiredApps()
{
return null;
}
}
AutomateIt Plugin SDK has been updated so it is now even easier to integrate actions and triggers into your apps, or build a brand new plugin.
It requires a single dependency in your build.gradle file and the rest is up to you!
Sample app is available as a git repo on bitbucket.org:
https://bitbucket.org/AutomateIt/automateit-plugin-demo
(Instructions were updated on the developer website and this post).
Hey Guys, today I will cover about how to create your own android Toolkit in C#. In this tutorial, I will cover some further pieces of stuff to create bit advance Android Tools.
Thanks to QuantumCipher for this amazing guide and RegawMod for his amazing library.
BasicsYou must know the following things before reading this guide
You just need to setup Visual Studio or SharpDevelop in order to create such projects.
You will see in every code, there are comments explaining them. Read them to at least get an idea what's happening. Below is the example of the comments in C#
Code:
// This is a Single line comment
/* This is a multiline
* comment in C#
*/
You should have the basic understanding of Toolbox controls and property box in Visual Studio. See the below image for better understanding!
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
You cannot implicitly convert Integer variable to String in C#, you can do it in python but for C# you have to do following things
Code:
int a = 6;
string demo;
demo = a; // Wrong stuff...
// To Fix this do,
demo = a.ToString();
// Or this,
demo = Convert.ToString(a);
Basic Understanding on How to use GitHub :angel:
Please forgive my bad English
UnderstandingYou need to first understand how actually this thing works (I assume that you have setup Visual studio before reading this guide). Basically, you first create a button like Reboot, set its function and add some process function to run 'adb reboot' through a console (which you can hide if you set process.StartInfo.CreateNoWindow=false). You see every time we add a function (the feature of adb) call adb explicitly and run a shell process. It simply means if you don't know about adb commands or don't know how to deal with cmd or batch files it will be obviously difficult on creating an Android Toolkit.
Let me show you an example, below is the batch file demonstrating an example of Rebooting to recovery using adb.
Code:
@echo off
adb reboot recovery
Using C# we can achieve this by this below code.
Code:
Process.Start("cmd.exe", "/c adb reboot recovery");
See, not much difference in C# we pass adb reboot recovery to cmd.exe, of course, this will create a window but if you don't want we need to set some properties for Process like below. Read comments for better understanding.
Code:
void run_process(string Commands) // Declaring a function for process
{
var p = new Process(); // Declaring new process
// Defining the process, setting its parameters
p.StartInfo.FileName = "cmd.exe"; // Passing main function to cmd.exe
p.StartInfo.Arguments = "/c " + Commands; // Commands string passed here. /c argument is used to passed parameters explicitly.
p.StartInfo.CreateNoWindow = true; // No displaying a window
p.StartInfo.UseShellExecute = false; // No using cmd.exe to execute shell
p.Start(); // Starting the process
do
{
Application.DoEvents(); // This will prevent application from hang
}while(!p.HasExited); // Will wait until process been released from memory
}
void Button_Click(object sender, EventArgs e) // Use Button to achieve it.
{
run_process("adb reboot recovery"); // Running the command safely.
}
We create a function which passing commands explicitly to cmd.exe with which we can run our adb and other stuff. Beside this, we can prevent Application from hang if suppose a long process being executed.
So you might understand this, in order to create Android Toolkits you need to know pretty basics of the batch language and you know what the main part is? They are very easy to learn like you can do it in a day if you will.
Complexity of ProcessThis is the main part, you might actually need to know. You have seen the above example. Writing a simple adb reboot program requires lots of codes down there. For eg, see below
This is the code for detecting android devices using normal Process C#
Code:
string run_process(string Commands) // Creating a function
{
var p = new Process(); // Declaring new process
// Defining the process, setting its parameters
p.StartInfo.FileName = "cmd.exe"; // Passing main function to cmd.exe
p.StartInfo.Arguments = "/c " + Commands; // Commands string passed here. /c argument is used to passed parameters explicitly.
p.StartInfo.CreateNoWindow = true; // No displaying a window
p.StartInfo.UseShellExecute = false; // No using cmd.exe to execute shell
p.StartInfo.RedirectStandardOutput = true;
p.Start(); // Starting the process
do
{
Application.DoEvents(); // This will prevent application from hang
}while(!p.HasExited); // Will wait until process been released from memory
return p.StandardOutput.ReadToEnd(); // This will return the output from the process.
}'
bool isConnected() // Function for detecting Connected Devices
{
var check = run_process("adb.exe get-serialno"); // Will check for connected devices and set it to a string
if (check.Contains("unknown")) // If string Contains 'unknown' means no device connected
{
// If Not Connected
pictureBox1.BackColor = Color.Red; // Change Color of Picture Box
label1.Text = "Device Not Connected!";
status.Text = "---";
return false; // Returning boolean value
}
else
{
// If Connected, we will also check its status
pictureBox1.BackColor = Color.Green;
label1.Text = "Device Connected!";
status.Text = run_process("adb.exe get-state"); // This will check status of device i.e Android, Recovery, Sideload, etc
return true; // Returning boolean value
}
}
void Button_Click(object sender, EventArgs e) // A button click to run connected device function
{
if (isConnected())
{
// TODO: It is Connected, do your stuff in this loop
}
}
See the complexity and length of the code, Now what! Let's rewrite same procedure using a library. For instance, I will be showing usage of AndroidLib created by Recognised XDA Developer, RegawMod. I will teach you later how to use this for now look the code below.
Code:
void Button_Click(object sender, EventArgs e) // A button click to Check connected device
{
AndroidController android=null;// Declaring some variables
android = AndroidController.Instance; // Setting Android instance
if (android.HasConnectedDevices) // Check for connected devices
{
// TODO : That's it, Device is connected. You can run something here.
}
}
What Actually Happened? By using some wrappers we have actually breakdown big code to a short code which only has one function to check connected device.
What I mean by this? I am sure you have understood what you need to do. Using the normal process for running typical adb process we have increased the size and complexity of the code too much, but a wrapper like AndroidLib made our day by decreasing the size and seriously you have to typically remember some few things in this dll and you're done. A cut-down of a big adb process can be done with this wrapper.
So, let us start using the wrapper for creating your own C# Advance Toolkit
Using Actual CodeLet's start creating new Project in Visual Studio or you may use SharpDevelop to Achieve this.
Click on File > New Project > Visual C# > Windows Form Application. Name any solution and OK. Now download AndroidLib from the bottom of the post.
Click on Project > Add Reference > Browse for AndroidLib.dll and 'OKWe gonna create a project like this.
We gonna create a project like this. You can find this project in my sample at the bottom of this page.
What Does this do? It will first detect our device in ADB Mode, show status and model number indicated by changing the back color of pictureBox to Green
Now Add the controls like above we have '1 label' saying that you need to add reference,, bla bla bla... '2 GroupBoxes' with Text=" ". '1 label' with Text=" " and Name=statuslabel , '1 Button' with Text="Check Device using AndroidLib" , '1 TextBox' at the center , '1 Button again' with Text="Go" and finally '1 ComboBox' containing items as 'Normal Reboot, Reboot to Recovery, Reboot to Bootloader' also Set its DropDownStyle=DropDownList from property box.
Coding StuffNow Click on View > Code to see the code of the Form, here we will add the codes for the form.
Declare some imports at the top of the .cs
Code:
using RegawMOD.Android; // Android lib imports
using System.IO; // If dealing with file reading and writing, not necessary for this guide.
Declare some variables just after 'public partial class'
Code:
Device device; AndroidController android=null; string serial;
Add Few lines of code in your Form Constructor, for me my form name is MainForm so my form constructor is MainForm. You have to add codes below InitializeComponents(); Your Final code be like this
Code:
public MainForm()
{
InitializeComponent();
android = AndroidController.Instance; // Setting Android instance
comboBox1.SelectedIndex = 0; // First option will be selected in comboBox which is Normal Reboot, If you don't do this you will get a comboBox with no selection may generate an error if button is clicked.
groupBox2.Enabled = false; // Disabling group box at the start which contains reboot options
}
You may see I have disabled second groupBox, this is because if device is not connected and still it is shown user might try to use it and may generate an exception. That's why disabling it at start and after 'no device connected' for device prevents exceptions
Now Create a Function for checking connected device, same as we did above.
Code:
bool isConnected()
{
bool i=false; // Initialize variable as false
groupBox2.Enabled = false; // Disabling group box so what if there is no connected device, we may not want user to click on reboot button to prevent error
android.UpdateDeviceList(); // Update connected device list
if (android.HasConnectedDevices) // Check for connected devices
{
serial = android.ConnectedDevices[0]; // Get first connected device's serial '0' means first
device = android.GetConnectedDevice(serial); // Use device serial to get device
i=true; // It is connected
groupBox2.Enabled = true; // Enabling groupBox as we know device is connected
}
else
i=false; // It is not connected
return i; // Return as function says
}
Now Go back to designer from View, and double click on button with text 'Check Device using AndroidLib' , this will automatically generate an method for button.
Now add the code for the button in the field, so that finally it looks like this
Code:
void Button1Click(object sender, EventArgs e)
{
if (isConnected())
{
pictureBox1.BackColor = Color.Green; // Setting color to green
statuslabel.Text = device.BuildProp.GetProp("ro.product.device") + ", " + device.State; // Show device name in status label as connected with its state
// Get some Additional details and show in textbox
textBox1.Text="-------Device Connected-------" + Environment.NewLine;
textBox1.AppendText("Product Model: " + device.BuildProp.GetProp("ro.product.model") + Environment.NewLine); // Show device's model, you can use other prop commands to, you will find it in basic device info project
textBox1.AppendText("Battery Status: " + device.Battery.Level.ToString() + Environment.NewLine); // Show battery percentage as string
textBox1.AppendText("Battery Temperature: "+ device.Battery.Temperature + Environment.NewLine); // Show battery temperature
textBox1.AppendText("Encryption: "+ device.BuildProp.GetProp("ro.crypto.state") + Environment.NewLine); // Show encryption state
textBox1.AppendText("Is Rooted: "+ device.HasRoot + Environment.NewLine); // Show if device rooted
textBox1.AppendText("Su Version: " + device.Su.Version + Environment.NewLine); // Show the SU Version installed
textBox1.AppendText("BusyBox: "+device.BusyBox.IsInstalled + ", " + device.BusyBox.Version + Environment.NewLine); // Checks if busybox installed and its version
}
else
{
textBox1.Text = "-------Error No Device Connected--------";
pictureBox1.BackColor = Color.Red; // Setting color to Red as not connected
statuslabel.Text = "---";
}
}
What we just did? In button code, you will see that we used isConnected Function to check if the device is connected or not. If it's connected we will change the status label text and color of picture box to green along with some general information shown in the textBox. If not i.e else we will change color or PictureBox to red.
Now-Again go back to Designer and Double click on Button with the text 'Go' to generate a method for it.
Add the following code to it. This will set reboot Option code for it. Remeber ComboxBox index 0 means the first element and 1 means second element and so on... Its same as an array as it starts from 0.
Code:
void Button2Click(object sender, EventArgs e)
{
switch(comboBox1.SelectedIndex) // Checking which option been selected in comboxBox using integers
{
case 0: // First element been selected
device.Reboot();
break;
case 1: // Second element been selected
device.RebootRecovery();
break;
case 2: // Third element been selected
device.RebootBootloader();
break;
}
}
We have used Switch case loop in this case, to check which one is actually selected and according to it codes have been added.
For instance here is my full code of Main Form
Code:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using RegawMOD.Android; // Android lib imports
namespace UsageOfAndroidLib
{
/// <summary>
/// Description of MainForm.
/// </summary>
public partial class MainForm : Form
{
// Declaring some variables
Device device; AndroidController android=null; string serial;
public MainForm()
{
InitializeComponent();
android = AndroidController.Instance; // Setting Android instance
comboBox1.SelectedIndex = 0; // First option will be selected in comboBox which is Normal Reboot
groupBox2.Enabled = false; // Disabling group box at the start which contains reboot options
}
/// <summary>
/// This will check if device is connected or not
/// </summary>
/// <returns>bool</returns>
bool isConnected()
{
bool i=false; // Initialize variable as false
groupBox2.Enabled = false; // Disabling group box so what if there is no connected device, we may not want user to click on reboot button to prevent error
android.UpdateDeviceList(); // Update connected device list
if (android.HasConnectedDevices) // Check for connected devices
{
serial = android.ConnectedDevices[0]; // Get first connected device's serial '0' means first
device = android.GetConnectedDevice(serial); // Use device serial to get device
i=true; // It is connected
groupBox2.Enabled = true; // Enabling groupBox as we know device is connected
}
else
i=false; // It is not connected
return i; // Return as function says
}
/// <summary>
/// Main click of the button1, you know what it does;
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Button1Click(object sender, EventArgs e)
{
if (isConnected())
{
pictureBox1.BackColor = Color.Green; // Setting color to green
statuslabel.Text = device.BuildProp.GetProp("ro.product.device") + ", " + device.State; // Show device name in status label as connected with its state
// Get some Additional details and show in textbox
textBox1.Text="-------Device Connected-------" + Environment.NewLine;
textBox1.AppendText("Product Model: " + device.BuildProp.GetProp("ro.product.model") + Environment.NewLine); // Show device's model, you can use other prop commands to, you will find it in basic device info project
textBox1.AppendText("Battery Status: " + device.Battery.Level.ToString() + Environment.NewLine); // Show battery percentage as string
textBox1.AppendText("Battery Temperature: "+ device.Battery.Temperature + Environment.NewLine); // Show battery temperature
textBox1.AppendText("Encryption: "+ device.BuildProp.GetProp("ro.crypto.state") + Environment.NewLine); // Show encryption state
textBox1.AppendText("Is Rooted: "+ device.HasRoot + Environment.NewLine); // Show if device rooted
textBox1.AppendText("Su Version: " + device.Su.Version + Environment.NewLine); // Show the SU Version installed
textBox1.AppendText("BusyBox: "+device.BusyBox.IsInstalled + ", " + device.BusyBox.Version + Environment.NewLine); // Checks if busybox installed and its version
}
else
{
textBox1.Text = "-------Error No Device Connected--------";
pictureBox1.BackColor = Color.Red; // Setting color to Red as not connected
statuslabel.Text = "---";
}
}
void Button2Click(object sender, EventArgs e)
{
switch(comboBox1.SelectedIndex) // Checking which option been selected in comboxBox using integers
{
case 0: // First element been selected
device.Reboot();
break;
case 1: // Second element been selected
device.RebootRecovery();
break;
case 2: // Third element been selected
device.RebootBootloader();
break;
}
}
}
}
That's it, go to Build > Build {your Project} and run it from the button which looks like play (green one)
Some Tips and Helps
My Idea behind the post was to spread the creativity about the Coding you can do. It's not just the project like above one. You have to think constantly about new ideas and find ways to implement it. Ask peoples, search on Google, XDA, StackOverflow these are some best sites for your doubt
clarification but don't just copy and paste code learn from it. Understand what it says, coding and making Tools it not only adding the feature and publish it onto the web, you need to think out of the box and add such thing which is practically hard doing in the normal way. Always remember toolkit make the process easier, don't make them too complex that user may not understand it
Here is below code on how you access Adb and Fastboot shells through the Library
Code:
Adb.ExecuteAdbCommand(Adb.FormAdbCommand("your command")); // Normal Adb Command
Adb.ExecuteAdbCommand(Adb.FormAdbShellCommand(device,false,"your command")); // ADB Shell command, by setting false to true you can access root shell
Fastboot.ExecuteFastbootCommand(Fastboot.FormFastbootCommand("your command")); // Use Fastboot commands
Whenever you fall into a trouble like you don't know what you can do further with this Library or any other in future, always remember a phrase "The Dot Operator tells you everything". Seems pretty funny but this is the truth like I don't know what this "device" (variable I've declared in above code demonstration) does? To find out, write it and add a dot
See it creates a context strip along with some tooltip text which tells me what is it for, from which I can figure out what I've to do.
AndroidLib: https://forum.xda-developers.com/showthread.php?t=1512685
Some other wrappers: AndroidCtrl, Madb
Sample Projects: https://github.com/KaustubhPatange/AdbSamples
Check my samples on github which contains some other demonstration on how to create Android Toolkit very easily. Read Comments in the code and figure out what is it trying to say
Have a Question ask in this websites: XDA,
StackOverFlow, CodeProject
So I think this is the end of the tutorial, I cannot explain everything on how to use this or that. Sometimes it is hard to code for an easy function but there is nothing wrong with asking people for your doubts, create QnA forums, asking in StackOverFlow. We learn from our mistakes, that's how the world works
So Thank you for reading this guide, if you want you can share it with your friends and if you have any doubt comment below :laugh:
Reserved?
I need some guidance for recording Test Results Automatically in JIRA Adaptavist Test Management Tool using Java and Testng or any other method within the framework of Java. my automation test cases are in JIRA Adaptavist Test Management Tool. The test cases are written in Java & TestNg and are running on a VM. Our Android team developed some code to deal with this problem. It is just that I don't know how to solve this. they used kotlin to build the code mapping the automation test case names to that of the TestIDs in JIRA Adaptavist Test Management Tool.
Every time we run the automation, the team has to input the results in JIRA Adaptavist Test Management Tool manually. It is becoming cumbersome for us to deal with this.
Here is the android team's code :
Open class rTestCaseHelper {
@Rule
@jvmField
val grantWriteStorageAccess: GrantPermissionRule = GrantPermissionRule.grant(
android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
@Rule
@jvmField
val reportRule :TestWatcher = Object : TestWatcher() {
var startTime = 0
override fun starting(description: Description?) {
startTime = System.currentTimeMillis().toInt()
if (sharedPreference.getTestCount() == 0) {
testManager.getTestIdAndStoreToSharedPreference()
testManager.createTestCycle()
}
}
override fun succeeded(description: Description?) {
if(description != null) {
val executionTime = System.currentTimeMillis().toInt() - startTime
testManager.generateExecutionReport(description.methodName, result: "Pass", executionTime)
}
}
override fun failed(e: Throwable?, description: Description?) {
super.failed(e.description)
if(description != null) {
val executionTime = System.currentTimeMillis().toInt() - startTime
testManager.generateExecutionReport(description.methodName, result: "Fail", executionTime, e)
}
}
override fun finished(description: Description?) {
sharedPreference.setTestCount(sharedPreference.getTestCount() -1)
//Post artfact report link to test cycle (TODO)
testManager.postWebLinkToTestCycle()
rSharedPreference.preference.edit().clear()
Log.d(tag: "QAA", msg: "Automation suite finished and sharedPreference data is cleared!")
}
}
}
fun initializeSDK(activeProtection: Boolean = false): Boolean {
rSdkEnvironment.initializeSDK(activeProtection, InstrumentationRegistry.getInstrumentation().targetContext)
return rSdk.isInitialized()
}
More articles like this, you can visit HUAWEI Developer Forum and Medium.
Forum link: https://forums.developer.huawei.com/forumPortal/en/home
Medium link: https://medium.com/huawei-developers
It is a tradition to exchange business cards with new colleges or partners, but keeping physical business cards is not easy at all. To solve this problem, many apps and mini programs providing the electronic business card function have emerged. You must be wondering how to develop such a function for your app.
Try integrating HUAWEI Nearby Service and use its Nearby Message feature to quickly implement the point-to-point business card exchange function. Check out the function demo below.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
If you are interested in the implementation details, download the source code from GitHub. You can optimize the code based on your app requirements.
Github demo link: https://github.com/HMS-Core/hms-nearby-demo/tree/master/NearbyCardExchange
The development procedure is as follows:
1. Getting Started
If you are already a Huawei developer, skip this step. If you are new to Huawei Mobile Services (HMS), you need to configure app information in AppGallery Connect, enable Nearby Service on the HUAWEI Developers console, and integrate the HMS Core SDK. For details, please refer to the documentation.
2. Adding Permissions
Before using Nearby Message, add the network, Bluetooth, and location permissions. Add the following permissions to the AndroidManifest.xml file of your project:
Code:
<uses-permission android:name="android.permission.INTERNET " />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- The location permission is also required in Android 6.0 or later. -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
1. Code Development
2.1 Submitting a Dynamic Permission Application
Ensure that the Bluetooth and location functions are enabled and the device has been connected to the Internet properly. Then submit a dynamic permission application for the location permission.
Code:
@Override
public void onStart() {
super.onStart();
getActivity().getApplication().registerActivityLifecycleCallbacks(this);
checkPermission();
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
for (int i = 0; i < permissions.length; ++i) {
if (grantResults[i] != 0) {
showWarnDialog(Constants.LOCATION_ERROR);
}
}
}
private void checkPermission() {
if (!BluetoothCheckUtil.isBlueEnabled()) {
showWarnDialog(Constants.BLUETOOTH_ERROR);
return;
}
if (!LocationCheckUtil.isLocationEnabled(this.getActivity())) {
showWarnDialog(Constants.LOCATION_SWITCH_ERROR);
return;
}
if (!NetCheckUtil.isNetworkAvailable(this.getActivity())) {
showWarnDialog(Constants.NETWORK_ERROR);
return;
}
String[] deniedPermission = PermissionUtil.getDeniedPermissions(this.getActivity(), new String[] {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
});
if (deniedPermission.length > 0) {
PermissionUtil.requestPermissions(this.getActivity(), deniedPermission, 10);
}
}
2.2 Encapsulating the Business Card Publishing and Subscription APIs
When a subscribed business card message is detected by the onFound method, display it in the business card searching pop-up; when a business card message is no longer discoverable (onLost), delete it from the business card searching pop-up.
Code:
private MessageHandler mMessageHandler = new MessageHandler() {
@Override
public void onFound(Message message) {
CardInfo cardInfo = JsonUtils.json2Object(new String(message.getContent(), Charset.forName("UTF-8")),
CardInfo.class);
if (cardInfo == null) {
return;
}
mSearchCardDialogFragment.addCardInfo(cardInfo);
}
@Override
public void onLost(Message message) {
CardInfo cardInfo = JsonUtils.json2Object(new String(message.getContent(), Charset.forName("UTF-8")),
CardInfo.class);
if (cardInfo == null) {
return;
}
mSearchCardDialogFragment.removeCardInfo(cardInfo);
}
};
private void publish(String namespace, String type, int ttlSeconds, OnCompleteListener<Void> listener) {
Message message = new Message(JsonUtils.object2Json(mCardInfo).getBytes(Charset.forName("UTF-8")), type,
namespace);
Policy policy = new Policy.Builder().setTtlSeconds(ttlSeconds).build();
PutOption option = new PutOption.Builder().setPolicy(policy).build();
Nearby.getMessageEngine(getActivity()).put(message, option).addOnCompleteListener(listener);
}
private void subscribe(String namespace, String type, int ttlSeconds, OnCompleteListener<Void> listener,
GetCallback callback) {
Policy policy = new Policy.Builder().setTtlSeconds(ttlSeconds).build();
MessagePicker picker = new MessagePicker.Builder().includeNamespaceType(namespace, type).build();
GetOption.Builder builder = new GetOption.Builder().setPolicy(policy).setPicker(picker);
if (callback != null) {
builder.setCallback(callback);
}
Nearby.getMessageEngine(getActivity()).get(mMessageHandler, builder.build()).addOnCompleteListener(listener);
}
2.3 Processing the Business Card Exchange Menu
When two users exchange business cards face to face, exchange the business card exchange codes. When the business card message of the remote endpoint is published, subscribe to it.
Code:
private boolean onExchangeItemSelected() {
PinCodeDialogFragment dialogFragment = new PinCodeDialogFragment(passwrod -> {
MyCardFragment.this.publish(passwrod, passwrod, Policy.POLICY_TTL_SECONDS_MAX, result -> {
if (!result.isSuccessful()) {
String str = "Exchange card fail, because publish my card fail. exception: "
+ result.getException().getMessage();
Log.e(TAG, str);
Toast.makeText(getActivity(), str, Toast.LENGTH_LONG).show();
return;
}
MyCardFragment.this.subscribe(passwrod, passwrod, Policy.POLICY_TTL_SECONDS_INFINITE, ret -> {
if (!ret.isSuccessful()) {
MyCardFragment.this.unpublish(passwrod, passwrod, task -> {
String str = "Exchange card fail, because subscribe is fail, exception("
+ ret.getException().getMessage() + ")";
if (!task.isSuccessful()) {
str = str + " and unpublish fail, exception(" + task.getException().getMessage()
+ ")";
}
Log.e(TAG, str);
Toast.makeText(getActivity(), str, Toast.LENGTH_LONG).show();
});
return;
}
mSearchCardDialogFragment.setOnCloseListener(() -> {
MyCardFragment.this.unpublish(passwrod, passwrod, task -> {
if (!task.isSuccessful()) {
Toast.makeText(getActivity(), "Unpublish my card fail, exception: "
+ task.getException().getMessage(), Toast.LENGTH_LONG).show();
}
});
MyCardFragment.this.unsubscribe(task -> {
if (!task.isSuccessful()) {
Toast.makeText(getActivity(), "Unsubscribe fail, exception: "
+ task.getException().getMessage(), Toast.LENGTH_LONG).show();
}
});
});
mSearchCardDialogFragment.show(getParentFragmentManager(), "Search Card");
}, null);
});
});
dialogFragment.show(getParentFragmentManager(), "pin code");
return true;
}
2.4 Adding a Business Card to Favorites
When a user adds a business card to favorites, add the card to the favorites list; when a user removes a business card from favorites, remote the card from the favorites list. In addition, store related data locally.
Code:
@Override
public void onFavorite(CardInfo cardInfo, boolean isFavorite) {
if (isFavorite) {
mFavoriteMap.put(cardInfo.getId(), cardInfo);
} else {
mFavoriteMap.remove(cardInfo.getId());
}
Set<String> set = new HashSet<>(mFavoriteMap.size());
for (CardInfo card : mFavoriteMap.values()) {
set.add(JsonUtils.object2Json(card));
}
SharedPreferences sharedPreferences = getContext().getSharedPreferences("data", Context.MODE_PRIVATE);
sharedPreferences.edit().putStringSet(Constants.MY_FAVORITES_KEY, set).apply();
}
5. Conclusion
This demo uses Nearby Message feature of HUAWEI Nearby Service. What Nearby Message is capable of is more than just developing functions for exchanging business cards face-to-face. Here are some examples:
1. Face-to-face teaming in multiplayer sports games
2. Face-to-face round joining in board games
3. Near-field go-Dutch payment function
4. Music sharing
If you are interested and want to learn more, check our development guide at
https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/nearby-service-introduction
Find more Huawei HMS development guides in the XDA HMS community forums.
If you are interested and want to learn more, check out our development guide at Interesting app, but I recommend that you also consider a broader security analogy if you are linking an app to financial transactions.
Namely, pay attention to two-factor authentication, with the generation of a one-time password. The basis is directly two factor authentication solutions by providing a security function. Today, 2FA service provides top-level protection and 2fa platform as its Foundation contributes to this. providing a multi-factor authentication solution using the uniqueness method.
protectimus.com
In conclusion, the On-Premise 2FA Platform has a number of security features, such as the cloud and the versatility of security tokens. This will be a great addition to the development of your app and business in General.