Android 6.0 Fingerprint Identification app development case _android

Source: Internet
Author: User
Tags wrapper

In Android 6.0, Google finally added fingerprint recognition to the Android system, a feature that has already been implemented on the iphone and has been implemented in-house in a number of custom ROMs by many vendors, a bit late for this feature. In Google's new release of Nexus Devices: Nexus 5x and Nexus 6p are carrying a fingerprint identification chip on the back of the device, as shown in the following image (image from the network):

The equipment in the author's hand is the black Nexus 5x in the picture, saying this machine is very good-looking! Great touch!
Needless to say, below I have a fingerprint identification of the demo app, and detailed instructions on how to develop a Google API based fingerprint identification app. Demo of the source code on my github:
Https://github.com/CreateChance/AndroidFingerPrintDemo

Fingerprint identification interface in Android m

This is one of the first things to focus on, and before you actually start writing apps you need to know that the latest platforms provide us with those fingerprint-recognition interfaces. All fingerprint identification interfaces are all under the Android.hardware.fingerprint package, the classes in this package are not many, as follows:

API Doc Link Address:
Https://developer.android.com/reference/android/hardware/fingerprint/package-summary.html
We'd better fq to see for ourselves.
In the diagram above, we see a total of 4 classes in this package, and here's a brief description of them:
1.FingerprintManager: Mainly used for coordinated management and access to fingerprint identification hardware devices
2.fingerprintmanager.authenticationcallback This is a callback interface, when the fingerprint authentication system will callback this interface to notify the app certification results are what
3.fingerprintmanager.authenticationresult This is a class that represents the authentication result, which is given as a parameter in the callback interface
4.fingerprintmanager.cryptoobject This is an encrypted object class that is used to secure authentication, which is an important point that we will analyze below.
Okay, here's a quick overview of what Android 6.0 does for the fingerprint-recognition interface.

To develop a fingerprint identification app

Now, we're going to write a fingerprint-recognition app that uses the interface above, which is very simple, an activity that activates fingerprint recognition and then prompts the user to press the fingerprint and displays the results of the certification.

Begin

Before we begin, we need to know the basic steps for using fingerprint identification hardware:
1. In Androidmanifest.xml, the following rights are stated:

<uses-permission android:name= "Android.permission.USE_FINGERPRINT"/>

2. Access to Fingerprintmanager object references
3. The operation is to check the equipment fingerprint identification compatibility, such as whether there are fingerprint identification equipment. Let's say the above steps in detail:

Assert permission

This step is relatively simple, as long as the above mentioned in the Androidmanifest.xml to add the permissions on it.

Get Fingerprintmanager Object reference

This is a common way to obtain system service objects in app development, as follows:

Using the Android Support Library v4
Fingerprintmanager = Fingerprintmanagercompat.from (this);
Using API level:
Fingerprintmanager = (fingerprintmanager) getsystemservice (Context.fingerprint_service);

There are two ways to do this, the first is to get compatible object references through the V4 support package, which is Google's practice, and to get object references directly using interfaces in the API framework.

Check running conditions

To make our fingerprint-recognition app work, there are some conditions that must be met.

1). API level 23
The Fingerprint Identification API is added to API level 23, Android 6.0, so our app must be running on top of this system version. So Google recommends using the Android Support Library v4 package to get the Fingerprintmanagercompat object, because the packet checks the current system platform version when it is obtained.

2). Hardware
Fingerprint identification will definitely require fingerprint-aware hardware on your device, so you need to check the system for fingerprint-aware hardware at run time:

if (!fingerprintmanager.ishardwaredetected ()) {
 //No fingerprint sensor is detected, show dialog to tell user.
 Alertdialog.builder Builder = new Alertdialog.builder (this);
 Builder.settitle (r.string.no_sensor_dialog_title);
 Builder.setmessage (r.string.no_sensor_dialog_message);
 Builder.seticon (Android. r.drawable.stat_sys_warning);
 Builder.setcancelable (false);
 Builder.setnegativebutton (R.string.cancel_btn_dialog, New Dialoginterface.onclicklistener () {
  @Override
  public void OnClick (Dialoginterface dialog, int which) {
   finish ();
  }
 });
 Show this dialog.
 Builder.create (). Show ();


Call the above interface to know whether there is a system of such hardware, if not, then need to do something appropriate, such as prompting users the current system does not have fingerprint identification hardware.

3). The current device must be in security
This condition means that your device must be protected with a screen lock, which can be password,pin or patterned. Why is that so? Because Google's native logic is: To use fingerprint identification, you have to first make the screen lock to do, this is the same as the smart lock logic in Android 5.0, because Google believes that the current fingerprint identification technology is still inadequate, Security is still not comparable to the traditional approach.
We can use the following code to check if the current device is in security:

Keyguardmanager Keyguardmanager = (keyguardmanager) getsystemservice (context.keyguard_service);
if (Keyguardmanager.iskeyguardsecure ()) {
 //This device is secure.
}

We can tell by using the Keyguardmanager Iskeyguardsecure interface.

4). Is there a registered fingerprint in the system?
In Android 6.0, if a generic app wants to use fingerprint recognition, the user must first register at least one fingerprint in the setting, otherwise it won't work. So here we need to check if there are any registered fingerprint information in the current system:

if (!fingerprintmanager.hasenrolledfingerprints ()) {
 //No fingerprint image has been enrolled.
 Alertdialog.builder Builder = new Alertdialog.builder (this);
 Builder.settitle (r.string.no_fingerprint_enrolled_dialog_title);
 Builder.setmessage (r.string.no_fingerprint_enrolled_dialog_message);
 Builder.seticon (Android. r.drawable.stat_sys_warning);
 Builder.setcancelable (false);
 Builder.setnegativebutton (R.string.cancel_btn_dialog, New Dialoginterface.onclicklistener () {
  @Override
  public void OnClick (Dialoginterface dialog, int which) {
   finish ();
  }
 });
 Show this dialog
 Builder.create (). Show ();


If the user has not registered a fingerprint, then our app can prompt the user: If you want to use the fingerprint is the function, please setting to register one of your fingerprints. If you've ever done Bluetooth or other device development, you know you can start a Bluetooth-enabled interface by sending a intent, as long as you have the right to manage Bluetooth. However, Google is still not open to allow the General app to start the fingerprint registration interface, which we can see from setting's Androidmanifest:

<activity android:name= ". Fingerprint. Fingerprintsettings "android:exported=" false "/> <activity android:name=". Fingerprint. Fingerprintenrollonboard "android:exported=" false "/> <activity android:name=". Fingerprint. Fingerprintenrollfindsensor "android:exported=" false "/> <activity android:name=". Fingerprint. Fingerprintenrollenrolling "android:exported=" false "/> <activity android:name=". Fingerprint. Fingerprintenrollfinish "android:exported=" false "/> <activity android:name=". Fingerprint. Fingerprintenrollintroduction "android:exported=" false "/> <activity android:name=". Fingerprint. Setupfingerprintenrollonboard "android:exported=" false "/> <activity android:name=". Fingerprint. Setupfingerprintenrollfindsensor "android:exported=" false "/> <activity android:name=". Fingerprint. Setupfingerprintenrollenrolling "android:exported=" false "/> <activity android:name=". Fingerprint. Setupfingerprintenrollfinish "android:exported=" false "/> <activity android:name= ". Fingerprint. Setupfingerprintenrollintroduction "android:exported=" true "android:permission=" android.permission.MANAGE_ Fingerprint "android:theme=" @style/setupwizarddisableappstartingtheme "> <intent-filter> <action Android:name= "Android.settings.FINGERPRINT_SETUP"/> <category android:name= "

 Android.intent.category.DEFAULT "/> </intent-filter> </activity>

Most of the fingerprint settings have no exporte, only setupfingerprintenrollintroduction, but this interface requires android.permission.MANAGE_ Fingerprint this permission, and this permission can only be used by the system app, which prevents the third party app from starting the interface directly. (I don't know if Google will open this right in the future ...) )

A good app, you should check the above conditions at runtime to prevent accidental errors in the app.

Scan user's pressed fingerprint

To start scanning the user's pressed fingerprint is simple, just call the Fingerprintmanager authenticate method, and now let's take a look at this interface:

The image above is a description of Google's API documentation, and now let's explain what these parameters are:
1. Crypto this is an object of encryption, which the fingerprint scanner uses to determine the validity of the authentication result. This object can be null, but in this case, it means the result of the app's unconditional trust authentication, although theoretically this process may be attacked and the data can be tampered with, which is the risk that app must take in this situation. Therefore, it is recommended that this parameter not be null. The instantiation of this class is a bit troublesome, mainly using the Javax security interface implementation, later my demo program will give a helper class, this class encapsulates the logic of internal implementation, developers can directly use my class to simplify the instantiation process.
2. Cancel this is an object of the Cancellationsignal class, which is used when the fingerprint reader scans the user's fingerprint is the time to cancel the current scan operation, if not canceled, then the fingerprint scanner will be ported until the timeout (typically 30s, Depending on the manufacturer's implementation), this will be more power consumption. It is recommended that this parameter not be null.
3. Flags identification bit, according to the document described in the above figure, this bit should be 0 for the time being, this sign should be reserved for future use.
4. Callback This is the object of the Fingerprintmanager.authenticationcallback class, which is the most important parameter in this interface in addition to the first parameter. When the system completes the fingerprint authentication process (failure or success), it will callback the interface in this object and notify the app of the results of the authentication. This parameter cannot be null.
5. Handler this is the object of the handler class, and if this parameter is not NULL, then Fingerprintmanager will use the Looper in this handler to process messages from the fingerprint identification hardware. Generally speaking, development does not provide this parameter and can be set directly to NULL, because Fingerprintmanager will use the app's main looper to handle it by default.

Cancel the fingerprint scan.

Above we mentioned the cancellation of fingerprint scanning operations, this operation is very common. This can be accomplished by using the Cancel method of the Cancellationsignal class:

This method is specifically used to send a canceled command to a specific listener to cancel the current operation.
Therefore, the app can call the Cancel method when needed to cancel the fingerprint scan operation.

Creating Cryptoobject class Objects

When we analyze the authenticate method of Fingerprintmanager, we see that the first parameter of this method is the object of the Cryptoobject class, and now we look at how the object is instantiated.
We know that the reliability of fingerprint identification is very important, we certainly do not want the certification process to be a third party in some form of attack, because we introduced fingerprint authentication to improve security. However, from a theoretical point of view, the process of fingerprint authentication is likely to be malicious attacks by Third-party middleware, the common means of attack is to intercept and tamper with the results of the fingerprint recognizer. Here we can provide Cryptoobject objects to authenticate methods to avoid this form of attack.
Fingerprintmanager.cryptoobject is a wrapper class based on the Java Encryption API and is fingerprintmanager to ensure the integrity of the authentication results. Usually, the mechanism used to encrypt the fingerprint scan result is a Javax.Crypto.Cipher object. The Cipher object itself generates a key using the API invoked by the application to invoke the Android KeyStore to implement the protection described above.
To understand how these classes work together, here's a wrapper-class code to instantiate the Cryptoobject object, so let's look at how the code is implemented and explain why.

public class Cryptoobjecthelper {//This can is key name you want.
 Should is unique for the app.

 Static final String key_name = "Com.createchance.android.sample.fingerprint_authentication_key";
 We always use this keystore on Android.

 Static final String keystore_name = "Androidkeystore";
 Should be no need to change these values.
 Static final String key_algorithm = Keyproperties.key_algorithm_aes;
 Static final String Block_mode = KEYPROPERTIES.BLOCK_MODE_CBC;
 Static final String encryption_padding = KEYPROPERTIES.ENCRYPTION_PADDING_PKCS7;
 Static final String transformation = key_algorithm + "/" + Block_mode + "/" + encryption_padding;

 Final KeyStore _keystore;
  Public Cryptoobjecthelper () throws Exception {_keystore = Keystore.getinstance (keystore_name);
 _keystore.load (NULL); Public Fingerprintmanagercompat.cryptoobject Buildcryptoobject () throws Exception {Cipher Cipher = createcipher (tr
  UE); return new Fingerprintmanagercompat.cryptoobject (cipher);
  } Cipher Createcipher (Boolean retry) throws Exception {Key key = Getkey ();
  Cipher Cipher = cipher.getinstance (transformation); try {cipher.init (Cipher.encrypt_mode |
  Cipher.decrypt_mode, key);
   catch (Keypermanentlyinvalidatedexception e) {_keystore.deleteentry (key_name);
   if (retry) {Createcipher (false);
   else {throw new Exception ("Could not create" cipher for fingerprint authentication. ", e);
 } return cipher;
  Key Getkey () throws Exception {key secretkey;
  if (!_keystore.iskeyentry (key_name)) {CreateKey ();
  } Secretkey = _keystore.getkey (key_name, NULL);
 return secretkey;
  } void CreateKey () throws Exception {Keygenerator KeyGen = keygenerator.getinstance (Key_algorithm, keystore_name); Keygenparameterspec Keygenspec = new Keygenparameterspec.builder (Key_name, Keyproperties.purpose_encrypt | Keyproperties.purpose_decrypt). Setblockmodes (Block_mode). Setencryptionpaddings (encryption_padding). Setuserauthenticationrequired (True). Build ();
  Keygen.init (KEYGENSPEC);
 Keygen.generatekey ();

 }
}

The above class creates a new cipher object for each Cryptoobject object, and uses the key generated by the application. The name of the key is defined using the Key_name variable, which is supposed to be unique, and it is recommended that you use a domain name difference. The Getkey method attempts to use the Android KeyStore API to parse a key (the name is defined above), and if the key does not exist, call the CreateKey method to create a new key.
The instantiation of the cipher variable is obtained by invoking the Cipher.getinstance method, which takes a transformation parameter that makes the data encrypted and decrypted. The Cipher.init method is then invoked to complete the instantiation of the cipher object using the applied key.
One thing to emphasize here is that Android will assume that the current key is invalid in the following situations:
1. A new fingerprint image has been registered in the system
2. The previously registered fingerprint in the current device does not exist, it may have been removed
3. The user has turned off the screen lock function
4. User changes the way the screen locks
When the above situation occurs, the Cipher.init method throws the Keypermanentlyinvalidatedexception exception, which is captured in my code, and deletes the currently invalid key, and then attempts to create it again based on the parameters.
The above code uses the Android Keygenerator to create a key and store it in the device. The Keygenerator class creates a key, but requires some raw data to create the key, which is provided by the object of the Keygenparameterspec class. The instantiation of the Keygenerator class object is done using its factory method getinstance, from which we can see the AES (Advanced encryption Standard) encryption algorithm used here, and AES divides the data into several groups , and then encrypt for several groups.
Next, the Keygenparameterspec instantiation is the builder method of using it, Keygenparameterspec.builder encapsulates the following important information:
1. Key's name
2. Key must be valid for encryption and decryption
3. The above code Block_mode is set to cipher block chaining is KEYPROPERTIES.BLOCK_MODE_CBC, which means that each of the data blocks that have been cut by AES are different or calculated from the previous data blocks. The goal is to establish dependencies between each block of data.
4. The Cryptoobjecthelper class uses the PKSC7 (public Key Cryptography Standard #7) to produce bytes that populate the AES data block. This is to ensure that the size of each block is equivalent (because of the need for different or computational and other aspects of the algorithm for data processing, detailed view of AES algorithm principle).
5. Setuserauthenticationrequired (true) invocation means that the user's identity needs to be authenticated before the key is used.
Each time the Keygenparameterspec is created, he is used to initialize the Keygenerator, which produces the key stored on the device.

How do you use Cryptoobjecthelper?

Let's take a look at how to use the Cryptoobjecthelper class and we'll see the code directly:

Cryptoobjecthelper cryptoobjecthelper = new Cryptoobjecthelper ();
Fingerprintmanager.authenticate (Cryptoobjecthelper.buildcryptoobject (), 0,
       cancellationsignal, Myauthcallback, NULL);

Use is relatively simple, first new Cryptoobjecthelper object, and then call the Buildcryptoobject method can get Cryptoobject object.

Processing the user's fingerprint authentication results

Before we analyzed the authenticate interface, we said, This interface must be invoked when the object of the Fingerprintmanager.authenticationcallback class is provided, and the object will be called back to notify the results of the app certification after the fingerprint authentication is complete. In Android 6.0, fingerprint scanning and authentication is done in another process (fingerprint system service), so when the bottom is able to complete the authentication of our app can not be assumed. Therefore, we can only take the asynchronous mode of operation, that is, when the system is completed at the time of the initiative to notify us, The way to notify is by recalling the Fingerprintmanager.authenticationcallback class that we implemented ourselves, which defines some callback methods for us to do the necessary processing:

Here's a brief description of what these interfaces mean:
1. Onauthenticationerror (int errorcode, icharsequence errstring) This interface will be called when the system fingerprint authentication occurs unrecoverable error, and the parameter errorcode gives the error code, Identifies the cause of the error. The only thing the app can do at this point is to prompt the user to try again.
2. onauthenticationfailed () This interface will be callback if the system fingerprint authentication fails. Note that the authentication failure here and the above authentication error is not the same, although the results are not certified. Authentication failure means that all information is collected intact, and there is no exception, but the fingerprint is not consistent with the previously registered fingerprint, but the authentication error refers to the error in the process of collecting or authenticating, such as the abnormal work of the fingerprint sensor. That is to say, authentication failure is a normal condition that can be expected, and authentication errors are unexpected anomalies.
3. Onauthenticationhelp (int helpmsgid, icharsequence helpstring) The authentication failure above is an exception in the authentication process, we say that because of an unrecoverable error, The Onauthenticationhelp method here is that there is an exception that can be answered before it is invoked. What are the exceptions that can be recovered? A common example is that the fingers move too fast, and when we put our fingers on the sensor, if we move our fingers quickly, the fingerprint sensor may only collect some information, so the authentication fails. But this error can be recovered, so just prompt the user to press the fingerprint again, and do not remove too quickly to resolve.
4. onauthenticationsucceeded (fingerprintmanagercompati.authenticationresult result) This interface will be recalled after successful authentication. We can prompt the user to authenticate successfully in this method. Here's what we need to say, if our cryptoobject is not NULL when we call authenticate, Then we can use Authenticationresult to get the Cypher object and then call its Dofinal method in this method. The Dofinal method checks whether the result is blocked or tampered with and throws an exception if it is. When we find these anomalies, we should treat the authentication as a failure, and we will do so for security advice.

There are 2 more points to add to the above interface:
1. Above we said that there would be errors or help codes in the Onauthenticationerror and Onauthenticationhelp methods to indicate why the authentication was unsuccessful. The Android system defines several bugs and help codes in the Fingerprintmanager class, as follows:

It is best to handle these errors and help codes when our callback class is implemented.

2. When the fingerprint scanner is working, if we cancel this operation, the system will also callback Onauthenticationerror method, only this time the error code is FINGERPRINTMANAGER.FINGERPRINT_ERROR_ CANCELED (a value of 5), so the app needs to be treated differently.
Here is a callback subclass of the implementation in my Code:

Package Com.createchance.fingerprintdemo;
Import Android.os.Handler;

Import Android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
Import javax.crypto.BadPaddingException;

Import javax.crypto.IllegalBlockSizeException;
 /** * Created by Baniel on 7/21/16. 

 * * public class Myauthcallback extends Fingerprintmanagercompat.authenticationcallback {private Handler Handler = null;

  Public Myauthcallback (Handler Handler) {super ();
 This.handler = handler; @Override public void Onauthenticationerror (int errmsgid, charsequence errstring) {Super.onauthenticationerror (err

  MsgId, errstring);
  if (handler!= null) {handler.obtainmessage (mainactivity.msg_auth_error, Errmsgid, 0). Sendtotarget (); @Override public void onauthenticationhelp (int helpmsgid, charsequence helpstring) {super.onauthenticationhelp (

  Helpmsgid, helpstring);
  if (handler!= null) {handler.obtainmessage (mainactivity.msg_auth_help, Helpmsgid, 0). Sendtotarget (); }} @OverriDe public void onauthenticationsucceeded (Fingerprintmanagercompat.authenticationresult result) {

  super.onauthenticationsucceeded (result);

   try {result.getcryptoobject (). Getcipher (). dofinal ();
   if (handler!= null) {handler.obtainmessage (mainactivity.msg_auth_success). Sendtotarget ();
  } catch (Illegalblocksizeexception e) {e.printstacktrace ();
  catch (Badpaddingexception e) {e.printstacktrace ();

  @Override public void onauthenticationfailed () {super.onauthenticationfailed ();
  if (handler!= null) {handler.obtainmessage (mainactivity.msg_auth_failed). Sendtotarget ();

 }
 }
}

This subclass implementation is very simple, the main implementation is to throw the message to the main interface of the handler to deal with:

Handler = new Handler () {
 @Override public
 void handlemessage (Message msg) {
  super.handlemessage (msg);

  LOG.D (TAG, "msg:" + Msg.what + ", arg1:" + msg.arg1);
  Switch (msg.what) {case
   msg_auth_success:
    setresultinfo (r.string.fingerprint_success);
    Mcancelbtn.setenabled (false);
    Mstartbtn.setenabled (true);
    cancellationsignal = null;
    break;
   Case msg_auth_failed:
    setresultinfo (r.string.fingerprint_not_recognized);
    Mcancelbtn.setenabled (false);
    Mstartbtn.setenabled (true);
    cancellationsignal = null;
    break;
   Case Msg_auth_error:
    handleerrorcode (MSG.ARG1);
    break;
   Case MSG_AUTH_HELP:
    handlehelpcode (MSG.ARG1);
    break;
  }
 }
;

This handles the four-Handleerrorcode callback separately, and calls the method handle for the error code:

private void Handleerrorcode (int code) {
 switch (code) {Case
  fingerprintmanager.fingerprint_error_canceled:
   Setresultinfo (r.string.errorcanceled_warning);
   break;
  Case fingerprintmanager.fingerprint_error_hw_unavailable:
   setresultinfo (r.string.errorhwunavailable_warning );
   break;
  Case Fingerprintmanager.fingerprint_error_lockout:
   setresultinfo (r.string.errorlockout_warning);
   break;
  Case Fingerprintmanager.fingerprint_error_no_space:
   setresultinfo (r.string.errornospace_warning);
   break;
  Case Fingerprintmanager.fingerprint_error_timeout:
   setresultinfo (r.string.errortimeout_warning);
   break;
  Case fingerprintmanager.fingerprint_error_unable_to_process:
   setresultinfo (R.string.errorunabletoprocess_ Warning);
   break;
 }
}

Very simple, that is, for different error codes, set the interface of different display text to prompt the user. Here you can change the logic according to your own needs.
Call Handlehelpcode method processing for help code:

private void Handlehelpcode (int code) {
 switch (code) {Case
  Fingerprintmanager.fingerprint_acquired_good:
   Setresultinfo (r.string.acquiredgood_warning);
   break;
  Case Fingerprintmanager.fingerprint_acquired_imager_dirty:
   setresultinfo (r.string.acquiredimagedirty_ Warning);
   break;
  Case fingerprintmanager.fingerprint_acquired_insufficient:
   setresultinfo (R.string.acquiredinsufficient_ Warning);
   break;
  Case fingerprintmanager.fingerprint_acquired_partial:
   setresultinfo (r.string.acquiredpartial_warning);
   break;
  Case Fingerprintmanager.fingerprint_acquired_too_fast:
   setresultinfo (r.string.acquiredtoofast_warning);
   break;
  Case Fingerprintmanager.fingerprint_acquired_too_slow:
   setresultinfo (r.string.acquiredtoslow_warning);
   break;
 }
}

The processing here is the same as the Handleerrorcode.

Summarize

Here are a few of the key points of fingerprint identification development on Android 6.0:
1. It is recommended that you use the Android Support Library V4 compatibility API instead of using the APIs in the direct framework.
2. Before using fingerprint hardware, be sure to check a few of the above mentioned inspection conditions
3. According to Google's recommendations it is best to use Google's fingerprint is the icon to indicate your fingerprint identification interface:

The purpose of this is to explicitly prompt the user that this is a fingerprint recognition operation, just as people see the small logo Bluetooth to know that this is the same Bluetooth operation. Of course, this is only a practical recommendation of Google, not mandatory.
4. App needs to inform users of the current operation and the results of the operation, such as the need to explicitly tell the user is currently scanning fingerprint, please put your fingerprint on the sensor.
5. The final note is that the Fingerprintmanager class name in the Android Support Library V4 is Fingerprintmanagercompat, and their authenticate method parameter order is different, The position of flags and cancel is not the same in two classes, and this needs to be noted (personally, will this be Google's fault???) Hey..... )

Demo Running effect screenshot (run in Nexus 5x)

Initial state


Scan status


Scan failed (there is a recoverable error, here is the finger moving too fast)


Authentication failed


Certified Successful

The above is the entire content of this article, I hope to help you learn, but also hope that we support the cloud habitat community.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.