Adjust communication with OneAgent SDK for Android
After instrumentation is complete, check the following aspects regarding communication with OneAgent.
Network security configuration
If your Android app has network security configured, ensure that the HTTP traffic to the beaconUrl
endpoint is not blocked by the network security configuration.
Firewall
Ensure that the GET and POST requests to the beaconUrl
endpoint are not blocked by a firewall.
Include certificates
For the HTTPS communication, OneAgent verifies the server certificate and the host name. OneAgent communication fails when the verification steps aren't successfully completed.
If your Cluster ActiveGate doesn't have a certificate that is issued by a trusted intermediate or root CA, you must provide a certificate for SSL communication. This can be done by either providing the server certificate in the network security configuration (for Android Target SDK 24 or later) or by including the certificate you are using into a KeyStore file and providing it to OneAgent by performing a manual startup via the DESKConfigurationBuilder
API.
Network security configuration
If you want to use the network security configuration feature, add a domain-config
section to your network-security-config
XML.
For example:
<domain-config>
<domain includeSubdomains="true">your.domain.com</domain>
<trust-anchors>
<certificates src="@raw/your_server_certificate" />
</trust-anchors>
</domain-config>
KeyStore object via ConfigurationBuilder
If you want to support Android API levels below 24 or don't want to use the network security configuration feature, provide a KeyStore
object. This object must hold the certificate chain of the Cluster ActiveGate that you want to connect to.
For example:
KeyStore trusted = KeyStore.getInstance("BKS");
try (InputStream in = getResources().openRawResource(R.raw.mykeystore)) {
trusted.load(in, "myverysecretpassword".toCharArray());
}
DESK.startup(this, new DESKConfigurationBuilder("<YourApplicationID>", "<ProvidedBeaconURL>")
.withKeyStore(trusted)
.buildConfiguration());
val trusted = KeyStore.getInstance("BKS")
resources.openRawResource(R.raw.mykeystore).use { inputStream ->
trusted.load(inputStream, "myverysecretpassword".toCharArray())
}
DESK.startup(this, DESKConfigurationBuilder("<YourApplicationID>", "<ProvidedBeaconURL>")
.withKeyStore(trusted)
.buildConfiguration()
)
Note: If you're using BouncyCastle as your keystore instance, ensure that the keystore is generated with BouncyCastle 1.45. The BouncyCastle 1.46 file format is not compatible with older versions of Android. For more details see Android AOSP issue.
Also note that if you define both, network security configuration and a keystore, the keystore takes precedence.
Deactivate certificate validation
You can also deactivate the certificate validation. Use this option with caution and not in production code as it dismantles the connection authenticity. Hostname verification can't be deactivated.
- via DESK Android Gradle plugin
You can deactivate the certificate validation via the certificateValidation
property.
desk {
configurations {
sampleConfig {
debug {
certificateValidation false
}
}
}
}
configure<com.desk.tools.android.dsl.DESKExtension> {
configurations {
create("sampleConfig") {
debug {
certificateValidation(false)
}
}
}
}
- via OneAgent SDK
You can also deactivate the certificate validation with the ConfigurationBuilder.withCertificateValidation(boolean)
method.
DESK.startup(this, new DESKConfigurationBuilder("<YourApplicationID>", "<ProvidedBeaconURL>")
.withCertificateValidation(false)
.buildConfiguration());
DESK.startup(this, DESKConfigurationBuilder("<YourApplicationID>", "<ProvidedBeaconURL>")
.withCertificateValidation(false)
.buildConfiguration())
Certificate pinning
To use certificate pinning, see the instructions provided by Android at network security configuration - Certificate pinning.
Custom HTTP headers
If the HTTP requests of OneAgent do not fulfil the security requirements of your server infrastructure, you can modify the HTTP headers of OneAgent with the DESK.setBeaconHeaders(Map<String, String>)
method. This feature allows you to add an Authorization
header to the HTTP requests and immediately reconnect to the Cluster ActiveGate when the token has expired. To delete the old headers call DESK.setBeaconHeaders(null)
.
Basic authorization
When the authorization information is already available at the app start, call the DESK.setBeaconHeaders
method before the starting up DESK.startup
method. Every HTTP request of the OneAgent will then have the correct headers.
Map<String, String> headers = new HashMap<>();
headers.put("Cookie", "n1=v1; n2=v2");
headers.put("ExampleHeader", "ExampleValue");
headers.put("Authorization", basicAuthorization(username, password));
DESK.setBeaconHeaders(headers);
DESK.startup(this, new DESKConfigurationBuilder("<YourApplicationID>", "<ProvidedBeaconURL>")
.buildConfiguration());
val headers = HashMap<String, String>()
headers["Cookie"] = "n1=v1; n2=v2"
headers["ExampleHeader"] = "ExampleValue"
headers["Authorization"] = basicAuthorization(username, password)
DESK.setBeaconHeaders(headers)
DESK.startup(this, DESKConfigurationBuilder("<YourApplicationID>", "<ProvidedBeaconURL>")
.buildConfiguration()
)
If the authorization information is not available at the app start, call the DESK.setBeaconHeaders
method when the information is available. The startup DESK.startup
method should still be called in the Application.onCreate
method to track the correct start time. OneAgent will be automatically deactivated when the server sends an invalid status code response. The DESK.setBeaconHeaders
method will activate OneAgent and will immediately reconnect to the Cluster ActiveGate.
Authorization with a token
If you use an authorization procedure, which requires you to regularly update a token, then you should add a CommunicationProblemListener
. The listener must be added via the DESKConfigurationBuilder
in the DESK.startup
method.
DESK.startup(this, new DESKConfigurationBuilder("<YourApplicationID>", "<ProvidedBeaconURL>")
.withCommunicationProblemListener(new YourDESKListener())
.buildConfiguration());
DESK.startup(this, DESKConfigurationBuilder("<YourApplicationID>", "<ProvidedBeaconURL>")
.withCommunicationProblemListener(YourDESKListener())
.buildConfiguration())
When you use a CommunicationProblemListener
, OneAgent communication behavior is slightly different from the normal behavior. If the Cluster ActiveGate reacts with an invalid status code, like 403 Forbidden
, OneAgent won't reconnect to the server. Instead, OneAgent will wait until you have specified the correct headers with the method DESK.setBeaconHeaders
. In this case, OneAgent will notify the CommunicationProblemListener
asynchronously in a background thread via the onFailure(int, String, String)
interface method. The following code snippet shows a sample implementation for the CommunicationProblemListener
interface:
public class YourDESKListener implements CommunicationProblemListener {
@Override
public void onFailure(int responseCode, String responseMessage, String body) {
String token = refreshToken();
DESK.setBeaconHeaders(generateAuthorizationHeader(token));
}
@Override
public void onError(Throwable throwable) {
//do nothing
}
}
class YourDESKListener : CommunicationProblemListener {
override fun onFailure(responseCode: Int, responseMessage: String?, body: String?) {
String token = refreshToken()
DESK.setBeaconHeaders(generateAuthorizationHeader(token))
}
override fun onError(throwable: Throwable?) {
//do nothing
}
}
The interface method onError(Throwable)
is asynchronously called when a communication problem occurs, such as a connection timeout or an SSL handshake error. In this case, OneAgent waits for a certain time and then reconnects to the Cluster ActiveGate. Normally you don't have to react on this callback method.
Offline monitoring
For efficiency, DESK does not accept monitoring data older than 10 minutes. If the app is not connected to the internet for a longer time period, OneAgent discards the old monitoring data and stops monitoring the app until the device establishes a new network connection.