aboutsummaryrefslogtreecommitdiff
path: root/experimental
diff options
context:
space:
mode:
authorJordan Jozwiak <jjoz@google.com>2019-12-23 21:43:48 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2019-12-23 21:43:48 +0000
commitd0f0fb15e5522ea8679192cab2873e548892aea9 (patch)
tree612678b3af4b0d9808ce07d8c8c740dc868e944f /experimental
parent56f0e121c84ee9305cc70f5a2355b8e4223bf447 (diff)
parent9789ac56447795283ca5bbdcb828a63a4070b71c (diff)
downloadCar-d0f0fb15e5522ea8679192cab2873e548892aea9.tar.gz
Merge "Consume driver awareness values from suppliers"
Diffstat (limited to 'experimental')
-rw-r--r--experimental/experimental_api/src/android/car/experimental/DriverAwarenessEvent.aidl19
-rw-r--r--experimental/experimental_api/src/android/car/experimental/DriverAwarenessEvent.java113
-rw-r--r--experimental/experimental_api/src/android/car/experimental/DriverAwarenessSupplierConfig.aidl19
-rw-r--r--experimental/experimental_api/src/android/car/experimental/DriverAwarenessSupplierConfig.java82
-rw-r--r--experimental/experimental_api/src/android/car/experimental/DriverAwarenessSupplierService.java153
-rw-r--r--experimental/experimental_api/src/android/car/experimental/IDriverAwarenessSupplier.aidl33
-rw-r--r--experimental/experimental_api/src/android/car/experimental/IDriverAwarenessSupplierCallback.aidl50
-rw-r--r--experimental/experimentalcarservice_unit_test/Android.bp48
-rw-r--r--experimental/experimentalcarservice_unit_test/AndroidManifest.xml31
-rw-r--r--experimental/experimentalcarservice_unit_test/src/com/android/experimentalcar/DriverDistractionExperimentalFeatureServiceTest.java189
-rw-r--r--experimental/experimentalcarservice_unit_test/src/com/android/experimentalcar/FakeTimeSource.java44
-rw-r--r--experimental/experimentalcarservice_unit_test/src/com/android/experimentalcar/FakeTimer.java44
-rw-r--r--experimental/service/Android.bp34
-rw-r--r--experimental/service/AndroidManifest.xml13
-rw-r--r--experimental/service/src/com/android/experimentalcar/DriverDistractionExperimentalFeatureService.java516
-rw-r--r--experimental/service/src/com/android/experimentalcar/IExperimentalCarImpl.java5
-rw-r--r--experimental/service/src/com/android/experimentalcar/ITimeSource.java30
-rw-r--r--experimental/service/src/com/android/experimentalcar/ITimer.java35
-rw-r--r--experimental/service/src/com/android/experimentalcar/SampleExternalDriverAwarenessSupplier.java58
-rw-r--r--experimental/service/src/com/android/experimentalcar/SystemTimeSource.java30
-rw-r--r--experimental/service/src/com/android/experimentalcar/SystemTimer.java39
-rw-r--r--experimental/service/src/com/android/experimentalcar/TouchDriverAwarenessSupplier.java99
22 files changed, 1672 insertions, 12 deletions
diff --git a/experimental/experimental_api/src/android/car/experimental/DriverAwarenessEvent.aidl b/experimental/experimental_api/src/android/car/experimental/DriverAwarenessEvent.aidl
new file mode 100644
index 0000000000..e9f38b5cec
--- /dev/null
+++ b/experimental/experimental_api/src/android/car/experimental/DriverAwarenessEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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 android.car.experimental;
+
+parcelable DriverAwarenessEvent; \ No newline at end of file
diff --git a/experimental/experimental_api/src/android/car/experimental/DriverAwarenessEvent.java b/experimental/experimental_api/src/android/car/experimental/DriverAwarenessEvent.java
new file mode 100644
index 0000000000..d961a4311c
--- /dev/null
+++ b/experimental/experimental_api/src/android/car/experimental/DriverAwarenessEvent.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 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 android.car.experimental;
+
+import android.annotation.FloatRange;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Event about a driver's awareness level at a certain point in time.
+ *
+ * <p>Driver Awareness is an abstract concept based on a driver's cognitive situational awareness
+ * of the environment around them. This metric can be approximated based on signals about the
+ * driver's behavior, such as where they are looking or how much their interact with the headunit
+ * in the car.
+ *
+ * <p>Furthermore, what constitutes the boundaries of no awareness and full awareness must be based
+ * on the UX Research through real-world studies and driving simulation. It is the responsibility
+ * of {@link DriverAwarenessSupplier}s to understand how their sensor input fits with current
+ * research in order to determine the appropriate awareness value.
+ *
+ * @hide
+ */
+public final class DriverAwarenessEvent implements Parcelable {
+
+ private final long mTimeStamp;
+
+ @FloatRange(from = 0.0f, to = 1.0f)
+ private final float mAwarenessValue;
+
+ /**
+ * Creator for {@link Parcelable}.
+ */
+ public static final Parcelable.Creator<DriverAwarenessEvent> CREATOR =
+ new Parcelable.Creator<DriverAwarenessEvent>() {
+ public DriverAwarenessEvent createFromParcel(Parcel in) {
+ return new DriverAwarenessEvent(in);
+ }
+
+ public DriverAwarenessEvent[] newArray(int size) {
+ return new DriverAwarenessEvent[size];
+ }
+ };
+
+ /**
+ * Creates an instance of a {@link DriverAwarenessEvent}.
+ *
+ * @param timeStamp the time that the awareness value was sampled, in milliseconds elapsed
+ * since system boot
+ * @param awarenessValue the driver's awareness level at this point in time, ranging from 0, no
+ * awareness, to 1, full awareness
+ */
+ public DriverAwarenessEvent(long timeStamp,
+ @FloatRange(from = 0.0f, to = 1.0f) float awarenessValue) {
+ mTimeStamp = timeStamp;
+ mAwarenessValue = awarenessValue;
+ }
+
+ /**
+ * Parcelable constructor.
+ */
+ private DriverAwarenessEvent(Parcel in) {
+ mTimeStamp = in.readLong();
+ mAwarenessValue = in.readFloat();
+ }
+
+ /**
+ * Returns the time at which this driver awareness value was inferred based on the car's
+ * sensors. It is the elapsed time in milliseconds since system boot.
+ */
+ public long getTimeStamp() {
+ return mTimeStamp;
+ }
+
+ /**
+ * The current driver awareness value, where 0 is no awareness and 1 is full awareness.
+ */
+ @FloatRange(from = 0.0f, to = 1.0f)
+ public float getAwarenessValue() {
+ return mAwarenessValue;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mTimeStamp);
+ dest.writeFloat(mAwarenessValue);
+ }
+
+
+ @Override
+ public String toString() {
+ return String.format("DriverAwarenessEvent{timeStamp=%s, awarenessValue=%s}",
+ mTimeStamp, mAwarenessValue);
+ }
+}
diff --git a/experimental/experimental_api/src/android/car/experimental/DriverAwarenessSupplierConfig.aidl b/experimental/experimental_api/src/android/car/experimental/DriverAwarenessSupplierConfig.aidl
new file mode 100644
index 0000000000..f7b13981d5
--- /dev/null
+++ b/experimental/experimental_api/src/android/car/experimental/DriverAwarenessSupplierConfig.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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 android.car.experimental;
+
+parcelable DriverAwarenessSupplierConfig; \ No newline at end of file
diff --git a/experimental/experimental_api/src/android/car/experimental/DriverAwarenessSupplierConfig.java b/experimental/experimental_api/src/android/car/experimental/DriverAwarenessSupplierConfig.java
new file mode 100644
index 0000000000..1fd867aeb6
--- /dev/null
+++ b/experimental/experimental_api/src/android/car/experimental/DriverAwarenessSupplierConfig.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2019 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 android.car.experimental;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Configuration for an instance of {@link android.car.experimental.IDriverAwarenessSupplier}.
+ */
+public class DriverAwarenessSupplierConfig implements Parcelable {
+
+ private final long mMaxStalenessMillis;
+
+ /**
+ * Creator for {@link Parcelable}.
+ */
+ public static final Parcelable.Creator<DriverAwarenessSupplierConfig> CREATOR =
+ new Parcelable.Creator<DriverAwarenessSupplierConfig>() {
+ public DriverAwarenessSupplierConfig createFromParcel(Parcel in) {
+ return new DriverAwarenessSupplierConfig(in);
+ }
+
+ public DriverAwarenessSupplierConfig[] newArray(int size) {
+ return new DriverAwarenessSupplierConfig[size];
+ }
+ };
+
+ /**
+ * Creates an instance of a {@link DriverAwarenessSupplierConfig}.
+ */
+ public DriverAwarenessSupplierConfig(long maxStalenessMillis) {
+ mMaxStalenessMillis = maxStalenessMillis;
+ }
+
+ /**
+ * Parcelable constructor.
+ */
+ private DriverAwarenessSupplierConfig(Parcel in) {
+ mMaxStalenessMillis = in.readLong();
+ }
+
+ /**
+ * Returns the duration in milliseconds after which the input from this supplier should be
+ * considered stale.
+ *
+ * <p>Data should be sent more frequently than the staleness rate defined here.
+ */
+ public long getMaxStalenessMillis() {
+ return mMaxStalenessMillis;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mMaxStalenessMillis);
+ }
+
+
+ @Override
+ public String toString() {
+ return String.format("DriverAwarenessEvent{mMaxStalenessMillis=%s}", mMaxStalenessMillis);
+ }
+}
diff --git a/experimental/experimental_api/src/android/car/experimental/DriverAwarenessSupplierService.java b/experimental/experimental_api/src/android/car/experimental/DriverAwarenessSupplierService.java
new file mode 100644
index 0000000000..df48d101ca
--- /dev/null
+++ b/experimental/experimental_api/src/android/car/experimental/DriverAwarenessSupplierService.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2019 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 android.car.experimental;
+
+
+import android.annotation.CallSuper;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * The supplier for providing a stream of the driver's current situational awareness.
+ *
+ * <p>A perfect understanding of driver awareness requires years of extensive research and signals
+ * that suggest the cognitive situational awareness of the driver. Implementations of this class
+ * attempt to approximate driver awareness using concrete, but less accurate signals, such as gaze
+ * or touch.
+ *
+ * <p>Suppliers should notify of updates to the driver awareness level through {@link
+ * #onDriverAwarenessUpdated(DriverAwarenessEvent)}.
+ *
+ * <p>Suppliers define the amount of time until their data should be considered stale through
+ * {@link #getMaxStalenessMillis()}. After that amount of time data from this supplier will no
+ * longer be considered fresh. {@link #NO_STALENESS} is meant to be used by change-based suppliers
+ * such as a touch supplier - it is not appropriate for data signals that change continuous over
+ * time.
+ *
+ * <p>If this supplier has its own internal configuration, that configuration must be configurable
+ * by locale.
+ *
+ * <p>It is the attention supplier's responsibility to make sure that it only sends high-quality
+ * data events.
+ */
+public abstract class DriverAwarenessSupplierService extends Service {
+ private static final String TAG = "DriverAwarenessSupplierService";
+
+ /**
+ * Value that can be returned by {@link #getMaxStalenessMillis()} to indicate that an attention
+ * supplier sends change-events instead of push events on a regular interval. Should only be
+ * used for a supplier that is guaranteed to always be running (e.g. it won't crash or have
+ * periods of poor data).
+ */
+ public static final long NO_STALENESS = -1;
+
+ private final Object mLock = new Object();
+
+ private SupplierBinder mSupplierBinder;
+
+ @GuardedBy("mLock")
+ private IDriverAwarenessSupplierCallback mDriverAwarenessSupplierCallback;
+
+ /**
+ * Returns the duration in milliseconds after which the input from this supplier should be
+ * considered stale. This method should return a positive value greater than 0. There is no
+ * technical limit on the value returned here, but a value of 1000ms (1 second) would likely be
+ * considered too high since the driving environment can change drastically in that amount of
+ * time.
+ *
+ * <p>This can also return {@link #NO_STALENESS} if the supplier only emits change events and
+ * has no risk of failing to emit those change events within a reasonable amount of time.
+ *
+ * <p>Data should be sent more frequently than the staleness period defined here.
+ */
+ public abstract long getMaxStalenessMillis();
+
+ /**
+ * The distraction service is ready to start receiving events via {@link
+ * #onDriverAwarenessUpdated(DriverAwarenessEvent)}.
+ */
+ protected abstract void onReady();
+
+ @Override
+ @CallSuper
+ public IBinder onBind(Intent intent) {
+ logd("onBind, intent: " + intent);
+ if (mSupplierBinder == null) {
+ mSupplierBinder = new SupplierBinder();
+ }
+ return mSupplierBinder;
+ }
+
+ /**
+ * The driver awareness has changed - emit that update to the {@link
+ * com.android.experimentalcar.DriverDistractionExperimentalFeatureService}.
+ */
+ protected void onDriverAwarenessUpdated(DriverAwarenessEvent event) {
+ logd("onDriverAwarenessUpdated: " + event);
+ synchronized (mLock) {
+ if (mDriverAwarenessSupplierCallback != null) {
+ try {
+ mDriverAwarenessSupplierCallback.onDriverAwarenessUpdated(event);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote exception", e);
+ }
+ }
+ }
+ }
+
+ private void handleReady() {
+ synchronized (mLock) {
+ try {
+ mDriverAwarenessSupplierCallback.onConfigLoaded(
+ new DriverAwarenessSupplierConfig(getMaxStalenessMillis()));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to send config - abandoning ready process", e);
+ return;
+ }
+ }
+ onReady();
+ }
+
+ /**
+ * The binder between this service and
+ * {@link com.android.experimentalcar.DriverDistractionExperimentalFeatureService}.
+ */
+ private class SupplierBinder extends IDriverAwarenessSupplier.Stub {
+
+ @Override
+ public void onReady() {
+ handleReady();
+ }
+
+ @Override
+ public void setCallback(IDriverAwarenessSupplierCallback callback) {
+ synchronized (mLock) {
+ mDriverAwarenessSupplierCallback = callback;
+ }
+ }
+ }
+
+ private static void logd(String message) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, message);
+ }
+ }
+}
diff --git a/experimental/experimental_api/src/android/car/experimental/IDriverAwarenessSupplier.aidl b/experimental/experimental_api/src/android/car/experimental/IDriverAwarenessSupplier.aidl
new file mode 100644
index 0000000000..6eaadebba1
--- /dev/null
+++ b/experimental/experimental_api/src/android/car/experimental/IDriverAwarenessSupplier.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 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 android.car.experimental;
+
+import android.car.experimental.IDriverAwarenessSupplierCallback;
+
+/**
+ * Binder API for a driver awareness supplier.
+ *
+ * @hide
+ */
+oneway interface IDriverAwarenessSupplier {
+
+ /** Called once the distraction service is ready to receive events */
+ void onReady() = 0;
+
+ /** Set the callback to be used for this supplier */
+ void setCallback(IDriverAwarenessSupplierCallback callback) = 1;
+} \ No newline at end of file
diff --git a/experimental/experimental_api/src/android/car/experimental/IDriverAwarenessSupplierCallback.aidl b/experimental/experimental_api/src/android/car/experimental/IDriverAwarenessSupplierCallback.aidl
new file mode 100644
index 0000000000..7b6ec1fa8e
--- /dev/null
+++ b/experimental/experimental_api/src/android/car/experimental/IDriverAwarenessSupplierCallback.aidl
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 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 android.car.experimental;
+
+import android.car.experimental.DriverAwarenessEvent;
+import android.car.experimental.DriverAwarenessSupplierConfig;
+
+/**
+ * Binder callback for IDriverAwarenessSupplier.
+ *
+ * @hide
+ */
+interface IDriverAwarenessSupplierCallback {
+ /**
+ * Sets the awareness level for the driver. Determining sufficient data quality is the
+ * responsibility of the AwarenessSupplier and events with insufficient data quality should not
+ * be sent.
+ *
+ * <p>Suppliers could crash in the background or fail to send continuously high data and
+ * therefore should push events, even if the Awareness level hasn't changed, with a frequency
+ * greater than their specified AwarenessSupplier#getMaxStalenessMillis().
+ *
+ * <p>Should be called once when first registered.
+ *
+ * @param event a snapshot of the driver's awareness at a certain point in time.
+ */
+ void onDriverAwarenessUpdated(in DriverAwarenessEvent event) = 0;
+
+ /**
+ * Sends the configuration for IDriverAwarenessSupplier configuration that this is a callback
+ * for.
+ *
+ * @param config for the IDriverAwarenessSupplier
+ */
+ void onConfigLoaded(in DriverAwarenessSupplierConfig config) = 1;
+}
diff --git a/experimental/experimentalcarservice_unit_test/Android.bp b/experimental/experimentalcarservice_unit_test/Android.bp
new file mode 100644
index 0000000000..891a151340
--- /dev/null
+++ b/experimental/experimentalcarservice_unit_test/Android.bp
@@ -0,0 +1,48 @@
+// Copyright (C) 2019 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.
+
+// Unit tests for the experimental car service
+android_test {
+
+ name: "ExperimentalCarServiceTests",
+
+ srcs: ["src/**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+
+ libs: [
+ "android.car",
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "car-frameworks-service",
+ "mockito-target-extended",
+ "truth-prebuilt",
+ "experimentalcar-service-test-static-lib"
+ ],
+
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ],
+
+ instrumentation_for: "ExperimentalCarService",
+}
diff --git a/experimental/experimentalcarservice_unit_test/AndroidManifest.xml b/experimental/experimentalcarservice_unit_test/AndroidManifest.xml
new file mode 100644
index 0000000000..247d8f1681
--- /dev/null
+++ b/experimental/experimentalcarservice_unit_test/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:sharedUserId="com.google.android.car.uid.kitchensink"
+ package="com.android.experimentalcar.experimentalcarservice_unittest">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.experimentalcar.experimentalcarservice_unittest"
+ android:label="Unit Tests for Experimental Car APIs"/>
+
+ <application android:label="ExperimentalCarServiceUnitTest"
+ tools:replace="android:label"
+ android:debuggable="true">
+ <uses-library android:name="android.test.runner"/>
+ </application>
+</manifest>
diff --git a/experimental/experimentalcarservice_unit_test/src/com/android/experimentalcar/DriverDistractionExperimentalFeatureServiceTest.java b/experimental/experimentalcarservice_unit_test/src/com/android/experimentalcar/DriverDistractionExperimentalFeatureServiceTest.java
new file mode 100644
index 0000000000..59ad9c519a
--- /dev/null
+++ b/experimental/experimentalcarservice_unit_test/src/com/android/experimentalcar/DriverDistractionExperimentalFeatureServiceTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2019 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.experimentalcar;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.car.experimental.DriverAwarenessEvent;
+import android.car.experimental.DriverAwarenessSupplierConfig;
+import android.car.experimental.DriverAwarenessSupplierService;
+import android.car.experimental.IDriverAwarenessSupplier;
+import android.car.experimental.IDriverAwarenessSupplierCallback;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Pair;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Arrays;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DriverDistractionExperimentalFeatureServiceTest {
+
+ private static final long INITIAL_TIME = 1000L;
+ private static final long PREFERRED_SUPPLIER_STALENESS = 10L;
+
+ private final IDriverAwarenessSupplier mFallbackSupplier =
+ new IDriverAwarenessSupplier.Stub() {
+ @Override
+ public void onReady() throws RemoteException {
+ }
+
+ @Override
+ public void setCallback(IDriverAwarenessSupplierCallback callback)
+ throws RemoteException {
+ }
+ };
+ private final DriverAwarenessSupplierConfig mFallbackConfig = new DriverAwarenessSupplierConfig(
+ DriverAwarenessSupplierService.NO_STALENESS);
+
+ private final IDriverAwarenessSupplier mPreferredSupplier =
+ new IDriverAwarenessSupplier.Stub() {
+ @Override
+ public void onReady() throws RemoteException {
+ }
+
+ @Override
+ public void setCallback(IDriverAwarenessSupplierCallback callback)
+ throws RemoteException {
+ }
+ };
+ private final DriverAwarenessSupplierConfig mPreferredSupplierConfig =
+ new DriverAwarenessSupplierConfig(PREFERRED_SUPPLIER_STALENESS);
+
+ @Mock
+ private Context mContext;
+
+ private DriverDistractionExperimentalFeatureService mService;
+ private FakeTimeSource mTimeSource;
+ private FakeTimer mTimer;
+
+ @Before
+ public void setUp() throws Exception {
+ mTimeSource = new FakeTimeSource(INITIAL_TIME);
+ mTimer = new FakeTimer();
+ mService = new DriverDistractionExperimentalFeatureService(mContext, mTimeSource, mTimer);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mService != null) {
+ mService.release();
+ }
+ }
+
+ @Test
+ public void testHandleDriverAwarenessEvent_updatesCurrentValue_withLatestEvent()
+ throws Exception {
+ mService.setDriverAwarenessSuppliers(Arrays.asList(
+ new Pair<>(mFallbackSupplier, mFallbackConfig)));
+
+ float firstEmittedEvent = 0.7f;
+ emitEvent(mFallbackSupplier, INITIAL_TIME + 1, firstEmittedEvent);
+
+ assertThat(getCurrentAwarenessValue()).isEqualTo(firstEmittedEvent);
+ }
+
+ @Test
+ public void testHandleDriverAwarenessEvent_hasPreferredEvent_ignoresFallbackEvent()
+ throws Exception {
+ mService.setDriverAwarenessSuppliers(Arrays.asList(
+ new Pair<>(mFallbackSupplier, mFallbackConfig),
+ new Pair<>(mPreferredSupplier, mPreferredSupplierConfig)));
+
+ // emit an event from the preferred supplier before the fallback supplier
+ float preferredValue = 0.6f;
+ emitEvent(mPreferredSupplier, INITIAL_TIME + 1, preferredValue);
+ float fallbackValue = 0.7f;
+ emitEvent(mFallbackSupplier, INITIAL_TIME + 2, fallbackValue);
+
+ // even though the fallback supplier has a more recent timestamp, it is not the current
+ // since the event from the preferred supplier is still fresh
+ assertThat(getCurrentAwarenessValue()).isEqualTo(preferredValue);
+ }
+
+ @Test
+ public void testHandleDriverAwarenessEvent_ignoresOldEvents() throws Exception {
+ mService.setDriverAwarenessSuppliers(Arrays.asList(
+ new Pair<>(mFallbackSupplier, mFallbackConfig)));
+
+ float firstEmittedEvent = 0.7f;
+ emitEvent(mFallbackSupplier, INITIAL_TIME + 1, firstEmittedEvent);
+ long oldTime = INITIAL_TIME - 100;
+ emitEvent(mFallbackSupplier, oldTime, 0.6f);
+
+ // the event with the old timestamp shouldn't overwrite the value with a more recent
+ // timestamp
+ assertThat(getCurrentAwarenessValue()).isEqualTo(firstEmittedEvent);
+ }
+
+ @Test
+ public void testPreferredAwarenessEvent_becomesStale_fallsBackToFallbackEvent()
+ throws Exception {
+ mService.setDriverAwarenessSuppliers(Arrays.asList(
+ new Pair<>(mFallbackSupplier, mFallbackConfig),
+ new Pair<>(mPreferredSupplier, mPreferredSupplierConfig)));
+
+ // emit an event from the preferred supplier before the fallback supplier
+ float preferredValue = 0.6f;
+ long preferredEventTime = INITIAL_TIME + 1;
+ mTimeSource.setElapsedRealtime(preferredEventTime);
+ emitEvent(mPreferredSupplier, preferredEventTime, preferredValue);
+ float fallbackValue = 0.7f;
+ long fallbackEventTime = INITIAL_TIME + 2;
+ mTimeSource.setElapsedRealtime(fallbackEventTime);
+ emitEvent(mFallbackSupplier, fallbackEventTime, fallbackValue);
+
+ // the preferred supplier still has a fresh event
+ assertThat(getCurrentAwarenessValue()).isEqualTo(preferredValue);
+
+ // go into the future
+ mTimeSource.setElapsedRealtime(preferredEventTime + PREFERRED_SUPPLIER_STALENESS + 1);
+ mTimer.executePendingTask();
+
+ // the preferred supplier's data has become stale
+ assertThat(getCurrentAwarenessValue()).isEqualTo(fallbackValue);
+ }
+
+ private float getCurrentAwarenessValue() {
+ return mService.getCurrentDriverAwareness().mAwarenessEvent.getAwarenessValue();
+ }
+
+ /**
+ * Handle an event as if it were emitted from the specified supplier with the specified time and
+ * value.
+ */
+ private void emitEvent(IDriverAwarenessSupplier supplier, long time, float value)
+ throws RemoteException {
+ long maxStaleness;
+ if (supplier == mFallbackSupplier) {
+ maxStaleness = DriverAwarenessSupplierService.NO_STALENESS;
+ } else {
+ maxStaleness = PREFERRED_SUPPLIER_STALENESS;
+ }
+ mService.handleDriverAwarenessEvent(
+ new DriverDistractionExperimentalFeatureService.DriverAwarenessEventWrapper(
+ new DriverAwarenessEvent(time, value),
+ supplier,
+ maxStaleness));
+ }
+}
diff --git a/experimental/experimentalcarservice_unit_test/src/com/android/experimentalcar/FakeTimeSource.java b/experimental/experimentalcarservice_unit_test/src/com/android/experimentalcar/FakeTimeSource.java
new file mode 100644
index 0000000000..eb82e1eeb8
--- /dev/null
+++ b/experimental/experimentalcarservice_unit_test/src/com/android/experimentalcar/FakeTimeSource.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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.experimentalcar;
+
+/**
+ * Fake implementation of {@link ITimeSource}.
+ */
+public class FakeTimeSource implements ITimeSource {
+
+ private long mElapsedRealtime;
+
+ /**
+ * Create an instance of {@link FakeTimeSource} with an initial time.
+ */
+ FakeTimeSource(long elapsedRealtime) {
+ mElapsedRealtime = elapsedRealtime;
+ }
+
+ /**
+ * Set the value that will be returned {@link #elapsedRealtime()}.
+ */
+ void setElapsedRealtime(long elapsedRealtime) {
+ mElapsedRealtime = elapsedRealtime;
+ }
+
+ @Override
+ public long elapsedRealtime() {
+ return mElapsedRealtime;
+ }
+}
diff --git a/experimental/experimentalcarservice_unit_test/src/com/android/experimentalcar/FakeTimer.java b/experimental/experimentalcarservice_unit_test/src/com/android/experimentalcar/FakeTimer.java
new file mode 100644
index 0000000000..0bd9748324
--- /dev/null
+++ b/experimental/experimentalcarservice_unit_test/src/com/android/experimentalcar/FakeTimer.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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.experimentalcar;
+
+import java.util.TimerTask;
+
+/**
+ * Fake implementation for {@link ITimer}.
+ */
+public class FakeTimer implements ITimer {
+
+ private TimerTask mPendingTask;
+
+ @Override
+ public void reset() {
+ mPendingTask = null;
+ }
+
+ @Override
+ public void schedule(TimerTask task, long delay) {
+ mPendingTask = task;
+ }
+
+ /**
+ * Immediately execute the scheduled task.
+ */
+ void executePendingTask() {
+ mPendingTask.run();
+ }
+}
diff --git a/experimental/service/Android.bp b/experimental/service/Android.bp
index f2fba99097..b0c59c44fe 100644
--- a/experimental/service/Android.bp
+++ b/experimental/service/Android.bp
@@ -16,13 +16,12 @@
// Build the Experimental Car service.
+experimentalcar_service_sources = ["src/**/*.java"]
android_app {
name: "ExperimentalCarService",
- srcs: [
- "src/**/*.java"
- ],
+ srcs: experimentalcar_service_sources,
resource_dirs: ["res"],
@@ -53,3 +52,32 @@ android_app {
},
},
}
+
+//####################################################################################
+// Build a static library to help mocking various car services in testing. This is meant to be used
+// for internal unit tests around the car service.
+//####################################################################################
+android_library {
+ name: "experimentalcar-service-test-static-lib",
+
+ srcs: experimentalcar_service_sources,
+
+ resource_dirs: ["res"],
+
+ libs: [
+ "android.car",
+ ],
+
+ static_libs: [
+ "car-service-common-util-static-lib",
+ "car-experimental-api-static-lib",
+ ],
+
+ min_sdk_version: "25",
+
+ product_variables: {
+ pdk: {
+ enabled: false,
+ },
+ },
+}
diff --git a/experimental/service/AndroidManifest.xml b/experimental/service/AndroidManifest.xml
index c28f400d67..86060d7907 100644
--- a/experimental/service/AndroidManifest.xml
+++ b/experimental/service/AndroidManifest.xml
@@ -15,19 +15,20 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="com.android.experimentalcar"
- coreApp="true"
- android:sharedUserId="android.uid.system">
+ package="com.android.experimentalcar"
+ coreApp="true"
+ android:sharedUserId="android.uid.system">
- <original-package android:name="com.android.experimentalcar" />
+ <original-package android:name="com.android.experimentalcar"/>
<application android:label="@string/app_title"
android:directBootAware="true"
android:allowBackup="false"
android:persistent="false">
<service android:name=".ExperimentalCarService"
- android:singleUser="true">
+ android:singleUser="true">
</service>
+ <service android:name=".TouchDriverAwarenessSupplier"/>
+ <service android:name=".SampleExternalDriverAwarenessSupplier"/>
</application>
</manifest>
diff --git a/experimental/service/src/com/android/experimentalcar/DriverDistractionExperimentalFeatureService.java b/experimental/service/src/com/android/experimentalcar/DriverDistractionExperimentalFeatureService.java
index 7a4f630ac7..f0d7d56d1c 100644
--- a/experimental/service/src/com/android/experimentalcar/DriverDistractionExperimentalFeatureService.java
+++ b/experimental/service/src/com/android/experimentalcar/DriverDistractionExperimentalFeatureService.java
@@ -16,28 +16,540 @@
package com.android.experimentalcar;
+import android.annotation.Nullable;
+import android.car.experimental.DriverAwarenessEvent;
+import android.car.experimental.DriverAwarenessSupplierConfig;
+import android.car.experimental.DriverAwarenessSupplierService;
+import android.car.experimental.IDriverAwarenessSupplier;
+import android.car.experimental.IDriverAwarenessSupplierCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Pair;
+
import com.android.car.CarServiceBase;
+import com.android.car.Utils;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TimerTask;
/**
* Driver Distraction Service for using the driver's awareness, the required awareness of the
* driving environment to expose APIs for the driver's current distraction level.
+ *
+ * <p>Allows the registration of multiple {@link IDriverAwarenessSupplier} so that higher accuracy
+ * signals can be used when possible, with a fallback to less accurate signals. The {@link
+ * TouchDriverAwarenessSupplier} is always set to the fallback implementation - it is configured
+ * to send change-events, so its data will not become stale.
*/
public final class DriverDistractionExperimentalFeatureService implements CarServiceBase {
+ private static final String TAG = "CAR.DriverDistractionService";
+
+ private static final float DEFAULT_AWARENESS_VALUE_FOR_LOG = 1.0f;
+ private static final int MAX_DRIVER_AWARENESS_EVENT_LOG_COUNT = 50;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final ArrayDeque<Utils.TransitionLog> mDriverAwarenessTransitionLogs =
+ new ArrayDeque<>();
+
+ /**
+ * All the active service connections.
+ */
+ @GuardedBy("mLock")
+ private final List<ServiceConnection> mServiceConnections = new ArrayList<>();
+
+ /**
+ * The binder for each supplier.
+ */
+ @GuardedBy("mLock")
+ private final Map<ComponentName, IDriverAwarenessSupplier> mSupplierBinders = new HashMap<>();
+
+ /**
+ * The configuration for each supplier.
+ */
+ @GuardedBy("mLock")
+ private final Map<IDriverAwarenessSupplier, DriverAwarenessSupplierConfig> mSupplierConfigs =
+ new HashMap<>();
+
+ /**
+ * List of driver awareness suppliers that can be used to understand the current driver
+ * awareness level. Ordered from highest to lowest priority.
+ */
+ @GuardedBy("mLock")
+ private final List<IDriverAwarenessSupplier> mPrioritizedDriverAwarenessSuppliers =
+ new ArrayList<>();
+
+ /**
+ * Helper map for looking up the priority rank of a supplier by name. A higher integer value
+ * represents a higher priority.
+ */
+ @GuardedBy("mLock")
+ private final Map<IDriverAwarenessSupplier, Integer> mDriverAwarenessSupplierPriorities =
+ new HashMap<>();
+
+ /**
+ * Comparator used to sort {@link #mDriverAwarenessSupplierPriorities}.
+ */
+ private final Comparator<IDriverAwarenessSupplier> mPrioritizedSuppliersComparator =
+ (left, right) -> {
+ int leftPri = mDriverAwarenessSupplierPriorities.get(left);
+ int rightPri = mDriverAwarenessSupplierPriorities.get(right);
+ // sort descending
+ return rightPri - leftPri;
+ };
+
+ /**
+ * Keep track of the most recent awareness event for each supplier for use when the data from
+ * higher priority suppliers becomes stale. This is necessary in order to seamlessly handle
+ * fallback scenarios when data from preferred providers becomes stale.
+ */
+ @GuardedBy("mLock")
+ private final Map<IDriverAwarenessSupplier, DriverAwarenessEventWrapper>
+ mCurrentAwarenessEventsMap =
+ new HashMap<>();
+
+ /**
+ * The awareness event that is currently being used to determine the driver awareness level.
+ *
+ * <p>This is null until it is set by the first awareness supplier to send an event
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private DriverAwarenessEventWrapper mCurrentDriverAwareness;
+
+ /**
+ * Timer to alert when the current driver awareness event has become stale.
+ */
+ @GuardedBy("mLock")
+ private ITimer mExpiredDriverAwarenessTimer;
+
+ private final ITimeSource mTimeSource;
+ private final Context mContext;
+
+ /**
+ * Create an instance of {@link DriverDistractionExperimentalFeatureService}.
+ *
+ * @param context the context
+ * @param timeSource the source that provides the current time
+ * @param timer the timer used for scheduling
+ */
+ DriverDistractionExperimentalFeatureService(
+ Context context,
+ ITimeSource timeSource,
+ ITimer timer) {
+ mContext = context;
+ mTimeSource = timeSource;
+ mExpiredDriverAwarenessTimer = timer;
+ }
+
@Override
public void init() {
- // Nothing to do
+ // The touch supplier is an internal implementation, so it can be started initiated by its
+ // constructor, unlike other suppliers
+ ComponentName touchComponent = new ComponentName(mContext,
+ TouchDriverAwarenessSupplier.class);
+ TouchDriverAwarenessSupplier touchSupplier = new TouchDriverAwarenessSupplier();
+ addDriverAwarenessSupplier(touchComponent, touchSupplier, 0);
+ touchSupplier.setCallback(new DriverAwarenessSupplierCallback(touchComponent));
+ touchSupplier.onReady();
+
+ // TODO(b/143492728) load preferred suppliers from xml - this is just an example
+ ComponentName externalComponent = new ComponentName(mContext,
+ SampleExternalDriverAwarenessSupplier.class);
+ bindDriverAwarenessSupplierService(externalComponent, /* priority= */ 1);
}
@Override
public void release() {
- // Nothing to do
+ logd("release");
+ synchronized (mLock) {
+ for (ServiceConnection serviceConnection : mServiceConnections) {
+ mContext.unbindService(serviceConnection);
+ }
+ }
}
@Override
public void dump(PrintWriter writer) {
writer.println("*DriverDistractionExperimentalFeatureService*");
+ writer.println("Prioritized Driver Awareness Suppliers (highest to lowest priority):");
+ synchronized (mLock) {
+ for (int i = 0; i < mPrioritizedDriverAwarenessSuppliers.size(); i++) {
+ writer.println(
+ String.format(" %d: %s", i, mPrioritizedDriverAwarenessSuppliers.get(
+ i).getClass().getName()));
+ }
+ writer.println("Current Driver Awareness:");
+ writer.println(" Value: "
+ + (mCurrentDriverAwareness == null ? "unknown"
+ : mCurrentDriverAwareness.mAwarenessEvent.getAwarenessValue()));
+ writer.println(" Supplier: " + (mCurrentDriverAwareness == null ? "unknown"
+ : mCurrentDriverAwareness.mSupplier.getClass().getSimpleName()));
+ writer.println(" Timestamp (since boot): "
+ + (mCurrentDriverAwareness == null ? "unknown"
+ : mCurrentDriverAwareness.mAwarenessEvent.getTimeStamp()));
+ writer.println("Driver Awareness change log:");
+ for (Utils.TransitionLog log : mDriverAwarenessTransitionLogs) {
+ writer.println(log);
+ }
+ }
+ }
+
+ /**
+ * Bind to a {@link DriverAwarenessSupplierService} by its component name.
+ *
+ * @param componentName the name of the {@link DriverAwarenessSupplierService} to bind to.
+ * @param priority the priority rank of this supplier
+ */
+ private void bindDriverAwarenessSupplierService(ComponentName componentName, int priority) {
+ Intent intent = new Intent();
+ intent.setComponent(componentName);
+ ServiceConnection connection = new DriverAwarenessServiceConnection(priority);
+ synchronized (mLock) {
+ mServiceConnections.add(connection);
+ }
+ if (!mContext.bindServiceAsUser(intent, connection,
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
+ Log.e(TAG, "Unable to bind with intent: " + intent);
+ // TODO(b/146471650) attempt to rebind
+ }
+ }
+
+ @VisibleForTesting
+ void handleDriverAwarenessEvent(DriverAwarenessEventWrapper awarenessEventWrapper) {
+ synchronized (mLock) {
+ handleDriverAwarenessEventLocked(awarenessEventWrapper);
+ }
+ }
+
+ /**
+ * Handle the driver awareness event by:
+ * <ul>
+ * <li>Cache the driver awareness event for its supplier</li>
+ * <li>Update the current awareness value</li>
+ * <li>Register to refresh the awareness value again when the new current expires</li>
+ * </ul>
+ *
+ * @param awarenessEventWrapper the driver awareness event that has occurred
+ */
+ @GuardedBy("mLock")
+ private void handleDriverAwarenessEventLocked(
+ DriverAwarenessEventWrapper awarenessEventWrapper) {
+ // update the current awareness event for the supplier, checking that it is the newest event
+ IDriverAwarenessSupplier supplier = awarenessEventWrapper.mSupplier;
+ long timestamp = awarenessEventWrapper.mAwarenessEvent.getTimeStamp();
+ if (!mCurrentAwarenessEventsMap.containsKey(supplier)
+ || mCurrentAwarenessEventsMap.get(supplier).mAwarenessEvent.getTimeStamp()
+ < timestamp) {
+ mCurrentAwarenessEventsMap.put(awarenessEventWrapper.mSupplier, awarenessEventWrapper);
+ }
+
+ int oldSupplierPriority = mDriverAwarenessSupplierPriorities.get(supplier);
+ float oldAwarenessValue = DEFAULT_AWARENESS_VALUE_FOR_LOG;
+ if (mCurrentDriverAwareness != null) {
+ oldAwarenessValue = mCurrentDriverAwareness.mAwarenessEvent.getAwarenessValue();
+ }
+
+ updateCurrentAwarenessValueLocked();
+
+ int newSupplierPriority = mDriverAwarenessSupplierPriorities.get(
+ mCurrentDriverAwareness.mSupplier);
+ if (mSupplierConfigs.get(mCurrentDriverAwareness.mSupplier).getMaxStalenessMillis()
+ != DriverAwarenessSupplierService.NO_STALENESS
+ && newSupplierPriority >= oldSupplierPriority) {
+ // only reschedule an expiration if this is for a supplier that is the same or higher
+ // priority than the old value. If there is a higher priority supplier with non-stale
+ // data, then mCurrentDriverAwareness won't change even though we received a new event.
+ scheduleExpirationTimerLocked();
+ }
+
+ if (oldAwarenessValue != mCurrentDriverAwareness.mAwarenessEvent.getAwarenessValue()) {
+ logd("Driver awareness updated: "
+ + mCurrentDriverAwareness.mAwarenessEvent.getAwarenessValue());
+ addDriverAwarenessTransitionLogLocked(oldAwarenessValue,
+ awarenessEventWrapper.mAwarenessEvent.getAwarenessValue(),
+ awarenessEventWrapper.mSupplier.getClass().getSimpleName());
+ }
+ }
+
+ /**
+ * Get the current awareness value.
+ */
+ @VisibleForTesting
+ DriverAwarenessEventWrapper getCurrentDriverAwareness() {
+ return mCurrentDriverAwareness;
+ }
+
+ /**
+ * Set the drier awareness suppliers. Allows circumventing the {@link #init()} logic.
+ */
+ @VisibleForTesting
+ void setDriverAwarenessSuppliers(
+ List<Pair<IDriverAwarenessSupplier, DriverAwarenessSupplierConfig>> suppliers) {
+ mPrioritizedDriverAwarenessSuppliers.clear();
+ mDriverAwarenessSupplierPriorities.clear();
+ for (int i = 0; i < suppliers.size(); i++) {
+ Pair<IDriverAwarenessSupplier, DriverAwarenessSupplierConfig> pair = suppliers.get(i);
+ mSupplierConfigs.put(pair.first, pair.second);
+ mDriverAwarenessSupplierPriorities.put(pair.first, i);
+ mPrioritizedDriverAwarenessSuppliers.add(pair.first);
+ }
+ mPrioritizedDriverAwarenessSuppliers.sort(mPrioritizedSuppliersComparator);
+ }
+
+ /**
+ * Internally register the supplier with the specified priority.
+ */
+ private void addDriverAwarenessSupplier(
+ ComponentName componentName,
+ IDriverAwarenessSupplier awarenessSupplier,
+ int priority) {
+ synchronized (mLock) {
+ mSupplierBinders.put(componentName, awarenessSupplier);
+ mDriverAwarenessSupplierPriorities.put(awarenessSupplier, priority);
+ mPrioritizedDriverAwarenessSuppliers.add(awarenessSupplier);
+ mPrioritizedDriverAwarenessSuppliers.sort(mPrioritizedSuppliersComparator);
+ }
+ }
+
+ /**
+ * Remove references to a supplier.
+ */
+ private void removeDriverAwarenessSupplier(ComponentName componentName) {
+ synchronized (mLock) {
+ IDriverAwarenessSupplier supplier = mSupplierBinders.get(componentName);
+ mSupplierBinders.remove(componentName);
+ mDriverAwarenessSupplierPriorities.remove(supplier);
+ mPrioritizedDriverAwarenessSuppliers.remove(supplier);
+ }
+ }
+
+ /**
+ * Update {@link #mCurrentDriverAwareness} based on the current driver awareness events for each
+ * supplier.
+ */
+ @GuardedBy("mLock")
+ private void updateCurrentAwarenessValueLocked() {
+ for (IDriverAwarenessSupplier supplier : mPrioritizedDriverAwarenessSuppliers) {
+ long supplierMaxStaleness = mSupplierConfigs.get(supplier).getMaxStalenessMillis();
+ DriverAwarenessEventWrapper eventForSupplier = mCurrentAwarenessEventsMap.get(supplier);
+ if (eventForSupplier == null) {
+ continue;
+ }
+ if (supplierMaxStaleness == DriverAwarenessSupplierService.NO_STALENESS) {
+ // this supplier can't be stale, so use its information
+ mCurrentDriverAwareness = eventForSupplier;
+ return;
+ }
+
+ long oldestFreshTimestamp = mTimeSource.elapsedRealtime() - supplierMaxStaleness;
+ if (eventForSupplier.mAwarenessEvent.getTimeStamp() > oldestFreshTimestamp) {
+ // value is still fresh, so use it
+ mCurrentDriverAwareness = eventForSupplier;
+ return;
+ }
+ }
+
+ if (mCurrentDriverAwareness == null) {
+ // There must always at least be a fallback supplier with NO_STALENESS configuration.
+ // Since we control this configuration, getting this exception represents a developer
+ // error in initialization.
+ throw new IllegalStateException(
+ "Unable to determine the current driver awareness value");
+ }
+ }
+
+ /**
+ * Sets a timer to update the refresh the awareness value once the current value has become
+ * stale.
+ */
+ @GuardedBy("mLock")
+ private void scheduleExpirationTimerLocked() {
+ // reschedule the current awareness expiration task
+ mExpiredDriverAwarenessTimer.reset();
+ long delay = mCurrentDriverAwareness.mAwarenessEvent.getTimeStamp()
+ - mTimeSource.elapsedRealtime()
+ + mCurrentDriverAwareness.mMaxStaleness;
+ if (delay < 0) {
+ // somehow the event is already stale
+ synchronized (mLock) {
+ updateCurrentAwarenessValueLocked();
+ }
+ return;
+ }
+ mExpiredDriverAwarenessTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ logd("Driver awareness has become stale. Selecting new awareness level.");
+ synchronized (mLock) {
+ updateCurrentAwarenessValueLocked();
+ }
+ }
+ }, delay);
+
+ logd(String.format(
+ "Current awareness value is stale after %sms and is scheduled to expire in %sms",
+ mCurrentDriverAwareness.mMaxStaleness, delay));
+ }
+
+ /**
+ * Add the driver awareness state change to the transition log.
+ *
+ * @param oldValue the old driver awareness value
+ * @param newValue the new driver awareness value
+ * @param supplierName the name of the supplier that is responsible for the new value
+ */
+ @GuardedBy("mLock")
+ private void addDriverAwarenessTransitionLogLocked(float oldValue, float newValue,
+ String supplierName) {
+ if (mDriverAwarenessTransitionLogs.size() >= MAX_DRIVER_AWARENESS_EVENT_LOG_COUNT) {
+ mDriverAwarenessTransitionLogs.remove();
+ }
+
+ Utils.TransitionLog tLog = new Utils.TransitionLog(TAG, (int) (oldValue * 100),
+ (int) (newValue * 100), System.currentTimeMillis(),
+ "Driver awareness updated by " + supplierName);
+ mDriverAwarenessTransitionLogs.add(tLog);
+ }
+
+ private static void logd(String message) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, message);
+ }
+ }
+
+ /**
+ * The service connection between this distraction service and a {@link
+ * DriverAwarenessSupplierService}, communicated through {@link IDriverAwarenessSupplier}.
+ */
+ private class DriverAwarenessServiceConnection implements ServiceConnection {
+
+ final int mPriority;
+
+ /**
+ * Create an instance of {@link DriverAwarenessServiceConnection}.
+ *
+ * @param priority the priority of the {@link DriverAwarenessSupplierService} that this
+ * connection is for
+ */
+ DriverAwarenessServiceConnection(int priority) {
+ mPriority = priority;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ logd("onServiceConnected, name: " + name + ", binder: " + binder);
+ IDriverAwarenessSupplier service = IDriverAwarenessSupplier.Stub.asInterface(
+ binder);
+ addDriverAwarenessSupplier(name, service, mPriority);
+ try {
+ service.setCallback(new DriverAwarenessSupplierCallback(name));
+ service.onReady();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call onReady on supplier", e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ logd("onServiceDisconnected, name: " + name);
+ removeDriverAwarenessSupplier(name);
+ // TODO(b/146471650) rebind to driver awareness suppliers on service disconnect
+ }
+ }
+
+ /**
+ * Driver awareness listener that keeps a references to some attributes of the supplier.
+ */
+ private class DriverAwarenessSupplierCallback extends IDriverAwarenessSupplierCallback.Stub {
+
+ private final ComponentName mComponentName;
+
+ /**
+ * Construct an instance of {@link DriverAwarenessSupplierCallback}.
+ *
+ * @param componentName the driver awareness supplier for this listener
+ */
+ DriverAwarenessSupplierCallback(ComponentName componentName) {
+ mComponentName = componentName;
+ }
+
+ @Override
+ public void onDriverAwarenessUpdated(DriverAwarenessEvent event) {
+ IDriverAwarenessSupplier supplier;
+ long maxStaleness;
+ synchronized (mLock) {
+ supplier = mSupplierBinders.get(mComponentName);
+ maxStaleness = mSupplierConfigs.get(supplier).getMaxStalenessMillis();
+ }
+ if (supplier == null) {
+ // this should never happen. Initialization process would not be correct.
+ throw new IllegalStateException(
+ "No supplier registered for component " + mComponentName);
+ }
+ logd(String.format("Driver awareness updated for %s: %s",
+ supplier.getClass().getSimpleName(), event));
+ handleDriverAwarenessEvent(
+ new DriverAwarenessEventWrapper(event, supplier, maxStaleness));
+ }
+
+ @Override
+ public void onConfigLoaded(DriverAwarenessSupplierConfig config) throws RemoteException {
+ synchronized (mLock) {
+ mSupplierConfigs.put(mSupplierBinders.get(mComponentName), config);
+ }
+ }
+ }
+
+ /**
+ * Wrapper for {@link DriverAwarenessEvent} that includes some information from the supplier
+ * that emitted the event.
+ */
+ @VisibleForTesting
+ static class DriverAwarenessEventWrapper {
+ final DriverAwarenessEvent mAwarenessEvent;
+ final IDriverAwarenessSupplier mSupplier;
+ final long mMaxStaleness;
+
+ /**
+ * Construct an instance of {@link DriverAwarenessEventWrapper}.
+ *
+ * @param awarenessEvent the driver awareness event being wrapped
+ * @param supplier the driver awareness supplier for this listener
+ * @param maxStaleness the max staleness of the supplier that emitted this event (included
+ * to avoid making a binder call)
+ */
+ DriverAwarenessEventWrapper(
+ DriverAwarenessEvent awarenessEvent,
+ IDriverAwarenessSupplier supplier,
+ long maxStaleness) {
+ mAwarenessEvent = awarenessEvent;
+ mSupplier = supplier;
+ mMaxStaleness = maxStaleness;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "DriverAwarenessEventWrapper{mAwarenessChangeEvent=%s, mSupplier=%s, "
+ + "mMaxStaleness=%s}",
+ mAwarenessEvent, mSupplier, mMaxStaleness);
+ }
}
}
diff --git a/experimental/service/src/com/android/experimentalcar/IExperimentalCarImpl.java b/experimental/service/src/com/android/experimentalcar/IExperimentalCarImpl.java
index 4d50d67ba7..bed163d94e 100644
--- a/experimental/service/src/com/android/experimentalcar/IExperimentalCarImpl.java
+++ b/experimental/service/src/com/android/experimentalcar/IExperimentalCarImpl.java
@@ -174,7 +174,10 @@ public final class IExperimentalCarImpl extends IExperimentalCar.Stub {
case ExperimentalCar.TEST_EXPERIMENTAL_FEATURE_SERVICE:
return new TestDemoExperimentalFeatureService();
case ExperimentalCar.DRIVER_DISTRACTION_EXPERIMENTAL_FEATURE_SERVICE:
- return new DriverDistractionExperimentalFeatureService();
+ return new DriverDistractionExperimentalFeatureService(
+ mContext,
+ new SystemTimeSource(),
+ new SystemTimer());
default:
return null;
}
diff --git a/experimental/service/src/com/android/experimentalcar/ITimeSource.java b/experimental/service/src/com/android/experimentalcar/ITimeSource.java
new file mode 100644
index 0000000000..3a597c314b
--- /dev/null
+++ b/experimental/service/src/com/android/experimentalcar/ITimeSource.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 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.experimentalcar;
+
+import android.os.SystemClock;
+
+/**
+ * Interface to abstract dependency on {@link android.os.SystemClock}.
+ */
+public interface ITimeSource {
+
+ /**
+ * See {@link SystemClock#elapsedRealtime()}.
+ */
+ long elapsedRealtime();
+}
diff --git a/experimental/service/src/com/android/experimentalcar/ITimer.java b/experimental/service/src/com/android/experimentalcar/ITimer.java
new file mode 100644
index 0000000000..dbaa3c0111
--- /dev/null
+++ b/experimental/service/src/com/android/experimentalcar/ITimer.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 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.experimentalcar;
+
+import java.util.TimerTask;
+
+/**
+ * Interface to abstract dependency on {@link java.util.Timer}.
+ */
+public interface ITimer {
+
+ /**
+ * Cancel the timer and prepare for new events.
+ */
+ void reset();
+
+ /**
+ * See {@link java.util.Timer#schedule(TimerTask, long)}.
+ */
+ void schedule(TimerTask task, long delay);
+}
diff --git a/experimental/service/src/com/android/experimentalcar/SampleExternalDriverAwarenessSupplier.java b/experimental/service/src/com/android/experimentalcar/SampleExternalDriverAwarenessSupplier.java
new file mode 100644
index 0000000000..917ac20ae9
--- /dev/null
+++ b/experimental/service/src/com/android/experimentalcar/SampleExternalDriverAwarenessSupplier.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 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.experimentalcar;
+
+import android.car.experimental.DriverAwarenessEvent;
+import android.car.experimental.DriverAwarenessSupplierService;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.util.Log;
+
+/**
+ * Simple example of how an external driver awareness supplier service could be implemented.
+ */
+public class SampleExternalDriverAwarenessSupplier extends DriverAwarenessSupplierService {
+
+ private static final String TAG = "SampleExternalDriverAwarenessSupplier";
+ private static final float INITIAL_DRIVER_AWARENESS_VALUE = 1.0f;
+ private static final long MAX_STALENESS_MILLIS = 100L;
+
+ @Override
+ public long getMaxStalenessMillis() {
+ return MAX_STALENESS_MILLIS;
+ }
+
+ @Override
+ public void onReady() {
+ // send an initial event, as required by the IDriverAwarenessSupplierCallback spec
+ onDriverAwarenessUpdated(new DriverAwarenessEvent(SystemClock.elapsedRealtime(),
+ INITIAL_DRIVER_AWARENESS_VALUE));
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ logd("onBind, intent: " + intent);
+ return super.onBind(intent);
+ }
+
+ private static void logd(String message) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, message);
+ }
+ }
+}
diff --git a/experimental/service/src/com/android/experimentalcar/SystemTimeSource.java b/experimental/service/src/com/android/experimentalcar/SystemTimeSource.java
new file mode 100644
index 0000000000..217b6fe91a
--- /dev/null
+++ b/experimental/service/src/com/android/experimentalcar/SystemTimeSource.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 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.experimentalcar;
+
+import android.os.SystemClock;
+
+/**
+ * Time source implementation using {@link SystemClock}.
+ */
+public class SystemTimeSource implements ITimeSource {
+
+ @Override
+ public long elapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+}
diff --git a/experimental/service/src/com/android/experimentalcar/SystemTimer.java b/experimental/service/src/com/android/experimentalcar/SystemTimer.java
new file mode 100644
index 0000000000..9eb1dc5842
--- /dev/null
+++ b/experimental/service/src/com/android/experimentalcar/SystemTimer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 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.experimentalcar;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * Implementation of {@link ITimer} that uses {@link Timer}.
+ */
+public class SystemTimer implements ITimer {
+
+ private Timer mTimer = new Timer();
+
+ @Override
+ public void reset() {
+ mTimer.cancel();
+ mTimer = new Timer();
+ }
+
+ @Override
+ public void schedule(TimerTask task, long delay) {
+ mTimer.schedule(task, delay);
+ }
+}
diff --git a/experimental/service/src/com/android/experimentalcar/TouchDriverAwarenessSupplier.java b/experimental/service/src/com/android/experimentalcar/TouchDriverAwarenessSupplier.java
new file mode 100644
index 0000000000..f144ad413a
--- /dev/null
+++ b/experimental/service/src/com/android/experimentalcar/TouchDriverAwarenessSupplier.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2019 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.experimentalcar;
+
+import android.car.experimental.DriverAwarenessEvent;
+import android.car.experimental.DriverAwarenessSupplierConfig;
+import android.car.experimental.DriverAwarenessSupplierService;
+import android.car.experimental.IDriverAwarenessSupplier;
+import android.car.experimental.IDriverAwarenessSupplierCallback;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.Random;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * A driver awareness supplier that estimates the driver's current awareness level based on touches
+ * on the headunit.
+ */
+// TODO(b/136663803) update with actual implementation and configuration
+public class TouchDriverAwarenessSupplier extends IDriverAwarenessSupplier.Stub {
+
+ private static final String TAG = "Car.TouchDriverAwarenessSupplier";
+ private static final float INITIAL_DRIVER_AWARENESS_VALUE = 1.0f;
+ private static final long MAX_STALENESS = DriverAwarenessSupplierService.NO_STALENESS;
+
+ // demo classes - emit a random value every 1s as a proof of concept
+ private final Random mRandom = new Random();
+ private final ScheduledExecutorService mDemoScheduler =
+ Executors.newScheduledThreadPool(1);
+
+ private ScheduledFuture<?> mDemoScheduleHandle;
+ private IDriverAwarenessSupplierCallback mDriverAwarenessSupplierCallback;
+
+ private final Runnable mEmitDemoAwarenessRunnable = () -> {
+ long timestamp = SystemClock.elapsedRealtime();
+ float demoAwareness = mRandom.nextFloat();
+ try {
+ mDriverAwarenessSupplierCallback.onDriverAwarenessUpdated(
+ new DriverAwarenessEvent(timestamp, demoAwareness));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to emit awareness event", e);
+ }
+ };
+
+ @Override
+ public void onReady() {
+ try {
+ mDriverAwarenessSupplierCallback.onConfigLoaded(
+ new DriverAwarenessSupplierConfig(MAX_STALENESS));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to send config - abandoning ready process", e);
+ return;
+ }
+ startSupplyingDemoDriverAwareness();
+ }
+
+ @Override
+ public void setCallback(IDriverAwarenessSupplierCallback callback) {
+ mDriverAwarenessSupplierCallback = callback;
+ }
+
+ private void startSupplyingDemoDriverAwareness() {
+ // send an initial event, as required by the IDriverAwarenessSupplierCallback spec
+ try {
+ mDriverAwarenessSupplierCallback.onDriverAwarenessUpdated(
+ new DriverAwarenessEvent(SystemClock.elapsedRealtime(),
+ INITIAL_DRIVER_AWARENESS_VALUE));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to emit initial awareness event", e);
+ }
+
+ // TODO(b/136663803) update with actual implementation and configuration
+ mDemoScheduleHandle = mDemoScheduler.scheduleAtFixedRate(
+ mEmitDemoAwarenessRunnable,
+ /* initialDelay= */ 0,
+ /* period= */ 1,
+ TimeUnit.SECONDS);
+ }
+}