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