am 8fd76dac: am 10aa4a47: Merge "Adding CTS and CTS Verifier for Heart Rate" into klp-modular-dev

* commit '8fd76dac965983fbc00d395c9d916d4ef35c1698':
  Adding CTS and CTS Verifier for Heart Rate
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index b535384..2507955 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -27,6 +27,7 @@
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BODY_SENSORS"/>
     <uses-permission android:name="android.permission.CAMERA" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
@@ -577,6 +578,18 @@
                        android:value="android.hardware.sensor.gyroscope"/>
         </activity>
 
+        <activity android:name=".sensors.HeartRateMonitorTestActivity"
+                  android:label="@string/snsr_heartrate_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" />
+            <meta-data android:name="test_required_features"
+                       android:value="android.hardware.sensor.heartrate" />
+        </activity>
+
         <!-- Disable the test until calibration routine is verified -->
         <!--activity android:name=".sensors.MagneticFieldMeasurementTestActivity"
                   android:label="@string/snsr_mag_m_test"
diff --git a/apps/CtsVerifier/res/layout/snsr_hrm.xml b/apps/CtsVerifier/res/layout/snsr_hrm.xml
new file mode 100644
index 0000000..612b424
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/snsr_hrm.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <include android:id="@+id/pass_fail_buttons"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons" />
+
+    <TextView android:id="@+id/sensor_value"
+              android:background="@drawable/gray_bubble"
+              android:drawablePadding="10dip"
+              android:layout_above="@+id/pass_fail_buttons"
+              android:layout_centerInParent="true"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:layout_marginBottom="10dip"
+              android:paddingLeft="10dip"
+              android:paddingRight="10dip"
+              android:paddingTop="5dip"
+              android:paddingBottom="5dip"
+              android:textSize="28dip"
+        />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 00ff965..b6c846f 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -410,6 +410,12 @@
     <string name="snsr_gyro_test_degrees_message">These values looks like degrees per second. These should be radians per second!</string>
     <string name="snsr_gyro_m_test">Gyroscope Measurement Test</string>
 
+    <!-- Heart Rate -->
+    <string name="snsr_heartrate_test">Heart Rate Test</string>
+    <string name="snsr_heartrate_test_info">This test verifies that the heart rate monitor is working properly.\n\nVerify that the resting heart rate is between 0 and 100.</string>
+    <string name="snsr_heartrate_test_no_heartrate_title">No heart rate monitor?</string>
+    <string name="snsr_heartrate_test_no_heartrate_message">It doesn\'t seem like you have a heart rate monitor, so you don\'t need to run this test.</string>
+
     <!-- Magnetic Field -->
     <string name="snsr_mag_m_test">Magnetic Field Measurement Tests</string>
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
index 6b87261..581121e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
@@ -188,6 +188,10 @@
             new Feature(PackageManager.FEATURE_SENSOR_STEP_DETECTOR, false),
     };
 
+    public static final Feature[] ALL_KITKAT_WATCH_FEATURES = {
+            new Feature(PackageManager.FEATURE_SENSOR_HEART_RATE, false),
+    };
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -218,6 +222,9 @@
 
         // add features from latest to last so that the latest requirements are put in the set first
         int apiVersion = Build.VERSION.SDK_INT;
+        if (apiVersion >= Build.VERSION_CODES.KITKAT_WATCH) {
+            Collections.addAll(features, ALL_KITKAT_WATCH_FEATURES);
+        }
         if (apiVersion >= Build.VERSION_CODES.KITKAT) {
             Collections.addAll(features, ALL_KITKAT_FEATURES);
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/HeartRateMonitorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/HeartRateMonitorTestActivity.java
new file mode 100644
index 0000000..d0cf41f
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/HeartRateMonitorTestActivity.java
@@ -0,0 +1,112 @@
+/*
+ * 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 android.app.AlertDialog;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+/**
+ * CTS Verifier case for verifying correct integration of heart rate monitor.
+ * If a user is wearing a device with an HRM, the value is between <> and <>
+ */
+public class HeartRateMonitorTestActivity extends PassFailButtons.Activity {
+    private SensorManager mSensorManager;
+    private Sensor mSensor;
+    private SensorListener mSensorListener;
+    private AlertDialog mNoHeartRateWarningDialog;
+    private TextView mSensorText;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.snsr_hrm);
+        setInfoResources(R.string.snsr_heartrate_test, R.string.snsr_heartrate_test_info, 0);
+        setPassFailButtonClickListeners();
+
+        mSensorText = (TextView) findViewById(R.id.sensor_value);
+
+        mSensorManager = (SensorManager) getApplicationContext().getSystemService(
+                Context.SENSOR_SERVICE);
+        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE);
+        mSensorListener = new SensorListener();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (!mSensorManager.registerListener(mSensorListener, mSensor,
+                SensorManager.SENSOR_DELAY_UI)) {
+            showNoHeartRateWarningDialog();
+            PassFailButtons.setTestResultAndFinish(this, getTestId(), getTestDetails(), true);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        mSensorManager.unregisterListener(mSensorListener, mSensor);
+    }
+
+    private void showNoHeartRateWarningDialog() {
+        if (mNoHeartRateWarningDialog == null) {
+            mNoHeartRateWarningDialog = new AlertDialog.Builder(this)
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .setTitle(R.string.snsr_heartrate_test_no_heartrate_title)
+                    .setMessage(R.string.snsr_heartrate_test_no_heartrate_message)
+                    .setPositiveButton(android.R.string.ok, null)
+                    .create();
+        }
+        if (!mNoHeartRateWarningDialog.isShowing()) {
+            mNoHeartRateWarningDialog.show();
+        }
+    }
+
+    private class SensorListener implements SensorEventListener {
+        private static final double MIN_HEART_RATE = 40;
+        private static final double MAX_HEART_RATE = 200;
+        @Override
+        public void onSensorChanged(SensorEvent sensorEvent) {
+            float value = sensorEvent.values[0];
+            if (value > MAX_HEART_RATE || value < MIN_HEART_RATE) {
+                updateWidgets(value, R.drawable.fs_error);
+            } else {
+                updateWidgets(value, R.drawable.fs_good);
+            }
+        }
+
+        void updateWidgets(float sensorValue, int icon) {
+            mSensorText.setText(String.format("%+.2f", sensorValue));
+            mSensorText.setCompoundDrawablesWithIntrinsicBounds(0, 0, icon, 0);
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int i) {
+
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index 24dd368..907759f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -99,6 +99,17 @@
             assertNull(sensor);
         }
 
+        sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE);
+        boolean hasHeartRate = getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_SENSOR_HEART_RATE);
+        // heartrate sensor is optional
+        if (hasHeartRate) {
+            assertEquals(Sensor.TYPE_HEART_RATE, sensor.getType());
+            assertSensorValues(sensor);
+        } else {
+            assertNull(sensor);
+        }
+
         sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
         boolean hasCompass = getContext().getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_SENSOR_COMPASS);