1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 package com.android.cts.verifier.sensors;
17 
18 import android.app.AlarmManager;
19 import android.app.PendingIntent;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.hardware.Sensor;
25 import android.hardware.SensorEvent;
26 import android.hardware.SensorEventListener;
27 import android.hardware.SensorManager;
28 import android.hardware.cts.helpers.SensorNotSupportedException;
29 import android.hardware.cts.helpers.SuspendStateMonitor;
30 import android.os.PowerManager;
31 import android.os.SystemClock;
32 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
33 import android.util.Log;
34 
35 import com.android.cts.verifier.R;
36 import com.android.cts.verifier.sensors.helpers.SensorTestScreenManipulator;
37 import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
38 
39 import java.util.concurrent.CountDownLatch;
40 import java.util.concurrent.TimeUnit;
41 
42 import junit.framework.Assert;
43 import static junit.framework.Assert.fail;
44 
45 
46 /**
47  * Manual test for testing the low-latency offbody detect sensor. This test consists of 3
48  * sub-tests designed to verify the sensor event data, verify event trigger response times
49  * are within spec for the sensor type, and to verify that the sensor can wake the device.
50  */
51 public class OffBodySensorTestActivity
52         extends SensorCtsVerifierTestActivity {
53     private static final String TAG="OffbodySensorTest";
54     private static String ACTION_ALARM = "OffBodySensorTestActivity.ACTION_ALARM";
55     private static final int MAX_OFF_BODY_EVENT_LATENCY_MS = 1000;
56     private static final int MAX_ON_BODY_EVENT_LATENCY_MS = 5000;
57     private static final int COUNTDOWN_INTERVAL_MS = 1000;
58     private static final int LLOB_EVENT_MAX_DELAY_SEC = 20;
59     private static final long MAX_ALLOWED_DELAY_MS = TimeUnit.SECONDS.toMillis(1);
60     private static final long RESULT_REPORT_SHOW_TIME_MS = TimeUnit.SECONDS.toMillis(5);
61     private static final int OFFBODY_EVENT_VALUES_LENGTH = 1;
62     private static final int COUNTDOWN_NUM_INTERVALS = 3;
63 
64     private static final float OFF_BODY_EVENT_VALUE = 0;
65     private static final float ON_BODY_EVENT_VALUE = 1;
66     private static final float BAD_VALUE_SEEN_INIT = 0;
67     private static float mBadValueSeen = BAD_VALUE_SEEN_INIT;
68 
69     private enum State {
70         OFF_BODY, ON_BODY, UNKNOWN
71     }
72 
73     // time to wait for offbody event after the device has gone into suspend. Even after
74     // 45 secs if LLOB sensor does not trigger, the test will fail.
75     private static final long ALARM_WAKE_UP_AP_DELAY_MS = TimeUnit.SECONDS.toMillis(45);
76 
77     // acceptable time difference between event time and AP wake up time.
78     private static final long MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS =
79             TimeUnit.MILLISECONDS.toNanos(1200);
80 
81     private static final int NANOSECONDS_PER_MILLISECOND = 1000000;
82     private AlarmManager mAlarmManager;
83     private SensorManager mSensorManager;
84     private Sensor mOffBodySensor;
85     private boolean mOffBodySensorRegistered;
86     private long mTestStartTimestampMs;
87     private State mPreviousSensorState;
88     private PendingIntent mPendingIntent;
89     private PowerManager.WakeLock mDeviceSuspendLock;
90     private SensorEventVerifier mVerifier;
91     private SensorTestScreenManipulator mScreenManipulator;
92 
93     public class SensorEventRegistry {
94         public final SensorEvent event;
95         public final long receiveTimestampNanos;
96 
SensorEventRegistry(SensorEvent event, long realtimeTimestampNanos)97         public SensorEventRegistry(SensorEvent event, long realtimeTimestampNanos) {
98             this.event = event;
99             this.receiveTimestampNanos = realtimeTimestampNanos;
100         }
101     }
102 
103     private class SensorEventVerifier implements SensorEventListener {
104         private volatile CountDownLatch mCountDownLatch;
105         private volatile SensorEventRegistry mEventRegistry;
106         private volatile long mTimestampForLastSensorEvent = 0;
107 
108         @Override
onAccuracyChanged(Sensor sensor, int accuracy)109         public void onAccuracyChanged(Sensor sensor, int accuracy) {}
110 
111         @Override
onSensorChanged(SensorEvent event)112         public void onSensorChanged(SensorEvent event) {
113             long elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
114             int type = event.sensor.getType();
115 
116             if (type == Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT) {
117                 switch((int) event.values[0]) {
118                     case (int) OFF_BODY_EVENT_VALUE:
119                         Log.i(TAG, "onSensorChanged(): OFF_BODY ts="+event.timestamp+
120                                 ", now="+elapsedRealtimeNanos+", delta="+
121                                 (elapsedRealtimeNanos-event.timestamp)/1000000+"mS");
122                         mPreviousSensorState = State.OFF_BODY;
123                         break;
124                     case (int) ON_BODY_EVENT_VALUE:
125                         Log.i(TAG, "onSensorChanged(): ON_BODY ts = "+event.timestamp+
126                                 ", now="+elapsedRealtimeNanos+", delta="+
127                                 (elapsedRealtimeNanos-event.timestamp)/1000000+"mS");
128                         mPreviousSensorState = State.ON_BODY;
129                         break;
130                     default:
131                         Log.e(TAG, "onSensorChanged(): invalid value "+event.values[0]+
132                                 " received");
133                         mBadValueSeen = event.values[0];
134                         break;
135                 }
136                 mEventRegistry = new SensorEventRegistry(event, elapsedRealtimeNanos);
137                 getTestLogger().logMessage(
138                         R.string.snsr_offbody_state_change,
139                         (int) event.values[0],
140                         elapsedRealtimeNanos);
141                 releaseLatch();
142             }
143         }
144 
releaseLatch()145         public void releaseLatch() {
146             if (mCountDownLatch != null) {
147                 mCountDownLatch.countDown();
148             }
149         }
150 
getTimeStampForSensorEvent()151         public long getTimeStampForSensorEvent() {
152             return mTimestampForLastSensorEvent;
153         }
154 
awaitAndVerifyEvent(float expectedResponseValue)155         public String awaitAndVerifyEvent(float expectedResponseValue) throws Throwable {
156             return awaitAndVerifyEvent(expectedResponseValue, 0);
157         }
158 
awaitAndVerifyEvent(float expectedResponseValue, int maxEventLatencyMs)159         public String awaitAndVerifyEvent(float expectedResponseValue, int maxEventLatencyMs)
160                 throws Throwable {
161             SensorEventRegistry registry = waitForEvent();
162             String eventArrivalMessage;
163             if ((registry == null) || (registry.event == null)) {
164                 eventArrivalMessage = getString(R.string.snsr_offbody_event_arrival, false);
165                 Assert.fail(eventArrivalMessage);
166             }
167 
168             // verify an event arrived, and it is indeed a Low Latency Offbody Detect event
169             SensorEvent event = registry.event;
170             eventArrivalMessage = getString(R.string.snsr_offbody_event_arrival, event != null);
171             Assert.assertNotNull(eventArrivalMessage, event);
172 
173             String result = verifyEvent(registry, expectedResponseValue, maxEventLatencyMs);
174             return result;
175         }
176 
verifyEvent(SensorEventRegistry registry, float expectedResponseValue, int maxEventLatencyMs)177         public String verifyEvent(SensorEventRegistry registry, float expectedResponseValue,
178                 int maxEventLatencyMs) throws Throwable {
179             int eventType = registry.event.sensor.getType();
180             String eventTypeMessage = getString(
181                     R.string.snsr_offbody_event_type,
182                     Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT,
183                     eventType);
184             Assert.assertEquals(eventTypeMessage,
185                     Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT,
186                     eventType);
187 
188             float value = registry.event.values[0];
189             String sensorName = registry.event.sensor.getName();
190             String eventName = (value == ON_BODY_EVENT_VALUE) ? "ON-BODY" : "OFF-BODY";
191 
192             long eventLatencyMs = (registry.receiveTimestampNanos/NANOSECONDS_PER_MILLISECOND)
193                     - mTestStartTimestampMs;
194 
195             int valuesLength = registry.event.values.length;
196             String valuesLengthMessage = getString(
197                     R.string.snsr_event_length,
198                     OFFBODY_EVENT_VALUES_LENGTH,
199                     valuesLength,
200                     sensorName);
201             Assert.assertEquals(valuesLengthMessage, OFFBODY_EVENT_VALUES_LENGTH, valuesLength);
202 
203             String valuesMessage = getString(
204                     R.string.snsr_event_value,
205                     expectedResponseValue,
206                     value,
207                     sensorName);
208             Assert.assertEquals(valuesMessage, expectedResponseValue, value);
209 
210             if (maxEventLatencyMs != 0) {
211                 Log.i(TAG, "event latency was "+eventLatencyMs+" ms for "+
212                         eventName+" event");
213                 String responseViolationMessage = getString(
214                     R.string.snsr_offbody_response_timing_violation,
215                     eventName,
216                     maxEventLatencyMs,
217                     eventLatencyMs);
218                 boolean violation = (eventLatencyMs > maxEventLatencyMs);
219                 Assert.assertFalse(responseViolationMessage, violation);
220             }
221             return null;
222         }
223 
verifyOffbodyEventNotInvalid()224         private void verifyOffbodyEventNotInvalid() throws InterruptedException {
225             if (mBadValueSeen != BAD_VALUE_SEEN_INIT) {
226                 Assert.fail(
227                     String.format(getString(R.string.snsr_offbody_event_invalid_value),
228                     OFF_BODY_EVENT_VALUE,
229                     ON_BODY_EVENT_VALUE,
230                     mBadValueSeen));
231             }
232         }
233 
waitForEvent()234         private SensorEventRegistry waitForEvent() throws InterruptedException {
235             return waitForEvent(null);
236         }
237 
waitForEvent(PowerManager.WakeLock suspendLock)238         private SensorEventRegistry waitForEvent(PowerManager.WakeLock suspendLock)
239                 throws InterruptedException {
240             mCountDownLatch = new CountDownLatch(1);
241 
242             if ((suspendLock != null) && suspendLock.isHeld()) {
243                 suspendLock.release();
244             }
245 
246             mCountDownLatch.await(LLOB_EVENT_MAX_DELAY_SEC, TimeUnit.SECONDS);
247 
248             if ((suspendLock != null) && !suspendLock.isHeld()) {
249                 suspendLock.acquire();
250             }
251 
252             SensorEventRegistry registry = mEventRegistry;
253 
254             // Save the last timestamp when the event triggered.
255             if (mEventRegistry != null && mEventRegistry.event != null) {
256                 mTimestampForLastSensorEvent = mEventRegistry.event.timestamp;
257             }
258 
259             mEventRegistry = null;
260             verifyOffbodyEventNotInvalid();
261             return registry != null ? registry : new SensorEventRegistry(null, 0);
262         }
263 
waitForSensorEvent()264         public SensorEvent waitForSensorEvent() throws InterruptedException {
265             SensorEvent event = null;
266             mCountDownLatch = new CountDownLatch(1);
267             mCountDownLatch.await(LLOB_EVENT_MAX_DELAY_SEC, TimeUnit.SECONDS);
268 
269             if (mEventRegistry != null && mEventRegistry.event != null) {
270                 event = mEventRegistry.event;
271             }
272 
273             mEventRegistry = null;
274             verifyOffbodyEventNotInvalid();
275             return event;
276         }
277     }
278 
OffBodySensorTestActivity()279     public OffBodySensorTestActivity() {
280         super(OffBodySensorTestActivity.class);
281     }
282 
283 
284     @Override
activitySetUp()285     protected void activitySetUp() throws InterruptedException {
286         PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
287         mDeviceSuspendLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
288                                             "OffBodySensorTestActivity");
289         mDeviceSuspendLock.acquire();
290         mOffBodySensorRegistered = false;
291         mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
292         mOffBodySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT,
293                 true);
294         if (mOffBodySensor == null) {
295             setTestResultAndFinish(true);
296             return;
297         }
298         LocalBroadcastManager.getInstance(this).registerReceiver(myBroadCastReceiver,
299                                         new IntentFilter(ACTION_ALARM));
300         Intent intent = new Intent(this, AlarmReceiver.class);
301         mPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
302         mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
303         mScreenManipulator = new SensorTestScreenManipulator(this);
304         try {
305             mScreenManipulator.initialize(this);
306         } catch (InterruptedException e) {
307         }
308     }
309 
startTimeoutTimer(long delayMs)310     private void startTimeoutTimer(long delayMs) {
311         mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
312                                 SystemClock.elapsedRealtime() + delayMs,
313                                 mPendingIntent);
314     }
315 
stopTimeoutTimer()316     private void stopTimeoutTimer() {
317         mAlarmManager.cancel(mPendingIntent);
318     }
319 
stopOffbodySensorListener(SensorEventVerifier verifier)320     private void stopOffbodySensorListener(SensorEventVerifier verifier) {
321         if (mOffBodySensorRegistered) {
322             mSensorManager.unregisterListener(verifier);
323             mOffBodySensorRegistered = false;
324         }
325     }
326 
startOffbodySensorListener(SensorEventVerifier verifier)327     private boolean startOffbodySensorListener(SensorEventVerifier verifier) {
328         if (!mOffBodySensorRegistered) {
329             if (!mSensorManager.registerListener(verifier, mOffBodySensor,
330                         SensorManager.SENSOR_DELAY_FASTEST)) {
331                 Log.e(TAG, "error registering listener for LLOB");
332                 setTestResultAndFinish(true);
333                 return false;
334             }
335             mOffBodySensorRegistered = true;
336         }
337         return true;
338     }
339 
340     public static class AlarmReceiver extends BroadcastReceiver {
341         @Override
onReceive(Context context, Intent intent)342         public void onReceive(Context context, Intent intent) {
343             Intent alarm_intent = new Intent(context, OffBodySensorTestActivity.class);
344             alarm_intent.setAction(OffBodySensorTestActivity.ACTION_ALARM);
345             LocalBroadcastManager.getInstance(context).sendBroadcastSync(alarm_intent);
346         }
347     }
348 
349     public BroadcastReceiver myBroadCastReceiver = new BroadcastReceiver() {
350         @Override
351         public void onReceive(Context context, Intent intent) {
352             mVerifier.releaseLatch();
353             mScreenManipulator.turnScreenOn();
354             if (!mDeviceSuspendLock.isHeld()) {
355                 mDeviceSuspendLock.acquire();
356             }
357         }
358     };
359 
testOffbodyDetectResponseTime()360     public String testOffbodyDetectResponseTime() throws Throwable {
361         Sensor wakeUpSensor = mSensorManager.getDefaultSensor(
362                 Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT, true);
363         if (wakeUpSensor == null) {
364             throw new SensorNotSupportedException(Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT, true);
365         }
366         return runOffbodyDetectResponseTimeTest(wakeUpSensor);
367     }
368 
testOnbodyDetectResponseTime()369     public String testOnbodyDetectResponseTime() throws Throwable {
370         Sensor wakeUpSensor = mSensorManager.getDefaultSensor(
371                 Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT, true);
372         if (wakeUpSensor == null) {
373             throw new SensorNotSupportedException(Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT, true);
374         }
375         return runOnbodyDetectResponseTimeTest(wakeUpSensor);
376     }
377 
testWakeAPOffbodyDetect()378     public String testWakeAPOffbodyDetect() throws Throwable {
379         Sensor wakeUpSensor = mSensorManager.getDefaultSensor(
380                 Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT, true);
381         if (wakeUpSensor == null) {
382             throw new SensorNotSupportedException(Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT, true);
383         }
384         return runWakeAPOffbodyDetectTest(wakeUpSensor);
385     }
386 
runOffbodyDetectResponseTimeTest(Sensor sensor)387     public String runOffbodyDetectResponseTimeTest(Sensor sensor) throws Throwable {
388         boolean success;
389         String eventArrivalMessage;
390         mOffBodySensor = sensor;
391         mBadValueSeen = BAD_VALUE_SEEN_INIT;
392 
393         try {
394             // If device not currently on-body, instruct user to put it on wrist
395             mTestStartTimestampMs = 0;
396             mVerifier = new SensorEventVerifier();
397             success = startOffbodySensorListener(mVerifier);
398             Assert.assertTrue(
399                     getString(R.string.snsr_offbody_sensor_registration, success),
400                     success);
401             SensorEvent event = mVerifier.waitForSensorEvent();
402             eventArrivalMessage = getString(R.string.snsr_offbody_event_arrival, event != null);
403             Assert.assertNotNull(eventArrivalMessage, event);
404 
405             SensorTestLogger logger = getTestLogger();
406             if (event.values[0] != ON_BODY_EVENT_VALUE) {
407                 // Instruct user on how to perform offbody detect test
408                 logger.logInstructions(R.string.snsr_start_offbody_sensor_test_instr);
409                 waitForUserToBegin();
410                 if (mPreviousSensorState != State.ON_BODY) {
411                     event = mVerifier.waitForSensorEvent();
412                     eventArrivalMessage = getString(R.string.snsr_offbody_event_arrival,
413                             event != null);
414                     Assert.assertNotNull(eventArrivalMessage, event);
415                     if (event.values[0] != ON_BODY_EVENT_VALUE) {
416                         Assert.fail(
417                             String.format(getString(R.string.snsr_offbody_event_wrong_value),
418                             ON_BODY_EVENT_VALUE,
419                             event.values[0]));
420                     }
421                 }
422             }
423 
424             // Instruct user on how to perform offbody detect test
425             logger.logInstructions(R.string.snsr_offbody_detect_test_instr);
426             waitForUserToBegin();
427 
428             // Count down before actually starting, leaving time to react after pressing the Next
429             // button.
430             for (int i = 0; i < COUNTDOWN_NUM_INTERVALS; i++) {
431                 try {
432                     Thread.sleep(COUNTDOWN_INTERVAL_MS);
433                 } catch (InterruptedException e) {
434                     // Ignore the interrupt and continue counting down.
435                 }
436                 logger.logInstructions(R.string.snsr_offbody_detect_test_countdown,
437                         COUNTDOWN_NUM_INTERVALS - i - 1);
438             }
439             mTestStartTimestampMs = SystemClock.elapsedRealtime();
440 
441             // Verify off-body event latency is within spec
442             mVerifier.awaitAndVerifyEvent(OFF_BODY_EVENT_VALUE, MAX_OFF_BODY_EVENT_LATENCY_MS);
443         } finally {
444             stopOffbodySensorListener(mVerifier);
445         }
446         return null;
447     }
448 
runOnbodyDetectResponseTimeTest(Sensor sensor)449     public String runOnbodyDetectResponseTimeTest(Sensor sensor) throws Throwable {
450         mOffBodySensor = sensor;
451         SensorTestLogger logger = getTestLogger();
452         mBadValueSeen = BAD_VALUE_SEEN_INIT;
453 
454         try {
455             // If device not currently off-body, instruct user to remove it from wrist
456             mTestStartTimestampMs = 0;
457             mVerifier = new SensorEventVerifier();
458             boolean success = startOffbodySensorListener(mVerifier);
459             Assert.assertTrue(
460                     getString(R.string.snsr_offbody_sensor_registration, success),
461                     success);
462             SensorEvent event = mVerifier.waitForSensorEvent();
463             String eventArrivalMessage = getString(R.string.snsr_offbody_event_arrival,
464                     event != null);
465             Assert.assertNotNull(eventArrivalMessage, event);
466             if (event.values[0] != OFF_BODY_EVENT_VALUE) {
467                 // Instruct user on how to perform offbody detect test
468                 logger.logInstructions(R.string.snsr_start_onbody_sensor_test_instr);
469                 waitForUserToBegin();
470                 if (mPreviousSensorState != State.OFF_BODY) {
471                     event = mVerifier.waitForSensorEvent();
472                     eventArrivalMessage = getString(R.string.snsr_offbody_event_arrival,
473                             event != null);
474                     Assert.assertNotNull(eventArrivalMessage, event);
475                     if (event.values[0] != OFF_BODY_EVENT_VALUE) {
476                         Assert.fail(
477                             String.format(getString(R.string.snsr_offbody_event_wrong_value),
478                             OFF_BODY_EVENT_VALUE,
479                             event.values[0]));
480                     }
481                 }
482             }
483 
484             // Display on-body latency test instructions
485             logger.logInstructions(R.string.snsr_onbody_detect_test_instr);
486             waitForUserToBegin();
487             mTestStartTimestampMs = SystemClock.elapsedRealtime();
488             mVerifier.awaitAndVerifyEvent(ON_BODY_EVENT_VALUE, MAX_ON_BODY_EVENT_LATENCY_MS);
489         } finally {
490             stopOffbodySensorListener(mVerifier);
491         }
492         return null;
493     }
494 
runWakeAPOffbodyDetectTest(Sensor sensor)495     public String runWakeAPOffbodyDetectTest(Sensor sensor) throws Throwable {
496         final long ALARM_WAKE_UP_DELAY_MS = 40000;
497         String eventArrivalMessage;
498         SensorEventRegistry registry;
499         SensorTestLogger logger = getTestLogger();
500         mBadValueSeen = BAD_VALUE_SEEN_INIT;
501         mVerifier = new SensorEventVerifier();
502         mOffBodySensor = sensor;
503         mTestStartTimestampMs = 0;
504 
505         mTestStartTimestampMs = SystemClock.elapsedRealtime();
506         SuspendStateMonitor suspendStateMonitor = new SuspendStateMonitor();
507         try {
508             boolean success = startOffbodySensorListener(mVerifier);
509             Assert.assertTrue(
510                     getString(R.string.snsr_offbody_sensor_registration, success),
511                     success);
512 
513             // grab the current off-body state, which should be ON-BODY
514             if (mPreviousSensorState != State.ON_BODY) {
515                 registry = mVerifier.waitForEvent();
516                 if ((registry == null) || (registry.event == null) ||
517                         (registry.event.values[0] != ON_BODY_EVENT_VALUE)) {
518                     eventArrivalMessage = getString(R.string.snsr_offbody_event_arrival, false);
519                     Assert.fail(eventArrivalMessage);
520 
521                     // Tell user to put watch on wrist
522                     logger.logInstructions(R.string.snsr_start_offbody_sensor_test_instr);
523                     waitForUserToBegin();
524                     if (mPreviousSensorState != State.ON_BODY) {
525                         registry = mVerifier.waitForEvent();
526                         if ((registry == null) || (registry.event == null)) {
527                             eventArrivalMessage = getString(R.string.snsr_offbody_event_arrival, false);
528                             Assert.fail(eventArrivalMessage);
529                         } else {
530                             Assert.assertTrue(
531                                 String.format(getString(R.string.snsr_offbody_event_wrong_value),
532                                 ON_BODY_EVENT_VALUE,
533                                 registry.event.values[0]),
534                                 ON_BODY_EVENT_VALUE == registry.event.values[0]);
535                         }
536                     }
537                 }
538             }
539 
540             // Instruct user on how to perform offbody detect sleep test
541             logger.logInstructions(R.string.snsr_ap_wake_offbody_detect_test_instr);
542             waitForUserToBegin();
543 
544             long testStartTimeNs = SystemClock.elapsedRealtimeNanos();
545             startTimeoutTimer(ALARM_WAKE_UP_AP_DELAY_MS);
546 
547             // Wait for the first event to trigger. Device is expected to go into suspend here.
548             registry = mVerifier.waitForEvent(mDeviceSuspendLock);
549             if ((registry == null) || (registry.event == null)) {
550                 eventArrivalMessage = getString(R.string.snsr_offbody_event_arrival, false);
551                 Assert.fail(eventArrivalMessage);
552             }
553 
554             mVerifier.verifyEvent(registry, OFF_BODY_EVENT_VALUE, 0);
555 
556             long eventTimeStampNs = registry.event.timestamp;
557             long endTimeNs = SystemClock.elapsedRealtimeNanos();
558             long lastWakeupTimeNs = TimeUnit.MILLISECONDS.toNanos(
559                     suspendStateMonitor.getLastWakeUpTime());
560             Assert.assertTrue(getString(R.string.snsr_device_did_not_go_into_suspend),
561                               testStartTimeNs < lastWakeupTimeNs && lastWakeupTimeNs < endTimeNs);
562             long timestampDelta = Math.abs(lastWakeupTimeNs - eventTimeStampNs);
563             Assert.assertTrue(
564                     String.format(getString(R.string.snsr_device_did_not_wake_up_at_trigger),
565                               TimeUnit.NANOSECONDS.toMillis(lastWakeupTimeNs),
566                               TimeUnit.NANOSECONDS.toMillis(eventTimeStampNs)),
567                               timestampDelta < MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS);
568         } finally {
569             stopTimeoutTimer();
570             suspendStateMonitor.cancel();
571             mScreenManipulator.turnScreenOn();
572             playSound();
573         }
574         return null;
575     }
576 
577     @Override
578     protected void activityCleanUp() {
579         if (mOffBodySensorRegistered) {
580             stopOffbodySensorListener(mVerifier);
581         }
582         stopTimeoutTimer();
583         LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadCastReceiver);
584         if (mOffBodySensor != null) {
585             mOffBodySensor = null;
586         }
587         if (mScreenManipulator != null){
588             mScreenManipulator.close();
589         }
590         if ((mDeviceSuspendLock != null) && mDeviceSuspendLock.isHeld()) {
591             mDeviceSuspendLock.release();
592         }
593     }
594 }
595