Measurement Sync (Android)¶
The measurement synchronization feature (available since v1.16.0) automatically uploads test results to AVEQ's servers. When enabled, the SDK queues completed measurements and uploads them to the server in the background. The queue is persistent (stored in a local database), so measurements survive app restarts and are retried on network failures.
Note
This feature is only available for select customers who have opted to have their measurement data hosted on AVEQ's servers. Standalone builds of the SDK do not include this functionality — measurements are only returned locally via the result callbacks, and no data is sent to AVEQ except for telemetry data. If you are interested in enabling measurement sync for your app, please contact AVEQ support!
Timing¶
Measurement data are queued immediately after a test completes. The upload is deferred while a test is actively running (to avoid network contention). In other words, the queue pauses uploads while a quality test is running. This is handled internally by the SDK — you don't need to manage this yourself. If you're implementing custom test orchestration, you can use:
// Check if a test is currently active
boolean testRunning = MeasurementQueueManager.isTestActive();
Queue Status¶
You can query the current state of the measurement queue:
import com.aveq.qualitytestlib.queue.MeasurementQueueManager;
// Get the number of pending measurements
int pending = MeasurementQueueManager.getPendingCount(context);
// Check if measurements are currently being sent
boolean sending = MeasurementQueueManager.isSending(context);
Broadcast Notifications¶
The SDK broadcasts local intents when measurements are sent successfully or fail. You can register a BroadcastReceiver to receive these notifications and update your UI accordingly.
The following broadcast actions and extras are available:
| Constant | Value | Description |
|---|---|---|
ACTION_MEASUREMENT_SENT |
com.aveq.qualitytestlib.MEASUREMENT_SENT |
Broadcast when a measurement is successfully uploaded |
ACTION_MEASUREMENT_FAILED |
com.aveq.qualitytestlib.MEASUREMENT_FAILED |
Broadcast when a measurement permanently fails to upload |
For ACTION_MEASUREMENT_SENT, the following extras are included:
| Extra | Type | Description |
|---|---|---|
EXTRA_MEASUREMENT_ID |
int |
The server-assigned measurement ID |
EXTRA_MEASUREMENT_TYPE |
String |
The measurement type (e.g., video_measurement, web_measurement) |
For ACTION_MEASUREMENT_FAILED, the following extra is included:
| Extra | Type | Description |
|---|---|---|
EXTRA_ERROR_MESSAGE |
String |
Description of the failure reason |
Example: Registering a BroadcastReceiver¶
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.aveq.qualitytestlib.queue.MeasurementSendWorker;
public class MyActivity extends AppCompatActivity {
private final BroadcastReceiver measurementReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (MeasurementSendWorker.ACTION_MEASUREMENT_SENT.equals(action)) {
int measurementId = intent.getIntExtra(
MeasurementSendWorker.EXTRA_MEASUREMENT_ID, -1);
String type = intent.getStringExtra(
MeasurementSendWorker.EXTRA_MEASUREMENT_TYPE);
// Handle successful upload
Log.i("MyApp", "Measurement uploaded: id=" + measurementId);
} else if (MeasurementSendWorker.ACTION_MEASUREMENT_FAILED.equals(action)) {
String error = intent.getStringExtra(
MeasurementSendWorker.EXTRA_ERROR_MESSAGE);
// Handle upload failure
Log.e("MyApp", "Measurement upload failed: " + error);
}
}
};
@Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter();
filter.addAction(MeasurementSendWorker.ACTION_MEASUREMENT_SENT);
filter.addAction(MeasurementSendWorker.ACTION_MEASUREMENT_FAILED);
LocalBroadcastManager.getInstance(this)
.registerReceiver(measurementReceiver, filter);
}
@Override
protected void onStop() {
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(measurementReceiver);
super.onStop();
}
}
Manual Queue Control¶
In most cases, the queue operates on its own. However, you can manually trigger an immediate sync attempt:
// Trigger an immediate sync attempt (if not already sending)
MeasurementQueueManager.scheduleImmediateSend(context);
If measurements get stuck in a "sending" state (e.g., due to a crash during upload), you can reset them:
// Reset stuck measurements back to "pending" status
MeasurementQueueManager.resetStuckMeasurements(context);
Retries¶
A measurement that cannot be delivered stays in the queue and is tried again later, with a growing delay between attempts.
How long it keeps trying depends on why the upload did not go through:
- The server could not be reached, because the device is offline or the server is down. The measurement keeps its place in the queue and is retried for as long as that lasts, so a device that spends a day out of coverage still delivers what it measured once it is back.
- The server answered with an error. Those attempts are counted, and the measurement is given up on after five of them, so that one the server will never accept does not sit in the queue forever.
- The server rejected the request outright, with a 4xx status. The measurement is given up on straight away, since trying again would not change the answer. The exceptions are a 401 and a 403, which ask the server whether it still accepts this device, see below.
When the Device Loses Its Registration¶
A measurement is uploaded under the identity of the device that took it, so when that identity goes, the queue goes with it. The SDK empties the queue whenever it discards the stored credentials: when a per-device usage limit runs out, when your app calls unregisterClientAsync(), and when the server refuses the device, which happens after it has been disabled in the Surfmeter Dashboard, after its license has run out, and when the credentials are not ones the server issued. See When the Server Stops Accepting a Device.
Nothing is uploaded after that, and no further test can be built, so there is nothing left for the queue to do. If your app shows the pending count, expect it to drop to zero at that point rather than staying stuck.