diff --git a/core/api/current.txt b/core/api/current.txt
index 4d27dde716288..cdc5d0c9345cc 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -18071,6 +18071,7 @@ package android.hardware {
field public static final String STRING_TYPE_GRAVITY = "android.sensor.gravity";
field public static final String STRING_TYPE_GYROSCOPE = "android.sensor.gyroscope";
field public static final String STRING_TYPE_GYROSCOPE_UNCALIBRATED = "android.sensor.gyroscope_uncalibrated";
+ field public static final String STRING_TYPE_HEAD_TRACKER = "android.sensor.head_tracker";
field public static final String STRING_TYPE_HEART_BEAT = "android.sensor.heart_beat";
field public static final String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate";
field public static final String STRING_TYPE_HINGE_ANGLE = "android.sensor.hinge_angle";
@@ -18101,6 +18102,7 @@ package android.hardware {
field public static final int TYPE_GRAVITY = 9; // 0x9
field public static final int TYPE_GYROSCOPE = 4; // 0x4
field public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16; // 0x10
+ field public static final int TYPE_HEAD_TRACKER = 37; // 0x25
field public static final int TYPE_HEART_BEAT = 31; // 0x1f
field public static final int TYPE_HEART_RATE = 21; // 0x15
field public static final int TYPE_HINGE_ANGLE = 36; // 0x24
@@ -18163,6 +18165,7 @@ package android.hardware {
method public void onFlushCompleted(android.hardware.Sensor);
method public void onSensorAdditionalInfo(android.hardware.SensorAdditionalInfo);
method public void onSensorChanged(android.hardware.SensorEvent);
+ method public void onSensorDiscontinuity(@NonNull android.hardware.Sensor);
}
public interface SensorEventListener {
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 0e42b02d59561..37cfb4935f0d3 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -711,6 +711,20 @@ public final class Sensor {
*/
public static final String STRING_TYPE_HINGE_ANGLE = "android.sensor.hinge_angle";
+ /**
+ * A constant describing a head tracker sensor.
+ *
+ * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+ */
+ public static final int TYPE_HEAD_TRACKER = 37;
+
+ /**
+ * A constant string describing a head tracker sensor.
+ *
+ * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+ */
+ public static final String STRING_TYPE_HEAD_TRACKER = "android.sensor.head_tracker";
+
/**
* A constant describing all sensor types.
*/
@@ -831,6 +845,7 @@ public final class Sensor {
1, // SENSOR_TYPE_LOW_LATENCY_OFFBODY_DETECT
6, // SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED
1, // SENSOR_TYPE_HINGE_ANGLE
+ 6, // SENSOR_TYPE_HEAD_TRACKER (discontinuity count is excluded)
};
/**
@@ -1283,6 +1298,9 @@ public final class Sensor {
case TYPE_HINGE_ANGLE:
mStringType = STRING_TYPE_HINGE_ANGLE;
return true;
+ case TYPE_HEAD_TRACKER:
+ mStringType = STRING_TYPE_HEAD_TRACKER;
+ return true;
default:
return false;
}
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 232f234297207..c77c8cc635e68 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -641,6 +641,41 @@ public class SensorEvent {
*
values[0]: Measured hinge angle between 0 and 360 degrees inclusive
*
*
+ * {@link android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER}:
+ *
+ * A sensor of this type measures the orientation of a user's head relative to an arbitrary
+ * reference frame, as well as the rate of rotation.
+ *
+ * Events produced by this sensor follow a special head-centric coordinate frame, where:
+ *
+ * - The X axis crosses through the user's ears, with the positive X direction extending
+ * out of the user's right ear
+ * - The Y axis crosses from the back of the user's head through their nose, with the
+ * positive direction extending out of the nose, and the X/Y plane being nominally
+ * parallel to the ground when the user is upright and looking straight ahead
+ * - The Z axis crosses from the neck through the top of the user's head, with the
+ * positive direction extending out from the top of the head
+ *
+ *
+ * Data is provided in Euler vector representation, which is a vector whose direction indicates
+ * the axis of rotation and magnitude indicates the angle to rotate around that axis, in
+ * radians.
+ *
+ * The first three elements provide the transform from the (arbitrary, possibly slowly drifting)
+ * reference frame to the head frame. The magnitude of this vector is in range [0, π]
+ * radians, while the value of individual axes is in range [-π, π]. The next three
+ * elements provide the estimated rotational velocity of the user's head relative to itself, in
+ * radians per second.
+ *
+ *
+ * - values[0] : X component of Euler vector representing rotation
+ * - values[1] : Y component of Euler vector representing rotation
+ * - values[2] : Z component of Euler vector representing rotation
+ * - values[3] : X component of Euler vector representing angular velocity
+ * - values[4] : Y component of Euler vector representing angular velocity
+ * - values[5] : Z component of Euler vector representing angular velocity
+ *
+ *
* @see GeomagneticField
*/
public final float[] values;
diff --git a/core/java/android/hardware/SensorEventCallback.java b/core/java/android/hardware/SensorEventCallback.java
index bac212ab5b209..7b0092da1196c 100644
--- a/core/java/android/hardware/SensorEventCallback.java
+++ b/core/java/android/hardware/SensorEventCallback.java
@@ -16,6 +16,8 @@
package android.hardware;
+import android.annotation.NonNull;
+
/**
* Used for receiving sensor additional information frames.
*/
@@ -52,4 +54,21 @@ public abstract class SensorEventCallback implements SensorEventListener2 {
* reported from sensor hardware.
*/
public void onSensorAdditionalInfo(SensorAdditionalInfo info) {}
+
+ /**
+ * Called when the next {@link android.hardware.SensorEvent SensorEvent} to be delivered via the
+ * {@link #onSensorChanged(SensorEvent) onSensorChanged} method represents the first event after
+ * a discontinuity.
+ *
+ * The exact meaning of discontinuity depends on the sensor type. For {@link
+ * android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER}, this means that the
+ * reference frame has suddenly and significantly changed.
+ *
+ * Note that this concept is either not relevant to or not supported by most sensor types,
+ * {@link android.hardware.Sensor#TYPE_HEAD_TRACKER Sensor.TYPE_HEAD_TRACKER} being the notable
+ * exception.
+ *
+ * @param sensor The {@link android.hardware.Sensor Sensor} which experienced the discontinuity.
+ */
+ public void onSensorDiscontinuity(@NonNull Sensor sensor) {}
}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 3a8513b9323f7..32a5ee77508ec 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -676,6 +676,7 @@ public class SystemSensorManager extends SensorManager {
private long mNativeSensorEventQueue;
private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
+ protected final SparseIntArray mSensorDiscontinuityCounts = new SparseIntArray();
private final CloseGuard mCloseGuard = CloseGuard.get();
protected final SystemSensorManager mManager;
@@ -875,10 +876,21 @@ public class SystemSensorManager extends SensorManager {
// call onAccuracyChanged() only if the value changes
final int accuracy = mSensorAccuracies.get(handle);
- if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {
+ if (t.accuracy >= 0 && accuracy != t.accuracy) {
mSensorAccuracies.put(handle, t.accuracy);
mListener.onAccuracyChanged(t.sensor, t.accuracy);
}
+
+ // call onSensorDiscontinuity() if the discontinuity counter changed
+ if (t.sensor.getType() == Sensor.TYPE_HEAD_TRACKER
+ && mListener instanceof SensorEventCallback) {
+ final int lastCount = mSensorDiscontinuityCounts.get(handle);
+ final int curCount = Float.floatToIntBits(values[6]);
+ if (lastCount >= 0 && lastCount != curCount) {
+ ((SensorEventCallback) mListener).onSensorDiscontinuity(t.sensor);
+ }
+ }
+
mListener.onSensorChanged(t);
}