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, true);
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 
144         setFirstExecutionInstruction(R.string.snsr_significant_motion_test_deactivation);
145 
146         TriggerVerifier verifier = new TriggerVerifier();
147         mSensorManager.requestTriggerSensor(verifier, mSensorSignificantMotion);
148         getTestLogger().logWaitForSound();
149 
150         String result;
151         try {
152             mPartialWakeLock.acquire();
153 
154             // wait for the first event to trigger
155             verifier.verifyEventTriggered();
156 
157             // wait for a second event not to trigger
158             result = verifier.verifyEventNotTriggered();
159         } finally {
160             playSound();
161             mScreenManipulator.turnScreenOn();
162             mPartialWakeLock.release();
163         }
164         return result;
165     }
166 
167     public static class AlarmReceiver extends BroadcastReceiver {
168         @Override
onReceive(Context context, Intent intent)169         public void onReceive(Context context, Intent intent) {
170             Intent alarm_intent = new Intent(context, SignificantMotionTestActivity.class);
171             alarm_intent.setAction(SignificantMotionTestActivity.ACTION_ALARM);
172             LocalBroadcastManager.getInstance(context).sendBroadcastSync(alarm_intent);
173         }
174     }
175 
176     public BroadcastReceiver myBroadCastReceiver = new BroadcastReceiver() {
177         @Override
178         public void onReceive(Context context, Intent intent) {
179             mVerifier.releaseLatch();
180             mScreenManipulator.turnScreenOn();
181             try {
182                 playSound();
183             } catch (InterruptedException e) {
184                 // Ignore ...
185             }
186         }
187     };
188 
189     @SuppressWarnings("unused")
testAPWakeUpOnSMDTrigger()190     public String testAPWakeUpOnSMDTrigger() throws Throwable {
191 
192         setFirstExecutionInstruction(R.string.snsr_significant_motion_ap_suspend);
193 
194         mVerifier = new TriggerVerifier();
195         mSensorManager.requestTriggerSensor(mVerifier, mSensorSignificantMotion);
196         long testStartTimeNs = SystemClock.elapsedRealtimeNanos();
197         Handler handler = new Handler(Looper.getMainLooper());
198         SuspendStateMonitor suspendStateMonitor = new SuspendStateMonitor();
199 
200         Intent intent = new Intent(this, AlarmReceiver.class);
201         PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
202 
203         AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
204         am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
205                      SystemClock.elapsedRealtime() + ALARM_WAKE_TIME_DELAY_MS, pendingIntent);
206         try {
207             // Wait for the first event to trigger. Device is expected to go into suspend here.
208             mVerifier.verifyEventTriggered();
209             long eventTimeStampNs = mVerifier.getTimeStampForTriggerEvent();
210             long endTimeNs = SystemClock.elapsedRealtimeNanos();
211             long lastWakeupTimeNs = TimeUnit.MILLISECONDS.toNanos(
212                     suspendStateMonitor.getLastWakeUpTime());
213             Assert.assertTrue(getString(R.string.snsr_device_did_not_go_into_suspend),
214                               testStartTimeNs < lastWakeupTimeNs && lastWakeupTimeNs < endTimeNs);
215             long timestampDelta = Math.abs(lastWakeupTimeNs - eventTimeStampNs);
216             Assert.assertTrue(
217                     String.format(getString(R.string.snsr_device_did_not_wake_up_at_trigger),
218                               TimeUnit.NANOSECONDS.toMillis(lastWakeupTimeNs),
219                               TimeUnit.NANOSECONDS.toMillis(eventTimeStampNs)),
220                               timestampDelta < MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS);
221         } finally {
222             am.cancel(pendingIntent);
223             suspendStateMonitor.cancel();
224             mScreenManipulator.turnScreenOn();
225             playSound();
226         }
227         return null;
228     }
229 
230     /**
231      * @param instructionsResId Instruction to be shown to testers
232      * @param isMotionExpected Should the device detect significant motion event
233      *            for this test?
234      * @param cancelEventNotification If TRUE, motion notifications will be
235      *            requested first and request will be cancelled
236      * @param vibrate If TRUE, vibration will be concurrent with the test
237      * @throws Throwable
238      */
239     private String runTest(
240             int instructionsResId,
241             boolean isMotionExpected,
242             boolean cancelEventNotification,
243             boolean vibrate) throws Throwable {
244 
245         setFirstExecutionInstruction(instructionsResId);
246 
247         if (vibrate) {
248             vibrate(VIBRATE_DURATION_MILLIS);
249         }
250 
251         TriggerVerifier verifier = new TriggerVerifier();
252         boolean success = mSensorManager.requestTriggerSensor(verifier, mSensorSignificantMotion);
253         Assert.assertTrue(
254                 getString(R.string.snsr_significant_motion_registration, success),
255                 success);
256         if (cancelEventNotification) {
257             Assert.assertTrue(
258                     getString(R.string.snsr_significant_motion_cancelation),
259                     mSensorManager.cancelTriggerSensor(verifier, mSensorSignificantMotion));
260         }
261         getTestLogger().logWaitForSound();
262 
263         String result;
264         try {
265             mPartialWakeLock.acquire();
266 
267             if (isMotionExpected) {
268                 result = verifier.verifyEventTriggered();
269             } else {
270                 result = verifier.verifyEventNotTriggered();
271             }
272         } finally {
273             mSensorManager.cancelTriggerSensor(verifier, mSensorSignificantMotion);
274 
275             // notify user test finished
276             playSound();
277             mScreenManipulator.turnScreenOn();
278             mPartialWakeLock.release();
279         }
280         return result;
281     }
282 
283     @Override
284     protected void activitySetUp() {
285         mSensorManager = (SensorManager) getApplicationContext()
286                 .getSystemService(Context.SENSOR_SERVICE);
287         mSensorSignificantMotion = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
288         if (mSensorSignificantMotion == null) {
289             throw new SensorNotSupportedException(Sensor.TYPE_SIGNIFICANT_MOTION);
290         }
291 
292         mScreenManipulator = new SensorTestScreenManipulator(this);
293         try {
294             mScreenManipulator.initialize(this);
295         } catch (InterruptedException e) {
296         }
297         PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
298         mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SignificantMotionTestActivity");
299         LocalBroadcastManager.getInstance(this).registerReceiver(myBroadCastReceiver,
300                                             new IntentFilter(ACTION_ALARM));
301     }
302 
303     @Override
304     protected void activityCleanUp() {
305         if (mScreenManipulator != null) {
306             // after this screen does not have to be on constantly
307             mScreenManipulator.releaseScreenOn();
308         }
309         if (mPartialWakeLock != null && mPartialWakeLock.isHeld()) {
310             mPartialWakeLock.release();
311         }
312         LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadCastReceiver);
313     }
314 
315     @Override
316     protected void onDestroy() {
317         super.onDestroy();
318         if (mScreenManipulator != null){
319             mScreenManipulator.close();
320         }
321     }
322 
323     /**
324      * Helper Trigger listener for testing.
325      * It cannot be reused.
326      */
327     private class TriggerVerifier extends TriggerEventListener {
328         private volatile CountDownLatch mCountDownLatch;
329         private volatile TriggerEventRegistry mEventRegistry;
330         private volatile long mTimestampForTriggeredEvent = 0;
331 
332         // TODO: refactor out if needed
333         private class TriggerEventRegistry {
334             public final TriggerEvent triggerEvent;
335             public final long realtimeTimestampNanos;
336 
337             public TriggerEventRegistry(TriggerEvent event, long realtimeTimestampNanos) {
338                 this.triggerEvent = event;
339                 this.realtimeTimestampNanos = realtimeTimestampNanos;
340             }
341         }
342 
343         public void onTrigger(TriggerEvent event) {
344             long elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
345             mEventRegistry = new TriggerEventRegistry(event, elapsedRealtimeNanos);
346             mCountDownLatch.countDown();
347         }
348 
349         public void releaseLatch() {
350             if (mCountDownLatch != null) {
351                 mCountDownLatch.countDown();
352             }
353         }
354 
355         public long getTimeStampForTriggerEvent() {
356             return mTimestampForTriggeredEvent;
357         }
358 
359         public String verifyEventTriggered() throws Throwable {
360             TriggerEventRegistry registry = awaitForEvent();
361 
362             // verify an event arrived, and it is indeed a Significant Motion event
363             TriggerEvent event = registry.triggerEvent;
364             String eventArrivalMessage =
365                     getString(R.string.snsr_significant_motion_event_arrival, event != null);
366             Assert.assertNotNull(eventArrivalMessage, event);
367 
368             int eventType = event.sensor.getType();
369             String eventTypeMessage = getString(
370                     R.string.snsr_significant_motion_event_type,
371                     Sensor.TYPE_SIGNIFICANT_MOTION,
372                     eventType);
373             Assert.assertEquals(eventTypeMessage, Sensor.TYPE_SIGNIFICANT_MOTION, eventType);
374 
375             String sensorName = event.sensor.getName();
376             int valuesLength = event.values.length;
377             String valuesLengthMessage = getString(
378                     R.string.snsr_event_length,
379                     EVENT_VALUES_LENGTH,
380                     valuesLength,
381                     sensorName);
382             Assert.assertEquals(valuesLengthMessage, EVENT_VALUES_LENGTH, valuesLength);
383 
384             float value = event.values[0];
385             String valuesMessage = getString(
386                     R.string.snsr_event_value,
387                     EXPECTED_EVENT_VALUE,
388                     value,
389                     sensorName);
390             Assert.assertEquals(valuesMessage, EXPECTED_EVENT_VALUE, value);
391 
392             long deltaThreshold = MAX_ACCEPTABLE_EVENT_TIME_DELAY_NANOS
393                     + TestSensorEnvironment.getSensorMaxDetectionLatencyNs(event.sensor);
394             return assertTimestampSynchronization(
395                     event.timestamp,
396                     registry.realtimeTimestampNanos,
397                     deltaThreshold,
398                     sensorName);
399         }
400 
401         public String verifyEventNotTriggered() throws Throwable {
402             TriggerEventRegistry registry = awaitForEvent();
403 
404             TriggerEvent event = registry.triggerEvent;
405             String eventMessage =
406                     getString(R.string.snsr_significant_motion_event_unexpected, event != null);
407             Assert.assertNull(eventMessage, event);
408             return eventMessage;
409         }
410 
411         private TriggerEventRegistry awaitForEvent() throws InterruptedException {
412             mCountDownLatch = new CountDownLatch(1);
413             mCountDownLatch.await(TRIGGER_MAX_DELAY_SECONDS, TimeUnit.SECONDS);
414             TriggerEventRegistry registry = mEventRegistry;
415 
416             // Save the last timestamp when the event triggered.
417             if (mEventRegistry != null && mEventRegistry.triggerEvent != null) {
418                 mTimestampForTriggeredEvent = mEventRegistry.triggerEvent.timestamp;
419             }
420 
421             mEventRegistry = null;
422             return registry != null ? registry : new TriggerEventRegistry(null, 0);
423         }
424     }
425 }
426