Merge "resolved conflicts for merge of 6d814d9f to lmp-dev" into lmp-dev
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index b67bdf8..3373ef1 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -34,6 +34,7 @@
     <uses-permission android:name="android.permission.FULLSCREEN" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.NFC" />
+    <uses-permission android:name="android.permission.VIBRATE" />
     <uses-feature android:name="android.hardware.camera.front"
                   android:required="false" />
     <uses-feature android:name="android.hardware.camera.autofocus"
@@ -43,7 +44,6 @@
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
-
     <uses-feature android:name="android.hardware.usb.accessory" />
 
     <!-- Needed by the Audio Quality Verifier to store the sound samples that will be mailed. -->
@@ -927,6 +927,32 @@
             <meta-data android:name="test_category" android:value="@string/test_category_deskclock" />
         </activity>
 
+        <activity
+                android:name="com.android.cts.verifier.sensors.StepCounterTestActivity"
+                android:label="@string/snsr_step_counter_test"
+                android:screenOrientation="nosensor" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_sensors" />
+        </activity>
+
+        <activity
+            android:name="com.android.cts.verifier.sensors.SignificantMotionTestActivity"
+            android:label="@string/snsr_significant_motion_test"
+            android:screenOrientation="nosensor" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+
+            <meta-data
+                android:name="test_category"
+                android:value="@string/test_category_sensors" />
+        </activity>
+
         <receiver android:name=".widget.WidgetCtsProvider">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 1fb7ecb..d7e6d33 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -442,6 +442,12 @@
     <!-- Magnetic Field -->
     <string name="snsr_mag_m_test">Magnetic Field Measurement Tests</string>
 
+    <!-- Step Counter and Detector -->
+    <string name="snsr_step_counter_test">Step Counter and Detector Tests</string>
+
+    <!-- Significant Motion -->
+    <string name="snsr_significant_motion_test">Significant Motion Tests</string>
+
     <!-- Strings for Sample Test Activities -->
     <string name="share_button_text">Share</string>
     <string name="sample_framework_test">Sample Framework Test</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
new file mode 100644
index 0000000..9258ba6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
@@ -0,0 +1,224 @@
+/*
+ * 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 junit.framework.Assert;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.TriggerEvent;
+import android.hardware.TriggerEventListener;
+import android.media.AudioManager;
+import android.media.ToneGenerator;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Vibrator;
+
+@TargetApi(Build.VERSION_CODES.KITKAT)
+class TriggerListener extends TriggerEventListener {
+    // how much difference between system time and event time considered to be
+    // acceptable [msec]
+    private final long MAX_ACCEPTABLE_EVENT_TIME_DELAY_MILLIS = 500;
+
+    // state used for internal recording of the event detection
+    private boolean mEventDetected = false;
+
+    public void onTrigger(TriggerEvent event) {
+        final long NANOS_PER_MS = 1000000L;
+
+        Assert.assertEquals("values should be of length 1 for significant motion event", 1,
+                event.values.length);
+        Assert.assertEquals("values[0] should be 1.0 for significant motion event", 1.0f,
+                event.values[0]);
+
+        // Check that timestamp is within MAX_ACCEPTABLE_EVENT_TIME_DELAY_MILLIS
+        // It might take time to determine Significant Motion, but then that
+        // event should be reported to the host in a timely fashion.
+        long timeReportedMillis = event.timestamp / NANOS_PER_MS;
+        long timeActualMillis = System.currentTimeMillis();
+        Assert.assertEquals("Incorrect time reported in the event",
+                timeReportedMillis, timeActualMillis, MAX_ACCEPTABLE_EVENT_TIME_DELAY_MILLIS);
+
+        // Verify event type is truly Significant Motion
+        Assert.assertEquals("Triggered event type is not Significant Motion",
+                event.sensor.getType(), Sensor.TYPE_SIGNIFICANT_MOTION);
+
+        // Event detected flag should be false if indeed only one event per
+        // request
+        Assert.assertFalse("Significant Motion sensor did not automatically "
+                + "disable itself from subsequent detection", mEventDetected);
+
+        // audible cue to indicate Significant Motion occurred
+        beep();
+        mEventDetected = true;
+    }
+
+    public boolean wasEventTriggered() {
+        return mEventDetected;
+    }
+
+    public void reset() {
+        mEventDetected = false;
+    }
+
+    private void beep() {
+        final ToneGenerator tg = new ToneGenerator(
+                AudioManager.STREAM_NOTIFICATION, 100);
+        tg.startTone(ToneGenerator.TONE_PROP_BEEP);
+    }
+}
+
+@TargetApi(Build.VERSION_CODES.KITKAT)
+public class SignificantMotionTestActivity extends BaseSensorSemiAutomatedTestActivity {
+    // minimum time for test to consider valid [msec]
+    private final int MIN_TEST_TIME_MILLIS = 20000;
+    private final int VIBRATE_DURATION_MILLIS = 10000;
+
+    private SensorManager mSensorManager;
+    private Sensor mSensorSignificantMotion;
+    private final TriggerListener mTriggeredListener = new TriggerListener();
+    private long mTestStartTimestamp;
+    private static int sNumPassedTests = 0;
+
+    @Override
+    protected void onRun() throws Throwable {
+        switch (sNumPassedTests) {
+        // avoid re-running passed tests, so purposely want fallthroughs here
+            case 0:
+                // use walking to change location and trigger significant motion
+                runTest("walk 15 steps for significant motion to be detected", true, false, false);
+            case 1:
+                runTest("walk another 15 steps to ensure significant motion "
+                        + "is not reported after trigger cancelled", false, true, false);
+            case 2:
+                // use vibrator to ensure significant motion is not triggered
+                runTest("leave the device on a level surface", false, false, true);
+            case 3:
+                // use natural motion that does not change location to ensure
+                // significant motion is not triggered
+                runTest("hold the device in hand while performing natural "
+                        + "hand movements", false, false, false);
+            case 4:
+                runTest("keep the device in pocket and move naturally while "
+                        + "sitting in a chair", false, false, false);
+            default:
+                break;
+        }
+    }
+
+    private void vibrateDevice(int timeInMs) {
+        Vibrator vibrator = (Vibrator) this.getSystemService(Context.VIBRATOR_SERVICE);
+        vibrator.vibrate(timeInMs);
+    }
+
+    /**
+     * @param instructions 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 void runTest(String instructions, final boolean isMotionExpected,
+            final boolean cancelEventNotification, final boolean vibrate) throws Throwable {
+
+        appendText("Click 'Next' and " + instructions);
+        waitForUser();
+
+        if (vibrate) {
+            vibrateDevice(VIBRATE_DURATION_MILLIS);
+        }
+
+        mTestStartTimestamp = System.currentTimeMillis();
+        startMeasurements(cancelEventNotification);
+
+        long testTime = System.currentTimeMillis() - mTestStartTimestamp;
+
+        while (!mTriggeredListener.wasEventTriggered()
+                && testTime < MIN_TEST_TIME_MILLIS) {
+            int timeWaitSec = Math
+                    .round((MIN_TEST_TIME_MILLIS - testTime) / 1000);
+            clearText();
+            appendText("Current test: " + instructions);
+            appendText(
+                    String.format("%d seconds for the test to complete", timeWaitSec),
+                    Color.GRAY);
+
+            Thread.sleep(1000);
+            testTime = System.currentTimeMillis() - mTestStartTimestamp;
+        }
+        clearText();
+        appendText("Current test: " + instructions);
+        playSound();
+        verifyMeasurements(isMotionExpected);
+        sNumPassedTests++;
+    }
+
+    private void startMeasurements(boolean isCancelTriggerRequested) throws Throwable {
+        mTriggeredListener.reset();
+
+        mSensorManager.requestTriggerSensor(mTriggeredListener, mSensorSignificantMotion);
+
+        if (isCancelTriggerRequested) {
+            mSensorManager.cancelTriggerSensor(mTriggeredListener, mSensorSignificantMotion);
+        }
+    }
+
+    private void verifyMeasurements(boolean isMotionExpected) throws Throwable {
+        Assert.assertEquals("Significant motion event expected/detected mismatch: "
+                + isMotionExpected + " / " + mTriggeredListener.wasEventTriggered(),
+                isMotionExpected, mTriggeredListener.wasEventTriggered());
+        appendText("Significant motion event " + isMotionExpected + " as expected", Color.GRAY);
+        logSuccess();
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mSensorManager = (SensorManager) getApplicationContext()
+                .getSystemService(Context.SENSOR_SERVICE);
+
+        mSensorSignificantMotion = mSensorManager
+                .getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        if (mSensorManager != null && mSensorSignificantMotion != null) {
+            mSensorManager.requestTriggerSensor(mTriggeredListener,
+                    mSensorSignificantMotion);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        if (mSensorManager != null && mSensorSignificantMotion != null) {
+            mSensorManager.cancelTriggerSensor(mTriggeredListener,
+                    mSensorSignificantMotion);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java
new file mode 100644
index 0000000..0dfe341
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java
@@ -0,0 +1,361 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Color;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.media.AudioManager;
+import android.media.ToneGenerator;
+import android.os.Build;
+import android.os.SystemClock;
+import android.os.Vibrator;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import com.android.cts.verifier.R;
+
+@TargetApi(Build.VERSION_CODES.KITKAT)
+public class StepCounterTestActivity extends BaseSensorSemiAutomatedTestActivity
+        implements SensorEventListener {
+
+    private SensorManager mSensorManager;
+    private Sensor mSensorStepCounter;
+    private Sensor mSensorStepDetector;
+
+    private int mStepsReported = 0; // number of steps as reported by user
+    private int mInitialStepCount = 0; // step counter at the start of test
+    private int mStepsDetected = 0; // number of steps during the test
+
+    private List<Long> mTimestampsUserReported = new ArrayList<Long>();
+    private List<Long> mTimestampsStepCounter = new ArrayList<Long>();
+    private List<Long> mTimestampsStepDetector = new ArrayList<Long>();
+
+    private final int MIN_TEST_TIME_MILLIS = 20000; // 20 sec
+    private final double NANOSECONDS_IN_SEC = 1e9;
+    private final int MIN_NUM_STEPS_PER_TEST = 10;
+    private final int MAX_STEP_DISCREPANCY = 4;
+    private final int MAX_TOLERANCE_STEP_TIME_LATENCY_SECONDS = 8;
+
+    private boolean mCheckForMotion = false;
+
+    private Sensor mSensorAcceleration;
+    private boolean mMoveDetected = false;
+    private static int sNumPassedTests = 0;
+
+    @Override
+    protected void onRun() throws Throwable {
+        View screen = (View) findViewById(R.id.log_text).getParent();
+        Assert.assertNotNull(screen);
+        screen.setOnClickListener(mClickListener);
+
+        switch (sNumPassedTests) {
+        // avoid re-running passed tests, so purposely want fallthroughs here
+            case 0:
+                runTest("walk at least " + MIN_NUM_STEPS_PER_TEST
+                        + " steps and tap on the screen with each step",
+                        MIN_NUM_STEPS_PER_TEST, MAX_STEP_DISCREPANCY, false, false);
+            case 1:
+                runTest("hold device still in hand", 0, MAX_STEP_DISCREPANCY, true, true);
+            case 2:
+                runTest("wave device in hand throughout test", 0, MAX_STEP_DISCREPANCY, false,
+                        true);
+            default:
+                break;
+        }
+    }
+
+    private OnClickListener mClickListener = new OnClickListener() {
+        public void onClick(View v) {
+            if (!mCheckForMotion) {
+                SensorCtsHelper.beep(ToneGenerator.TONE_PROP_BEEP);
+                mTimestampsUserReported.add(SystemClock.elapsedRealtimeNanos());
+                mStepsReported = mTimestampsUserReported.size();
+            }
+        }
+    };
+
+    /**
+     * @param instructions Instruction to be shown to testers
+     * @param expectedSteps Number of steps expected in this test
+     * @param tolerance Number of steps the count can be off by and still pass
+     * @param vibrate If TRUE, vibration will be concurrent with the test
+     * @param onlyWarn If TRUE, only warn the user if the test fails. This
+     *            option will be removed on a future release of CTS. TODO:
+     *            remove this option
+     * @throws Throwable
+     */
+    static long[] sVibratePattern = {
+            1000L, 500L, 1000L, 750L, 1000L, 500L, 1000L, 750L, 1000L, 1000L, 500L, 1000L,
+            750L, 1000L, 500L, 1000L
+    };
+    private void runTest(String instructions, int expectedSteps, int tolerance, boolean vibrate,
+            boolean onlyWarn)
+            throws Throwable {
+
+        mTimestampsUserReported.clear();
+        mTimestampsStepCounter.clear();
+        mTimestampsStepDetector.clear();
+
+        mMoveDetected = false;
+        mCheckForMotion = true;
+
+        appendText("Click 'Next' and " + instructions);
+        waitForUser();
+
+        mInitialStepCount = 0;
+        mStepsDetected = 0;
+        mStepsReported = 0;
+        if (vibrate) {
+            vibrate(sVibratePattern);
+        }
+
+        mCheckForMotion = (expectedSteps == 0);
+        startMeasurements();
+
+        long testStartTime = System.currentTimeMillis();
+        long testTime = 0;
+
+        while (testTime < MIN_TEST_TIME_MILLIS) {
+            int timeWaitSec = Math.round((MIN_TEST_TIME_MILLIS - testTime) / 1000);
+            clearText();
+            appendText("Current test: " + instructions);
+            appendText(String.format("%d seconds left, %d steps detected, %d reported",
+                    timeWaitSec, mStepsDetected, mStepsReported), Color.GRAY);
+            Thread.sleep(1000);
+            testTime = System.currentTimeMillis() - testStartTime;
+        }
+        clearText();
+        appendText("Current test: " + instructions);
+        verifyMeasurements(expectedSteps, tolerance, onlyWarn);
+        appendText(mERNWarning + "\n" + mSCWarning, Color.YELLOW);
+        mCheckForMotion = false;
+        sNumPassedTests++;
+        mERNWarning = "";
+        mSCWarning = "";
+    }
+
+    private void startMeasurements() throws Throwable {
+        mSensorStepCounter = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
+        if (mSensorStepCounter != null) {
+            mSensorManager.registerListener(this, mSensorStepCounter,
+                    SensorManager.SENSOR_DELAY_NORMAL);
+        } else {
+            appendText("Failed test, step counter sensor was not found", Color.RED);
+            Assert.fail("Step counter sensor was not found");
+        }
+
+        mSensorStepDetector = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
+        if (mSensorStepDetector != null) {
+            mSensorManager.registerListener(this, mSensorStepDetector,
+                    SensorManager.SENSOR_DELAY_NORMAL);
+        } else {
+            appendText("Failed test, step detector sensor was not found", Color.RED);
+            Assert.fail("Step detector sensor was not found");
+        }
+
+        mSensorAcceleration = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+        if (mSensorAcceleration != null && mCheckForMotion) {
+            mSensorManager.registerListener(this, mSensorAcceleration,
+                    SensorManager.SENSOR_DELAY_NORMAL);
+        }
+    }
+
+    private void verifyMeasurements(int stepsExpected, int tolerance, boolean onlyWarn)
+            throws Throwable {
+        if (mSensorManager != null) {
+            mSensorManager.unregisterListener(this);
+        }
+
+        Assert.assertFalse(String.format("You need to report at least %d steps", stepsExpected),
+                mStepsReported < stepsExpected);
+        double maxStepReportTime = compareTimestamps();
+        Assert.assertTrue(String.format("Step report time %f longer than %d seconds",
+                maxStepReportTime, MAX_TOLERANCE_STEP_TIME_LATENCY_SECONDS),
+                maxStepReportTime < MAX_TOLERANCE_STEP_TIME_LATENCY_SECONDS);
+
+        if (mCheckForMotion && !mMoveDetected) {
+            String message = "Movement is needed during this test";
+
+            warnOrAssert(onlyWarn, message);
+        }
+
+        if (Math.abs(mStepsDetected - mStepsReported) > tolerance) {
+            String message = String.format("Step count test: "
+                    + "detected %d steps but %d were expected (to within %d steps)",
+                    mStepsDetected, mStepsReported, tolerance);
+            warnOrAssert(onlyWarn, message);
+        }
+
+        appendText("PASS step count test", Color.GREEN);
+
+        if (Math.abs(mTimestampsStepDetector.size() - mStepsReported) > tolerance) {
+            String message = String.format("Step detector test: "
+                    + "detected %d steps but %d were expected (to within %d steps)",
+                    mTimestampsStepDetector.size(), mStepsReported, tolerance);
+            warnOrAssert(onlyWarn, message);
+        }
+
+        appendText("PASS step detection test", Color.GREEN);
+
+        logSuccess();
+    }
+
+    public final void onAccuracyChanged(Sensor sensor, int accuracy) {
+    }
+
+    private void warnOrAssert(boolean onlyWarn, String message) throws Throwable {
+        if (onlyWarn) {
+            appendText("WARNING: " + message, Color.YELLOW);
+        } else {
+            Assert.fail("FAILED " + message);
+        }
+    }
+
+    String mERNWarning = "";
+    String mSCWarning = "";
+
+    public long checkTimestamp(long eventTimestamp) {
+        long timestamp = SystemClock.elapsedRealtimeNanos();
+        if (Math.abs(timestamp - eventTimestamp) > MIN_TEST_TIME_MILLIS * 1e6) {
+            // elapsedRealtimeNanos will lead to test failure, warn for now
+            mERNWarning = "WARNING: elapsedRealtimeNanos is significantly different than "
+                    + " sensor event timestamps.  This should be rectified.";
+        } else {
+            timestamp = eventTimestamp;
+        }
+        return timestamp;
+    }
+
+    public void onStepCounterChanged(SensorEvent event) throws Throwable {
+        int steps = (int) event.values[0] - mInitialStepCount;
+
+        if (mInitialStepCount == 0) { // set the initial number of steps
+            mInitialStepCount = steps;
+        } else if (steps > 0) {
+            mTimestampsStepCounter.add(checkTimestamp(event.timestamp));
+            Assert.assertTrue(String.format("Step counter did not increase monotonically: "
+                    + "%d changed to %d", mStepsDetected, steps), steps >= mStepsDetected);
+            mStepsDetected = steps;
+        } else {
+            Assert.fail("Step Counter change called when no steps reported");
+        }
+    }
+
+    public void onStepDetectorChanged(SensorEvent event) throws Throwable {
+        Assert.assertEquals("Incorrect value[0] in step detector event", event.values[0], 1.0f);
+        mTimestampsStepDetector.add(checkTimestamp(event.timestamp));
+    }
+
+    public final void onSensorChanged(SensorEvent event) {
+        int type = event.sensor.getType();
+        try {
+            if (type == Sensor.TYPE_STEP_COUNTER) {
+                onStepCounterChanged(event);
+            } else if (type == Sensor.TYPE_STEP_DETECTOR) {
+                onStepDetectorChanged(event);
+            } else if (type == Sensor.TYPE_ACCELEROMETER) {
+                mMoveDetected = SensorCtsHelper.checkMovementDetection(event);
+            } else {
+                Assert.fail("Sensor type " + type + " called when not registered for by this test");
+            }
+        } catch (Throwable ae) {
+            mSCWarning = ae.getMessage();
+        }
+    }
+
+    protected double compareTimestamps() {
+        double timeDeltaInSec;
+        double maxTimeDeltaInSec = 0;
+        StringBuilder reportLine = new StringBuilder();
+        reportLine.append("Reported Step: Step Detector / Counter Latency (sec)\n");
+        for (int eventCounter = 0; eventCounter < mStepsReported; eventCounter++) {
+            reportLine.append((eventCounter + 1) + ":  ");
+
+            if (eventCounter < mTimestampsStepDetector.size()) {
+                timeDeltaInSec = (mTimestampsStepDetector.get(eventCounter)
+                        - mTimestampsUserReported.get(eventCounter)) / NANOSECONDS_IN_SEC;
+                maxTimeDeltaInSec = Math.max(maxTimeDeltaInSec, Math.abs(timeDeltaInSec));
+                reportLine.append(String.format("%.2f", timeDeltaInSec));
+            } else {
+                reportLine.append("--");
+            }
+
+            reportLine.append("  /  ");
+            if (eventCounter < mTimestampsStepCounter.size()) {
+                timeDeltaInSec = (mTimestampsStepCounter.get(eventCounter)
+                        - mTimestampsUserReported.get(eventCounter)) / NANOSECONDS_IN_SEC;
+                maxTimeDeltaInSec = Math.max(maxTimeDeltaInSec, Math.abs(timeDeltaInSec));
+                reportLine.append(String.format("%.2f", timeDeltaInSec));
+            } else {
+                reportLine.append("--");
+            }
+            reportLine.append("\n");
+        }
+        appendText(reportLine.toString(), Color.GRAY);
+
+        return maxTimeDeltaInSec;
+    }
+
+    protected void vibrate(long[] pattern) {
+        Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
+        if(v==null) {
+            appendText("Cannot access vibrator for this test...continuing anyway", Color.YELLOW);
+        } else {
+            v.vibrate(pattern, -1);
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (mSensorManager == null) {
+            mSensorManager = (SensorManager) getApplicationContext()
+                    .getSystemService(Context.SENSOR_SERVICE);
+        }
+
+        if (mSensorStepCounter != null) {
+            mSensorManager.registerListener(this, mSensorStepCounter,
+                    SensorManager.SENSOR_DELAY_NORMAL);
+        }
+        if (mSensorStepDetector != null) {
+            mSensorManager.registerListener(this, mSensorStepDetector,
+                    SensorManager.SENSOR_DELAY_NORMAL);
+        }
+        if (mSensorAcceleration != null && mCheckForMotion) {
+            mSensorManager.registerListener(this, mSensorAcceleration,
+                    SensorManager.SENSOR_DELAY_NORMAL);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mSensorManager != null) {
+            mSensorManager.unregisterListener(this);
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
index ed55b01..ed6560f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -17,7 +17,10 @@
 
 import android.content.Context;
 import android.hardware.Sensor;
+import android.hardware.SensorEvent;
 import android.hardware.SensorManager;
+import android.media.AudioManager;
+import android.media.ToneGenerator;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -248,4 +251,45 @@
         }
         return sensorManager;
     }
+
+    public static void beep(int tone) {
+	ToneGenerator mToneGenerator = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100);
+	mToneGenerator.startTone(tone);
+    }
+
+    private final static float mAccelerationThresholdForMoveDetection = 4.0f;
+    private static float[] mGravity = {0.0f, 0.0f, 0.0f};
+    public static boolean checkMovementDetection(SensorEvent event) {
+        // Alpha is calculated as t / (t + dT),
+        // where t is the low-pass filter's time-constant and
+        // dT is the event delivery rate.
+	boolean mMoveDetected = false;
+        final float alpha = 0.8f;
+        float[] linear_acceleration = {0.0f, 0.0f, 0.0f};
+
+        if (mGravity[0] == 0f && mGravity[2] == 0f) {
+            mGravity[0] = event.values[0];
+            mGravity[1] = event.values[1];
+            mGravity[2] = event.values[2];
+        } else {
+            // Isolate the force of gravity with the low-pass filter.
+            mGravity[0] = alpha * mGravity[0] + (1 - alpha) * event.values[0];
+            mGravity[1] = alpha * mGravity[1] + (1 - alpha) * event.values[1];
+            mGravity[2] = alpha * mGravity[2] + (1 - alpha) * event.values[2];
+        }
+
+        // Remove the gravity contribution with the high-pass filter.
+        linear_acceleration[0] = event.values[0] - mGravity[0];
+        linear_acceleration[1] = event.values[1] - mGravity[1];
+        linear_acceleration[2] = event.values[2] - mGravity[2];
+
+        float totalAcceleration = Math.abs(linear_acceleration[0])
+	    + Math.abs(linear_acceleration[1])
+	    + Math.abs(linear_acceleration[2]);
+
+        if (totalAcceleration > mAccelerationThresholdForMoveDetection) {
+            mMoveDetected = true;
+        }
+	return mMoveDetected;
+    }
 }
diff --git a/tests/tests/tv/AndroidManifest.xml b/tests/tests/tv/AndroidManifest.xml
index 66f4ac0..fb84509 100644
--- a/tests/tests/tv/AndroidManifest.xml
+++ b/tests/tests/tv/AndroidManifest.xml
@@ -21,8 +21,8 @@
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
 
-    <uses-permission android:name="android.permission.READ_EPG_DATA" />
-    <uses-permission android:name="android.permission.WRITE_EPG_DATA" />
+    <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
+    <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
 
     <application>
         <uses-library android:name="android.test.runner" />