1 /*
2  * Copyright (C) 2014 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 
17 package com.android.cts.verifier.sensors;
18 
19 import java.util.Timer;
20 import java.util.TimerTask;
21 import java.util.concurrent.CountDownLatch;
22 import java.util.concurrent.TimeUnit;
23 
24 import com.android.cts.verifier.R;
25 import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
26 import com.android.cts.verifier.sensors.helpers.SensorTestScreenManipulator;
27 
28 import android.app.AlarmManager;
29 import android.app.PendingIntent;
30 import android.content.BroadcastReceiver;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.hardware.cts.helpers.SensorNotSupportedException;
35 import android.hardware.cts.helpers.SuspendStateMonitor;
36 import android.hardware.cts.helpers.TestSensorEnvironment;
37 import android.hardware.Sensor;
38 import android.hardware.SensorManager;
39 import android.hardware.TriggerEvent;
40 import android.hardware.TriggerEventListener;
41 import android.os.Handler;
42 import android.os.Looper;
43 import android.os.PowerManager;
44 import android.os.PowerManager.WakeLock;
45 import android.os.SystemClock;
46 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
47 import android.util.Log;
48 
49 import junit.framework.Assert;
50 
51 /**
52  * Test cases for Significant Motion sensor.
53  * They use walking motion to change the location and trigger Significant Motion.
54  */
55 public class SignificantMotionTestActivity extends SensorCtsVerifierTestActivity {
SignificantMotionTestActivity()56     public SignificantMotionTestActivity() {
57         super(SignificantMotionTestActivity.class);
58     }
59 
60     // acceptable time difference between event time and system time
61     private static final long MAX_ACCEPTABLE_EVENT_TIME_DELAY_NANOS =
62             TimeUnit.MILLISECONDS.toNanos(500);
63 
64     // acceptable time difference between event time and AP wake up time.
65     private static final long MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS =
66             TimeUnit.MILLISECONDS.toNanos(2000);
67 
68     // time to wait for SMD after the device has gone into suspend. Even after
69     // 45 secs if SMD does not trigger, the test will fail.
70     private static final long ALARM_WAKE_TIME_DELAY_MS = TimeUnit.SECONDS.toMillis(45);
71 
72     // time for the test to wait for a trigger
73     private static final int TRIGGER_MAX_DELAY_SECONDS = 30;
74     private static final int VIBRATE_DURATION_MILLIS = 10000;
75 
76     private static final int EVENT_VALUES_LENGTH = 1;
77     private static final float EXPECTED_EVENT_VALUE = 1.0f;
78     private static String ACTION_ALARM = "SignificantMotionTestActivity.ACTION_ALARM";
79 
80     private SensorManager mSensorManager;
81     private Sensor mSensorSignificantMotion;
82     private TriggerVerifier mVerifier;
83     private SensorTestScreenManipulator mScreenManipulator;
84     private WakeLock mPartialWakeLock;
85 
86     /**
87      * Test cases.
88      */
89     @SuppressWarnings("unused")
testTrigger()90     public String testTrigger() throws Throwable {
91         return runTest(
92                 R.string.snsr_significant_motion_test_trigger,
93                 true /* isMotionExpected */,
94                 false /* cancelEventNotification */,
95                 false /* vibrate */);
96     }
97 
98     @SuppressWarnings("unused")
testNotTriggerAfterCancel()99     public String testNotTriggerAfterCancel() throws Throwable {
100         return runTest(
101                 R.string.snsr_significant_motion_test_cancel,
102                 false /* isMotionExpected */,
103                 true /* cancelEventNotification */,
104                 false /* vibrate */);
105     }
106 
107     /**
108      * Verifies that Significant Motion is not trigger by the vibrator motion.
109      */
110     @SuppressWarnings("unused")
testVibratorDoesNotTrigger()111     public String testVibratorDoesNotTrigger() throws Throwable {
112      return runTest(
113              R.string.snsr_significant_motion_test_vibration,
114              false /* isMotionExpected */,
115              false /* cancelEventNotification */,
116              true /* vibrate */);
117     }
118 
119     /**
120      * Verifies that the natural motion of keeping the device in hand does not change the location.
121      * It ensures that Significant Motion will not trigger in that scenario.
122      */
123     @SuppressWarnings("unused")
testInHandDoesNotTrigger()124     public String testInHandDoesNotTrigger() throws Throwable {
125         return runTest(
126                 R.string.snsr_significant_motion_test_in_hand,
127                 false /* isMotionExpected */,
128                 false /* cancelEventNotification */,
129                 false /* vibrate */);
130     }
131 
132     @SuppressWarnings("unused")
testSittingDoesNotTrigger()133     public String testSittingDoesNotTrigger() throws Throwable {
134         return runTest(
135                 R.string.snsr_significant_motion_test_sitting,
136                 false /* isMotionExpected */,
137                 false /* cancelEventNotification */,
138                 false /* vibrate */);
139     }
140 
141     @SuppressWarnings("unused")
testTriggerDeactivation()142     public String testTriggerDeactivation() throws Throwable {
143         SensorTestLogger logger = getTestLogger();
144         logger.logInstructions(R.string.snsr_significant_motion_test_deactivation);
145         waitForUserToBegin();
146 
147         TriggerVerifier verifier = new TriggerVerifier();
148         mSensorManager.requestTriggerSensor(verifier, mSensorSignificantMotion);
149         logger.logWaitForSound();
150 
151         String result;
152         try {
153             mPartialWakeLock.acquire();
154 
155             // wait for the first event to trigger
156             verifier.verifyEventTriggered();
157 
158             // wait for a second event not to trigger
159             result = verifier.verifyEventNotTriggered();
160         } finally {
161             playSound();
162             mScreenManipulator.turnScreenOn();
163             mPartialWakeLock.release();
164         }
165         return result;
166     }
167 
168     public static class AlarmReceiver extends BroadcastReceiver {
169         @Override
onReceive(Context context, Intent intent)170         public void onReceive(Context context, Intent intent) {
171             Intent alarm_intent = new Intent(context, SignificantMotionTestActivity.class);
172             alarm_intent.setAction(SignificantMotionTestActivity.ACTION_ALARM);
173             LocalBroadcastManager.getInstance(context).sendBroadcastSync(alarm_intent);
174         }
175     }
176 
177     public BroadcastReceiver myBroadCastReceiver = new BroadcastReceiver() {
178         @Override
179         public void onReceive(Context context, Intent intent) {
180             mVerifier.releaseLatch();
181             mScreenManipulator.turnScreenOn();
182             try {
183                 playSound();
184             } catch (InterruptedException e) {
185                 // Ignore ...
186             }
187         }
188     };
189 
190     @SuppressWarnings("unused")
testAPWakeUpOnSMDTrigger()191     public String testAPWakeUpOnSMDTrigger() throws Throwable {
192         SensorTestLogger logger = getTestLogger();
193         logger.logInstructions(R.string.snsr_significant_motion_ap_suspend);
194         waitForUserToBegin();
195         mVerifier = new TriggerVerifier();
196         mSensorManager.requestTriggerSensor(mVerifier, mSensorSignificantMotion);
197         long testStartTimeNs = SystemClock.elapsedRealtimeNanos();
198         Handler handler = new Handler(Looper.getMainLooper());
199         SuspendStateMonitor suspendStateMonitor = new SuspendStateMonitor();
200 
201         Intent intent = new Intent(this, AlarmReceiver.class);
202         PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
203 
204         AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
205         am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
206                      SystemClock.elapsedRealtime() + ALARM_WAKE_TIME_DELAY_MS, pendingIntent);
207         try {
208             // Wait for the first event to trigger. Device is expected to go into suspend here.
209             mVerifier.verifyEventTriggered();
210             long eventTimeStampNs = mVerifier.getTimeStampForTriggerEvent();
211             long endTimeNs = SystemClock.elapsedRealtimeNanos();
212             long lastWakeupTimeNs = TimeUnit.MILLISECONDS.toNanos(
213                     suspendStateMonitor.getLastWakeUpTime());
214             Assert.assertTrue(getString(R.string.snsr_device_did_not_go_into_suspend),
215                               testStartTimeNs < lastWakeupTimeNs && lastWakeupTimeNs < endTimeNs);
216             long timestampDelta = Math.abs(lastWakeupTimeNs - eventTimeStampNs);
217             Assert.assertTrue(
218                     String.format(getString(R.string.snsr_device_did_not_wake_up_at_trigger),
219                               TimeUnit.NANOSECONDS.toMillis(lastWakeupTimeNs),
220                               TimeUnit.NANOSECONDS.toMillis(eventTimeStampNs)),
221                               timestampDelta < MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS);
222         } finally {
223             am.cancel(pendingIntent);
224             suspendStateMonitor.cancel();
225             mScreenManipulator.turnScreenOn();
226             playSound();
227         }
228         return null;
229     }
230 
231     /**
232      * @param instructionsResId Instruction to be shown to testers
233      * @param isMotionExpected Should the device detect significant motion event
234      *            for this test?
235      * @param cancelEventNotification If TRUE, motion notifications will be
236      *            requested first and request will be cancelled
237      * @param vibrate If TRUE, vibration will be concurrent with the test
238      * @throws Throwable
239      */
240     private String runTest(
241             int instructionsResId,
242             boolean isMotionExpected,
243             boolean cancelEventNotification,
244             boolean vibrate) throws Throwable {
245         SensorTestLogger logger = getTestLogger();
246         logger.logInstructions(instructionsResId);
247         waitForUserToBegin();
248 
249         if (vibrate) {
250             vibrate(VIBRATE_DURATION_MILLIS);
251         }
252 
253         TriggerVerifier verifier = new TriggerVerifier();
254         boolean success = mSensorManager.requestTriggerSensor(verifier, mSensorSignificantMotion);
255         Assert.assertTrue(
256                 getString(R.string.snsr_significant_motion_registration, success),
257                 success);
258         if (cancelEventNotification) {
259             Assert.assertTrue(
260                     getString(R.string.snsr_significant_motion_cancelation),
261                     mSensorManager.cancelTriggerSensor(verifier, mSensorSignificantMotion));
262         }
263         logger.logWaitForSound();
264 
265         String result;
266         try {
267             mPartialWakeLock.acquire();
268 
269             if (isMotionExpected) {
270                 result = verifier.verifyEventTriggered();
271             } else {
272                 result = verifier.verifyEventNotTriggered();
273             }
274         } finally {
275             mSensorManager.cancelTriggerSensor(verifier, mSensorSignificantMotion);
276 
277             // notify user test finished
278             playSound();
279             mScreenManipulator.turnScreenOn();
280             mPartialWakeLock.release();
281         }
282         return result;
283     }
284 
285     @Override
286     protected void activitySetUp() {
287         mSensorManager = (SensorManager) getApplicationContext()
288                 .getSystemService(Context.SENSOR_SERVICE);
289         mSensorSignificantMotion = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
290         if (mSensorSignificantMotion == null) {
291             throw new SensorNotSupportedException(Sensor.TYPE_SIGNIFICANT_MOTION);
292         }
293 
294         mScreenManipulator = new SensorTestScreenManipulator(this);
295         try {
296             mScreenManipulator.initialize(this);
297         } catch (InterruptedException e) {
298         }
299         PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
300         mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SignificantMotionTestActivity");
301         LocalBroadcastManager.getInstance(this).registerReceiver(myBroadCastReceiver,
302                                             new IntentFilter(ACTION_ALARM));
303     }
304 
305     @Override
306     protected void activityCleanUp() {
307         if (mScreenManipulator != null) {
308             // after this screen does not have to be on constantly
309             mScreenManipulator.releaseScreenOn();
310         }
311         if (mPartialWakeLock != null && mPartialWakeLock.isHeld()) {
312             mPartialWakeLock.release();
313         }
314         LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadCastReceiver);
315     }
316 
317     @Override
318     protected void onDestroy() {
319         super.onDestroy();
320         if (mScreenManipulator != null){
321             mScreenManipulator.close();
322         }
323     }
324 
325     /**
326      * Helper Trigger listener for testing.
327      * It cannot be reused.
328      */
329     private class TriggerVerifier extends TriggerEventListener {
330         private volatile CountDownLatch mCountDownLatch;
331         private volatile TriggerEventRegistry mEventRegistry;
332         private volatile long mTimestampForTriggeredEvent = 0;
333 
334         // TODO: refactor out if needed
335         private class TriggerEventRegistry {
336             public final TriggerEvent triggerEvent;
337             public final long realtimeTimestampNanos;
338 
339             public TriggerEventRegistry(TriggerEvent event, long realtimeTimestampNanos) {
340                 this.triggerEvent = event;
341                 this.realtimeTimestampNanos = realtimeTimestampNanos;
342             }
343         }
344 
345         public void onTrigger(TriggerEvent event) {
346             long elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
347             mEventRegistry = new TriggerEventRegistry(event, elapsedRealtimeNanos);
348             mCountDownLatch.countDown();
349         }
350 
351         public void releaseLatch() {
352             if (mCountDownLatch != null) {
353                 mCountDownLatch.countDown();
354             }
355         }
356 
357         public long getTimeStampForTriggerEvent() {
358             return mTimestampForTriggeredEvent;
359         }
360 
361         public String verifyEventTriggered() throws Throwable {
362             TriggerEventRegistry registry = awaitForEvent();
363 
364             // verify an event arrived, and it is indeed a Significant Motion event
365             TriggerEvent event = registry.triggerEvent;
366             String eventArrivalMessage =
367                     getString(R.string.snsr_significant_motion_event_arrival, event != null);
368             Assert.assertNotNull(eventArrivalMessage, event);
369 
370             int eventType = event.sensor.getType();
371             String eventTypeMessage = getString(
372                     R.string.snsr_significant_motion_event_type,
373                     Sensor.TYPE_SIGNIFICANT_MOTION,
374                     eventType);
375             Assert.assertEquals(eventTypeMessage, Sensor.TYPE_SIGNIFICANT_MOTION, eventType);
376 
377             String sensorName = event.sensor.getName();
378             int valuesLength = event.values.length;
379             String valuesLengthMessage = getString(
380                     R.string.snsr_event_length,
381                     EVENT_VALUES_LENGTH,
382                     valuesLength,
383                     sensorName);
384             Assert.assertEquals(valuesLengthMessage, EVENT_VALUES_LENGTH, valuesLength);
385 
386             float value = event.values[0];
387             String valuesMessage = getString(
388                     R.string.snsr_event_value,
389                     EXPECTED_EVENT_VALUE,
390                     value,
391                     sensorName);
392             Assert.assertEquals(valuesMessage, EXPECTED_EVENT_VALUE, value);
393 
394             long deltaThreshold = MAX_ACCEPTABLE_EVENT_TIME_DELAY_NANOS
395                     + TestSensorEnvironment.getSensorMaxDetectionLatencyNs(event.sensor);
396             return assertTimestampSynchronization(
397                     event.timestamp,
398                     registry.realtimeTimestampNanos,
399                     deltaThreshold,
400                     sensorName);
401         }
402 
403         public String verifyEventNotTriggered() throws Throwable {
404             TriggerEventRegistry registry = awaitForEvent();
405 
406             TriggerEvent event = registry.triggerEvent;
407             String eventMessage =
408                     getString(R.string.snsr_significant_motion_event_unexpected, event != null);
409             Assert.assertNull(eventMessage, event);
410             return eventMessage;
411         }
412 
413         private TriggerEventRegistry awaitForEvent() throws InterruptedException {
414             mCountDownLatch = new CountDownLatch(1);
415             mCountDownLatch.await(TRIGGER_MAX_DELAY_SECONDS, TimeUnit.SECONDS);
416             TriggerEventRegistry registry = mEventRegistry;
417 
418             // Save the last timestamp when the event triggered.
419             if (mEventRegistry != null && mEventRegistry.triggerEvent != null) {
420                 mTimestampForTriggeredEvent = mEventRegistry.triggerEvent.timestamp;
421             }
422 
423             mEventRegistry = null;
424             return registry != null ? registry : new TriggerEventRegistry(null, 0);
425         }
426     }
427 }
428