Skip to content

OboeTester should use AlarmManager for the duration in automatic tests #2289

@robertwu1

Description

@robertwu1

The current method of using handlers to schedule stops after a certain duration is not good because the app can go to the suspend state.

                Handler handler = new Handler(Looper.getMainLooper()); // UI thread
                handler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        stopAutomaticTest();
                    }
                }, durationSeconds * 1000);

We should use AlarmManager instead.

Recommended Solution: AlarmManager
The AlarmManager is an Android system service that lets you schedule your application to run at some point in the future. The OS holds the alarm for your app, so it will fire even if your app is not running.

Here’s how you can implement it:

  1. Create a BroadcastReceiver
    This receiver will listen for the alarm and execute your stopAutomaticTest() method when it fires.
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class TestTimeoutReceiver extends BroadcastReceiver {
    private static final String TAG = "TestTimeoutReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "Alarm received. Stopping test.");
        // You might need a way to get a reference to your activity
        // or a singleton class to call the stop method.
        // For example, if your test runner is a singleton:
        // AutomatedTestRunner.getInstance().stopAutomaticTest();
        
        // Or send a local broadcast back to your active activity.
    }
}
  1. Register the Receiver in AndroidManifest.xml
<manifest ...>
    <application ...>
        <receiver android:name=".TestTimeoutReceiver" android:exported="false"/>
        ...
    </application>
</manifest>
  1. Schedule and Cancel the Alarm
    Modify your code to schedule an alarm instead of using the Handler.
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;

public class MyTestScheduler {

    private static final int REQUEST_CODE = 12345;

    public void startTestWithTimeout(Context context, int durationSeconds) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, TestTimeoutReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(
                context,
                REQUEST_CODE,
                intent,
                PendingIntent.FLAG_IMMUTABLE // Use FLAG_IMMUTABLE for security
        );

        // Use elapsed real time, which is better for intervals as it's not affected by clock changes.
        long triggerAtMillis = SystemClock.elapsedRealtime() + durationSeconds * 1000L;

        // setExactAndAllowWhileIdle is needed to ensure the alarm fires precisely
        // even when the device is in a low-power idle (Doze) mode.
        if (alarmManager != null) {
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis, pendingIntent);
        }
    }

    public void cancelTestTimeout(Context context) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, TestTimeoutReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(
                context,
                REQUEST_CODE,
                intent,
                PendingIntent.FLAG_IMMUTABLE
        );

        if (alarmManager != null) {
            alarmManager.cancel(pendingIntent);
        }
    }
}
Important Note on Permissions: For Android 12 (API 31) and higher, apps that target this SDK version must have the SCHEDULE_EXACT_ALARM permission to use setExactAndAllowWhileIdle(). Since this is for a testing tool, it's a reasonable permission to request.

Add this to your AndroidManifest.xml:

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions