Run foreground service during Device Suspend Tests

The Device Suspend Tests verify that wake-up sensor events trigger a
device wake-up and the events are delivered to clients. Since these
tests require that the device enter the suspend state, it is possible
that the test application becomes idle while waiting for sensor
events. In Android P, applications that are idle are not able to
receive sensor events. Thus, if the test application becomes idle, it
will not receive the expected wake-up events, causing the tests to
erroneously fail.

This patch ensures that the test application maintains a foreground
service while it is running to ensure the application does not enter
the idle state and is able to receive sensor events.

Bug: 122817140
Test: Ensured the test application never enters the background state
      while the Device Suspend Tests are active.

Change-Id: I25944cc64a7471f2b5a6f3f070a6cbb6861c64fb
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index f8b21fb..9f035ff 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -2261,6 +2261,11 @@
                     android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.automotive" />
         </activity>
 
+        <service
+                android:name="com.android.cts.verifier.sensors.DeviceSuspendTestActivity$DeviceSuspendTestService"
+                android:label="@string/snsr_device_suspend_service"
+                android:icon="@drawable/icon" />
+
         <receiver android:name="com.android.cts.verifier.sensors.DeviceSuspendTestActivity$AlarmReceiver">
         </receiver>
 
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index bd60fef..2ddbdd2 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1016,6 +1016,9 @@
     firstEventReceivedMs=%2$d diffMs=%3$d toleranceMs=%4$d </string>
     <string name="snsr_device_suspend_test_instr">One you begin the test, disconnect USB, turn off the display and allow
     the device to go into suspend mode. The screen will turn on and a sound will be played once all the tests are completed.</string>
+    <string name="snsr_device_suspend_service">Device Suspend Service</string>
+    <string name="snsr_device_suspend_service_active">Device Suspend Test Active</string>
+    <string name="snsr_device_suspend_service_notification">Device Suspend Test is using sensors.</string>
 
     <!-- Significant Motion -->
     <string name="snsr_significant_motion_test">Significant Motion Tests</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java
index 217878d..a233250 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java
@@ -12,7 +12,12 @@
 import com.android.cts.verifier.sensors.helpers.SensorTestScreenManipulator;
 
 import android.app.AlarmManager;
+import android.app.IntentService;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.Service;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -35,6 +40,7 @@
 import android.hardware.cts.helpers.SensorNotSupportedException;
 import android.hardware.cts.helpers.sensorverification.BatchArrivalVerification;
 import android.hardware.cts.helpers.sensorverification.TimestampClockSourceVerification;
+import android.os.IBinder;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 import android.os.SystemClock;
@@ -75,6 +81,11 @@
             PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
             mDeviceSuspendLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                                                 "DeviceSuspendTestActivity");
+
+            // Launch a foreground service to ensure that the test remains in the foreground and is
+            // able to be woken-up when sensor data is delivered.
+            startForegroundService(new Intent(this, DeviceSuspendTestService.class));
+
             mDeviceSuspendLock.acquire();
             SensorTestLogger logger = getTestLogger();
             logger.logInstructions(R.string.snsr_device_suspend_test_instr);
@@ -93,6 +104,8 @@
             if (mDeviceSuspendLock != null && mDeviceSuspendLock.isHeld()) {
                 mDeviceSuspendLock.release();
             }
+
+            stopService(new Intent(this, DeviceSuspendTestService.class));
         }
 
         @Override
@@ -122,6 +135,39 @@
             }
         };
 
+        public static class DeviceSuspendTestService extends Service {
+            private static final String NOTIFICATION_CHANNEL_ID =
+                    "com.android.cts.verifier.sensors.DeviceSuspendTestActivity.Notification";
+            private static final String NOTIFICATION_CHANNEL_NAME = "Device Suspend Test";
+
+            @Override
+            public IBinder onBind(Intent intent) {
+                return null;
+            }
+
+            @Override
+            public int onStartCommand(Intent intent, int flags, int startId) {
+                NotificationChannel channel = new NotificationChannel(
+                        NOTIFICATION_CHANNEL_ID,
+                        NOTIFICATION_CHANNEL_NAME,
+                        NotificationManager.IMPORTANCE_DEFAULT);
+                NotificationManager notificationManager =
+                        getSystemService(NotificationManager.class);
+                notificationManager.createNotificationChannel(channel);
+                Notification notification =
+                        new Notification.Builder(getApplicationContext(), NOTIFICATION_CHANNEL_ID)
+                        .setContentTitle(getString(R.string.snsr_device_suspend_service_active))
+                        .setContentText(getString(
+                                R.string.snsr_device_suspend_service_notification))
+                        .setSmallIcon(R.drawable.icon)
+                        .setAutoCancel(true)
+                        .build();
+                startForeground(1, notification);
+
+                return START_NOT_STICKY;
+            }
+        }
+
         public String testAPWakeUpWhenReportLatencyExpiresAccel() throws Throwable {
             Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER, true);
             if (wakeUpSensor == null) {