com.android.cts.verifier.sensors.SignificantMotionTestActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.android.cts.verifier.sensors.SignificantMotionTestActivity.java

Source

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.cts.verifier.sensors;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import com.android.cts.verifier.R;
import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
import com.android.cts.verifier.sensors.helpers.SensorTestScreenManipulator;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.hardware.cts.helpers.SensorNotSupportedException;
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.SuspendStateMonitor;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.SystemClock;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import junit.framework.Assert;

/**
 * Test cases for Significant Motion sensor.
 * They use walking motion to change the location and trigger Significant Motion.
 */
public class SignificantMotionTestActivity extends SensorCtsVerifierTestActivity {
    public SignificantMotionTestActivity() {
        super(SignificantMotionTestActivity.class);
    }

    // acceptable time difference between event time and system time
    private static final long MAX_ACCEPTABLE_EVENT_TIME_DELAY_NANOS = TimeUnit.MILLISECONDS.toNanos(500);

    // acceptable time difference between event time and AP wake up time.
    private static final long MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS = TimeUnit.MILLISECONDS.toNanos(2000);

    // time to wait for SMD after the device has gone into suspend. Even after
    // 45 secs if SMD does not trigger, the test will fail.
    private static final long ALARM_WAKE_TIME_DELAY_MS = TimeUnit.SECONDS.toMillis(45);

    // time for the test to wait for a trigger
    private static final int TRIGGER_MAX_DELAY_SECONDS = 30;
    private static final int VIBRATE_DURATION_MILLIS = 10000;

    private static final int EVENT_VALUES_LENGTH = 1;
    private static final float EXPECTED_EVENT_VALUE = 1.0f;
    private static String ACTION_ALARM = "SignificantMotionTestActivity.ACTION_ALARM";

    private SensorManager mSensorManager;
    private Sensor mSensorSignificantMotion;
    private TriggerVerifier mVerifier;
    private SensorTestScreenManipulator mScreenManipulator;

    /**
     * Test cases.
     */
    @SuppressWarnings("unused")
    public String testTrigger() throws Throwable {
        return runTest(R.string.snsr_significant_motion_test_trigger, true /* isMotionExpected */,
                false /* cancelEventNotification */, false /* vibrate */);
    }

    @SuppressWarnings("unused")
    public String testNotTriggerAfterCancel() throws Throwable {
        return runTest(R.string.snsr_significant_motion_test_cancel, false /* isMotionExpected */,
                true /* cancelEventNotification */, false /* vibrate */);
    }

    /**
     * Verifies that Significant Motion is not trigger by the vibrator motion.
     */
    @SuppressWarnings("unused")
    public String testVibratorDoesNotTrigger() throws Throwable {
        return runTest(R.string.snsr_significant_motion_test_vibration, false /* isMotionExpected */,
                false /* cancelEventNotification */, true /* vibrate */);
    }

    /**
     * Verifies that the natural motion of keeping the device in hand does not change the location.
     * It ensures that Significant Motion will not trigger in that scenario.
     */
    @SuppressWarnings("unused")
    public String testInHandDoesNotTrigger() throws Throwable {
        return runTest(R.string.snsr_significant_motion_test_in_hand, false /* isMotionExpected */,
                false /* cancelEventNotification */, false /* vibrate */);
    }

    @SuppressWarnings("unused")
    public String testSittingDoesNotTrigger() throws Throwable {
        return runTest(R.string.snsr_significant_motion_test_sitting, false /* isMotionExpected */,
                false /* cancelEventNotification */, false /* vibrate */);
    }

    @SuppressWarnings("unused")
    public String testTriggerDeactivation() throws Throwable {
        SensorTestLogger logger = getTestLogger();
        logger.logInstructions(R.string.snsr_significant_motion_test_deactivation);
        waitForUserToBegin();

        TriggerVerifier verifier = new TriggerVerifier();
        mSensorManager.requestTriggerSensor(verifier, mSensorSignificantMotion);
        logger.logWaitForSound();

        // wait for the first event to trigger
        verifier.verifyEventTriggered();

        // wait for a second event not to trigger
        String result = verifier.verifyEventNotTriggered();
        playSound();
        return result;
    }

    public static class AlarmReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Intent alarm_intent = new Intent(context, SignificantMotionTestActivity.class);
            alarm_intent.setAction(SignificantMotionTestActivity.ACTION_ALARM);
            LocalBroadcastManager.getInstance(context).sendBroadcastSync(alarm_intent);
        }
    }

    public BroadcastReceiver myBroadCastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            mVerifier.releaseLatch();
            mScreenManipulator.turnScreenOn();
            try {
                playSound();
            } catch (InterruptedException e) {
                // Ignore ...
            }
        }
    };

    @SuppressWarnings("unused")
    public String testAPWakeUpOnSMDTrigger() throws Throwable {
        SensorTestLogger logger = getTestLogger();
        logger.logInstructions(R.string.snsr_significant_motion_ap_suspend);
        waitForUserToBegin();
        mVerifier = new TriggerVerifier();
        mSensorManager.requestTriggerSensor(mVerifier, mSensorSignificantMotion);
        long testStartTimeNs = SystemClock.elapsedRealtimeNanos();
        Handler handler = new Handler(Looper.getMainLooper());
        SuspendStateMonitor suspendStateMonitor = new SuspendStateMonitor();

        Intent intent = new Intent(this, AlarmReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);

        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
        am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + ALARM_WAKE_TIME_DELAY_MS,
                pendingIntent);
        try {
            // Wait for the first event to trigger. Device is expected to go into suspend here.
            mVerifier.verifyEventTriggered();
            long eventTimeStampNs = mVerifier.getTimeStampForTriggerEvent();
            long endTimeNs = SystemClock.elapsedRealtimeNanos();
            long lastWakeupTimeNs = TimeUnit.MILLISECONDS.toNanos(suspendStateMonitor.getLastWakeUpTime());
            Assert.assertTrue(getString(R.string.snsr_device_did_not_go_into_suspend),
                    testStartTimeNs < lastWakeupTimeNs && lastWakeupTimeNs < endTimeNs);
            long timestampDelta = Math.abs(lastWakeupTimeNs - eventTimeStampNs);
            Assert.assertTrue(
                    String.format(getString(R.string.snsr_device_did_not_wake_up_at_trigger),
                            TimeUnit.NANOSECONDS.toMillis(lastWakeupTimeNs),
                            TimeUnit.NANOSECONDS.toMillis(eventTimeStampNs)),
                    timestampDelta < MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS);
        } finally {
            am.cancel(pendingIntent);
            suspendStateMonitor.cancel();
            mScreenManipulator.turnScreenOn();
            playSound();
        }
        return null;
    }

    /**
     * @param instructionsResId Instruction to be shown to testers
     * @param isMotionExpected Should the device detect significant motion event
     *            for this test?
     * @param cancelEventNotification If TRUE, motion notifications will be
     *            requested first and request will be cancelled
     * @param vibrate If TRUE, vibration will be concurrent with the test
     * @throws Throwable
     */
    private String runTest(int instructionsResId, boolean isMotionExpected, boolean cancelEventNotification,
            boolean vibrate) throws Throwable {
        SensorTestLogger logger = getTestLogger();
        logger.logInstructions(instructionsResId);
        waitForUserToBegin();

        if (vibrate) {
            vibrate(VIBRATE_DURATION_MILLIS);
        }

        TriggerVerifier verifier = new TriggerVerifier();
        boolean success = mSensorManager.requestTriggerSensor(verifier, mSensorSignificantMotion);
        Assert.assertTrue(getString(R.string.snsr_significant_motion_registration, success), success);
        if (cancelEventNotification) {
            Assert.assertTrue(getString(R.string.snsr_significant_motion_cancelation),
                    mSensorManager.cancelTriggerSensor(verifier, mSensorSignificantMotion));
        }
        logger.logWaitForSound();

        String result;
        try {
            if (isMotionExpected) {
                result = verifier.verifyEventTriggered();
            } else {
                result = verifier.verifyEventNotTriggered();
            }
        } finally {
            mSensorManager.cancelTriggerSensor(verifier, mSensorSignificantMotion);
            playSound();
        }
        return result;
    }

    @Override
    protected void activitySetUp() {
        mSensorManager = (SensorManager) getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
        mSensorSignificantMotion = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
        if (mSensorSignificantMotion == null) {
            throw new SensorNotSupportedException(Sensor.TYPE_SIGNIFICANT_MOTION);
        }

        mScreenManipulator = new SensorTestScreenManipulator(this);
        try {
            mScreenManipulator.initialize(this);
        } catch (InterruptedException e) {
        }
        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        LocalBroadcastManager.getInstance(this).registerReceiver(myBroadCastReceiver,
                new IntentFilter(ACTION_ALARM));
    }

    @Override
    protected void activityCleanUp() {
        if (mScreenManipulator != null) {
            mScreenManipulator.turnScreenOff();
        }
        LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadCastReceiver);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mScreenManipulator != null) {
            mScreenManipulator.close();
        }
    }

    /**
     * Helper Trigger listener for testing.
     * It cannot be reused.
     */
    private class TriggerVerifier extends TriggerEventListener {
        private volatile CountDownLatch mCountDownLatch;
        private volatile TriggerEventRegistry mEventRegistry;
        private volatile long mTimestampForTriggeredEvent = 0;

        // TODO: refactor out if needed
        private class TriggerEventRegistry {
            public final TriggerEvent triggerEvent;
            public final long realtimeTimestampNanos;

            public TriggerEventRegistry(TriggerEvent event, long realtimeTimestampNanos) {
                this.triggerEvent = event;
                this.realtimeTimestampNanos = realtimeTimestampNanos;
            }
        }

        public void onTrigger(TriggerEvent event) {
            long elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
            mEventRegistry = new TriggerEventRegistry(event, elapsedRealtimeNanos);
            mCountDownLatch.countDown();
        }

        public void releaseLatch() {
            if (mCountDownLatch != null) {
                mCountDownLatch.countDown();
            }
        }

        public long getTimeStampForTriggerEvent() {
            return mTimestampForTriggeredEvent;
        }

        public String verifyEventTriggered() throws Throwable {
            TriggerEventRegistry registry = awaitForEvent();

            // verify an event arrived, and it is indeed a Significant Motion event
            TriggerEvent event = registry.triggerEvent;
            String eventArrivalMessage = getString(R.string.snsr_significant_motion_event_arrival, event != null);
            Assert.assertNotNull(eventArrivalMessage, event);

            int eventType = event.sensor.getType();
            String eventTypeMessage = getString(R.string.snsr_significant_motion_event_type,
                    Sensor.TYPE_SIGNIFICANT_MOTION, eventType);
            Assert.assertEquals(eventTypeMessage, Sensor.TYPE_SIGNIFICANT_MOTION, eventType);

            String sensorName = event.sensor.getName();
            int valuesLength = event.values.length;
            String valuesLengthMessage = getString(R.string.snsr_event_length, EVENT_VALUES_LENGTH, valuesLength,
                    sensorName);
            Assert.assertEquals(valuesLengthMessage, EVENT_VALUES_LENGTH, valuesLength);

            float value = event.values[0];
            String valuesMessage = getString(R.string.snsr_event_value, EXPECTED_EVENT_VALUE, value, sensorName);
            Assert.assertEquals(valuesMessage, EXPECTED_EVENT_VALUE, value);

            long deltaThreshold = MAX_ACCEPTABLE_EVENT_TIME_DELAY_NANOS
                    + TestSensorEnvironment.getSensorMaxDetectionLatencyNs(event.sensor);
            return assertTimestampSynchronization(event.timestamp, registry.realtimeTimestampNanos, deltaThreshold,
                    sensorName);
        }

        public String verifyEventNotTriggered() throws Throwable {
            TriggerEventRegistry registry = awaitForEvent();

            TriggerEvent event = registry.triggerEvent;
            String eventMessage = getString(R.string.snsr_significant_motion_event_unexpected, event != null);
            Assert.assertNull(eventMessage, event);
            return eventMessage;
        }

        private TriggerEventRegistry awaitForEvent() throws InterruptedException {
            mCountDownLatch = new CountDownLatch(1);
            mCountDownLatch.await(TRIGGER_MAX_DELAY_SECONDS, TimeUnit.SECONDS);
            TriggerEventRegistry registry = mEventRegistry;

            // Save the last timestamp when the event triggered.
            if (mEventRegistry != null && mEventRegistry.triggerEvent != null) {
                mTimestampForTriggeredEvent = mEventRegistry.triggerEvent.timestamp;
            }

            mEventRegistry = null;
            playSound();
            return registry != null ? registry : new TriggerEventRegistry(null, 0);
        }
    }
}