Skip to content

Measurement Sync

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);