aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--car-cluster-demo-renderer/Android.mk34
-rw-r--r--car-cluster-demo-renderer/AndroidManifest.xml27
-rw-r--r--car-cluster-demo-renderer/proguard.flags3
-rw-r--r--car-cluster-demo-renderer/res/drawable-hdpi/ic_contactavatar_large_light.pngbin6973 -> 0 bytes
-rw-r--r--car-cluster-demo-renderer/res/drawable-hdpi/ic_launcher.pngbin9397 -> 0 bytes
-rw-r--r--car-cluster-demo-renderer/res/drawable-ldpi/ic_launcher.pngbin2729 -> 0 bytes
-rw-r--r--car-cluster-demo-renderer/res/drawable-mdpi/ic_contactavatar_large_light.pngbin4580 -> 0 bytes
-rw-r--r--car-cluster-demo-renderer/res/drawable-mdpi/ic_launcher.pngbin5237 -> 0 bytes
-rw-r--r--car-cluster-demo-renderer/res/drawable-xhdpi/ic_contactavatar_large_light.pngbin9772 -> 0 bytes
-rw-r--r--car-cluster-demo-renderer/res/drawable-xhdpi/ic_launcher.pngbin14383 -> 0 bytes
-rw-r--r--car-cluster-demo-renderer/res/drawable-xxhdpi/ic_contactavatar_large_light.pngbin8882 -> 0 bytes
-rw-r--r--car-cluster-demo-renderer/res/layout/instrument_cluster.xml118
-rw-r--r--car-cluster-demo-renderer/res/values/strings.xml36
-rw-r--r--car-cluster-demo-renderer/src/android/car/cluster/demorenderer/CallStateMonitor.java114
-rw-r--r--car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterRenderer.java132
-rw-r--r--car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterView.java184
-rw-r--r--car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoMediaRenderer.java78
-rw-r--r--car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoNavigationRenderer.java102
-rw-r--r--car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoPhoneRenderer.java101
-rw-r--r--car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterPresentation.java31
-rw-r--r--car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterRendererFactory.java29
-rw-r--r--car-cluster-demo-renderer/src/android/car/cluster/demorenderer/MediaStateMonitor.java141
-rw-r--r--car-cluster-demo-renderer/src/android/car/cluster/demorenderer/PhoneBook.java332
-rw-r--r--car-cluster-demo-renderer/src/android/car/cluster/demorenderer/RendererHandler.java45
-rw-r--r--car-cluster-logging-renderer/AndroidManifest.xml7
-rw-r--r--car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingClusterRenderingService.java (renamed from car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingInstrumentClusterRenderer.java)26
-rw-r--r--car-lib/api/current.txt26
-rw-r--r--car-lib/api/system-current.txt36
-rw-r--r--car-lib/src/android/car/Car.java17
-rw-r--r--car-lib/src/android/car/CarAppContextManager.java272
-rw-r--r--car-lib/src/android/car/CarAppFocusManager.java396
-rw-r--r--car-lib/src/android/car/CarLibLog.java1
-rw-r--r--car-lib/src/android/car/IAppFocus.aidl (renamed from car-lib/src/android/car/IAppContext.aidl)17
-rw-r--r--car-lib/src/android/car/IAppFocusListener.aidl (renamed from car-lib/src/android/car/hardware/hvac/CarHvacEvent.aidl)8
-rw-r--r--car-lib/src/android/car/IAppFocusOwnershipListener.aidl (renamed from car-lib/src/android/car/IAppContextListener.aidl)5
-rw-r--r--car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl35
-rw-r--r--car-lib/src/android/car/cluster/renderer/IInstrumentClusterNavigation.aidl (renamed from car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/InstrumentClusterRendererFactory.java)22
-rw-r--r--car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java225
-rw-r--r--car-lib/src/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java (renamed from car-cluster-demo-renderer/src/android/car/cluster/demorenderer/ThreadSafeNavigationRenderer.java)51
-rw-r--r--car-lib/src/android/car/content/pm/CarPackageManager.java30
-rw-r--r--car-lib/src/android/car/content/pm/ICarPackageManager.aidl2
-rw-r--r--car-lib/src/android/car/hardware/CarHvacManager.java254
-rw-r--r--car-lib/src/android/car/hardware/hvac/CarHvacManager.java408
-rw-r--r--car-lib/src/android/car/hardware/property/CarPropertyEvent.aidl (renamed from service/src/com/android/car/cluster/InstrumentClusterPresentation.java)17
-rw-r--r--car-lib/src/android/car/hardware/property/CarPropertyEvent.java (renamed from car-lib/src/android/car/hardware/hvac/CarHvacEvent.java)32
-rw-r--r--car-lib/src/android/car/hardware/property/CarPropertyManagerBase.java315
-rw-r--r--car-lib/src/android/car/hardware/property/ICarProperty.aidl (renamed from car-lib/src/android/car/hardware/hvac/ICarHvac.aidl)18
-rw-r--r--car-lib/src/android/car/hardware/property/ICarPropertyEventListener.aidl (renamed from car-lib/src/android/car/hardware/hvac/ICarHvacEventListener.aidl)12
-rw-r--r--car-lib/src/android/car/media/CarAudioManager.java116
-rw-r--r--car-lib/src/android/car/media/ICarAudio.aidl4
-rw-r--r--car-lib/src/android/car/navigation/CarNavigationManager.java138
-rw-r--r--car-support-lib/api/current.txt36
-rw-r--r--car-support-lib/src/android/support/car/Car.java14
-rw-r--r--car-support-lib/src/android/support/car/CarAppContextManager.java123
-rw-r--r--car-support-lib/src/android/support/car/CarAppContextManagerEmbedded.java161
-rw-r--r--car-support-lib/src/android/support/car/CarAppFocusManager.java154
-rw-r--r--car-support-lib/src/android/support/car/CarAppFocusManagerEmbedded.java218
-rw-r--r--car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java12
-rw-r--r--car-support-lib/src/android/support/car/media/CarAudioManager.java26
-rw-r--r--car-support-lib/src/android/support/car/media/CarAudioRecord.java6
-rw-r--r--car-support-lib/src/android/support/car/navigation/CarNavigationStatusManager.java (renamed from car-support-lib/src/android/support/car/navigation/CarNavigationManager.java)45
-rw-r--r--car-support-lib/src/android/support/car/navigation/CarNavigationStatusManagerEmbedded.java (renamed from car-support-lib/src/android/support/car/navigation/CarNavigationManagerEmbedded.java)67
-rw-r--r--car-systemtest-lib/src/android/car/test/VehicleHalEmulator.java1
-rw-r--r--car_product/build/car.mk1
-rw-r--r--car_product/build/car_base.mk1
-rw-r--r--car_product/overlay/frameworks/base/core/res/res/values/config.xml2
-rw-r--r--car_product/sepolicy/atfwd.te14
-rw-r--r--car_product/sepolicy/bluetooth.te18
-rw-r--r--car_product/sepolicy/can.te18
-rw-r--r--car_product/sepolicy/config_bluetooth.te22
-rw-r--r--car_product/sepolicy/device.te14
-rw-r--r--car_product/sepolicy/domain.te47
-rw-r--r--car_product/sepolicy/file.te21
-rw-r--r--car_product/sepolicy/file_contexts92
-rw-r--r--car_product/sepolicy/init.te2
-rw-r--r--car_product/sepolicy/irsc_util.te6
-rw-r--r--car_product/sepolicy/mm-pp-daemon.te13
-rw-r--r--car_product/sepolicy/modem-sh.te8
-rw-r--r--car_product/sepolicy/mpdecision.te36
-rw-r--r--car_product/sepolicy/netmgrd.te28
-rw-r--r--car_product/sepolicy/priv_app.te1
-rw-r--r--car_product/sepolicy/property.te12
-rw-r--r--car_product/sepolicy/property_contexts12
-rw-r--r--car_product/sepolicy/qcom-c_core-sh.te15
-rw-r--r--car_product/sepolicy/qcom-c_main-sh.te16
-rw-r--r--car_product/sepolicy/qcom-post-boot.te50
-rw-r--r--car_product/sepolicy/qcom-sh.te21
-rw-r--r--car_product/sepolicy/qcom-usb-sh.te29
-rw-r--r--car_product/sepolicy/qmuxd.te22
-rw-r--r--car_product/sepolicy/rild.te6
-rw-r--r--car_product/sepolicy/rmt_storage.te32
-rw-r--r--car_product/sepolicy/service.te1
-rw-r--r--car_product/sepolicy/service_contexts1
-rw-r--r--car_product/sepolicy/start_hci_filter.te10
-rw-r--r--car_product/sepolicy/system_app.te2
-rw-r--r--car_product/sepolicy/system_server.te13
-rw-r--r--car_product/sepolicy/te_macros11
-rw-r--r--car_product/sepolicy/thermal-engine.te38
-rw-r--r--car_product/sepolicy/time_daemon.te19
-rw-r--r--car_product/sepolicy/ueventd.te11
-rw-r--r--car_product/sepolicy/usf-post-boot.te8
-rw-r--r--car_product/sepolicy/vehicle_network_service.te4
-rw-r--r--car_product/sepolicy/vns.te4
-rw-r--r--car_product/sepolicy/wcnss_service.te17
-rw-r--r--libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetwork.java6
-rw-r--r--libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetworkConsts.java240
-rw-r--r--libvehiclenetwork/native/VehicleNetwork.cpp30
-rwxr-xr-xlibvehiclenetwork/tool/vehiclehal_code_gen.py23
-rw-r--r--service/AndroidManifest.xml33
-rw-r--r--service/res/layout/activity_blocking.xml30
-rw-r--r--service/res/values/config.xml16
-rw-r--r--service/res/values/strings.xml7
-rw-r--r--service/src/com/android/car/AppContextService.java342
-rw-r--r--service/src/com/android/car/AppFocusService.java452
-rw-r--r--service/src/com/android/car/AudioRoutingPolicy.java5
-rw-r--r--service/src/com/android/car/CarAudioAttributesUtil.java50
-rw-r--r--service/src/com/android/car/CarAudioService.java448
-rw-r--r--service/src/com/android/car/CarHvacService.java179
-rw-r--r--service/src/com/android/car/CarLog.java16
-rw-r--r--service/src/com/android/car/CarPropertyServiceBase.java206
-rw-r--r--service/src/com/android/car/CarVolumeControllerFactory.java51
-rw-r--r--service/src/com/android/car/CarVolumeService.java7
-rw-r--r--service/src/com/android/car/ICarImpl.java76
-rw-r--r--service/src/com/android/car/SystemActivityMonitoringService.java462
-rw-r--r--service/src/com/android/car/cluster/CarNavigationService.java229
-rw-r--r--service/src/com/android/car/cluster/InstrumentClusterRendererLoader.java159
-rw-r--r--service/src/com/android/car/cluster/InstrumentClusterService.java178
-rw-r--r--service/src/com/android/car/hal/AudioHalService.java132
-rw-r--r--service/src/com/android/car/hal/HvacHalService.java191
-rw-r--r--service/src/com/android/car/hal/PropertyHalServiceBase.java219
-rw-r--r--service/src/com/android/car/hal/VehicleHal.java6
-rw-r--r--service/src/com/android/car/pm/ActivityBlockingActivity.java130
-rw-r--r--service/src/com/android/car/pm/CarPackageManagerService.java215
-rw-r--r--tests/EmbeddedKitchenSinkApp/res/layout/hvac_test.xml2
-rw-r--r--tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml4
-rw-r--r--tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java15
-rw-r--r--tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java100
-rw-r--r--tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java133
-rw-r--r--tests/android_car_api_test/Android.mk2
-rw-r--r--tests/android_car_api_test/src/com/android/car/apitest/CarAppContextManagerTest.java240
-rw-r--r--tests/android_car_api_test/src/com/android/car/apitest/CarAppFocusManagerTest.java333
-rw-r--r--tests/android_car_api_test/src/com/android/car/apitest/CarNavigationManagerTest.java59
-rw-r--r--tests/android_support_car_api_test/Android.mk2
-rw-r--r--tests/android_support_car_api_test/src/com/android/support/car/apitest/CarAppContextManagerTest.java239
-rw-r--r--tests/android_support_car_api_test/src/com/android/support/car/apitest/CarAppFocusManagerTest.java333
-rw-r--r--tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationStatusManagerTest.java (renamed from tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationManagerTest.java)55
-rw-r--r--tests/carservice_test/src/com/android/car/test/CarAudioExtFocusTest.java1047
-rw-r--r--tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java28
-rw-r--r--tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java15
-rw-r--r--tests/carservice_test/src/com/android/support/car/test/AppContextTest.java106
-rw-r--r--tests/carservice_test/src/com/android/support/car/test/AppFocusTest.java106
-rw-r--r--vehicle_network_service/VehicleHalPropertyUtil.h14
-rw-r--r--vehicle_network_service/VehicleNetworkService.cpp12
-rw-r--r--vehicle_network_service/VehiclePropertyAccessControl.cpp12
-rw-r--r--vehicle_network_service/VehiclePropertyAccessControl.h3
-rw-r--r--vns_policy/vns_policy.xml8
156 files changed, 7001 insertions, 5563 deletions
diff --git a/car-cluster-demo-renderer/Android.mk b/car-cluster-demo-renderer/Android.mk
deleted file mode 100644
index 02befa3138..0000000000
--- a/car-cluster-demo-renderer/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (C) 2015 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.
-#
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := android.car.cluster.demorenderer
-
-# Each update should be signed by OEMs
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_JAVA_LIBRARIES += android.car
-
-include $(BUILD_PACKAGE)
diff --git a/car-cluster-demo-renderer/AndroidManifest.xml b/car-cluster-demo-renderer/AndroidManifest.xml
deleted file mode 100644
index f11350198f..0000000000
--- a/car-cluster-demo-renderer/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?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"
- package="android.car.cluster.demorenderer"
- android:versionCode="1"
- android:versionName="1.0">
-
- <!-- We set TYPE_SYSTEM_ALERT window flag to presentation in order
- to show it outside of activity context -->
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
-
- <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
- </application>
-</manifest>
diff --git a/car-cluster-demo-renderer/proguard.flags b/car-cluster-demo-renderer/proguard.flags
deleted file mode 100644
index 22cc22df14..0000000000
--- a/car-cluster-demo-renderer/proguard.flags
+++ /dev/null
@@ -1,3 +0,0 @@
--verbose
--keep @com.android.internal.annotations.VisibleForTesting class *
-
diff --git a/car-cluster-demo-renderer/res/drawable-hdpi/ic_contactavatar_large_light.png b/car-cluster-demo-renderer/res/drawable-hdpi/ic_contactavatar_large_light.png
deleted file mode 100644
index 138b53ce30..0000000000
--- a/car-cluster-demo-renderer/res/drawable-hdpi/ic_contactavatar_large_light.png
+++ /dev/null
Binary files differ
diff --git a/car-cluster-demo-renderer/res/drawable-hdpi/ic_launcher.png b/car-cluster-demo-renderer/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 96a442e5b8..0000000000
--- a/car-cluster-demo-renderer/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/car-cluster-demo-renderer/res/drawable-ldpi/ic_launcher.png b/car-cluster-demo-renderer/res/drawable-ldpi/ic_launcher.png
deleted file mode 100644
index 99238729d8..0000000000
--- a/car-cluster-demo-renderer/res/drawable-ldpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/car-cluster-demo-renderer/res/drawable-mdpi/ic_contactavatar_large_light.png b/car-cluster-demo-renderer/res/drawable-mdpi/ic_contactavatar_large_light.png
deleted file mode 100644
index 7031b839de..0000000000
--- a/car-cluster-demo-renderer/res/drawable-mdpi/ic_contactavatar_large_light.png
+++ /dev/null
Binary files differ
diff --git a/car-cluster-demo-renderer/res/drawable-mdpi/ic_launcher.png b/car-cluster-demo-renderer/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 359047dfa4..0000000000
--- a/car-cluster-demo-renderer/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/car-cluster-demo-renderer/res/drawable-xhdpi/ic_contactavatar_large_light.png b/car-cluster-demo-renderer/res/drawable-xhdpi/ic_contactavatar_large_light.png
deleted file mode 100644
index a137cdb20c..0000000000
--- a/car-cluster-demo-renderer/res/drawable-xhdpi/ic_contactavatar_large_light.png
+++ /dev/null
Binary files differ
diff --git a/car-cluster-demo-renderer/res/drawable-xhdpi/ic_launcher.png b/car-cluster-demo-renderer/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 71c6d760f0..0000000000
--- a/car-cluster-demo-renderer/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/car-cluster-demo-renderer/res/drawable-xxhdpi/ic_contactavatar_large_light.png b/car-cluster-demo-renderer/res/drawable-xxhdpi/ic_contactavatar_large_light.png
deleted file mode 100644
index 7111bb9790..0000000000
--- a/car-cluster-demo-renderer/res/drawable-xxhdpi/ic_contactavatar_large_light.png
+++ /dev/null
Binary files differ
diff --git a/car-cluster-demo-renderer/res/layout/instrument_cluster.xml b/car-cluster-demo-renderer/res/layout/instrument_cluster.xml
deleted file mode 100644
index b4235c2e0b..0000000000
--- a/car-cluster-demo-renderer/res/layout/instrument_cluster.xml
+++ /dev/null
@@ -1,118 +0,0 @@
-<?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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="match_parent" android:id="@+id/instrument_cluster_layout"
- android:theme="@android:style/Theme.Material" android:weightSum="1"
- android:background="@android:color/background_dark">
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="214dp"
- android:layout_height="match_parent">
- </LinearLayout>
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical">
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:id="@+id/speed" android:textSize="120dp"
- android:textAlignment="center" android:gravity="center"
- android:layout_marginTop="100dp"/>
- <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="@string/fuel_level" android:id="@+id/textView"
- android:layout_gravity="left"/>
- <ProgressBar style="?android:attr/progressBarStyleHorizontal" android:layout_width="226dp"
- android:layout_height="wrap_content" android:id="@+id/fuel_level_progress"
- android:max="100" android:progress="30"
- />
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent" android:layout_marginTop="40dp"
- android:id="@+id/nav_layout">
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:id="@+id/nav_event_title"
- />
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:id="@+id/nav_distance" android:gravity="center"
- />
- </LinearLayout>
- </LinearLayout>
- <FrameLayout android:orientation="vertical" android:layout_width="wrap_content"
- android:layout_height="match_parent" android:layout_weight="0.63"
- android:layout_gravity="center_vertical" android:weightSum="1"
- android:layout_marginTop="0dp">
- <LinearLayout android:orientation="vertical" android:layout_width="match_parent"
- android:layout_height="wrap_content" android:layout_weight="0.63"
- android:layout_gravity="center_horizontal" android:weightSum="1"
- android:layout_marginTop="100dp"
- android:background="@android:color/background_dark"
- android:id="@+id/media_layout"
- android:visibility="gone">
- <ImageView android:layout_width="200dp" android:layout_height="200dp"
- android:id="@+id/media_image" android:layout_gravity="center_horizontal"
- android:background="@android:color/holo_blue_light"
- android:scaleType="center"/>
- <TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:id="@+id/media_album" android:gravity="center"
- android:layout_gravity="center_horizontal" android:visibility="gone"/>
- <TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:id="@+id/media_track" android:gravity="center"
- android:layout_gravity="center_horizontal" android:textSize="28sp"/>
- <TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:id="@+id/media_artist" android:gravity="center"
- android:textIsSelectable="false"
- />
- </LinearLayout>
- <LinearLayout android:orientation="vertical" android:layout_width="match_parent"
- android:layout_height="wrap_content" android:layout_weight="0.63"
- android:layout_gravity="center_horizontal" android:weightSum="1"
- android:layout_marginTop="100dp"
- android:background="@android:color/background_dark"
- android:id="@+id/phone_layout"
- android:visibility="gone">
- <ImageView android:layout_width="200dp" android:layout_height="200dp"
- android:id="@+id/phone_contact_photo" android:layout_gravity="center"
- android:background="#16161e"
- android:scaleType="fitCenter"/>
- <TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:id="@+id/phone_title" android:gravity="center"
- android:layout_gravity="center_horizontal" android:textSize="28sp"/>
- <TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:id="@+id/phone_subtitle" android:gravity="center"
- android:textIsSelectable="false"
- />
- </LinearLayout>
- </FrameLayout>
-</LinearLayout> \ No newline at end of file
diff --git a/car-cluster-demo-renderer/res/values/strings.xml b/car-cluster-demo-renderer/res/values/strings.xml
deleted file mode 100644
index bd280ed3fe..0000000000
--- a/car-cluster-demo-renderer/res/values/strings.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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.
--->
-<resources>
- <string name="app_name">ACTIVITY_ENTRY_NAME</string>
- <string name="fuel_level">Fuel level</string>
-
- <!-- Navigation -->
- <string name="turn_slight_left">Slight left</string>
- <string name="turn_slight_right">Slight right</string>
- <string name="turn_left">Turn left</string>
- <string name="turn_right">Turn right</string>
- <string name="turn_sharp_left">Sharp left turn</string>
- <string name="turn_sharp_right">Sharp right turn</string>
- <string name="turn_u_turn_left">Make u-turn</string>
- <string name="turn_u_turn_right">Make right u-turn</string>
- <string name="turn_on_ramp_left">Turn left on ramp</string>
- <string name="turn_on_ramp_right">Turn right on ramp</string>
- <string name="nav_event_title_format">%1$s on %2$s</string>
- <string name="meters">meters</string>
-
- <string name="call_state_ringing">Ringing</string>
- <string name="call_state_active">Active</string>
-</resources>
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/CallStateMonitor.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/CallStateMonitor.java
deleted file mode 100644
index c8b0616e6a..0000000000
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/CallStateMonitor.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.
- */
-
-package android.car.cluster.demorenderer;
-
-import android.annotation.Nullable;
-import android.car.cluster.demorenderer.PhoneBook.Contact;
-import android.car.cluster.demorenderer.PhoneBook.ContactLoadedListener;
-import android.car.cluster.demorenderer.PhoneBook.ContactPhotoLoadedListener;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Monitors call state.
- */
-public class CallStateMonitor implements ContactLoadedListener, ContactPhotoLoadedListener {
- private final static String TAG = CallStateMonitor.class.getSimpleName();
-
- private final PhoneBook mPhoneBook;
- private final TelephonyManager mTelephonyManager;
- private final PhoneStateListener mListener;
- private final CallStateListener mCallStateListener;
-
- CallStateMonitor(Context context, PhoneStateListener listener) {
- Log.d(TAG, "ctor, context: " + context + ", phoneRenderer: " + listener +
- ", contentResolver: " + context.getContentResolver() +
- ", applicationContext: " + context.getApplicationContext());
-
- mListener = listener;
- mTelephonyManager = context.getSystemService(TelephonyManager.class);
- mPhoneBook = new PhoneBook(context, mTelephonyManager);
- mCallStateListener = new CallStateListener(this);
- mTelephonyManager.listen(mCallStateListener,
- android.telephony.PhoneStateListener.LISTEN_CALL_STATE);
-
- updateRendererPhoneStatusIfAvailable();
- }
-
- public void release() {
- mTelephonyManager.listen(mCallStateListener,
- android.telephony.PhoneStateListener.LISTEN_NONE);
- }
-
- private void updateRendererPhoneStatusIfAvailable() {
- onCallStateChanged(mTelephonyManager.getCallState(), null);
- }
-
- private void onCallStateChanged(int state, final String number) {
- Log.d(TAG, "onCallStateChanged, state:" + state + ", phoneNumber: " + number);
-
- // Update call state immediately on instrument cluster.
- mListener.onCallStateChanged(state, PhoneBook.getFormattedNumber(number));
-
- // Now fetching details asynchronously.
- mPhoneBook.getContactDetailsAsync(number, this);
- }
-
- @Override
- public void onContactLoaded(String number, @Nullable Contact contact) {
- if (contact != null) {
- mListener.onContactDetailsUpdated(contact.getName(), contact.getType(),
- mPhoneBook.isVoicemail(number));
-
- mPhoneBook.getContactPictureAsync(contact.getId(), this);
- }
- }
-
- @Override
- public void onPhotoLoaded(int contactId, @Nullable Bitmap photo) {
- mListener.onContactPhotoUpdated(photo);
- }
-
- public interface PhoneStateListener {
- void onCallStateChanged(int state, @Nullable String number);
- void onContactDetailsUpdated(
- @Nullable CharSequence name,
- @Nullable CharSequence typeLabel,
- boolean isVoiceMail);
- void onContactPhotoUpdated(Bitmap picture);
- }
-
- private static class CallStateListener extends android.telephony.PhoneStateListener {
- private final WeakReference<CallStateMonitor> mServiceRef;
-
- CallStateListener(CallStateMonitor service) {
- mServiceRef = new WeakReference<>(service);
- }
-
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- CallStateMonitor service = mServiceRef.get();
- if (service != null) {
- service.onCallStateChanged(state, incomingNumber);
- }
- }
- }
-}
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterRenderer.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterRenderer.java
deleted file mode 100644
index 3b09be77ee..0000000000
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterRenderer.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.
- */
-package android.car.cluster.demorenderer;
-
-import android.app.Presentation;
-import android.car.cluster.renderer.InstrumentClusterRenderer;
-import android.car.cluster.renderer.NavigationRenderer;
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Demo implementation of {@code InstrumentClusterRenderer}.
- */
-public class DemoInstrumentClusterRenderer extends InstrumentClusterRenderer {
-
- private final static String TAG = DemoInstrumentClusterRenderer.class.getSimpleName();
-
- private DemoInstrumentClusterView mView;
- private Context mContext;
- private CallStateMonitor mPhoneStatusMonitor;
- private MediaStateMonitor mMediaStateMonitor;
- private DemoPhoneRenderer mPhoneRenderer;
- private DemoMediaRenderer mMediaRenderer;
- private Presentation mPresentation;
-
- @Override
- public void onCreate(Context context) {
- mContext = context;
-
- final Display display = getInstrumentClusterDisplay(context);
-
- if (display != null) {
- runOnMainThread(() -> {
- Log.d(TAG, "Initializing renderer in main thread.");
- try {
- mPresentation = new InstrumentClusterPresentation(mContext, display);
-
- ViewGroup rootView = (ViewGroup) LayoutInflater.from(mContext).inflate(
- R.layout.instrument_cluster, null);
-
- mPresentation.setContentView(rootView);
- View rendererView = createView();
- rootView.addView(rendererView);
- } catch (Exception e) {
- Log.e(TAG, e.getMessage(), e);
- throw e;
- }
- });
- }
- }
-
- private View createView() {
- mView = new DemoInstrumentClusterView(mContext);
- mPhoneRenderer = new DemoPhoneRenderer(mView);
- mMediaRenderer = new DemoMediaRenderer(mView);
- return mView;
- }
-
- @Override
- public void onStart() {
- runOnMainThread(() -> {
- Log.d(TAG, "onStart");
- mPhoneStatusMonitor = new CallStateMonitor(mContext, mPhoneRenderer);
- mMediaStateMonitor = new MediaStateMonitor(mContext, mMediaRenderer);
- mPresentation.show();
- });
- }
-
- @Override
- public void onStop() {
- runOnMainThread(() -> {
- if (mPhoneStatusMonitor != null) {
- mPhoneStatusMonitor.release();
- mPhoneStatusMonitor = null;
- }
-
- if (mMediaStateMonitor != null) {
- mMediaStateMonitor.release();
- mMediaStateMonitor = null;
- }
- mPhoneRenderer = null;
- mMediaRenderer = null;
- });
- }
-
- @Override
- protected NavigationRenderer createNavigationRenderer() {
- return ThreadSafeNavigationRenderer.createFor(
- Looper.getMainLooper(),
- new DemoNavigationRenderer(mView));
- }
-
- private static Display getInstrumentClusterDisplay(Context context) {
- DisplayManager displayManager = context.getSystemService(DisplayManager.class);
- Display[] displays = displayManager.getDisplays();
-
- Log.d(TAG, "There are currently " + displays.length + " displays connected.");
- for (Display display : displays) {
- Log.d(TAG, " " + display);
- }
-
- if (displays.length > 1) {
- // TODO: assuming that secondary display is instrument cluster. Put this into settings?
- return displays[1];
- }
- return null;
- }
-
- private static void runOnMainThread(Runnable runnable) {
- new Handler(Looper.getMainLooper()).post(runnable);
- }
-}
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterView.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterView.java
deleted file mode 100644
index 9ad691fa5b..0000000000
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoInstrumentClusterView.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * 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.
- */
-package android.car.cluster.demorenderer;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.util.Log;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-/**
- * This class is responsible for drawing the whole instrument cluster.
- */
-public class DemoInstrumentClusterView extends FrameLayout {
-
- private final String TAG = DemoInstrumentClusterView.class.getSimpleName();
-
- private TextView mSpeedView;
- private TextView mEventTitleView;
- private TextView mDistanceView;
- private View mNavPanel;
- private TextView mMediaArtistView;
- private TextView mMediaAlbumView;
- private TextView mMediaTrackView;
- private ImageView mMediaImageView;
- private View mMediaPanel;
-
- private View mPhonePanel;
- private TextView mPhoneTitle;
- private TextView mPhoneSubtitle;
- private ImageView mPhoneImage;
-
- private final Integer mAnimationDurationMs;
-
- public DemoInstrumentClusterView(Context context) {
- super(context);
- mAnimationDurationMs = getResources().getInteger(android.R.integer.config_longAnimTime);
- init();
- }
-
- public void setSpeed(String speed) {
- Log.d(TAG, "setSpeed, meterPerSecond: " + speed);
- mSpeedView.setText(speed);
- }
-
- public void showNavigation() {
- Log.d(TAG, "showNavigation");
- mEventTitleView.setText("");
- mDistanceView.setText("");
- mNavPanel.setVisibility(VISIBLE);
- }
-
- public void hideNavigation() {
- Log.d(TAG, "hideNavigation");
- mNavPanel.setVisibility(INVISIBLE);
- }
-
- public void setNextTurn(Bitmap image, String title) {
- Log.d(TAG, "setNextTurn, image: " + image + ", title: " + title);
- mEventTitleView.setText(title);
- }
-
- public void setNextTurnDistance(String distance) {
- Log.d(TAG, "setNextTurnDistance, distance: " + distance);
- mDistanceView.setText(distance);
- }
-
- public void setMediaData(final CharSequence artist, final CharSequence album,
- final CharSequence track, final Bitmap image) {
- Log.d(TAG, "setMediaData" + " artist = " + artist + ", album: " + album + ", track: " +
- track + ", bitmap: " + image);
-
- mMediaArtistView.setText(artist);
- mMediaAlbumView.setText(album);
- mMediaTrackView.setText(track);
- mMediaImageView.setImageBitmap(image);
- }
-
- private void showAnimated(final View view) {
- if (view.getVisibility() == VISIBLE && view.getAlpha() > 0) {
- return;
- }
- view.setAlpha(0);
- view.setVisibility(VISIBLE);
- view.animate()
- .alpha(1f)
- .setDuration(mAnimationDurationMs)
- .setListener(null);
- }
-
- private void hideAnimated(final View view) {
- if (view.getVisibility() == GONE) {
- return;
- }
- view.animate()
- .alpha(0f)
- .setDuration(mAnimationDurationMs)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setVisibility(GONE);
- }
- });
- }
-
- public void showMedia() {
- Log.d(TAG, "showMedia");
- showAnimated(mMediaPanel);
- }
-
- public void hideMedia() {
- Log.d(TAG, "hideMedia");
- hideAnimated(mMediaPanel);
- }
-
- public void showPhone() {
- Log.d(TAG, "showPhone");
- mPhoneSubtitle.setText("");
- mPhoneImage.setImageResource(0); // To clear previous contact photo (if any).
- mPhoneTitle.setText("");
- showAnimated(mPhonePanel);
- }
-
- public void hidePhone() {
- Log.d(TAG, "hidePhone");
- hideAnimated(mPhonePanel);
- }
-
- public void setPhoneTitle(String number) {
- Log.d(TAG, "setPhoneTitle, number: " + number);
- mPhoneTitle.setText(number);
- }
-
- public void setPhoneSubtitle(String contact) {
- Log.d(TAG, "setPhoneContact, contact: " + contact);
- mPhoneSubtitle.setText(contact);
- }
-
- public void setPhoneImage(Bitmap photo) {
- Log.d(TAG, "setPhoneImage, photo: " + photo);
- mPhoneImage.setImageBitmap(photo);
- }
-
- private void init() {
- Log.d(TAG, "init");
- View rootView = inflate(getContext(), R.layout.instrument_cluster, null);
- mSpeedView = (TextView) rootView.findViewById(R.id.speed);
- mEventTitleView = (TextView) rootView.findViewById(R.id.nav_event_title);
- mDistanceView = (TextView) rootView.findViewById(R.id.nav_distance);
- mNavPanel = rootView.findViewById(R.id.nav_layout);
-
- mMediaPanel = rootView.findViewById(R.id.media_layout);
- mMediaArtistView = (TextView) rootView.findViewById(R.id.media_artist);
- mMediaAlbumView = (TextView) rootView.findViewById(R.id.media_album);
- mMediaTrackView = (TextView) rootView.findViewById(R.id.media_track);
- mMediaImageView = (ImageView) rootView.findViewById(R.id.media_image);
-
- mPhonePanel = rootView.findViewById(R.id.phone_layout);
- mPhoneImage = (ImageView) rootView.findViewById(R.id.phone_contact_photo);
- mPhoneSubtitle = (TextView) rootView.findViewById(R.id.phone_subtitle);
- mPhoneTitle = (TextView) rootView.findViewById(R.id.phone_title);
-
- setSpeed("0");
-
- addView(rootView);
- }
-}
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoMediaRenderer.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoMediaRenderer.java
deleted file mode 100644
index 5ee23cc934..0000000000
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoMediaRenderer.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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.
- */
-package android.car.cluster.demorenderer;
-
-import android.car.cluster.demorenderer.MediaStateMonitor.MediaStateListener;
-import android.graphics.Bitmap;
-import android.media.MediaMetadata;
-import android.media.session.PlaybackState;
-import android.util.Log;
-
-/**
- * Demo of rendering media data in instrument cluster.
- */
-public class DemoMediaRenderer implements MediaStateListener {
-
- private static final String TAG = DemoMediaRenderer.class.getSimpleName();
-
-
- private final DemoInstrumentClusterView mView;
-
- private static final String[] PREFERRED_BITMAP_ORDER = {
- MediaMetadata.METADATA_KEY_ALBUM_ART,
- MediaMetadata.METADATA_KEY_ART,
- MediaMetadata.METADATA_KEY_DISPLAY_ICON
- };
-
-
- public DemoMediaRenderer(DemoInstrumentClusterView view) {
- mView = view;
- }
-
- @Override
- public void onPlaybackStateChanged(PlaybackState playbackState) {
- Log.d(TAG, "onPlaybackStateChanged: " + playbackState);
- }
-
- @Override
- public void onMetadataChanged(MediaMetadata metadata) {
- Log.d(TAG, "onMetadataChanged: " + metadata);
- if (metadata != null) {
- CharSequence artist = metadata.getText(MediaMetadata.METADATA_KEY_ARTIST);
- CharSequence album = metadata.getText(MediaMetadata.METADATA_KEY_ALBUM);
- CharSequence track = metadata.getText(MediaMetadata.METADATA_KEY_TITLE);
- Bitmap bitmap = getMetadataBitmap(metadata);
-
- mView.setMediaData(artist, album, track, bitmap);
- mView.showMedia();
- } else {
- mView.setMediaData(null, null, null, null);
- mView.hideMedia();
- }
- }
-
- private Bitmap getMetadataBitmap(MediaMetadata metadata) {
- // Get the best art bitmap we can find
- for (int i = 0; i < PREFERRED_BITMAP_ORDER.length; i++) {
- Bitmap bitmap = metadata.getBitmap(PREFERRED_BITMAP_ORDER[i]);
- if (bitmap != null) {
- return bitmap;
- }
- }
- return null;
- }
-
-}
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoNavigationRenderer.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoNavigationRenderer.java
deleted file mode 100644
index 5588546a09..0000000000
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoNavigationRenderer.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.
- */
-package android.car.cluster.demorenderer;
-
-import static android.car.navigation.CarNavigationManager.TURN_SIDE_LEFT;
-import static android.car.navigation.CarNavigationManager.TURN_SIDE_RIGHT;
-import static android.car.navigation.CarNavigationManager.TURN_TURN;
-
-import android.car.cluster.renderer.NavigationRenderer;
-import android.car.navigation.CarNavigationInstrumentCluster;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.util.Log;
-import android.util.Pair;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Demo implementation of {@link NavigationRenderer}.
- */
-public class DemoNavigationRenderer extends NavigationRenderer {
-
- private static final String TAG = DemoNavigationRenderer.class.getSimpleName();
-
- private final DemoInstrumentClusterView mView;
- private final Context mContext;
-
- private final static Map<Pair<Integer, Integer>, Integer> sTurns;
-
- static {
- sTurns = new HashMap<>();
- sTurns.put(new Pair<>(TURN_TURN, TURN_SIDE_LEFT), R.string.turn_left);
- sTurns.put(new Pair<>(TURN_TURN, TURN_SIDE_RIGHT), R.string.turn_right);
- // TODO: add more localized strings here.
- }
-
- DemoNavigationRenderer(DemoInstrumentClusterView view) {
- mView = view;
- mContext = view.getContext();
- }
-
- @Override
- public CarNavigationInstrumentCluster getNavigationProperties() {
- // TODO
- return null;
- }
-
- @Override
- public void onStartNavigation() {
- mView.showNavigation();
- }
-
- @Override
- public void onStopNavigation() {
- mView.hideNavigation();
- }
-
- @Override
- public void onNextTurnChanged(int event, String road, int turnAngle, int turnNumber,
- final Bitmap image, int turnSide) {
- String localizedAction = getLocalizedNavigationAction(event, turnSide);
- final String localizedTitle = String.format(
- mContext.getString(R.string.nav_event_title_format), localizedAction, road);
-
- mView.setNextTurn(image, localizedTitle);
- }
-
- @Override
- public void onNextTurnDistanceChanged(final int distanceMeters, int timeSeconds) {
- mView.setNextTurnDistance(toHumanReadableDistance(distanceMeters));
- }
-
- private String getLocalizedNavigationAction(int event, int turnSide) {
- Pair<Integer, Integer> key = new Pair<>(event, turnSide);
- if (sTurns.containsKey(key)) {
- Integer resourceId = sTurns.get(key);
- return mContext.getResources().getString(resourceId);
- } else {
- Log.w(TAG, "Navigation event / turn not localized: " + event + ", " + turnSide);
- return String.format("Event: %d, Side: %d", event, turnSide);
- }
- }
-
- private String toHumanReadableDistance(int meters) {
- // TODO: implement.
- return "in " + String.valueOf(meters) + " " + mContext.getString(R.string.meters);
- }
-}
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoPhoneRenderer.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoPhoneRenderer.java
deleted file mode 100644
index cc70b72c63..0000000000
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/DemoPhoneRenderer.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.
- */
-
-package android.car.cluster.demorenderer;
-
-import android.car.cluster.demorenderer.CallStateMonitor.PhoneStateListener;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-/**
- * Demo for rendering phone status in instrument cluster.
- */
-public class DemoPhoneRenderer implements PhoneStateListener {
- private static final String TAG = DemoPhoneRenderer.class.getSimpleName();
-
- private final DemoInstrumentClusterView mView;
- private final Context mContext;
-
- private static Bitmap sDefaultAvatar;
-
- private int mCurrentState;
- private String mCurrentNumber;
-
- DemoPhoneRenderer(DemoInstrumentClusterView view) {
- mView = view;
- mContext = view.getContext();
- }
-
- @Override
- public void onCallStateChanged(int state, String number) {
- Log.d(TAG, "onCallStateChanged, state: " + state + ", number: " + number);
- mCurrentState = state;
- mCurrentNumber = PhoneBook.getFormattedNumber(number);
-
- if (TelephonyManager.CALL_STATE_IDLE == state) {
- mView.hidePhone();
- } else {
- mView.showPhone();
- setPhoneTitleWithState(mCurrentNumber);
- mView.setPhoneImage(getDefaultAvatar());
- }
- }
-
- @Override
- public void onContactDetailsUpdated(CharSequence name, CharSequence typeLabel,
- boolean isVoiceMail) {
- Log.d(TAG, "onContactDetailsUpdated, name: " + name + ", typeLabel: " + typeLabel
- + ", isVoicemail: " + isVoiceMail);
- setPhoneTitleWithState(name.toString());
- mView.setPhoneSubtitle(mCurrentNumber);
- }
-
- private void setPhoneTitleWithState(String text) {
- mView.setPhoneTitle(getCallStateToDisplay(mCurrentState) + " · " + text);
- }
-
- @Override
- public void onContactPhotoUpdated(Bitmap picture) {
- Log.d(TAG, "onContactPhotoUpdated, picture: " + picture);
- if (picture != null) {
- mView.setPhoneImage(picture);
- }
- }
-
-
- private Bitmap getDefaultAvatar() {
- if (sDefaultAvatar == null) {
- sDefaultAvatar = BitmapFactory.decodeResource(mContext.getResources(),
- R.drawable.ic_contactavatar_large_light);
- }
- return sDefaultAvatar;
- }
-
- private String getCallStateToDisplay(int state) {
- switch (state) {
- case TelephonyManager.CALL_STATE_OFFHOOK:
- return mContext.getString(R.string.call_state_active);
- case TelephonyManager.CALL_STATE_RINGING:
- return mContext.getString(R.string.call_state_ringing);
- default:
- Log.w(TAG, "Unexpected call state: " + state);
- return "";
- }
- }
-}
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterPresentation.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterPresentation.java
deleted file mode 100644
index 1547a246b1..0000000000
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterPresentation.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.
- */
-package android.car.cluster.demorenderer;
-
-import android.app.Presentation;
-import android.content.Context;
-import android.view.Display;
-import android.view.WindowManager;
-
-/**
- * Presentation class.
- */
-public class InstrumentClusterPresentation extends Presentation {
- public InstrumentClusterPresentation(Context outerContext, Display display) {
- super(outerContext, display);
- getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- }
-}
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterRendererFactory.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterRendererFactory.java
deleted file mode 100644
index 744d550929..0000000000
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/InstrumentClusterRendererFactory.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.
- */
-package android.car.cluster.demorenderer;
-
-import android.car.cluster.renderer.InstrumentClusterRenderer;
-
-/**
- * This factory class will be requested by android.car using reflection in order to get an instance
- * of {@link InstrumentClusterRenderer}.
- */
-@SuppressWarnings("unused") /* Used by reflection. */
-public class InstrumentClusterRendererFactory {
- public static InstrumentClusterRenderer createRenderer() {
- return new DemoInstrumentClusterRenderer();
- }
-}
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/MediaStateMonitor.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/MediaStateMonitor.java
deleted file mode 100644
index ada064b970..0000000000
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/MediaStateMonitor.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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.
- */
-package android.car.cluster.demorenderer;
-
-import android.content.Context;
-import android.media.MediaMetadata;
-import android.media.session.MediaController;
-import android.media.session.MediaSessionManager;
-import android.media.session.MediaSessionManager.OnActiveSessionsChangedListener;
-import android.media.session.PlaybackState;
-import android.util.Log;
-
-import java.lang.ref.WeakReference;
-import java.util.List;
-
-/**
- * Reports current media status to instrument cluster renderer.
- */
-public class MediaStateMonitor {
-
- private final static String TAG = MediaStateMonitor.class.getSimpleName();
-
- private final Context mContext;
- private final MediaListener mMediaListener;
- private MediaController mPrimaryMediaController;
- private OnActiveSessionsChangedListener mActiveSessionsChangedListener;
- private MediaSessionManager mMediaSessionManager;
- private MediaStateListener mListener;
-
- public MediaStateMonitor(Context context, MediaStateListener listener) {
- mListener = listener;
- mContext = context;
- mMediaListener = new MediaListener(this);
- mActiveSessionsChangedListener = controllers -> onActiveSessionsChanged(controllers);
- mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
- mMediaSessionManager.addOnActiveSessionsChangedListener(
- mActiveSessionsChangedListener, null);
-
- onActiveSessionsChanged(mMediaSessionManager.getActiveSessions(null));
- }
-
- private void onActiveSessionsChanged(List<MediaController> controllers) {
- Log.d(TAG, "onActiveSessionsChanged, controllers found: " + controllers.size());
- MediaController newPrimaryController = null;
- if (controllers.size() > 0) {
- newPrimaryController = controllers.get(0);
- if (mPrimaryMediaController == newPrimaryController) {
- // Primary media controller has not been changed.
- return;
- }
- }
-
- releasePrimaryMediaController();
-
- if (newPrimaryController != null) {
- mPrimaryMediaController = newPrimaryController;
- mPrimaryMediaController.registerCallback(mMediaListener);
- }
- updateRendererMediaStatusIfAvailable();
-
- for (MediaController m : controllers) {
- Log.d(TAG, m + ": " + m.getPackageName());
- }
- }
-
- public void release() {
- releasePrimaryMediaController();
- if (mActiveSessionsChangedListener != null) {
- mMediaSessionManager.removeOnActiveSessionsChangedListener(
- mActiveSessionsChangedListener);
- mActiveSessionsChangedListener = null;
- }
- mMediaSessionManager = null;
- }
-
- private void releasePrimaryMediaController() {
- if (mPrimaryMediaController != null) {
- mPrimaryMediaController.unregisterCallback(mMediaListener);
- mPrimaryMediaController = null;
- }
- }
-
- private void updateRendererMediaStatusIfAvailable() {
- mListener.onMetadataChanged(
- mPrimaryMediaController == null ? null : mPrimaryMediaController.getMetadata());
- mListener.onPlaybackStateChanged(
- mPrimaryMediaController == null
- ? null : mPrimaryMediaController.getPlaybackState());
- }
-
- private void onPlaybackStateChanged(PlaybackState state) {
- mListener.onPlaybackStateChanged(state);
- }
-
- private void onMetadataChanged(MediaMetadata metadata) {
- mListener.onMetadataChanged(metadata);
- }
-
- public interface MediaStateListener {
- void onPlaybackStateChanged(PlaybackState playbackState);
- void onMetadataChanged(MediaMetadata metadata);
- }
-
-
- private static class MediaListener extends MediaController.Callback {
- private final WeakReference<MediaStateMonitor> mServiceRef;
-
- MediaListener(MediaStateMonitor service) {
- mServiceRef = new WeakReference<>(service);
- }
-
- @Override
- public void onPlaybackStateChanged(PlaybackState state) {
- MediaStateMonitor service = mServiceRef.get();
- if (service != null) {
- service.onPlaybackStateChanged(state);
- }
- }
-
- @Override
- public void onMetadataChanged(MediaMetadata metadata) {
- MediaStateMonitor service = mServiceRef.get();
- if (service != null) {
- service.onMetadataChanged(metadata);
- }
- }
- }
-}
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/PhoneBook.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/PhoneBook.java
deleted file mode 100644
index bcc1aea42d..0000000000
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/PhoneBook.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * 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.
- */
-
-package android.car.cluster.demorenderer;
-
-import static android.provider.ContactsContract.Contacts.openContactPhotoInputStream;
-
-import android.annotation.Nullable;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.CursorLoader;
-import android.content.Loader;
-import android.content.Loader.OnLoadCompleteListener;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapFactory.Options;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.PhoneLookup;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.LruCache;
-
-import java.io.InputStream;
-import java.lang.ref.WeakReference;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Locale;
-import java.util.Set;
-
-/**
- * Class that provides contact information.
- */
-class PhoneBook {
-
- private final static String TAG = PhoneBook.class.getSimpleName();
-
- private final ContentResolver mContentResolver;
- private final Context mContext;
- private final TelephonyManager mTelephonyManager;
- private final Object mSyncContact = new Object();
- private final Object mSyncPhoto = new Object();
-
- private volatile String mVoiceMail;
-
- private static final String[] CONTACT_ID_PROJECTION = new String[] {
- PhoneLookup.DISPLAY_NAME,
- PhoneLookup.TYPE,
- PhoneLookup.LABEL,
- PhoneLookup._ID
- };
-
- private HashMap<String, Contact> mContactByNumber;
- private LruCache<Integer, Bitmap> mContactPhotoById;
- private Set<Integer> mContactsWithoutImage;
-
- PhoneBook(Context context, TelephonyManager telephonyManager) {
- mContentResolver = context.getContentResolver();
- mContext = context;
- mTelephonyManager = telephonyManager;
- }
-
- /**
- * Formats provided number according to current locale.
- * */
- public static String getFormattedNumber(String number) {
- if (TextUtils.isEmpty(number)) {
- return "";
- }
-
- String countryIso = Locale.getDefault().getCountry();
- if (countryIso == null || countryIso.length() != 2) {
- countryIso = "US";
- }
- String e164 = PhoneNumberUtils.formatNumberToE164(number, countryIso);
- String formattedNumber = PhoneNumberUtils.formatNumber(number, e164, countryIso);
- formattedNumber = TextUtils.isEmpty(formattedNumber) ? number : formattedNumber;
- return formattedNumber;
- }
-
- /**
- * Loads contact details for a given phone number asynchronously. It may call listener's
- * callback function immediately if there were image in the cache.
- */
- public void getContactDetailsAsync(String number, ContactLoadedListener listener) {
- if (number == null || number.isEmpty()) {
- listener.onContactLoaded(number, null);
- return;
- }
-
- synchronized (mSyncContact) {
- if (mContactByNumber == null) {
- mContactByNumber = new HashMap<>();
- } else if (mContactByNumber.containsKey(number)) {
- listener.onContactLoaded(number, mContactByNumber.get(number));
- return;
- }
- }
-
- fetchContactAsync(number, listener);
- }
-
- /**
- * Loads photo for a given contactId asynchronously. It may call listener's callback function
- * immediately if there were image in the cache.
- */
- public void getContactPictureAsync(int contactId, ContactPhotoLoadedListener listener) {
- synchronized (mSyncPhoto) {
- if (mContactsWithoutImage != null && mContactsWithoutImage.contains(contactId)) {
- listener.onPhotoLoaded(contactId, null);
- return;
- }
-
- if (mContactPhotoById == null) {
- mContactPhotoById = new LruCache<Integer, Bitmap>(4 << 20 /* 4mb */) {
- @Override
- protected int sizeOf(Integer key, Bitmap value) {
- return value.getByteCount();
- }
- };
- } else {
- Bitmap photo = mContactPhotoById.get(contactId);
- if (photo != null) {
- listener.onPhotoLoaded(contactId, photo);
- return;
- }
- }
- }
-
- fetchPhotoAsync(contactId, listener);
- }
-
- /** Returns true if given phone number is a voice mail number. */
- public boolean isVoicemail(String number) {
- return !TextUtils.isEmpty(number) && number.equals(getVoiceMailNumber());
- }
-
- @Nullable
- private String getVoiceMailNumber() {
- if (mVoiceMail == null) {
- mVoiceMail = mTelephonyManager.getVoiceMailNumber();
- }
-
- return mVoiceMail;
- }
-
- interface ContactLoadedListener {
- void onContactLoaded(String number, @Nullable Contact contact);
- }
-
- interface ContactPhotoLoadedListener {
- void onPhotoLoaded(int contactId, @Nullable Bitmap picture);
- }
-
- private void fetchContactAsync(String number, ContactLoadedListener listener) {
- CursorLoader cursorLoader = new CursorLoader(mContext);
- cursorLoader.setUri(Uri.withAppendedPath(
- PhoneLookup.CONTENT_FILTER_URI,
- Uri.encode(number)));
- cursorLoader.setProjection(CONTACT_ID_PROJECTION);
- cursorLoader.registerListener(0, new LoadCompleteListener(this, number, listener));
- cursorLoader.startLoading();
- }
-
- private void fetchPhotoAsync(int contactId, ContactPhotoLoadedListener listener) {
- LoadPhotoAsyncTask.createAndExecute(this, contactId, listener);
- }
-
- private void cacheContactPhoto(int contactId, Bitmap bitmap) {
- synchronized (mSyncPhoto) {
- if (bitmap != null) {
- mContactPhotoById.put(contactId, bitmap);
- } else {
- if (mContactsWithoutImage == null) {
- mContactsWithoutImage = new HashSet<>();
- }
- mContactsWithoutImage.add(contactId);
- }
- }
- }
-
- static class Contact {
- private final int mId;
- private final String mName;
- private final CharSequence mType;
- private final String mNumber;
-
- Contact(Resources resources, String number, int id, String name, String label, int type) {
- mNumber = number;
- mId = id;
- mName = name;
- mType = Phone.getTypeLabel(resources, type, label);
- }
-
- int getId() {
- return mId;
- }
-
- public String getName() {
- return mName;
- }
-
- public CharSequence getType() {
- return mType;
- }
-
- public String getNumber() { return mNumber; }
- }
-
- private static class LoadPhotoAsyncTask extends AsyncTask<Void, Void, Bitmap> {
-
- private final WeakReference<PhoneBook> mPhoneBookRef;
- private final ContactPhotoLoadedListener mListener;
- private final int mContactId;
-
- static void createAndExecute(PhoneBook phoneBook, int contactId,
- ContactPhotoLoadedListener listener) {
- new LoadPhotoAsyncTask(phoneBook, contactId, listener)
- .execute();
- }
-
- private LoadPhotoAsyncTask(PhoneBook phoneBook, int contactId,
- ContactPhotoLoadedListener listener) {
- mPhoneBookRef = new WeakReference<>(phoneBook);
- mContactId = contactId;
- mListener = listener;
- }
-
- @Nullable
- private Bitmap fetchBitmap(int contactId) {
- Log.d(TAG, "fetchBitmap, contactId: " + contactId);
- PhoneBook phoneBook = mPhoneBookRef.get();
- if (phoneBook == null) {
- return null;
- }
-
- Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId);
- InputStream photoDataStream = openContactPhotoInputStream(
- phoneBook.mContentResolver, uri, true);
- Log.d(TAG, "fetchBitmap, uri: " + uri);
-
- Options options = new Options();
- options.inPreferQualityOverSpeed = true;
- options.inScaled = false;
- Rect nullPadding = null;
- Bitmap photo = BitmapFactory.decodeStream(photoDataStream, nullPadding, options);
- if (photo != null) {
- photo.setDensity(Bitmap.DENSITY_NONE);
- }
- Log.d(TAG, "bitmap fetched: " + photo);
- return photo;
- }
-
- @Override
- protected Bitmap doInBackground(Void... params) {
- return fetchBitmap(mContactId);
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- PhoneBook phoneBook = mPhoneBookRef.get();
- if (phoneBook != null) {
- phoneBook.cacheContactPhoto(mContactId, bitmap);
- }
- mListener.onPhotoLoaded(0, bitmap);
- }
- }
-
- private static class LoadCompleteListener implements OnLoadCompleteListener<Cursor> {
- private final String mNumber;
- private final ContactLoadedListener mContactLoadedListener;
- private final WeakReference<PhoneBook> mPhoneBookRef;
-
- private LoadCompleteListener(PhoneBook phoneBook, String number,
- ContactLoadedListener contactLoadedListener) {
- mPhoneBookRef = new WeakReference<>(phoneBook);
- mNumber = number;
- mContactLoadedListener = contactLoadedListener;
- }
-
- @Override
- public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
- Log.d(TAG, "onLoadComplete, cursor: " + cursor);
- PhoneBook phoneBook = mPhoneBookRef.get();
- Contact contact = null;
- if (cursor != null && phoneBook != null) {
- try {
- if (cursor.moveToFirst()) {
- int id = cursor.getInt(cursor.getColumnIndex(PhoneLookup._ID));
- String name = cursor
- .getString(cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME));
- String label = cursor.getString(cursor.getColumnIndex(PhoneLookup.LABEL));
- int type = cursor.getInt(cursor.getColumnIndex(PhoneLookup.TYPE));
- Resources resources = phoneBook.mContext.getResources();
- contact = new Contact(resources, mNumber, id, name, label, type);
- }
- } finally {
- cursor.close();
- }
-
- if (contact != null) {
- synchronized (phoneBook.mSyncContact) {
- phoneBook.mContactByNumber.put(mNumber, contact);
- }
- }
- }
-
- mContactLoadedListener.onContactLoaded(mNumber, contact);
- }
- }
-}
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/RendererHandler.java b/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/RendererHandler.java
deleted file mode 100644
index 6989a9c2d8..0000000000
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/RendererHandler.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.
- */
-package android.car.cluster.demorenderer;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Abstract {@link Handler} class that holds reference to a renderer object.
- */
-public abstract class RendererHandler<T> extends Handler {
-
- private final WeakReference<T> mRendererRef;
-
- RendererHandler(Looper looper, T renderer) {
- super(looper);
- mRendererRef = new WeakReference<>(renderer);
- }
-
- @Override
- public void handleMessage(Message msg) {
- T renderer = mRendererRef.get();
- if (renderer != null) {
- handleMessage(msg, renderer);
- }
- }
-
- public abstract void handleMessage(Message msg, T renderer);
-}
diff --git a/car-cluster-logging-renderer/AndroidManifest.xml b/car-cluster-logging-renderer/AndroidManifest.xml
index 20ee7d190d..780298889e 100644
--- a/car-cluster-logging-renderer/AndroidManifest.xml
+++ b/car-cluster-logging-renderer/AndroidManifest.xml
@@ -17,5 +17,10 @@
package="android.car.cluster.loggingrenderer"
android:versionCode="1"
android:versionName="1.0">
- <application android:label="@string/app_name" android:icon="@drawable/ic_launcher"/>
+ <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+ <service android:name=".LoggingClusterRenderingService"
+ android:exported="false"
+ android:permission="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"
+ />
+ </application>
</manifest>
diff --git a/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingInstrumentClusterRenderer.java b/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingClusterRenderingService.java
index 18757924fe..b2ac999e7b 100644
--- a/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingInstrumentClusterRenderer.java
+++ b/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/LoggingClusterRenderingService.java
@@ -15,37 +15,21 @@
*/
package android.car.cluster.loggingrenderer;
-import android.car.cluster.renderer.InstrumentClusterRenderer;
+import android.car.cluster.renderer.InstrumentClusterRenderingService;
import android.car.cluster.renderer.NavigationRenderer;
import android.car.navigation.CarNavigationInstrumentCluster;
-import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
/**
- * Dummy implementation of {@code InstrumentClusterRenderer} that just traces all interaction.
+ * Dummy implementation of {@link LoggingClusterRenderingService} to log all interaction.
*/
-public class LoggingInstrumentClusterRenderer extends InstrumentClusterRenderer {
+public class LoggingClusterRenderingService extends InstrumentClusterRenderingService {
- private final static String TAG = LoggingInstrumentClusterRenderer.class.getSimpleName();
+ private static final String TAG = LoggingClusterRenderingService.class.getSimpleName();
@Override
- public void onCreate(Context context) {
- Log.i(TAG, "onCreate, context: " + context);
- }
-
- @Override
- public void onStart() {
- Log.i(TAG, "onStart");
- }
-
- @Override
- public void onStop() {
- Log.i(TAG, "onStop");
- }
-
- @Override
- protected NavigationRenderer createNavigationRenderer() {
+ protected NavigationRenderer getNavigationRenderer() {
NavigationRenderer navigationRenderer = new NavigationRenderer() {
@Override
public CarNavigationInstrumentCluster getNavigationProperties() {
diff --git a/car-lib/api/current.txt b/car-lib/api/current.txt
index f7d48bd5fb..a0ffa8357d 100644
--- a/car-lib/api/current.txt
+++ b/car-lib/api/current.txt
@@ -9,7 +9,7 @@ package android.car {
method public java.lang.Object getCarManager(java.lang.String) throws android.car.CarNotConnectedException;
method public boolean isConnected();
method public boolean isConnecting();
- field public static final java.lang.String APP_CONTEXT_SERVICE = "app_context";
+ field public static final java.lang.String APP_FOCUS_SERVICE = "app_focus";
field public static final java.lang.String AUDIO_SERVICE = "audio";
field public static final int CONNECTION_TYPE_EMBEDDED = 5; // 0x5
field public static final java.lang.String INFO_SERVICE = "info";
@@ -19,6 +19,30 @@ package android.car {
field public static final java.lang.String PERMISSION_MILEAGE = "android.car.permission.CAR_MILEAGE";
field public static final java.lang.String PERMISSION_SPEED = "android.car.permission.CAR_SPEED";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
+ field public static final int VERSION = 1; // 0x1
+ }
+
+ public final class CarAppFocusManager {
+ method public void abandonAppFocus(android.car.CarAppFocusManager.AppFocusOwnershipChangeListener, int) throws android.car.CarNotConnectedException;
+ method public void abandonAppFocus(android.car.CarAppFocusManager.AppFocusOwnershipChangeListener) throws android.car.CarNotConnectedException;
+ method public int[] getActiveAppTypes() throws android.car.CarNotConnectedException;
+ method public boolean isOwningFocus(android.car.CarAppFocusManager.AppFocusOwnershipChangeListener, int) throws android.car.CarNotConnectedException;
+ method public void registerFocusListener(android.car.CarAppFocusManager.AppFocusChangeListener, int) throws android.car.CarNotConnectedException;
+ method public int requestAppFocus(android.car.CarAppFocusManager.AppFocusOwnershipChangeListener, int) throws android.car.CarNotConnectedException, java.lang.SecurityException;
+ method public void unregisterFocusListener(android.car.CarAppFocusManager.AppFocusChangeListener, int) throws android.car.CarNotConnectedException;
+ method public void unregisterFocusListener(android.car.CarAppFocusManager.AppFocusChangeListener) throws android.car.CarNotConnectedException;
+ field public static final int APP_FOCUS_REQUEST_FAILED = 0; // 0x0
+ field public static final int APP_FOCUS_REQUEST_GRANTED = 1; // 0x1
+ field public static final int APP_FOCUS_TYPE_NAVIGATION = 1; // 0x1
+ field public static final int APP_FOCUS_TYPE_VOICE_COMMAND = 2; // 0x2
+ }
+
+ public static abstract interface CarAppFocusManager.AppFocusChangeListener {
+ method public abstract void onAppFocusChange(int, boolean);
+ }
+
+ public static abstract interface CarAppFocusManager.AppFocusOwnershipChangeListener {
+ method public abstract void onAppFocusOwnershipLoss(int);
}
public class CarInfoManager {
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index b83b20614c..baa7fae570 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -9,7 +9,7 @@ package android.car {
method public java.lang.Object getCarManager(java.lang.String) throws android.car.CarNotConnectedException;
method public boolean isConnected();
method public boolean isConnecting();
- field public static final java.lang.String APP_CONTEXT_SERVICE = "app_context";
+ field public static final java.lang.String APP_FOCUS_SERVICE = "app_focus";
field public static final java.lang.String AUDIO_SERVICE = "audio";
field public static final java.lang.String CAMERA_SERVICE = "camera";
field public static final int CONNECTION_TYPE_EMBEDDED = 5; // 0x5
@@ -31,6 +31,30 @@ package android.car {
field public static final java.lang.String RADIO_SERVICE = "radio";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String TEST_SERVICE = "car-service-test";
+ field public static final int VERSION = 1; // 0x1
+ }
+
+ public final class CarAppFocusManager {
+ method public void abandonAppFocus(android.car.CarAppFocusManager.AppFocusOwnershipChangeListener, int) throws android.car.CarNotConnectedException;
+ method public void abandonAppFocus(android.car.CarAppFocusManager.AppFocusOwnershipChangeListener) throws android.car.CarNotConnectedException;
+ method public int[] getActiveAppTypes() throws android.car.CarNotConnectedException;
+ method public boolean isOwningFocus(android.car.CarAppFocusManager.AppFocusOwnershipChangeListener, int) throws android.car.CarNotConnectedException;
+ method public void registerFocusListener(android.car.CarAppFocusManager.AppFocusChangeListener, int) throws android.car.CarNotConnectedException;
+ method public int requestAppFocus(android.car.CarAppFocusManager.AppFocusOwnershipChangeListener, int) throws android.car.CarNotConnectedException, java.lang.SecurityException;
+ method public void unregisterFocusListener(android.car.CarAppFocusManager.AppFocusChangeListener, int) throws android.car.CarNotConnectedException;
+ method public void unregisterFocusListener(android.car.CarAppFocusManager.AppFocusChangeListener) throws android.car.CarNotConnectedException;
+ field public static final int APP_FOCUS_REQUEST_FAILED = 0; // 0x0
+ field public static final int APP_FOCUS_REQUEST_GRANTED = 1; // 0x1
+ field public static final int APP_FOCUS_TYPE_NAVIGATION = 1; // 0x1
+ field public static final int APP_FOCUS_TYPE_VOICE_COMMAND = 2; // 0x2
+ }
+
+ public static abstract interface CarAppFocusManager.AppFocusChangeListener {
+ method public abstract void onAppFocusChange(int, boolean);
+ }
+
+ public static abstract interface CarAppFocusManager.AppFocusOwnershipChangeListener {
+ method public abstract void onAppFocusOwnershipLoss(int);
}
public class CarInfoManager {
@@ -289,6 +313,13 @@ package android.car.cluster.renderer {
method public abstract void onStop();
}
+ public abstract class InstrumentClusterRenderingService extends android.app.Service {
+ ctor public InstrumentClusterRenderingService();
+ method protected abstract android.car.cluster.renderer.NavigationRenderer getNavigationRenderer();
+ method public android.os.IBinder onBind(android.content.Intent);
+ method protected void onKeyEvent(android.view.KeyEvent);
+ }
+
public abstract class NavigationRenderer {
ctor public NavigationRenderer();
method public abstract android.car.navigation.CarNavigationInstrumentCluster getNavigationProperties();
@@ -337,6 +368,7 @@ package android.car.content.pm {
public class CarPackageManager {
method public boolean isActivityAllowedWhileDriving(java.lang.String, java.lang.String) throws android.car.CarNotConnectedException;
+ method public boolean isActivityBackedBySafeActivity(android.content.ComponentName) throws android.car.CarNotConnectedException;
method public boolean isServiceAllowedWhileDriving(java.lang.String, java.lang.String) throws android.car.CarNotConnectedException;
method public void setAppBlockingPolicy(java.lang.String, android.car.content.pm.CarAppBlockingPolicy, int) throws android.car.CarNotConnectedException, java.lang.IllegalArgumentException, java.lang.SecurityException;
field public static final int FLAG_SET_POLICY_ADD = 2; // 0x2
@@ -596,8 +628,6 @@ package android.car.hardware.hvac {
method public void setFloatProperty(int, int, float) throws android.car.CarNotConnectedException;
method public void setIntProperty(int, int, int) throws android.car.CarNotConnectedException;
method public synchronized void unregisterListener(android.car.hardware.hvac.CarHvacManager.CarHvacEventListener) throws android.car.CarNotConnectedException;
- field public static final boolean DBG = true;
- field public static final java.lang.String TAG = "CarHvacManager";
}
public static abstract interface CarHvacManager.CarHvacEventListener {
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index dd0757450e..39dbb0b8e2 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -52,6 +52,11 @@ import java.util.HashMap;
*/
public class Car {
+ /**
+ * Represent the version of Car API.
+ */
+ public static final int VERSION = 1;
+
/** Service name for {@link CarSensorManager}, to be used in {@link #getCarManager(String)}. */
public static final String SENSOR_SERVICE = "sensor";
@@ -59,7 +64,7 @@ public class Car {
public static final String INFO_SERVICE = "info";
/** Service name for {@link CarAppContextManager}. */
- public static final String APP_CONTEXT_SERVICE = "app_context";
+ public static final String APP_FOCUS_SERVICE = "app_focus";
/** Service name for {@link CarPackageManager} */
public static final String PACKAGE_SERVICE = "package";
@@ -415,7 +420,7 @@ public class Car {
* @throws CarNotConnectedException
*/
public Object getCarManager(String serviceName) throws CarNotConnectedException {
- CarManagerBase manager = null;
+ CarManagerBase manager;
ICar service = getICarOrThrow();
synchronized (mCarManagerLock) {
manager = mServiceMap.get(serviceName);
@@ -431,7 +436,7 @@ public class Car {
if (manager == null) {
Log.w(CarLibLog.TAG_CAR,
"getCarManager could not create manager for service:" +
- serviceName);
+ serviceName);
return null;
}
mServiceMap.put(serviceName, manager);
@@ -484,14 +489,14 @@ public class Car {
case INFO_SERVICE:
manager = new CarInfoManager(binder);
break;
- case APP_CONTEXT_SERVICE:
- manager = new CarAppContextManager(binder, mLooper);
+ case APP_FOCUS_SERVICE:
+ manager = new CarAppFocusManager(binder, mLooper);
break;
case PACKAGE_SERVICE:
manager = new CarPackageManager(binder, mContext);
break;
case CAR_NAVIGATION_SERVICE:
- manager = new CarNavigationManager(binder, mLooper);
+ manager = new CarNavigationManager(binder);
break;
case CAMERA_SERVICE:
manager = new CarCameraManager(binder, mContext);
diff --git a/car-lib/src/android/car/CarAppContextManager.java b/car-lib/src/android/car/CarAppContextManager.java
deleted file mode 100644
index e73952ef2a..0000000000
--- a/car-lib/src/android/car/CarAppContextManager.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2015 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;
-
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-
-import java.lang.ref.WeakReference;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * CarAppContextManager allows applications to set and listen for the current application context
- * like active navigation or active voice command. Usually only one instance of such application
- * should run in the system, and other app setting the flag for the matching app should
- * lead into other app to stop.
- * @hide
- */
-public class CarAppContextManager implements CarManagerBase {
- /**
- * Listener to get notification for app getting information on app context change.
- */
- public interface AppContextChangeListener {
- /**
- * Application context has changed. Note that {@link CarAppContextManager} instance
- * causing the change will not get this notification.
- * @param activeContexts
- */
- void onAppContextChange(int activeContexts);
- }
-
- /**
- * Listener to get notification for app getting information on app context ownership loss.
- */
- public interface AppContextOwnershipChangeListener {
- /**
- * Lost ownership for the context, which happens when other app has set the context.
- * The app losing context should stop the action associated with the context.
- * For example, navigation app currently running active navigation should stop navigation
- * upon getting this for {@link CarAppContextManager#APP_CONTEXT_NAVIGATION}.
- * @param context
- */
- void onAppContextOwnershipLoss(int context);
- }
-
- /** @hide */
- public static final int APP_CONTEXT_START_FLAG = 0x1;
- /**
- * Flag for active navigation.
- */
- public static final int APP_CONTEXT_NAVIGATION = 0x1;
- /**
- * Flag for active voice command.
- */
- public static final int APP_CONTEXT_VOICE_COMMAND = 0x2;
- /**
- * Update this after adding a new flag.
- * @hide
- */
- public static final int APP_CONTEXT_END_FLAG = 0x2;
-
- private final IAppContext mService;
- private final Handler mHandler;
- private final IAppContextListenerImpl mBinderListener;
- private final Map<Integer, AppContextOwnershipChangeListener> mOwnershipListeners;
-
- private AppContextChangeListener mListener;
- private int mContextFilter;
-
- /**
- * @hide
- */
- CarAppContextManager(IBinder service, Looper looper) {
- mService = IAppContext.Stub.asInterface(service);
- mHandler = new Handler(looper);
- mBinderListener = new IAppContextListenerImpl(this);
- mOwnershipListeners = new HashMap<>();
- }
-
- /**
- * Register listener to monitor app context change. Only one listener can be registered and
- * registering multiple times will lead into only the last listener to be active.
- * @param listener
- * @param contextFilter Flags of cotexts to get notification.
- * @throws CarNotConnectedException
- */
- public void registerContextListener(AppContextChangeListener listener, int contextFilter)
- throws CarNotConnectedException {
- if (listener == null) {
- throw new IllegalArgumentException("null listener");
- }
- synchronized(this) {
- if (mListener == null || mContextFilter != contextFilter) {
- try {
- mService.registerContextListener(mBinderListener, contextFilter);
- } catch (RemoteException e) {
- throw new CarNotConnectedException(e);
- }
- }
- mListener = listener;
- mContextFilter = contextFilter;
- }
- }
-
- /**
- * Unregister listener and stop listening context change events. If app has owned a context
- * by {@link #setActiveContext(int)}, it will be reset to inactive state.
- * @throws CarNotConnectedException
- */
- public void unregisterContextListener() throws CarNotConnectedException {
- synchronized(this) {
- try {
- mService.unregisterContextListener(mBinderListener);
- } catch (RemoteException e) {
- throw new CarNotConnectedException(e);
- }
- mListener = null;
- mContextFilter = 0;
- }
- }
-
- public int getActiveAppContexts() throws CarNotConnectedException {
- try {
- return mService.getActiveAppContexts();
- } catch (RemoteException e) {
- throw new CarNotConnectedException(e);
- }
- }
-
- public boolean isOwningContext(int context) throws CarNotConnectedException {
- try {
- return mService.isOwningContext(mBinderListener, context);
- } catch (RemoteException e) {
- throw new CarNotConnectedException(e);
- }
- }
-
- /**
- * Set the given contexts as active. By setting this, the application is becoming owner
- * of the context, and will get
- * {@link AppContextOwnershipChangeListener#onAppContextOwnershipLoss(int)}
- * if ownership is given to other app by calling this. Fore-ground app will have higher priority
- * and other app cannot set the same context while owner is in fore-ground.
- * Only one listener per context can be registered and
- * registering multiple times will lead into only the last listener to be active.
- * @param ownershipListener
- * @param contexts
- * @throws CarNotConnectedException
- * @throws SecurityException If owner cannot be changed.
- */
- public void setActiveContexts(AppContextOwnershipChangeListener ownershipListener, int contexts)
- throws SecurityException, CarNotConnectedException {
- if (ownershipListener == null) {
- throw new IllegalArgumentException("null listener");
- }
- synchronized (this) {
- try {
- mService.setActiveContexts(mBinderListener, contexts);
- } catch (RemoteException e) {
- throw new CarNotConnectedException(e);
- }
- for (int flag = APP_CONTEXT_START_FLAG; flag <= APP_CONTEXT_END_FLAG; flag <<= 1) {
- if ((flag & contexts) != 0) {
- mOwnershipListeners.put(flag, ownershipListener);
- }
- }
- }
- }
-
- /**
- * Reset the given contexts, i.e. mark them as inactive. This also involves releasing ownership
- * for the context.
- * @param contexts
- * @throws CarNotConnectedException
- */
- public void resetActiveContexts(int contexts) throws CarNotConnectedException {
- try {
- mService.resetActiveContexts(mBinderListener, contexts);
- } catch (RemoteException e) {
- throw new CarNotConnectedException(e);
- }
- synchronized (this) {
- for (int flag = APP_CONTEXT_START_FLAG; flag <= APP_CONTEXT_END_FLAG; flag <<= 1) {
- if ((flag & contexts) != 0) {
- mOwnershipListeners.remove(flag);
- }
- }
- }
- }
-
- @Override
- public void onCarDisconnected() {
- // nothing to do
- }
-
- private void handleAppContextChange(int activeContexts) {
- AppContextChangeListener listener;
- int newContext;
- synchronized (this) {
- if (mListener == null) {
- return;
- }
- listener = mListener;
- newContext = activeContexts & mContextFilter;
- }
- listener.onAppContextChange(newContext);
- }
-
- private void handleAppContextOwnershipLoss(int context) {
- AppContextOwnershipChangeListener listener;
- synchronized (this) {
- listener = mOwnershipListeners.get(context);
- if (listener == null) {
- return;
- }
- }
- listener.onAppContextOwnershipLoss(context);
- }
-
- private static class IAppContextListenerImpl extends IAppContextListener.Stub {
-
- private final WeakReference<CarAppContextManager> mManager;
-
- private IAppContextListenerImpl(CarAppContextManager manager) {
- mManager = new WeakReference<>(manager);
- }
-
- @Override
- public void onAppContextChange(final int activeContexts) {
- final CarAppContextManager manager = mManager.get();
- if (manager == null) {
- return;
- }
- manager.mHandler.post(new Runnable() {
- @Override
- public void run() {
- manager.handleAppContextChange(activeContexts);
- }
- });
- }
-
- @Override
- public void onAppContextOwnershipLoss(final int context) {
- final CarAppContextManager manager = mManager.get();
- if (manager == null) {
- return;
- }
- manager.mHandler.post(new Runnable() {
- @Override
- public void run() {
- manager.handleAppContextOwnershipLoss(context);
- }
- });
- }
- }
-}
diff --git a/car-lib/src/android/car/CarAppFocusManager.java b/car-lib/src/android/car/CarAppFocusManager.java
new file mode 100644
index 0000000000..8697dd2a46
--- /dev/null
+++ b/car-lib/src/android/car/CarAppFocusManager.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2015 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;
+
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * CarAppFocusManager allows applications to set and listen for the current application focus
+ * like active navigation or active voice command. Usually only one instance of such application
+ * should run in the system, and other app setting the flag for the matching app should
+ * lead into other app to stop.
+ */
+public final class CarAppFocusManager implements CarManagerBase {
+ /**
+ * Listener to get notification for app getting information on application type status changes.
+ */
+ public interface AppFocusChangeListener {
+ /**
+ * Application focus has changed. Note that {@link CarAppFocusManager} instance
+ * causing the change will not get this notification.
+ * @param appType
+ * @param active
+ */
+ void onAppFocusChange(int appType, boolean active);
+ }
+
+ /**
+ * Listener to get notification for app getting information on app type ownership loss.
+ */
+ public interface AppFocusOwnershipChangeListener {
+ /**
+ * Lost ownership for the focus, which happens when other app has set the focus.
+ * The app losing focus should stop the action associated with the focus.
+ * For example, navigation app currently running active navigation should stop navigation
+ * upon getting this for {@link CarAppFocusManager#APP_FOCUS_TYPE_NAVIGATION}.
+ * @param appType
+ */
+ void onAppFocusOwnershipLoss(int appType);
+ }
+
+ /**
+ * Represents navigation focus.
+ */
+ public static final int APP_FOCUS_TYPE_NAVIGATION = 1;
+ /**
+ * Represents voice command focus.
+ */
+ public static final int APP_FOCUS_TYPE_VOICE_COMMAND = 2;
+ /**
+ * Update this after adding a new app type.
+ * @hide
+ */
+ public static final int APP_FOCUS_MAX = 2;
+
+ /**
+ * A failed focus change request.
+ */
+ public static final int APP_FOCUS_REQUEST_FAILED = 0;
+ /**
+ * A successful focus change request.
+ */
+ public static final int APP_FOCUS_REQUEST_GRANTED = 1;
+
+ private final IAppFocus mService;
+ private final Handler mHandler;
+ private final Map<AppFocusChangeListener, IAppFocusListenerImpl> mChangeBinders =
+ new HashMap<>();
+ private final Map<AppFocusOwnershipChangeListener, IAppFocusOwnershipListenerImpl>
+ mOwnershipBinders = new HashMap<>();
+
+ /**
+ * @hide
+ */
+ CarAppFocusManager(IBinder service, Looper looper) {
+ mService = IAppFocus.Stub.asInterface(service);
+ mHandler = new Handler(looper);
+ }
+
+ /**
+ * Register listener to monitor app focus change.
+ * @param listener
+ * @param appType Applitcaion type to get notification for.
+ * @throws CarNotConnectedException
+ */
+ public void registerFocusListener(AppFocusChangeListener listener, int appType)
+ throws CarNotConnectedException {
+ if (listener == null) {
+ throw new IllegalArgumentException("null listener");
+ }
+ IAppFocusListenerImpl binder;
+ synchronized (this) {
+ binder = mChangeBinders.get(listener);
+ if (binder == null) {
+ binder = new IAppFocusListenerImpl(this, listener);
+ mChangeBinders.put(listener, binder);
+ }
+ binder.addAppType(appType);
+ }
+ try {
+ mService.registerFocusListener(binder, appType);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Unregister listener for application type and stop listening focus change events.
+ * @param listener
+ * @param appType
+ * @throws CarNotConnectedException
+ */
+ public void unregisterFocusListener(AppFocusChangeListener listener, int appType)
+ throws CarNotConnectedException {
+ IAppFocusListenerImpl binder;
+ synchronized (this) {
+ binder = mChangeBinders.get(listener);
+ if (binder == null) {
+ return;
+ }
+ }
+ try {
+ mService.unregisterFocusListener(binder, appType);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ synchronized (this) {
+ binder.removeAppType(appType);
+ if (!binder.hasAppTypes()) {
+ mChangeBinders.remove(listener);
+ }
+
+ }
+ }
+
+ /**
+ * Unregister listener and stop listening focus change events.
+ * @param listener
+ * @throws CarNotConnectedException
+ */
+ public void unregisterFocusListener(AppFocusChangeListener listener)
+ throws CarNotConnectedException {
+ IAppFocusListenerImpl binder;
+ synchronized (this) {
+ binder = mChangeBinders.remove(listener);
+ if (binder == null) {
+ return;
+ }
+ }
+ try {
+ for (Integer appType : binder.getAppTypes()) {
+ mService.unregisterFocusListener(binder, appType);
+ }
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Returns application types currently active in the system.
+ * @throws CarNotConnectedException
+ */
+ public int[] getActiveAppTypes() throws CarNotConnectedException {
+ try {
+ return mService.getActiveAppTypes();
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Checks if listener is associated with active a focus
+ * @param listener
+ * @param focus
+ * @throws CarNotConnectedException
+ */
+ public boolean isOwningFocus(AppFocusOwnershipChangeListener listener, int appType)
+ throws CarNotConnectedException {
+ IAppFocusOwnershipListenerImpl binder;
+ synchronized (this) {
+ binder = mOwnershipBinders.get(listener);
+ if (binder == null) {
+ return false;
+ }
+ }
+ try {
+ return mService.isOwningFocus(binder, appType);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Requests application focus.
+ * By requesting this, the application is becoming owner of the focus, and will get
+ * {@link AppFocusOwnershipChangeListener#onAppFocusOwnershipLoss(int)}
+ * if ownership is given to other app by calling this. Fore-ground app will have higher priority
+ * and other app cannot set the same focus while owner is in fore-ground.
+ * @param ownershipListener
+ * @param appType
+ * @return {@link #APP_FOCUS_REQUEST_FAILED} or {@link #APP_FOCUS_REQUEST_GRANTED}
+ * @throws CarNotConnectedException
+ * @throws SecurityException If owner cannot be changed.
+ */
+ public int requestAppFocus(AppFocusOwnershipChangeListener ownershipListener, int appType)
+ throws SecurityException, CarNotConnectedException {
+ if (ownershipListener == null) {
+ throw new IllegalArgumentException("null listener");
+ }
+ IAppFocusOwnershipListenerImpl binder;
+ synchronized (this) {
+ binder = mOwnershipBinders.get(ownershipListener);
+ if (binder == null) {
+ binder = new IAppFocusOwnershipListenerImpl(this, ownershipListener);
+ mOwnershipBinders.put(ownershipListener, binder);
+ }
+ binder.addAppType(appType);
+ }
+ try {
+ return mService.requestAppFocus(binder, appType);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Abandon the given focus, i.e. mark it as inactive. This also involves releasing ownership
+ * for the focus.
+ * @param ownershipListener
+ * @param appType
+ * @throws CarNotConnectedException
+ */
+ public void abandonAppFocus(AppFocusOwnershipChangeListener ownershipListener, int appType)
+ throws CarNotConnectedException {
+ if (ownershipListener == null) {
+ throw new IllegalArgumentException("null listener");
+ }
+ IAppFocusOwnershipListenerImpl binder;
+ synchronized (this) {
+ binder = mOwnershipBinders.get(ownershipListener);
+ if (binder == null) {
+ return;
+ }
+ }
+ try {
+ mService.abandonAppFocus(binder, appType);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ synchronized (this) {
+ binder.removeAppType(appType);
+ if (!binder.hasAppTypes()) {
+ mOwnershipBinders.remove(ownershipListener);
+ }
+ }
+ }
+
+ /**
+ * Abandon all focuses, i.e. mark them as inactive. This also involves releasing ownership
+ * for the focus.
+ * @param ownershipListener
+ * @throws CarNotConnectedException
+ */
+ public void abandonAppFocus(AppFocusOwnershipChangeListener ownershipListener)
+ throws CarNotConnectedException {
+ IAppFocusOwnershipListenerImpl binder;
+ synchronized (this) {
+ binder = mOwnershipBinders.remove(ownershipListener);
+ if (binder == null) {
+ return;
+ }
+ }
+ try {
+ for (Integer appType : binder.getAppTypes()) {
+ mService.abandonAppFocus(binder, appType);
+ }
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /** @hide */
+ @Override
+ public void onCarDisconnected() {
+ // nothing to do
+ }
+
+ private static class IAppFocusListenerImpl extends IAppFocusListener.Stub {
+
+ private final WeakReference<CarAppFocusManager> mManager;
+ private final WeakReference<AppFocusChangeListener> mListener;
+ private final Set<Integer> mAppTypes = new HashSet<>();
+
+ private IAppFocusListenerImpl(CarAppFocusManager manager, AppFocusChangeListener listener) {
+ mManager = new WeakReference<>(manager);
+ mListener = new WeakReference<>(listener);
+ }
+
+ public void addAppType(int appType) {
+ mAppTypes.add(appType);
+ }
+
+ public void removeAppType(int appType) {
+ mAppTypes.remove(appType);
+ }
+
+ public Set<Integer> getAppTypes() {
+ return mAppTypes;
+ }
+
+ public boolean hasAppTypes() {
+ return !mAppTypes.isEmpty();
+ }
+
+ @Override
+ public void onAppFocusChange(final int appType, final boolean active) {
+ final CarAppFocusManager manager = mManager.get();
+ final AppFocusChangeListener listener = mListener.get();
+ if (manager == null || listener == null) {
+ return;
+ }
+ manager.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ listener.onAppFocusChange(appType, active);
+ }
+ });
+ }
+ }
+
+ private static class IAppFocusOwnershipListenerImpl extends IAppFocusOwnershipListener.Stub {
+
+ private final WeakReference<CarAppFocusManager> mManager;
+ private final WeakReference<AppFocusOwnershipChangeListener> mListener;
+ private final Set<Integer> mAppTypes = new HashSet<>();
+
+ private IAppFocusOwnershipListenerImpl(CarAppFocusManager manager,
+ AppFocusOwnershipChangeListener listener) {
+ mManager = new WeakReference<>(manager);
+ mListener = new WeakReference<>(listener);
+ }
+
+ public void addAppType(int appType) {
+ mAppTypes.add(appType);
+ }
+
+ public void removeAppType(int appType) {
+ mAppTypes.remove(appType);
+ }
+
+ public Set<Integer> getAppTypes() {
+ return mAppTypes;
+ }
+
+ public boolean hasAppTypes() {
+ return !mAppTypes.isEmpty();
+ }
+
+ @Override
+ public void onAppFocusOwnershipLoss(final int appType) {
+ final CarAppFocusManager manager = mManager.get();
+ final AppFocusOwnershipChangeListener listener = mListener.get();
+ if (manager == null || listener == null) {
+ return;
+ }
+ manager.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ listener.onAppFocusOwnershipLoss(appType);
+ }
+ });
+ }
+ }
+}
diff --git a/car-lib/src/android/car/CarLibLog.java b/car-lib/src/android/car/CarLibLog.java
index 4817a36e6a..0a78216923 100644
--- a/car-lib/src/android/car/CarLibLog.java
+++ b/car-lib/src/android/car/CarLibLog.java
@@ -21,4 +21,5 @@ public class CarLibLog {
public static final String TAG_CAR = "CAR.L";
public static final String TAG_SENSOR = TAG_CAR + ".SENSOR";
public static final String TAG_NAV = TAG_CAR + ".NAV";
+ public static final String TAG_CLUSTER = TAG_CAR + ".CLUSTER";
}
diff --git a/car-lib/src/android/car/IAppContext.aidl b/car-lib/src/android/car/IAppFocus.aidl
index 5f63c9b58f..1888c246ec 100644
--- a/car-lib/src/android/car/IAppContext.aidl
+++ b/car-lib/src/android/car/IAppFocus.aidl
@@ -16,17 +16,18 @@
package android.car;
-import android.car.IAppContextListener;
+import android.car.IAppFocusListener;
+import android.car.IAppFocusOwnershipListener;
/** @hide */
-interface IAppContext {
- void registerContextListener(IAppContextListener listener, int filter) = 0;
- void unregisterContextListener(IAppContextListener listener) = 1;
- int getActiveAppContexts() = 2;
+interface IAppFocus {
+ void registerFocusListener(IAppFocusListener listener, int appType) = 0;
+ void unregisterFocusListener(IAppFocusListener listener, int appType) = 1;
+ int[] getActiveAppTypes() = 2;
/** listener used as a token */
- boolean isOwningContext(IAppContextListener listener, int context) = 3;
+ boolean isOwningFocus(IAppFocusOwnershipListener listener, int appType) = 3;
/** listener used as a token */
- void setActiveContexts(IAppContextListener listener, int contexts) = 4;
+ int requestAppFocus(IAppFocusOwnershipListener listener, int appType) = 4;
/** listener used as a token */
- void resetActiveContexts(IAppContextListener listener, int contexts) = 5;
+ void abandonAppFocus(IAppFocusOwnershipListener listener, int appType) = 5;
}
diff --git a/car-lib/src/android/car/hardware/hvac/CarHvacEvent.aidl b/car-lib/src/android/car/IAppFocusListener.aidl
index 801d0d33df..b8db6ff025 100644
--- a/car-lib/src/android/car/hardware/hvac/CarHvacEvent.aidl
+++ b/car-lib/src/android/car/IAppFocusListener.aidl
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-package android.car.hardware.hvac;
-
-parcelable CarHvacEvent;
+package android.car;
+/** @hide */
+oneway interface IAppFocusListener {
+ void onAppFocusChange(int appType, boolean active) = 0;
+}
diff --git a/car-lib/src/android/car/IAppContextListener.aidl b/car-lib/src/android/car/IAppFocusOwnershipListener.aidl
index 0fdf103228..6ee27232fe 100644
--- a/car-lib/src/android/car/IAppContextListener.aidl
+++ b/car-lib/src/android/car/IAppFocusOwnershipListener.aidl
@@ -17,7 +17,6 @@
package android.car;
/** @hide */
-oneway interface IAppContextListener {
- void onAppContextChange(int activeContexts) = 0;
- void onAppContextOwnershipLoss(int context) = 1;
+oneway interface IAppFocusOwnershipListener {
+ void onAppFocusOwnershipLoss(int appType) = 0;
}
diff --git a/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl b/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
new file mode 100644
index 0000000000..9fb4b56587
--- /dev/null
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentCluster.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+package android.car.cluster.renderer;
+
+import android.car.cluster.renderer.IInstrumentClusterNavigation;
+import android.view.KeyEvent;
+
+/**
+ * Binder API for Instrument Cluster.
+ *
+ * @hide
+ */
+interface IInstrumentCluster {
+ /** Returns {@link IInstrumentClusterNavigation} that will be passed to the Nav app */
+ IInstrumentClusterNavigation getNavigationService();
+
+ /** Supplies Instrument Cluster Renderer with current owner of Navigation app context */
+ void setNavigationContextOwner(int uid, int pid);
+
+ /** Called when key event that was addressed to instrument cluster display has been received. */
+ void onKeyEvent(in KeyEvent keyEvent);
+}
diff --git a/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/InstrumentClusterRendererFactory.java b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterNavigation.aidl
index d73ede440e..fcd453239a 100644
--- a/car-cluster-logging-renderer/src/android/car/cluster/loggingrenderer/InstrumentClusterRendererFactory.java
+++ b/car-lib/src/android/car/cluster/renderer/IInstrumentClusterNavigation.aidl
@@ -13,17 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.car.cluster.loggingrenderer;
+package android.car.cluster.renderer;
-import android.car.cluster.renderer.InstrumentClusterRenderer;
+import android.graphics.Bitmap;
+import android.car.navigation.CarNavigationInstrumentCluster;
/**
- * This factory class will be requested by android.car using reflection in order to get an instance
- * of {@link InstrumentClusterRenderer}.
+ * Binder API for Instrument Cluster Navigation.
+ *
+ * @hide
*/
-@SuppressWarnings("unused") /* Used by reflection. */
-public class InstrumentClusterRendererFactory {
- public static InstrumentClusterRenderer createRenderer() {
- return new LoggingInstrumentClusterRenderer();
- }
+interface IInstrumentClusterNavigation {
+ void onStartNavigation();
+ void onStopNavigation();
+ void onNextManeuverChanged(
+ int event, String road, int turnAngle, int turnNumber, in Bitmap image, int turnSide);
+ void onNextManeuverDistanceChanged(int distanceMeters, int timeSeconds);
+ CarNavigationInstrumentCluster getInstrumentClusterInfo();
}
diff --git a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
new file mode 100644
index 0000000000..137e6fc6c4
--- /dev/null
+++ b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
@@ -0,0 +1,225 @@
+/*
+ * 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.
+ */
+package android.car.cluster.renderer;
+
+import android.annotation.CallSuper;
+import android.annotation.MainThread;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.car.CarLibLog;
+import android.car.navigation.CarNavigationInstrumentCluster;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pair;
+import android.view.KeyEvent;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+
+/**
+ * A service that used for interaction between Car Service and Instrument Cluster. Car Service may
+ * provide internal navigation binder interface to Navigation App and all notifications will be
+ * eventually land in the {@link NavigationRenderer} returned by {@link #getNavigationRenderer()}.
+ *
+ * <p>To extend this class, you must declare the service in your manifest file with
+ * the {@code android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE} permission
+ * <pre>
+ * &lt;service android:name=".MyInstrumentClusterService"
+ * android:permission="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE">
+ * &lt;/service></pre>
+ * <p>Also, you will need to register this service in the following configuration file:
+ * {@code packages/services/Car/service/res/values/config.xml}
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class InstrumentClusterRenderingService extends Service {
+
+ private static final String TAG = CarLibLog.TAG_CLUSTER;
+
+ private RendererBinder mRendererBinder;
+
+ @Override
+ @CallSuper
+ public IBinder onBind(Intent intent) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onBind, intent: " + intent);
+ }
+
+ if (mRendererBinder == null) {
+ mRendererBinder = new RendererBinder(getNavigationRenderer());
+ }
+
+ return mRendererBinder;
+ }
+
+ /** Returns {@link NavigationRenderer} or null if it's not supported. */
+ @MainThread
+ protected abstract NavigationRenderer getNavigationRenderer();
+
+ /** Called when key event that was addressed to instrument cluster display has been received. */
+ @MainThread
+ protected void onKeyEvent(KeyEvent keyEvent) {
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ writer.println("**" + getClass().getSimpleName() + "**");
+ writer.println("renderer binder: " + mRendererBinder);
+ if (mRendererBinder != null) {
+ writer.println("navigation renderer: " + mRendererBinder.mNavigationRenderer);
+ String owner = "none";
+ if (mRendererBinder.mNavContextOwner != null) {
+ owner = "[uid: " + mRendererBinder.mNavContextOwner.first
+ + ", pid: " + mRendererBinder.mNavContextOwner.second + "]";
+ }
+ writer.println("navigation focus owner: " + owner);
+ }
+ }
+
+ private class RendererBinder extends IInstrumentCluster.Stub {
+
+ private final NavigationRenderer mNavigationRenderer;
+ private final UiHandler mUiHandler;
+
+ private volatile NavigationBinder mNavigationBinder;
+ private volatile Pair<Integer, Integer> mNavContextOwner;
+
+ RendererBinder(NavigationRenderer navigationRenderer) {
+ mNavigationRenderer = navigationRenderer;
+ mUiHandler = new UiHandler(InstrumentClusterRenderingService.this);
+ }
+
+ @Override
+ public IInstrumentClusterNavigation getNavigationService() throws RemoteException {
+ if (mNavigationBinder == null) {
+ mNavigationBinder = new NavigationBinder(mNavigationRenderer);
+ if (mNavContextOwner != null) {
+ mNavigationBinder.setNavigationContextOwner(
+ mNavContextOwner.first, mNavContextOwner.second);
+ }
+ }
+ return mNavigationBinder;
+ }
+
+ @Override
+ public void setNavigationContextOwner(int uid, int pid) throws RemoteException {
+ mNavContextOwner = new Pair<>(uid, pid);
+ if (mNavigationBinder != null) {
+ mNavigationBinder.setNavigationContextOwner(uid, pid);
+ }
+ }
+
+ @Override
+ public void onKeyEvent(KeyEvent keyEvent) throws RemoteException {
+ mUiHandler.doKeyEvent(keyEvent);
+ }
+ }
+
+ private class NavigationBinder extends IInstrumentClusterNavigation.Stub {
+
+ private final NavigationRenderer mNavigationRenderer; // Thread-safe navigation renderer.
+
+ private volatile Pair<Integer, Integer> mNavContextOwner;
+
+ NavigationBinder(NavigationRenderer navigationRenderer) {
+ mNavigationRenderer = ThreadSafeNavigationRenderer.createFor(
+ Looper.getMainLooper(),
+ navigationRenderer);
+ }
+
+ void setNavigationContextOwner(int uid, int pid) {
+ mNavContextOwner = new Pair<>(uid, pid);
+ }
+
+ @Override
+ public void onStartNavigation() throws RemoteException {
+ assertContextOwnership();
+ mNavigationRenderer.onStartNavigation();
+ }
+
+ @Override
+ public void onStopNavigation() throws RemoteException {
+ assertContextOwnership();
+ mNavigationRenderer.onStopNavigation();
+ }
+
+ @Override
+ public void onNextManeuverChanged(int event, String road, int turnAngle, int turnNumber,
+ Bitmap image, int turnSide) throws RemoteException {
+ assertContextOwnership();
+ mNavigationRenderer.onNextTurnChanged(event, road, turnAngle, turnNumber,
+ image, turnSide);
+ }
+
+ @Override
+ public void onNextManeuverDistanceChanged(int distanceMeters, int timeSeconds)
+ throws RemoteException {
+ assertContextOwnership();
+ mNavigationRenderer.onNextTurnDistanceChanged(distanceMeters, timeSeconds);
+ }
+
+ @Override
+ public CarNavigationInstrumentCluster getInstrumentClusterInfo() throws RemoteException {
+ return mNavigationRenderer.getNavigationProperties();
+ }
+
+ private void assertContextOwnership() {
+ int uid = getCallingUid();
+ int pid = getCallingPid();
+
+ Pair<Integer, Integer> owner = mNavContextOwner;
+ if (owner == null || owner.first != uid || owner.second != pid) {
+ throw new IllegalStateException("Client (uid:" + uid + ", pid: " + pid + ") is"
+ + "not an owner of APP_CONTEXT_NAVIGATION");
+ }
+ }
+ }
+
+ private static class UiHandler extends Handler {
+ private static int KEY_EVENT = 0;
+ private final WeakReference<InstrumentClusterRenderingService> mRefService;
+
+ UiHandler(InstrumentClusterRenderingService service) {
+ mRefService = new WeakReference<>(service);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ InstrumentClusterRenderingService service = mRefService.get();
+ if (service == null) {
+ return;
+ }
+
+ if (msg.what == KEY_EVENT) {
+ service.onKeyEvent((KeyEvent) msg.obj);
+ } else {
+ throw new IllegalArgumentException("Unexpected message: " + msg);
+ }
+ }
+
+ void doKeyEvent(KeyEvent event) {
+ sendMessage(obtainMessage(KEY_EVENT, event));
+ }
+ }
+}
diff --git a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/ThreadSafeNavigationRenderer.java b/car-lib/src/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java
index e9327d8f65..1fcdbc0eb6 100644
--- a/car-cluster-demo-renderer/src/android/car/cluster/demorenderer/ThreadSafeNavigationRenderer.java
+++ b/car-lib/src/android/car/cluster/renderer/ThreadSafeNavigationRenderer.java
@@ -13,23 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.car.cluster.demorenderer;
+package android.car.cluster.renderer;
import android.annotation.Nullable;
-import android.car.cluster.renderer.NavigationRenderer;
import android.car.navigation.CarNavigationInstrumentCluster;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import java.lang.ref.WeakReference;
import java.util.concurrent.CountDownLatch;
/**
* A wrapper over {@link NavigationRenderer} that runs all its methods in the context of provided
* looper. It is guaranteed that all calls will be invoked in order they were called.
*/
-public class ThreadSafeNavigationRenderer extends NavigationRenderer {
+/* package */ class ThreadSafeNavigationRenderer extends NavigationRenderer {
private final Handler mHandler;
private final NavigationRenderer mRenderer;
@@ -41,7 +41,7 @@ public class ThreadSafeNavigationRenderer extends NavigationRenderer {
/** Creates thread-safe {@link NavigationRenderer}. Returns null if renderer == null */
@Nullable
- public static NavigationRenderer createFor(Looper looper, NavigationRenderer renderer) {
+ static NavigationRenderer createFor(Looper looper, NavigationRenderer renderer) {
return renderer == null ? null : new ThreadSafeNavigationRenderer(looper, renderer);
}
@@ -52,12 +52,17 @@ public class ThreadSafeNavigationRenderer extends NavigationRenderer {
@Override
public CarNavigationInstrumentCluster getNavigationProperties() {
- return runAndWaitResult(mHandler, new RunnableWithResult<CarNavigationInstrumentCluster>() {
- @Override
- protected CarNavigationInstrumentCluster createResult() {
- return mRenderer.getNavigationProperties();
- }
- });
+ if (mHandler.getLooper() == Looper.myLooper()) {
+ return mRenderer.getNavigationProperties();
+ } else {
+ return runAndWaitResult(mHandler,
+ new RunnableWithResult<CarNavigationInstrumentCluster>() {
+ @Override
+ protected CarNavigationInstrumentCluster createResult() {
+ return mRenderer.getNavigationProperties();
+ }
+ });
+ }
}
@Override
@@ -114,13 +119,13 @@ public class ThreadSafeNavigationRenderer extends NavigationRenderer {
}
private static <E> E runAndWaitResult(Handler handler, RunnableWithResult<E> runnable) {
- CountDownLatch latch = new CountDownLatch(1);
+ final CountDownLatch latch = new CountDownLatch(1);
handler.post(() -> {
runnable.run();
latch.countDown();
});
try {
- latch.wait();
+ latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
@@ -146,7 +151,7 @@ public class ThreadSafeNavigationRenderer extends NavigationRenderer {
}
}
- public abstract class RunnableWithResult<T> implements Runnable {
+ private static abstract class RunnableWithResult<T> implements Runnable {
private volatile T result;
protected abstract T createResult();
@@ -160,4 +165,24 @@ public class ThreadSafeNavigationRenderer extends NavigationRenderer {
return result;
}
}
+
+ private static abstract class RendererHandler<T> extends Handler {
+
+ private final WeakReference<T> mRendererRef;
+
+ RendererHandler(Looper looper, T renderer) {
+ super(looper);
+ mRendererRef = new WeakReference<>(renderer);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ T renderer = mRendererRef.get();
+ if (renderer != null) {
+ handleMessage(msg, renderer);
+ }
+ }
+
+ public abstract void handleMessage(Message msg, T renderer);
+ }
}
diff --git a/car-lib/src/android/car/content/pm/CarPackageManager.java b/car-lib/src/android/car/content/pm/CarPackageManager.java
index 1cdbfc7a27..ffef31e3e1 100644
--- a/car-lib/src/android/car/content/pm/CarPackageManager.java
+++ b/car-lib/src/android/car/content/pm/CarPackageManager.java
@@ -21,6 +21,7 @@ import android.annotation.SystemApi;
import android.car.CarApiUtil;
import android.car.CarManagerBase;
import android.car.CarNotConnectedException;
+import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
import android.os.Looper;
@@ -122,13 +123,40 @@ public class CarPackageManager implements CarManagerBase {
}
/**
+ * Check if finishing Activity will lead into safe Activity (=allowed Activity) to be shown.
+ * This can be used by unsafe activity blocking Activity to check if finishing itself can
+ * lead into being launched again due to unsafe activity shown. Note that checking this does not
+ * guarantee that blocking will not be done as driving state can change after this call is made.
+ *
+ * @param activityName
+ * @return true if there is a safe Activity (or car is stopped) in the back of task stack
+ * so that finishing the Activity will not trigger another Activity blocking. If
+ * the given Activity is not in foreground, then it will return true as well as
+ * finishing the Activity will not make any difference.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isActivityBackedBySafeActivity(ComponentName activityName)
+ throws CarNotConnectedException {
+ try {
+ return mService.isActivityBackedBySafeActivity(activityName);
+ } catch (IllegalStateException e) {
+ CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
+ } catch (RemoteException e) {
+ //ignore as CarApi will handle disconnection anyway.
+ }
+ return true;
+ }
+
+ /**
* Check if given activity is allowed while driving.
* @param packageName
* @param className
* @return
*/
public boolean isActivityAllowedWhileDriving(String packageName, String className)
- throws CarNotConnectedException{
+ throws CarNotConnectedException {
try {
return mService.isActivityAllowedWhileDriving(packageName, className);
} catch (IllegalStateException e) {
diff --git a/car-lib/src/android/car/content/pm/ICarPackageManager.aidl b/car-lib/src/android/car/content/pm/ICarPackageManager.aidl
index d451187f2a..306db6567c 100644
--- a/car-lib/src/android/car/content/pm/ICarPackageManager.aidl
+++ b/car-lib/src/android/car/content/pm/ICarPackageManager.aidl
@@ -17,10 +17,12 @@
package android.car.content.pm;
import android.car.content.pm.CarAppBlockingPolicy;
+import android.content.ComponentName;
/** @hide */
interface ICarPackageManager {
void setAppBlockingPolicy(in String packageName, in CarAppBlockingPolicy policy, int flags) = 0;
boolean isActivityAllowedWhileDriving(in String packageName, in String className) = 1;
boolean isServiceAllowedWhileDriving(in String packageName, in String className) = 2;
+ boolean isActivityBackedBySafeActivity(in ComponentName activityName) = 3;
}
diff --git a/car-lib/src/android/car/hardware/CarHvacManager.java b/car-lib/src/android/car/hardware/CarHvacManager.java
new file mode 100644
index 0000000000..9dc53f1221
--- /dev/null
+++ b/car-lib/src/android/car/hardware/CarHvacManager.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2015 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.hardware.hvac;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.car.Car;
+import android.car.CarManagerBase;
+import android.car.CarNotConnectedException;
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyEvent;
+import android.car.hardware.property.CarPropertyManagerBase;
+import android.car.hardware.property.CarPropertyManagerBase.CarPropertyEventListener;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.ArraySet;
+
+import java.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * API for controlling HVAC system in cars
+ * @hide
+ */
+@SystemApi
+public class CarHvacManager implements CarManagerBase {
+ private final static boolean DBG = true;
+ private final static String TAG = "CarHvacManager";
+ private final CarPropertyManagerBase mMgr;
+ private final ArraySet<CarHvacEventListener> mListeners = new ArraySet<>();
+ private CarPropertyEventListenerToBase mListenerToBase = null;
+
+ /**
+ * HVAC property IDs for get/set methods
+ */
+ @IntDef({
+ HvacPropertyId.MIRROR_DEFROSTER_ON,
+ HvacPropertyId.STEERING_WHEEL_TEMP,
+ HvacPropertyId.OUTSIDE_AIR_TEMP,
+ HvacPropertyId.MAX_GLOBAL_PROPERTY_ID,
+ HvacPropertyId.ZONED_TEMP_SETPOINT,
+ HvacPropertyId.ZONED_TEMP_ACTUAL,
+ HvacPropertyId.ZONED_TEMP_IS_FAHRENHEIT,
+ HvacPropertyId.ZONED_FAN_SPEED_SETPOINT,
+ HvacPropertyId.ZONED_FAN_SPEED_RPM,
+ HvacPropertyId.ZONED_FAN_POSITION_AVAILABLE,
+ HvacPropertyId.ZONED_FAN_POSITION,
+ HvacPropertyId.ZONED_SEAT_TEMP,
+ HvacPropertyId.ZONED_AC_ON,
+ HvacPropertyId.ZONED_AUTOMATIC_MODE_ON,
+ HvacPropertyId.ZONED_AIR_RECIRCULATION_ON,
+ HvacPropertyId.ZONED_MAX_AC_ON,
+ HvacPropertyId.ZONED_DUAL_ZONE_ON,
+ HvacPropertyId.ZONED_MAX_DEFROST_ON,
+ HvacPropertyId.WINDOW_DEFROSTER_ON,
+ })
+ public @interface HvacPropertyId {
+ /**
+ * Global HVAC properties. There is only a single instance in a car.
+ * Global properties are in the range of 0-0x3FFF.
+ */
+ /** Mirror defrosters state, bool. */
+ int MIRROR_DEFROSTER_ON = 0x0001;
+ /** Steering wheel temp: negative values indicate cooling, positive values indicate
+ * heat, int. */
+ int STEERING_WHEEL_TEMP = 0x0002;
+ /** Outside air temperature, float. */
+ int OUTSIDE_AIR_TEMP = 0x0003;
+
+ /** The maximum id that can be assigned to global (non-zoned) property. */
+ int MAX_GLOBAL_PROPERTY_ID = 0x3fff;
+
+ /**
+ * ZONED_* represents properties available on a per-zone basis. All zones in a car are
+ * not required to have the same properties. Zone specific properties start at 0x4000.
+ */
+ /** Temperature setpoint desired by the user, in terms of F or C, depending on
+ * TEMP_IS_FAHRENHEIT, int */
+ int ZONED_TEMP_SETPOINT = 0x4001;
+ /** Actual zone temperature is read only integer, in terms of F or C, int. */
+ int ZONED_TEMP_ACTUAL = 0x4002;
+ /** Temperature is in degrees fahrenheit if this is true, bool. */
+ int ZONED_TEMP_IS_FAHRENHEIT = 0x4003;
+ /** Fan speed setpoint is an integer from 0-n, depending on the number of fan speeds
+ * available. Selection determines the fan position, int. */
+ int ZONED_FAN_SPEED_SETPOINT = 0x4004;
+ /** Actual fan speed is a read-only value, expressed in RPM, int. */
+ int ZONED_FAN_SPEED_RPM = 0x4005;
+ /** Fan position available is a bitmask of positions available for each zone, int. */
+ int ZONED_FAN_POSITION_AVAILABLE = 0x4006;
+ /** Current fan position setting, int. */
+ int ZONED_FAN_POSITION = 0x4007;
+ /** Seat temperature is negative for cooling, positive for heating. Temperature is a
+ * setting, i.e. -3 to 3 for 3 levels of cooling and 3 levels of heating. int. */
+ int ZONED_SEAT_TEMP = 0x4008;
+ /** Air conditioner state, bool */
+ int ZONED_AC_ON = 0x4009;
+ /** HVAC is in automatic mode, bool. */
+ int ZONED_AUTOMATIC_MODE_ON = 0x400A;
+ /** Air recirculation is active, bool. */
+ int ZONED_AIR_RECIRCULATION_ON = 0x400B;
+ /** Max AC is active, bool. */
+ int ZONED_MAX_AC_ON = 0x400C;
+ /** Dual zone is enabled, bool. */
+ int ZONED_DUAL_ZONE_ON = 0x400D;
+ /** Max Defrost is active, bool. */
+ int ZONED_MAX_DEFROST_ON = 0x400E;
+ /** Defroster is based off of window position, bool */
+ int WINDOW_DEFROSTER_ON = 0x5001;
+ }
+
+ public interface CarHvacEventListener {
+ /** Called when a property is updated */
+ void onChangeEvent(CarPropertyValue value);
+
+ /** Called when an error is detected with a property */
+ void onErrorEvent(int propertyId, int zone);
+ }
+
+ private static class CarPropertyEventListenerToBase implements CarPropertyEventListener {
+ private final WeakReference<CarHvacManager> mManager;
+
+ public CarPropertyEventListenerToBase(CarHvacManager manager) {
+ mManager = new WeakReference<>(manager);
+ }
+
+ @Override
+ public void onChangeEvent(CarPropertyValue value) {
+ CarHvacManager manager = mManager.get();
+ if (manager != null) {
+ manager.handleOnChangeEvent(value);
+ }
+ }
+
+ @Override
+ public void onErrorEvent(int propertyId, int zone) {
+ CarHvacManager manager = mManager.get();
+ if (manager != null) {
+ manager.handleOnErrorEvent(propertyId, zone);
+ }
+ }
+ }
+
+ void handleOnChangeEvent(CarPropertyValue value) {
+ Collection<CarHvacEventListener> listeners;
+ synchronized (this) {
+ listeners = new ArraySet<>(mListeners);
+ }
+ if (!listeners.isEmpty()) {
+ for (CarHvacEventListener l: listeners) {
+ l.onChangeEvent(value);
+ }
+ }
+ }
+
+ void handleOnErrorEvent(int propertyId, int zone) {
+ Collection<CarHvacEventListener> listeners;
+ synchronized (this) {
+ listeners = new ArraySet<>(mListeners);
+ }
+ if (!listeners.isEmpty()) {
+ for (CarHvacEventListener l: listeners) {
+ l.onErrorEvent(propertyId, zone);
+ }
+ }
+ }
+
+ /**
+ * Get an instance of the CarHvacManager.
+ *
+ * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
+ * @hide
+ */
+ public CarHvacManager(IBinder service, Context context, Looper looper) {
+ mMgr = new CarPropertyManagerBase(service, context, looper, DBG, TAG);
+ }
+
+ /** Returns true if the property is a zoned type. */
+ public static boolean isZonedProperty(int propertyId) {
+ return propertyId > HvacPropertyId.MAX_GLOBAL_PROPERTY_ID;
+ }
+
+ /** Implement wrappers for contained CarPropertyManagerBase object */
+ public synchronized void registerListener(CarHvacEventListener listener) throws
+ CarNotConnectedException {
+ if (mListeners.isEmpty()) {
+ mListenerToBase = new CarPropertyEventListenerToBase(this);
+ mMgr.registerListener(mListenerToBase);
+ }
+ mListeners.add(listener);
+ }
+
+ public synchronized void unregisterListener(CarHvacEventListener listener) throws
+ CarNotConnectedException {
+ mListeners.remove(listener);
+ if (mListeners.isEmpty()) {
+ mMgr.unregisterListener(mListenerToBase);
+ mListenerToBase = null;
+ }
+ }
+
+ public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
+ return mMgr.getPropertyList();
+ }
+
+ public boolean getBooleanProperty(int prop, int area) throws CarNotConnectedException {
+ return mMgr.getBooleanProperty(prop, area);
+ }
+
+ public float getFloatProperty(int prop, int area) throws CarNotConnectedException {
+ return mMgr.getFloatProperty(prop, area);
+ }
+
+ public int getIntProperty(int prop, int area) throws CarNotConnectedException {
+ return mMgr.getIntProperty(prop, area);
+ }
+
+ public void setBooleanProperty(int prop, int area, boolean val)
+ throws CarNotConnectedException {
+ mMgr.setBooleanProperty(prop, area, val);
+ }
+
+ public void setFloatProperty(int prop, int area, float val) throws CarNotConnectedException {
+ mMgr.setFloatProperty(prop, area, val);
+ }
+
+ public void setIntProperty(int prop, int area, int val) throws CarNotConnectedException {
+ mMgr.setIntProperty(prop, area, val);
+ }
+
+ /** @hide */
+ @Override
+ public void onCarDisconnected() {
+ mMgr.onCarDisconnected();
+ }
+}
diff --git a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java b/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
deleted file mode 100644
index 38caf10483..0000000000
--- a/car-lib/src/android/car/hardware/hvac/CarHvacManager.java
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * Copyright (C) 2015 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.hardware.hvac;
-
-import static java.lang.Integer.toHexString;
-
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.car.Car;
-import android.car.CarManagerBase;
-import android.car.CarNotConnectedException;
-import android.car.hardware.CarPropertyConfig;
-import android.car.hardware.CarPropertyValue;
-import android.content.Context;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.ArraySet;
-import android.util.Log;
-
-import java.lang.ref.WeakReference;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * API for controlling HVAC system in cars
- * @hide
- */
-@SystemApi
-public class CarHvacManager implements CarManagerBase {
- public final static boolean DBG = true;
- public final static String TAG = "CarHvacManager";
-
- /**
- * HVAC property IDs for get/set methods
- */
- @IntDef({
- HvacPropertyId.MIRROR_DEFROSTER_ON,
- HvacPropertyId.STEERING_WHEEL_TEMP,
- HvacPropertyId.OUTSIDE_AIR_TEMP,
- HvacPropertyId.MAX_GLOBAL_PROPERTY_ID,
- HvacPropertyId.ZONED_TEMP_SETPOINT,
- HvacPropertyId.ZONED_TEMP_ACTUAL,
- HvacPropertyId.ZONED_TEMP_IS_FAHRENHEIT,
- HvacPropertyId.ZONED_FAN_SPEED_SETPOINT,
- HvacPropertyId.ZONED_FAN_SPEED_RPM,
- HvacPropertyId.ZONED_FAN_POSITION_AVAILABLE,
- HvacPropertyId.ZONED_FAN_POSITION,
- HvacPropertyId.ZONED_SEAT_TEMP,
- HvacPropertyId.ZONED_AC_ON,
- HvacPropertyId.ZONED_AUTOMATIC_MODE_ON,
- HvacPropertyId.ZONED_AIR_RECIRCULATION_ON,
- HvacPropertyId.ZONED_MAX_AC_ON,
- HvacPropertyId.ZONED_DUAL_ZONE_ON,
- HvacPropertyId.ZONED_MAX_DEFROST_ON,
- HvacPropertyId.WINDOW_DEFROSTER_ON,
- })
- public @interface HvacPropertyId {
- /**
- * Global HVAC properties. There is only a single instance in a car.
- * Global properties are in the range of 0-0x3FFF.
- */
- /** Mirror defrosters state, bool. */
- int MIRROR_DEFROSTER_ON = 0x0001;
- /** Steering wheel temp: negative values indicate cooling, positive values indicate
- * heat, int. */
- int STEERING_WHEEL_TEMP = 0x0002;
- /** Outside air temperature, float. */
- int OUTSIDE_AIR_TEMP = 0x0003;
-
- /** The maximum id that can be assigned to global (non-zoned) property. */
- int MAX_GLOBAL_PROPERTY_ID = 0x3fff;
-
- /**
- * ZONED_* represents properties available on a per-zone basis. All zones in a car are
- * not required to have the same properties. Zone specific properties start at 0x4000.
- */
- /** Temperature setpoint desired by the user, in terms of F or C, depending on
- * TEMP_IS_FAHRENHEIT, int */
- int ZONED_TEMP_SETPOINT = 0x4001;
- /** Actual zone temperature is read only integer, in terms of F or C, int. */
- int ZONED_TEMP_ACTUAL = 0x4002;
- /** Temperature is in degrees fahrenheit if this is true, bool. */
- int ZONED_TEMP_IS_FAHRENHEIT = 0x4003;
- /** Fan speed setpoint is an integer from 0-n, depending on the number of fan speeds
- * available. Selection determines the fan position, int. */
- int ZONED_FAN_SPEED_SETPOINT = 0x4004;
- /** Actual fan speed is a read-only value, expressed in RPM, int. */
- int ZONED_FAN_SPEED_RPM = 0x4005;
- /** Fan position available is a bitmask of positions available for each zone, int. */
- int ZONED_FAN_POSITION_AVAILABLE = 0x4006;
- /** Current fan position setting, int. */
- int ZONED_FAN_POSITION = 0x4007;
- /** Seat temperature is negative for cooling, positive for heating. Temperature is a
- * setting, i.e. -3 to 3 for 3 levels of cooling and 3 levels of heating. int. */
- int ZONED_SEAT_TEMP = 0x4008;
- /** Air conditioner state, bool */
- int ZONED_AC_ON = 0x4009;
- /** HVAC is in automatic mode, bool. */
- int ZONED_AUTOMATIC_MODE_ON = 0x400A;
- /** Air recirculation is active, bool. */
- int ZONED_AIR_RECIRCULATION_ON = 0x400B;
- /** Max AC is active, bool. */
- int ZONED_MAX_AC_ON = 0x400C;
- /** Dual zone is enabled, bool. */
- int ZONED_DUAL_ZONE_ON = 0x400D;
- /** Max Defrost is active, bool. */
- int ZONED_MAX_DEFROST_ON = 0x400E;
- /** Defroster is based off of window position, bool */
- int WINDOW_DEFROSTER_ON = 0x5001;
- }
-
- // Constants handled in the handler (see mHandler below).
- private final static int MSG_HVAC_EVENT = 0;
-
- /** Callback functions for HVAC events */
- public interface CarHvacEventListener {
- /** Called when an HVAC property is updated */
- void onChangeEvent(final CarPropertyValue value);
-
- /** Called when an error is detected with a property */
- void onErrorEvent(final int propertyId, final int zone);
- }
-
- private final ICarHvac mService;
- private final ArraySet<CarHvacEventListener> mListeners = new ArraySet<>();
- private CarHvacEventListenerToService mListenerToService = null;
-
- private static final class EventCallbackHandler extends Handler {
- WeakReference<CarHvacManager> mMgr;
-
- EventCallbackHandler(CarHvacManager mgr, Looper looper) {
- super(looper);
- mMgr = new WeakReference<>(mgr);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_HVAC_EVENT:
- CarHvacManager mgr = mMgr.get();
- if (mgr != null) {
- mgr.dispatchEventToClient((CarHvacEvent) msg.obj);
- }
- break;
- default:
- Log.e(TAG, "Event type not handled?" + msg);
- break;
- }
- }
- }
-
- private final Handler mHandler;
-
- private static class CarHvacEventListenerToService extends ICarHvacEventListener.Stub {
- private final WeakReference<CarHvacManager> mManager;
-
- public CarHvacEventListenerToService(CarHvacManager manager) {
- mManager = new WeakReference<>(manager);
- }
-
- @Override
- public void onEvent(CarHvacEvent event) {
- CarHvacManager manager = mManager.get();
- if (manager != null) {
- manager.handleEvent(event);
- }
- }
- }
-
- /**
- * Get an instance of the CarHvacManager.
- *
- * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead.
- * @hide
- */
- public CarHvacManager(IBinder service, Context context, Looper looper) {
- mService = ICarHvac.Stub.asInterface(service);
- mHandler = new EventCallbackHandler(this, looper);
- }
-
- /** Returns true if the property is a zoned type. */
- public static boolean isZonedProperty(int propertyId) {
- return propertyId > HvacPropertyId.MAX_GLOBAL_PROPERTY_ID;
- }
-
- /**
- * Register {@link CarHvacEventListener} to get HVAC property changes
- *
- * @param listener Implements onEvent() for property change updates
- */
- public synchronized void registerListener(CarHvacEventListener listener)
- throws CarNotConnectedException {
- if(mListeners.isEmpty()) {
- try {
- mListenerToService = new CarHvacEventListenerToService(this);
- mService.registerListener(mListenerToService);
- } catch (RemoteException ex) {
- Log.e(TAG, "Could not connect: " + ex.toString());
- throw new CarNotConnectedException(ex);
- } catch (IllegalStateException ex) {
- Car.checkCarNotConnectedExceptionFromCarService(ex);
- }
- }
- mListeners.add(listener);
- }
-
- /**
- * Unregister {@link CarHvacEventListener}.
- * @param listener CarHvacEventListener to unregister
- */
- public synchronized void unregisterListener(CarHvacEventListener listener)
- throws CarNotConnectedException {
- if (DBG) {
- Log.d(TAG, "unregisterListener");
- }
- try {
- mService.unregisterListener(mListenerToService);
- } catch (RemoteException e) {
- Log.e(TAG, "Could not unregister: " + e.toString());
- throw new CarNotConnectedException(e);
-
- }
- mListeners.remove(listener);
- if(mListeners.isEmpty()) {
- mListenerToService = null;
- }
- }
-
- /**
- * Returns the list of HVAC properties available.
- *
- * @return Caller must check the property type and typecast to the appropriate subclass
- * (CarHvacBooleanProperty, CarHvacFloatProperty, CarrHvacIntProperty)
- */
- public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
- List<CarPropertyConfig> carProps;
- try {
- carProps = mService.getHvacProperties();
- } catch (RemoteException e) {
- Log.w(TAG, "Exception in getPropertyList", e);
- throw new CarNotConnectedException(e);
- }
- return carProps;
- }
-
- /**
- * Returns value of a bool property
- *
- * @param prop Property ID to get
- * @param area Area of the property to get
- */
- public boolean getBooleanProperty(int prop, int area) throws CarNotConnectedException {
- CarPropertyValue<Boolean> carProp = getProperty(Boolean.class, prop, area);
- return carProp != null ? carProp.getValue() : false;
- }
-
- /**
- * Returns value of a float property
- *
- * @param prop Property ID to get
- * @param area Area of the property to get
- */
- public float getFloatProperty(int prop, int area) throws CarNotConnectedException {
- CarPropertyValue<Float> carProp = getProperty(Float.class, prop, area);
- return carProp != null ? carProp.getValue() : 0f;
- }
-
- /**
- * Returns value of a integer property
- *
- * @param prop Property ID to get
- * @param area Zone of the property to get
- */
- public int getIntProperty(int prop, int area) throws CarNotConnectedException {
- CarPropertyValue<Integer> carProp = getProperty(Integer.class, prop, area);
- return carProp != null ? carProp.getValue() : 0;
- }
-
- @Nullable
- @SuppressWarnings("unchecked")
- private <E> CarPropertyValue<E> getProperty(Class<E> clazz, int prop, int area)
- throws CarNotConnectedException {
- if (DBG) {
- Log.d(TAG, "getProperty, prop: 0x" + toHexString(prop)
- + ", area: 0x" + toHexString(area) + ", clazz: " + clazz);
- }
- try {
- CarPropertyValue<E> hvacProperty = mService.getProperty(prop, area);
- if (hvacProperty != null && hvacProperty.getValue() != null) {
- Class<?> actualClass = hvacProperty.getValue().getClass();
- if (actualClass != clazz) {
- throw new IllegalArgumentException("Invalid property type. "
- + "Expected: " + clazz + ", but was: " + actualClass);
- }
- }
- return hvacProperty;
- } catch (RemoteException e) {
- Log.e(TAG, "getProperty failed with " + e.toString()
- + ", propId: 0x" + toHexString(prop) + ", area: 0x" + toHexString(area), e);
- throw new CarNotConnectedException(e);
- }
- }
-
- /**
- * Modifies a property. If the property modification doesn't occur, an error event shall be
- * generated and propagated back to the application.
- *
- * @param prop Property ID to modify
- * @param area Area to apply the modification.
- * @param val Value to set
- */
- public void setBooleanProperty(int prop, int area, boolean val)
- throws CarNotConnectedException {
- if (DBG) {
- Log.d(TAG, "setBooleanProperty: prop = " + prop + " area = " + area + " val = " + val);
- }
- try {
- mService.setProperty(new CarPropertyValue<>(prop, area, val));
- } catch (RemoteException e) {
- Log.e(TAG, "setBooleanProperty failed with " + e.toString(), e);
- throw new CarNotConnectedException(e);
- }
- }
-
- public void setFloatProperty(int prop, int area, float val) throws CarNotConnectedException {
- if (DBG) {
- Log.d(TAG, "setFloatProperty: prop = " + prop + " area = " + area + " val = " + val);
- }
- try {
- mService.setProperty(new CarPropertyValue<>(prop, area, val));
- } catch (RemoteException e) {
- Log.e(TAG, "setBooleanProperty failed with " + e.toString(), e);
- throw new CarNotConnectedException(e);
- }
- }
-
- public void setIntProperty(int prop, int area, int val) throws CarNotConnectedException {
- if (DBG) {
- Log.d(TAG, "setIntProperty: prop = " + prop + " area = " + area + " val = " + val);
- }
- try {
- mService.setProperty(new CarPropertyValue<>(prop, area, val));
- } catch (RemoteException e) {
- Log.e(TAG, "setIntProperty failed with " + e.toString(), e);
- throw new CarNotConnectedException(e);
- }
- }
-
- private void dispatchEventToClient(CarHvacEvent event) {
- Collection<CarHvacEventListener> listeners;
- synchronized (this) {
- listeners = mListeners;
- }
- if (!listeners.isEmpty()) {
- CarPropertyValue hvacProperty = event.getCarPropertyValue();
- switch(event.getEventType()) {
- case CarHvacEvent.HVAC_EVENT_PROPERTY_CHANGE:
- for (CarHvacEventListener l: listeners) {
- l.onChangeEvent(hvacProperty);
- }
- break;
- case CarHvacEvent.HVAC_EVENT_ERROR:
- for (CarHvacEventListener l: listeners) {
- l.onErrorEvent(hvacProperty.getPropertyId(), hvacProperty.getAreaId());
- }
- break;
- default:
- throw new IllegalArgumentException();
- }
- } else {
- Log.e(TAG, "Listener died, not dispatching event.");
- }
- }
-
- private void handleEvent(CarHvacEvent event) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_HVAC_EVENT, event));
- }
-
- /** @hide */
- @Override
- public void onCarDisconnected() {
- for(CarHvacEventListener l: mListeners) {
- try {
- unregisterListener(l);
- } catch (CarNotConnectedException e) {
- // Ignore, car is disconnecting.
- }
- }
- }
-}
diff --git a/service/src/com/android/car/cluster/InstrumentClusterPresentation.java b/car-lib/src/android/car/hardware/property/CarPropertyEvent.aidl
index 53c73f8fc5..82e3de9b6b 100644
--- a/service/src/com/android/car/cluster/InstrumentClusterPresentation.java
+++ b/car-lib/src/android/car/hardware/property/CarPropertyEvent.aidl
@@ -13,19 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.car.cluster;
-import android.app.Presentation;
-import android.content.Context;
-import android.view.Display;
-import android.view.WindowManager;
+package android.car.hardware.property;
+
+parcelable CarPropertyEvent;
-/**
- * Presentation class.
- */
-public class InstrumentClusterPresentation extends Presentation {
- public InstrumentClusterPresentation(Context outerContext, Display display) {
- super(outerContext, display);
- getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- }
-}
diff --git a/car-lib/src/android/car/hardware/hvac/CarHvacEvent.java b/car-lib/src/android/car/hardware/property/CarPropertyEvent.java
index d243c80b52..638fbf2e2b 100644
--- a/car-lib/src/android/car/hardware/hvac/CarHvacEvent.java
+++ b/car-lib/src/android/car/hardware/property/CarPropertyEvent.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package android.car.hardware.hvac;
+package android.car.hardware.property;
import android.car.hardware.CarPropertyValue;
import android.os.Parcel;
import android.os.Parcelable;
/** @hide */
-public class CarHvacEvent implements Parcelable {
- public static final int HVAC_EVENT_PROPERTY_CHANGE = 0;
- public static final int HVAC_EVENT_ERROR = 1;
+public class CarPropertyEvent implements Parcelable {
+ public static final int PROPERTY_EVENT_PROPERTY_CHANGE = 0;
+ public static final int PROPERTY_EVENT_ERROR = 1;
/**
* EventType of this message
@@ -54,33 +54,33 @@ public class CarHvacEvent implements Parcelable {
dest.writeParcelable(mCarPropertyValue, flags);
}
- public static final Parcelable.Creator<CarHvacEvent> CREATOR
- = new Parcelable.Creator<CarHvacEvent>() {
- public CarHvacEvent createFromParcel(Parcel in) {
- return new CarHvacEvent(in);
+ public static final Parcelable.Creator<CarPropertyEvent> CREATOR
+ = new Parcelable.Creator<CarPropertyEvent>() {
+ public CarPropertyEvent createFromParcel(Parcel in) {
+ return new CarPropertyEvent(in);
}
- public CarHvacEvent[] newArray(int size) {
- return new CarHvacEvent[size];
+ public CarPropertyEvent[] newArray(int size) {
+ return new CarPropertyEvent[size];
}
};
/**
- * Constructor for {@link CarHvacEvent}.
+ * Constructor for {@link CarPropertyEvent}.
*/
- public CarHvacEvent(int eventType, CarPropertyValue<?> carHvacProperty) {
+ public CarPropertyEvent(int eventType, CarPropertyValue<?> carPropertyValue) {
mEventType = eventType;
- mCarPropertyValue = carHvacProperty;
+ mCarPropertyValue = carPropertyValue;
}
- private CarHvacEvent(Parcel in) {
+ private CarPropertyEvent(Parcel in) {
mEventType = in.readInt();
mCarPropertyValue = in.readParcelable(CarPropertyValue.class.getClassLoader());
}
@Override
public String toString() {
- return "CarHvacEvent{" +
+ return "CarPropertyEvent{" +
"mEventType=" + mEventType +
", mCarPropertyValue=" + mCarPropertyValue +
'}';
diff --git a/car-lib/src/android/car/hardware/property/CarPropertyManagerBase.java b/car-lib/src/android/car/hardware/property/CarPropertyManagerBase.java
new file mode 100644
index 0000000000..08514423d7
--- /dev/null
+++ b/car-lib/src/android/car/hardware/property/CarPropertyManagerBase.java
@@ -0,0 +1,315 @@
+/*
+ * 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.
+ */
+
+package android.car.hardware.property;
+
+import static java.lang.Integer.toHexString;
+
+import android.annotation.Nullable;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.CarPropertyValue;
+import android.content.Context;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.EventLog;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * API for creating Car*Manager
+ * @hide
+ */
+public class CarPropertyManagerBase {
+ private final boolean mDbg;
+ private final Handler mHandler;
+ private final ArraySet<CarPropertyEventListener> mListeners = new ArraySet<>();
+ private CarPropertyEventListenerToService mListenerToService = null;
+ private final ICarProperty mService;
+ private final String mTag;
+
+ /** Callback functions for property events */
+ public interface CarPropertyEventListener {
+ /** Called when a property is updated */
+ void onChangeEvent(CarPropertyValue value);
+
+ /** Called when an error is detected with a property */
+ void onErrorEvent(int propertyId, int zone);
+ }
+
+ private final static class EventCallbackHandler extends Handler {
+ /** Constants handled in the handler */
+ private static final int MSG_GENERIC_EVENT = 0;
+
+ private final WeakReference<CarPropertyManagerBase> mMgr;
+
+ EventCallbackHandler(CarPropertyManagerBase mgr, Looper looper) {
+ super(looper);
+ mMgr = new WeakReference<>(mgr);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_GENERIC_EVENT:
+ CarPropertyManagerBase mgr = mMgr.get();
+ if (mgr != null) {
+ mgr.dispatchEventToClient((CarPropertyEvent) msg.obj);
+ }
+ break;
+ default:
+ Log.e("EventtCallbackHandler", "Event type not handled: " + msg);
+ break;
+ }
+ }
+ }
+
+ private static class CarPropertyEventListenerToService extends ICarPropertyEventListener.Stub {
+ private final WeakReference<CarPropertyManagerBase> mManager;
+
+ public CarPropertyEventListenerToService(
+ CarPropertyManagerBase manager) {
+ mManager = new WeakReference<>(manager);
+ }
+
+ @Override
+ public void onEvent(CarPropertyEvent event) {
+ CarPropertyManagerBase manager = mManager.get();
+ if (manager != null) {
+ manager.handleEvent(event);
+ }
+ }
+ }
+
+ /**
+ * Get an instance of the CarPropertyManagerBase.
+ */
+ public CarPropertyManagerBase(IBinder service, Context context, Looper looper, boolean dbg,
+ String tag) {
+ mDbg = dbg;
+ mTag = tag;
+ mService = ICarProperty.Stub.asInterface(service);
+ mHandler = new EventCallbackHandler(this, looper);
+ }
+
+ /**
+ * Register {@link CarPropertyEventListener} to get property changes
+ *
+ * @param listener Implements onEvent() for property change updates
+ */
+ public synchronized void registerListener(CarPropertyEventListener listener)
+ throws CarNotConnectedException {
+ if(mListeners.isEmpty()) {
+ try {
+ mListenerToService = new CarPropertyEventListenerToService(this);
+ mService.registerListener(mListenerToService);
+ } catch (RemoteException ex) {
+ Log.e(mTag, "Could not connect: ", ex);
+ throw new CarNotConnectedException(ex);
+ } catch (IllegalStateException ex) {
+ Car.checkCarNotConnectedExceptionFromCarService(ex);
+ }
+ }
+ mListeners.add(listener);
+ }
+
+ /**
+ * Unregister {@link CarPropertyEventListener}.
+ * @param listener CarPropertyEventListener to unregister
+ */
+ public synchronized void unregisterListener(CarPropertyEventListener listener)
+ throws CarNotConnectedException {
+ if (mDbg) {
+ Log.d(mTag, "unregisterListener");
+ }
+ mListeners.remove(listener);
+ if(mListeners.isEmpty()) {
+ try {
+ mService.unregisterListener(mListenerToService);
+ } catch (RemoteException ex) {
+ Log.e(mTag, "Could not unregister: ", ex);
+ throw new CarNotConnectedException(ex);
+ }
+ mListenerToService = null;
+ }
+ }
+
+ /**
+ * Returns the list of properties available.
+ *
+ * @return Caller must check the property type and typecast to the appropriate subclass
+ * (CarPropertyBooleanProperty, CarPropertyFloatProperty, CarrPropertyIntProperty)
+ */
+ public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
+ try {
+ return mService.getPropertyList();
+ } catch (RemoteException e) {
+ Log.w(mTag, "Exception in getPropertyList", e);
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Returns value of a bool property
+ *
+ * @param prop Property ID to get
+ * @param area Area of the property to get
+ */
+ public boolean getBooleanProperty(int prop, int area) throws CarNotConnectedException {
+ CarPropertyValue<Boolean> carProp = getProperty(Boolean.class, prop, area);
+ return carProp != null ? carProp.getValue() : false;
+ }
+
+ /**
+ * Returns value of a float property
+ *
+ * @param prop Property ID to get
+ * @param area Area of the property to get
+ */
+ public float getFloatProperty(int prop, int area) throws CarNotConnectedException {
+ CarPropertyValue<Float> carProp = getProperty(Float.class, prop, area);
+ return carProp != null ? carProp.getValue() : 0f;
+ }
+
+ /**
+ * Returns value of a integer property
+ *
+ * @param prop Property ID to get
+ * @param area Zone of the property to get
+ */
+ public int getIntProperty(int prop, int area) throws CarNotConnectedException {
+ CarPropertyValue<Integer> carProp = getProperty(Integer.class, prop, area);
+ return carProp != null ? carProp.getValue() : 0;
+ }
+
+ @Nullable
+ @SuppressWarnings("unchecked")
+ private <E> CarPropertyValue<E> getProperty(Class<E> clazz, int propId, int area)
+ throws CarNotConnectedException {
+ if (mDbg) {
+ Log.d(mTag, "getProperty, propId: 0x" + toHexString(propId)
+ + ", area: 0x" + toHexString(area) + ", clazz: " + clazz);
+ }
+ try {
+ CarPropertyValue<E> propVal = mService.getProperty(propId, area);
+ if (propVal != null && propVal.getValue() != null) {
+ Class<?> actualClass = propVal.getValue().getClass();
+ if (actualClass != clazz) {
+ throw new IllegalArgumentException("Invalid property type. " + "Expected: "
+ + clazz + ", but was: " + actualClass);
+ }
+ }
+ return propVal;
+ } catch (RemoteException e) {
+ Log.e(mTag, "getProperty failed with " + e.toString()
+ + ", propId: 0x" + toHexString(propId) + ", area: 0x" + toHexString(area), e);
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Modifies a property. If the property modification doesn't occur, an error event shall be
+ * generated and propagated back to the application.
+ *
+ * @param prop Property ID to modify
+ * @param area Area to apply the modification.
+ * @param val Value to set
+ */
+ public void setBooleanProperty(int prop, int area, boolean val)
+ throws CarNotConnectedException {
+ if (mDbg) {
+ Log.d(mTag, "setBooleanProperty: prop = " + prop + " area = " + area + " val = " + val);
+ }
+ try {
+ mService.setProperty(new CarPropertyValue<>(prop, area, val));
+ } catch (RemoteException e) {
+ Log.e(mTag, "setBooleanProperty failed with " + e.toString(), e);
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ public void setFloatProperty(int prop, int area, float val) throws CarNotConnectedException {
+ if (mDbg) {
+ Log.d(mTag, "setFloatProperty: prop = " + prop + " area = " + area + " val = " + val);
+ }
+ try {
+ mService.setProperty(new CarPropertyValue<>(prop, area, val));
+ } catch (RemoteException e) {
+ Log.e(mTag, "setBooleanProperty failed with " + e.toString(), e);
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ public void setIntProperty(int prop, int area, int val) throws CarNotConnectedException {
+ if (mDbg) {
+ Log.d(mTag, "setIntProperty: prop = " + prop + " area = " + area + " val = " + val);
+ }
+ try {
+ mService.setProperty(new CarPropertyValue<>(prop, area, val));
+ } catch (RemoteException e) {
+ Log.e(mTag, "setIntProperty failed with " + e.toString(), e);
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ private synchronized void dispatchEventToClient(CarPropertyEvent event) {
+ Collection<CarPropertyEventListener> listeners;
+ synchronized (this) {
+ listeners = new ArraySet<>(mListeners);
+ }
+ if (!listeners.isEmpty()) {
+ CarPropertyValue propVal = event.getCarPropertyValue();
+ switch(event.getEventType()) {
+ case CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE:
+ for (CarPropertyEventListener l: listeners) {
+ l.onChangeEvent(propVal);
+ }
+ break;
+ case CarPropertyEvent.PROPERTY_EVENT_ERROR:
+ for (CarPropertyEventListener l: listeners) {
+ l.onErrorEvent(propVal.getPropertyId(), propVal.getAreaId());
+ }
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ } else {
+ Log.e(mTag, "Listener died, not dispatching event.");
+ }
+ }
+
+ private void handleEvent(CarPropertyEvent event) {
+ mHandler.sendMessage(mHandler.obtainMessage(EventCallbackHandler.MSG_GENERIC_EVENT, event));
+ }
+
+ public void onCarDisconnected() {
+ for(CarPropertyEventListener l: mListeners) {
+ try {
+ unregisterListener(l);
+ } catch (CarNotConnectedException e) {
+ // Ignore, car is disconnecting.
+ }
+ }
+ }
+}
diff --git a/car-lib/src/android/car/hardware/hvac/ICarHvac.aidl b/car-lib/src/android/car/hardware/property/ICarProperty.aidl
index 46c3917816..da0065f0c1 100644
--- a/car-lib/src/android/car/hardware/hvac/ICarHvac.aidl
+++ b/car-lib/src/android/car/hardware/property/ICarProperty.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,20 +14,22 @@
* limitations under the License.
*/
-package android.car.hardware.hvac;
+package android.car.hardware.property;
import android.car.hardware.CarPropertyConfig;
import android.car.hardware.CarPropertyValue;
-import android.car.hardware.hvac.ICarHvacEventListener;
+import android.car.hardware.property.ICarPropertyEventListener;
-/** @hide */
-interface ICarHvac {
+/**
+ * @hide
+ */
+interface ICarProperty {
- void registerListener(in ICarHvacEventListener listener) = 0;
+ void registerListener(in ICarPropertyEventListener listener) = 0;
- void unregisterListener(in ICarHvacEventListener listener) = 1;
+ void unregisterListener(in ICarPropertyEventListener listener) = 1;
- List<CarPropertyConfig> getHvacProperties() = 2;
+ List<CarPropertyConfig> getPropertyList() = 2;
CarPropertyValue getProperty(int prop, int zone) = 3;
diff --git a/car-lib/src/android/car/hardware/hvac/ICarHvacEventListener.aidl b/car-lib/src/android/car/hardware/property/ICarPropertyEventListener.aidl
index 1d5b99dd5c..b0d57fcab5 100644
--- a/car-lib/src/android/car/hardware/hvac/ICarHvacEventListener.aidl
+++ b/car-lib/src/android/car/hardware/property/ICarPropertyEventListener.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-package android.car.hardware.hvac;
+package android.car.hardware.property;
-import android.car.hardware.hvac.CarHvacEvent;
+import android.car.hardware.property.CarPropertyEvent;
/**
- * Binder callback for CarHvacEventListener.
+ * Binder callback for CarPropertyEventListener.
* This is generated per each CarClient.
* @hide
*/
-oneway interface ICarHvacEventListener {
+oneway interface ICarPropertyEventListener {
/**
* Called when an event is triggered in response to one of the calls (such as on tune) or
* asynchronously (such as on announcement).
*/
- void onEvent(in CarHvacEvent event) = 0;
+ void onEvent(in CarPropertyEvent event) = 0;
}
diff --git a/car-lib/src/android/car/media/CarAudioManager.java b/car-lib/src/android/car/media/CarAudioManager.java
index 4dd8be46b2..2d205620a6 100644
--- a/car-lib/src/android/car/media/CarAudioManager.java
+++ b/car-lib/src/android/car/media/CarAudioManager.java
@@ -77,18 +77,48 @@ public class CarAudioManager implements CarManagerBase {
* Audio usage for playing safety alert.
*/
public static final int CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT = 9;
+ /**
+ * Audio usage for external audio usage.
+ * @hide
+ */
+ public static final int CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE = 10;
/** @hide */
- public static final int CAR_AUDIO_USAGE_MAX = CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT;
+ public static final int CAR_AUDIO_USAGE_MAX = CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE;
/** @hide */
@IntDef({CAR_AUDIO_USAGE_DEFAULT, CAR_AUDIO_USAGE_MUSIC, CAR_AUDIO_USAGE_RADIO,
CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE, CAR_AUDIO_USAGE_VOICE_CALL,
CAR_AUDIO_USAGE_VOICE_COMMAND, CAR_AUDIO_USAGE_ALARM, CAR_AUDIO_USAGE_NOTIFICATION,
- CAR_AUDIO_USAGE_SYSTEM_SOUND, CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT})
+ CAR_AUDIO_USAGE_SYSTEM_SOUND, CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT,
+ CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE})
@Retention(RetentionPolicy.SOURCE)
public @interface CarAudioUsage {}
+ /** @hide */
+ public static final String CAR_RADIO_TYPE_AM_FM = "RADIO_AM_FM";
+ /** @hide */
+ public static final String CAR_RADIO_TYPE_AM_FM_HD = "RADIO_AM_FM_HD";
+ /** @hide */
+ public static final String CAR_RADIO_TYPE_DAB = "RADIO_DAB";
+ /** @hide */
+ public static final String CAR_RADIO_TYPE_SATELLITE = "RADIO_SATELLITE";
+
+ /** @hide */
+ public static final String CAR_EXTERNAL_SOURCE_TYPE_CD_DVD = "CD_DVD";
+ /** @hide */
+ public static final String CAR_EXTERNAL_SOURCE_TYPE_AUX_IN0 = "AUX_IN0";
+ /** @hide */
+ public static final String CAR_EXTERNAL_SOURCE_TYPE_AUX_IN1 = "AUX_IN1";
+ /** @hide */
+ public static final String CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE = "EXT_NAV_GUIDANCE";
+ /** @hide */
+ public static final String CAR_EXTERNAL_SOURCE_TYPE_EXT_VOICE_CALL = "EXT_VOICE_CALL";
+ /** @hide */
+ public static final String CAR_EXTERNAL_SOURCE_TYPE_EXT_VOICE_COMMAND = "EXT_VOICE_COMMAND";
+ /** @hide */
+ public static final String CAR_EXTERNAL_SOURCE_TYPE_EXT_SAFETY_ALERT = "EXT_SAFETY_ALERT";
+
private final ICarAudio mService;
private final AudioManager mAudioManager;
@@ -101,9 +131,79 @@ public class CarAudioManager implements CarManagerBase {
try {
return mService.getAudioAttributesForCarUsage(carUsage);
} catch (RemoteException e) {
- AudioAttributes.Builder builder = new AudioAttributes.Builder();
- return builder.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN).
- setUsage(AudioAttributes.USAGE_UNKNOWN).build();
+ return createAudioAttributes(AudioAttributes.CONTENT_TYPE_UNKNOWN,
+ AudioAttributes.USAGE_UNKNOWN);
+ }
+ }
+
+ /**
+ * Get AudioAttributes for radio. This is necessary when there are multiple types of radio
+ * in system.
+ *
+ * @param radioType String specifying the desired radio type. Should use only what is listed in
+ * {@link #getSupportedRadioTypes()}.
+ * @return
+ * @throws IllegalArgumentException If not supported type is passed.
+ *
+ * @hide
+ */
+ public AudioAttributes getAudioAttributesForRadio(String radioType)
+ throws IllegalArgumentException {
+ try {
+ return mService.getAudioAttributesForRadio(radioType);
+ } catch (RemoteException e) {
+ return createAudioAttributes(AudioAttributes.CONTENT_TYPE_UNKNOWN,
+ AudioAttributes.USAGE_UNKNOWN);
+ }
+ }
+
+ /**
+ * Get AudioAttributes for external audio source.
+ *
+ * @param externalSourceType String specifying the desired source type. Should use only what is
+ * listed in {@link #getSupportedExternalSourceTypes()}.
+ * @return
+ * @throws IllegalArgumentException If not supported type is passed.
+ *
+ * @hide
+ */
+ public AudioAttributes getAudioAttributesForExternalSource(String externalSourceType)
+ throws IllegalArgumentException {
+ try {
+ return mService.getAudioAttributesForExternalSource(externalSourceType);
+ } catch (RemoteException e) {
+ return createAudioAttributes(AudioAttributes.CONTENT_TYPE_UNKNOWN,
+ AudioAttributes.USAGE_UNKNOWN);
+ }
+ }
+
+ /**
+ * List all supported external audio sources.
+ *
+ * @return
+ *
+ * @hide
+ */
+ public String[] getSupportedExternalSourceTypes() {
+ try {
+ return mService.getSupportedExternalSourceTypes();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * List all supported radio sources.
+ *
+ * @return
+ *
+ * @hide
+ */
+ public String[] getSupportedRadioTypes() {
+ try {
+ return mService.getSupportedRadioTypes();
+ } catch (RemoteException e) {
+ return null;
}
}
@@ -271,7 +371,6 @@ public class CarAudioManager implements CarManagerBase {
@Override
public void onCarDisconnected() {
- // TODO Auto-generated method stub
}
/** @hide */
@@ -279,4 +378,9 @@ public class CarAudioManager implements CarManagerBase {
mService = ICarAudio.Stub.asInterface(service);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}
+
+ private AudioAttributes createAudioAttributes(int contentType, int usage) {
+ AudioAttributes.Builder builder = new AudioAttributes.Builder();
+ return builder.setContentType(contentType).setUsage(usage).build();
+ }
}
diff --git a/car-lib/src/android/car/media/ICarAudio.aidl b/car-lib/src/android/car/media/ICarAudio.aidl
index b4e1283572..1c07796db6 100644
--- a/car-lib/src/android/car/media/ICarAudio.aidl
+++ b/car-lib/src/android/car/media/ICarAudio.aidl
@@ -34,4 +34,8 @@ interface ICarAudio {
int getStreamVolume(int streamType) = 5;
boolean isMediaMuted() = 6;
boolean setMediaMute(boolean mute) = 7;
+ AudioAttributes getAudioAttributesForRadio(in String radioType) = 8;
+ AudioAttributes getAudioAttributesForExternalSource(in String externalSourceType) = 9;
+ String[] getSupportedExternalSourceTypes() = 10;
+ String[] getSupportedRadioTypes() = 11;
}
diff --git a/car-lib/src/android/car/navigation/CarNavigationManager.java b/car-lib/src/android/car/navigation/CarNavigationManager.java
index d1df559470..064c9e934c 100644
--- a/car-lib/src/android/car/navigation/CarNavigationManager.java
+++ b/car-lib/src/android/car/navigation/CarNavigationManager.java
@@ -19,34 +19,18 @@ import android.car.CarApiUtil;
import android.car.CarLibLog;
import android.car.CarManagerBase;
import android.car.CarNotConnectedException;
+import android.car.cluster.renderer.IInstrumentClusterNavigation;
import android.graphics.Bitmap;
-import android.os.Handler;
-import android.os.Handler.Callback;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
-import java.lang.ref.WeakReference;
-
/**
* API for providing navigation status for instrument cluster.
* @hide
*/
public class CarNavigationManager implements CarManagerBase {
- /**
- * Listener navigation related events.
- * Callbacks are called in the Looper context.
- */
- public interface CarNavigationListener {
- /** Instrument Cluster started in navigation mode */
- void onInstrumentClusterStart(CarNavigationInstrumentCluster instrumentCluster);
- /** Instrument cluster ended */
- void onInstrumentClusterStop();
- }
-
/** Navigation status */
public static final int STATUS_UNAVAILABLE = 0;
public static final int STATUS_ACTIVE = 1;
@@ -80,38 +64,15 @@ public class CarNavigationManager implements CarManagerBase {
private static final String TAG = CarLibLog.TAG_NAV;
- private final ICarNavigation mService;
- private ICarNavigationEventListenerImpl mICarNavigationEventListenerImpl;
- private CarNavigationListener mCarNavigationListener;
- private CarNavigationInstrumentCluster mInstrumentCluster;
- private final Handler mHandler;
- private final Callback mHandlerCallback = new Callback() {
- @Override
- public boolean handleMessage(Message msg) {
- Log.d(TAG, "handleMessage, listener: " + mCarNavigationListener + ", msg.what: " +
- msg.what);
- if (mCarNavigationListener != null) {
- switch (msg.what) {
- case START:
- Log.d(TAG, "mCarNavigationListener.onInstrumentClusterStart()");
- mCarNavigationListener.onInstrumentClusterStart(mInstrumentCluster);
- break;
- case STOP:
- mCarNavigationListener.onInstrumentClusterStop();
- break;
- }
- }
- return true;
- }
- };
+ private final IInstrumentClusterNavigation mService;
+
/**
* Only for CarServiceLoader
* @hide
*/
- public CarNavigationManager(IBinder service, Looper looper) {
- mHandler = new Handler(looper, mHandlerCallback);
- mService = ICarNavigation.Stub.asInterface(service);
+ public CarNavigationManager(IBinder service) {
+ mService = IInstrumentClusterNavigation.Stub.asInterface(service);
}
/**
@@ -121,7 +82,11 @@ public class CarNavigationManager implements CarManagerBase {
*/
public boolean sendNavigationStatus(int status) throws CarNotConnectedException {
try {
- mService.sendNavigationStatus(status);
+ if (status == STATUS_ACTIVE) {
+ mService.onStartNavigation();
+ } else {
+ mService.onStopNavigation();
+ }
} catch (IllegalStateException e) {
CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
} catch (RemoteException e) {
@@ -159,7 +124,7 @@ public class CarNavigationManager implements CarManagerBase {
public boolean sendNavigationTurnEvent(int event, String road, int turnAngle, int turnNumber,
Bitmap image, int turnSide) throws CarNotConnectedException {
try {
- mService.sendNavigationTurnEvent(event, road, turnAngle, turnNumber, image, turnSide);
+ mService.onNextManeuverChanged(event, road, turnAngle, turnNumber, image, turnSide);
} catch (IllegalStateException e) {
CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
} catch (RemoteException e) {
@@ -180,7 +145,7 @@ public class CarNavigationManager implements CarManagerBase {
public boolean sendNavigationTurnDistanceEvent(int distanceMeters, int timeSeconds)
throws CarNotConnectedException {
try {
- mService.sendNavigationTurnDistanceEvent(distanceMeters, timeSeconds);
+ mService.onNextManeuverDistanceChanged(distanceMeters, timeSeconds);
} catch (IllegalStateException e) {
CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
} catch (RemoteException e) {
@@ -191,56 +156,23 @@ public class CarNavigationManager implements CarManagerBase {
}
public boolean isInstrumentClusterSupported() throws CarNotConnectedException {
- try {
- return mService.isInstrumentClusterSupported();
- } catch (IllegalStateException e) {
- CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
- } catch (RemoteException e) {
- handleCarServiceRemoteExceptionAndThrow(e);
- }
- return false;
+ return mService != null;
}
@Override
public void onCarDisconnected() {
Log.d(TAG, "onCarDisconnected");
- unregisterListener();
}
- /**
- * @param listener {@link CarNavigationListener} to be registered, replacing any existing
- * listeners.
- * @throws CarNotConnectedException
- */
- public void registerListener(CarNavigationListener listener)
+ /** Returns navigation features of instrument cluster */
+ public CarNavigationInstrumentCluster getInstrumentClusterInfo()
throws CarNotConnectedException {
- mCarNavigationListener = listener;
- if (mICarNavigationEventListenerImpl == null) {
- mICarNavigationEventListenerImpl =
- new ICarNavigationEventListenerImpl(this);
- try {
- mService.registerEventListener(mICarNavigationEventListenerImpl);
- } catch (IllegalStateException e) {
- CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);
- } catch (RemoteException e) {
- handleCarServiceRemoteExceptionAndThrow(e);
- }
- }
- }
-
- /**
- * Unregisters {@link CarNavigationListener}.
- */
- public void unregisterListener() {
try {
- mService.unregisterEventListener(mICarNavigationEventListenerImpl);
- } catch (IllegalStateException e) {
- // ignore
+ return mService.getInstrumentClusterInfo();
} catch (RemoteException e) {
- // do not throw exception as this can happen during tearing down.
- handleCarServiceRemoteException(e);
+ handleCarServiceRemoteExceptionAndThrow(e);
}
- mCarNavigationListener = null;
+ return null;
}
private void handleCarServiceRemoteExceptionAndThrow(RemoteException e)
@@ -253,38 +185,4 @@ public class CarNavigationManager implements CarManagerBase {
Log.w(TAG, "RemoteException from car service:" + e.getMessage());
// nothing to do for now
}
-
- private void handleOnStart(CarNavigationInstrumentCluster instrumentCluster) {
- Log.d(TAG, "onStart(" + instrumentCluster + ")");
- mInstrumentCluster = new CarNavigationInstrumentCluster(instrumentCluster);
- mHandler.sendMessage(mHandler.obtainMessage(START));
- }
-
- private void handleOnStop() {
- mHandler.sendMessage(mHandler.obtainMessage(STOP));
- }
-
- private static class ICarNavigationEventListenerImpl extends ICarNavigationEventListener.Stub {
- private final WeakReference<CarNavigationManager> mManager;
-
- public ICarNavigationEventListenerImpl(CarNavigationManager manager) {
- mManager = new WeakReference<>(manager);
- }
-
- @Override
- public void onInstrumentClusterStart(CarNavigationInstrumentCluster instrumentClusterInfo) {
- CarNavigationManager manager = mManager.get();
- if (manager != null) {
- manager.handleOnStart(instrumentClusterInfo);
- }
- }
-
- @Override
- public void onInstrumentClusterStop() {
- CarNavigationManager manager = mManager.get();
- if (manager != null) {
- manager.handleOnStop();
- }
- }
- }
}
diff --git a/car-support-lib/api/current.txt b/car-support-lib/api/current.txt
index 0313f333a3..18d3ee310a 100644
--- a/car-support-lib/api/current.txt
+++ b/car-support-lib/api/current.txt
@@ -12,7 +12,7 @@ package android.support.car {
method public boolean isConnecting();
method public void registerCarConnectionListener(android.support.car.CarConnectionListener) throws android.support.car.CarNotConnectedException, java.lang.IllegalStateException;
method public void unregisterCarConnectionListener(android.support.car.CarConnectionListener);
- field public static final java.lang.String APP_CONTEXT_SERVICE = "app_context";
+ field public static final java.lang.String APP_FOCUS_SERVICE = "app_focus";
field public static final java.lang.String AUDIO_SERVICE = "audio";
field public static final int CONNECTION_TYPE_ADB_EMULATOR = 4; // 0x4
field public static final int CONNECTION_TYPE_EMBEDDED = 5; // 0x5
@@ -30,6 +30,30 @@ package android.support.car {
field public static final java.lang.String SENSOR_SERVICE = "sensor";
}
+ public abstract class CarAppFocusManager {
+ ctor public CarAppFocusManager();
+ method public abstract void abandonAppFocus(android.support.car.CarAppFocusManager.AppFocusOwnershipChangeListener, int) throws android.support.car.CarNotConnectedException;
+ method public abstract void abandonAppFocus(android.support.car.CarAppFocusManager.AppFocusOwnershipChangeListener) throws android.support.car.CarNotConnectedException;
+ method public abstract int[] getActiveAppTypes() throws android.support.car.CarNotConnectedException;
+ method public abstract boolean isOwningFocus(android.support.car.CarAppFocusManager.AppFocusOwnershipChangeListener, int) throws android.support.car.CarNotConnectedException;
+ method public abstract void registerFocusListener(android.support.car.CarAppFocusManager.AppFocusChangeListener, int) throws android.support.car.CarNotConnectedException;
+ method public abstract int requestAppFocus(android.support.car.CarAppFocusManager.AppFocusOwnershipChangeListener, int) throws android.support.car.CarNotConnectedException, java.lang.IllegalStateException, java.lang.SecurityException;
+ method public abstract void unregisterFocusListener(android.support.car.CarAppFocusManager.AppFocusChangeListener, int) throws android.support.car.CarNotConnectedException;
+ method public abstract void unregisterFocusListener(android.support.car.CarAppFocusManager.AppFocusChangeListener) throws android.support.car.CarNotConnectedException;
+ field public static final int APP_FOCUS_REQUEST_FAILED = 0; // 0x0
+ field public static final int APP_FOCUS_REQUEST_GRANTED = 1; // 0x1
+ field public static final int APP_FOCUS_TYPE_NAVIGATION = 1; // 0x1
+ field public static final int APP_FOCUS_TYPE_VOICE_COMMAND = 2; // 0x2
+ }
+
+ public static abstract interface CarAppFocusManager.AppFocusChangeListener {
+ method public abstract void onAppFocusChange(int, boolean);
+ }
+
+ public static abstract interface CarAppFocusManager.AppFocusOwnershipChangeListener {
+ method public abstract void onAppFocusOwnershipLoss(int);
+ }
+
public abstract interface CarConnectionListener {
method public abstract void onConnected(int);
method public abstract void onDisconnected();
@@ -647,11 +671,11 @@ package android.support.car.media {
public abstract class CarAudioManager {
ctor public CarAudioManager();
method public abstract int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
- method public abstract android.support.car.media.CarAudioRecord createCarAudioRecord(int) throws java.lang.SecurityException;
+ method public abstract android.support.car.media.CarAudioRecord createCarAudioRecord(int) throws android.support.car.CarNotConnectedException, android.support.car.CarNotSupportedException, java.lang.SecurityException;
method public abstract android.media.AudioAttributes getAudioAttributesForCarUsage(int);
method public abstract android.media.AudioFormat getAudioRecordAudioFormat();
- method public abstract int getAudioRecordMaxBufferSize();
- method public abstract int getAudioRecordMinBufferSize();
+ method public abstract int getAudioRecordMaxBufferSize() throws android.support.car.CarNotConnectedException, android.support.car.CarNotSupportedException;
+ method public abstract int getAudioRecordMinBufferSize() throws android.support.car.CarNotConnectedException, android.support.car.CarNotSupportedException;
method public abstract boolean isMediaMuted() throws android.support.car.CarNotConnectedException;
method public abstract int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
field public static final int CAR_AUDIO_USAGE_ALARM = 6; // 0x6
@@ -671,9 +695,9 @@ package android.support.car.media {
method public abstract int getBufferSize();
method public abstract int getRecordingState();
method public abstract int getState();
- method public abstract int read(byte[], int, int) throws java.lang.IllegalStateException;
+ method public abstract int read(byte[], int, int) throws android.support.car.CarNotConnectedException, java.lang.IllegalStateException;
method public abstract void release();
- method public abstract void startRecording();
+ method public abstract void startRecording() throws android.support.car.CarNotConnectedException;
method public abstract void stop();
}
diff --git a/car-support-lib/src/android/support/car/Car.java b/car-support-lib/src/android/support/car/Car.java
index d628e858a9..259c369ef8 100644
--- a/car-support-lib/src/android/support/car/Car.java
+++ b/car-support-lib/src/android/support/car/Car.java
@@ -16,17 +16,17 @@
package android.support.car;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.car.content.pm.CarPackageManager;
import android.support.car.hardware.CarSensorManager;
-import android.support.car.navigation.CarNavigationManager;
+import android.support.car.navigation.CarNavigationStatusManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -49,8 +49,8 @@ public class Car {
/** Service name for {@link CarInfoManager}, to be used in {@link #getCarManager(String)}. */
public static final String INFO_SERVICE = "info";
- /** Service name for {@link CarAppContextManager}. */
- public static final String APP_CONTEXT_SERVICE = "app_context";
+ /** Service name for {@link CarAppFocusManager}. */
+ public static final String APP_FOCUS_SERVICE = "app_focus";
/** Service name for {@link CarPackageManager} */
public static final String PACKAGE_SERVICE = "package";
@@ -58,7 +58,7 @@ public class Car {
/** Service name for {@link CarAudioManager} */
public static final String AUDIO_SERVICE = "audio";
/**
- * Service name for {@link CarNavigationManager}
+ * Service name for {@link CarNavigationStatusManager}
* @hide
*/
public static final String CAR_NAVIGATION_SERVICE = "car_navigation_service";
diff --git a/car-support-lib/src/android/support/car/CarAppContextManager.java b/car-support-lib/src/android/support/car/CarAppContextManager.java
deleted file mode 100644
index 6768e380a9..0000000000
--- a/car-support-lib/src/android/support/car/CarAppContextManager.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2015 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.support.car;
-
-/**
- * CarAppContextManager allows applications to set and listen for the current application context
- * like active navigation or active voice command. Usually only one instance of such application
- * should run in the system, and other app setting the flag for the matching app should
- * lead into other app to stop.
- * @hide
- */
-public abstract class CarAppContextManager implements CarManagerBase {
- /**
- * Listener to get notification for app getting information on app context change.
- */
- public interface AppContextChangeListener {
- /**
- * Application context has changed. Note that {@link CarAppContextManager} instance
- * causing the change will not get this notification.
- * @param activeContexts
- */
- void onAppContextChange(int activeContexts);
- }
-
- /**
- * Listener to get notification for app getting information on app context ownership loss.
- */
- public interface AppContextOwnershipChangeListener {
- /**
- * Lost ownership for the context, which happens when other app has set the context.
- * The app losing context should stop the action associated with the context.
- * For example, navigation app currently running active navigation should stop navigation
- * upon getting this for {@link CarAppContextManager#APP_CONTEXT_NAVIGATION}.
- * @param context
- */
- void onAppContextOwnershipLoss(int context);
- }
-
- /** @hide */
- public static final int APP_CONTEXT_START_FLAG = 0x1;
- /**
- * Flag for active navigation.
- */
- public static final int APP_CONTEXT_NAVIGATION = 0x1;
- /**
- * Flag for active voice command.
- */
- public static final int APP_CONTEXT_VOICE_COMMAND = 0x2;
- /**
- * Update this after adding a new flag.
- * @hide
- */
- public static final int APP_CONTEXT_END_FLAG = 0x2;
-
- /**
- * Register listener to monitor app context change. Only one listener can be registered and
- * registering multiple times will lead into only the last listener to be active.
- * @param listener
- * @param contextFilter Flags of contexts to get notification.
- * @throws CarNotConnectedException
- */
- public abstract void registerContextListener(AppContextChangeListener listener,
- int contextFilter) throws CarNotConnectedException;
-
- /**
- * Unregister listener and stop listening context change events. If app has owned a context
- * by {@link #setActiveContext(int)}, it will be reset to inactive state.
- * @throws CarNotConnectedException
- */
- public abstract void unregisterContextListener() throws CarNotConnectedException;
-
- /**
- * Retrieve currently active contexts.
- * @return
- * @throws CarNotConnectedException
- */
- public abstract int getActiveAppContexts() throws CarNotConnectedException;
-
- /**
- * Check if the current process is owning the given context.
- * @param context
- * @return
- * @throws CarNotConnectedException
- */
- public abstract boolean isOwningContext(int context) throws CarNotConnectedException;
-
- /**
- * Set the given contexts as active. By setting this, the application is becoming owner
- * of the context, and will get {@link AppContextChangeListener#onAppContextOwnershipLoss(int)}
- * if ownership is given to other app by calling this. Fore-ground app will have higher priority
- * and other app cannot set the same context while owner is in fore-ground.
- * Before calling this, {@link #registerContextListener(AppContextChangeListener, int)} should
- * be called first. Otherwise, it will throw IllegalStateException
- * @param contexts
- * @throws IllegalStateException If listener was not registered.
- * @throws SecurityException If owner cannot be changed.
- * @throws CarNotConnectedException
- */
- public abstract void setActiveContexts(AppContextOwnershipChangeListener ownershipListener,
- int contexts) throws IllegalStateException, SecurityException, CarNotConnectedException;
-
- /**
- * Reset the given contexts, i.e. mark them as inactive. This also involves releasing ownership
- * for the context.
- * @param contexts
- * @throws CarNotConnectedException
- */
- public abstract void resetActiveContexts(int contexts) throws CarNotConnectedException;
-}
diff --git a/car-support-lib/src/android/support/car/CarAppContextManagerEmbedded.java b/car-support-lib/src/android/support/car/CarAppContextManagerEmbedded.java
deleted file mode 100644
index cd145ac37b..0000000000
--- a/car-support-lib/src/android/support/car/CarAppContextManagerEmbedded.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * 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.
- */
-
-package android.support.car;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Implementation of {@link CarAppContextManager} for embedded.
- * @hide
- */
-public class CarAppContextManagerEmbedded extends CarAppContextManager {
-
- private final android.car.CarAppContextManager mManager;
- private AppContextChangeListenerProxy mListener;
- private final Map<Integer, AppContextOwnershipChangeListenerProxy> mOwnershipListeners;
-
- /**
- * @hide
- */
- CarAppContextManagerEmbedded(Object manager) {
- mManager = (android.car.CarAppContextManager) manager;
- mOwnershipListeners = new HashMap<>();
- }
-
- @Override
- public void registerContextListener(AppContextChangeListener listener, int contextFilter)
- throws CarNotConnectedException {
- if (listener == null) {
- throw new IllegalArgumentException("null listener");
- }
- AppContextChangeListenerProxy proxy = new AppContextChangeListenerProxy(listener);
- synchronized(this) {
- mListener = proxy;
- }
- try {
- mManager.registerContextListener(proxy, contextFilter);
- } catch (android.car.CarNotConnectedException e) {
- throw new CarNotConnectedException(e);
- }
- }
-
- @Override
- public void unregisterContextListener() throws CarNotConnectedException {
- synchronized(this) {
- mListener = null;
- }
- try {
- mManager.unregisterContextListener();
- } catch (android.car.CarNotConnectedException e) {
- throw new CarNotConnectedException(e);
- }
-
- }
-
- @Override
- public int getActiveAppContexts() throws CarNotConnectedException {
- try {
- return mManager.getActiveAppContexts();
- } catch (android.car.CarNotConnectedException e) {
- throw new CarNotConnectedException(e);
- }
- }
-
- @Override
- public boolean isOwningContext(int context) throws CarNotConnectedException {
- try {
- return mManager.isOwningContext(context);
- } catch (android.car.CarNotConnectedException e) {
- throw new CarNotConnectedException(e);
- }
- }
-
- @Override
- public void setActiveContexts(AppContextOwnershipChangeListener ownershipListener,
- int contexts) throws IllegalStateException, SecurityException,
- CarNotConnectedException {
- if (ownershipListener == null) {
- throw new IllegalArgumentException("null listener");
- }
- AppContextOwnershipChangeListenerProxy proxy =
- new AppContextOwnershipChangeListenerProxy(ownershipListener);
- synchronized(this) {
- for (int flag = APP_CONTEXT_START_FLAG; flag <= APP_CONTEXT_END_FLAG; flag <<= 1) {
- if ((flag & contexts) != 0) {
- mOwnershipListeners.put(flag, proxy);
- }
- }
- }
- try{
- mManager.setActiveContexts(proxy, contexts);
- } catch (android.car.CarNotConnectedException e) {
- throw new CarNotConnectedException(e);
- }
- }
-
- @Override
- public void resetActiveContexts(int contexts) throws CarNotConnectedException {
- try {
- mManager.resetActiveContexts(contexts);
- } catch (android.car.CarNotConnectedException e) {
- throw new CarNotConnectedException(e);
- }
- synchronized (this) {
- for (int flag = APP_CONTEXT_START_FLAG; flag <= APP_CONTEXT_END_FLAG; flag <<= 1) {
- if ((flag & contexts) != 0) {
- mOwnershipListeners.remove(flag);
- }
- }
- }
- }
-
- @Override
- public void onCarDisconnected() {
- // nothing to do
- }
-
- private static class AppContextChangeListenerProxy implements
- android.car.CarAppContextManager.AppContextChangeListener {
-
- private final AppContextChangeListener mListener;
-
- public AppContextChangeListenerProxy(AppContextChangeListener listener) {
- mListener = listener;
- }
-
- @Override
- public void onAppContextChange(int activeContexts) {
- mListener.onAppContextChange(activeContexts);
- }
- }
-
- private static class AppContextOwnershipChangeListenerProxy implements
- android.car.CarAppContextManager.AppContextOwnershipChangeListener {
-
- private final AppContextOwnershipChangeListener mListener;
-
- public AppContextOwnershipChangeListenerProxy(AppContextOwnershipChangeListener listener) {
- mListener = listener;
- }
-
- @Override
- public void onAppContextOwnershipLoss(int context) {
- mListener.onAppContextOwnershipLoss(context);
- }
- }
-}
diff --git a/car-support-lib/src/android/support/car/CarAppFocusManager.java b/car-support-lib/src/android/support/car/CarAppFocusManager.java
new file mode 100644
index 0000000000..f5c8679fb2
--- /dev/null
+++ b/car-support-lib/src/android/support/car/CarAppFocusManager.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 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.support.car;
+
+/**
+ * CarAppFocusManager allows applications to set and listen for the current application focus
+ * like active navigation or active voice command. Usually only one instance of such application
+ * should run in the system, and other app setting the flag for the matching app should
+ * lead into other app to stop.
+ */
+public abstract class CarAppFocusManager implements CarManagerBase {
+ /**
+ * Listener to get notification for app getting information on application type status changes.
+ */
+ public interface AppFocusChangeListener {
+ /**
+ * Application focus has changed. Note that {@link CarAppFocusManager} instance
+ * causing the change will not get this notification.
+ * @param appType Application type that got status changed.
+ * @param active Active state: true - active, false - inactive.
+ */
+ void onAppFocusChange(int appType, boolean active);
+ }
+
+ /**
+ * Listener to get notification for app getting information on app type ownership loss.
+ */
+ public interface AppFocusOwnershipChangeListener {
+ /**
+ * Lost ownership for the focus, which happens when other app has set the focus.
+ * The app losing focus should stop the action associated with the focus.
+ * For example, navigation app currently running active navigation should stop navigation
+ * upon getting this for {@link CarAppFocusManager#APP_FOCUS_TYPE_NAVIGATION}.
+ * @param appType
+ */
+ void onAppFocusOwnershipLoss(int appType);
+ }
+
+ /**
+ * Represents navigation focus.
+ */
+ public static final int APP_FOCUS_TYPE_NAVIGATION = 1;
+ /**
+ * Represents voice command focus.
+ */
+ public static final int APP_FOCUS_TYPE_VOICE_COMMAND = 2;
+ /**
+ * Update this after adding a new app type.
+ * @hide
+ */
+ public static final int APP_FOCUS_TYPE_MAX = 2;
+
+ /**
+ * A failed focus change request.
+ */
+ public static final int APP_FOCUS_REQUEST_FAILED = 0;
+ /**
+ * A successful focus change request.
+ */
+ public static final int APP_FOCUS_REQUEST_GRANTED = 1;
+
+ /**
+ * Register listener to monitor app focus change.
+ * Multiple listeners can be registered for a single focus and the same listener can be used
+ * for multiple focuses.
+ * @param listener Listener to register for focus events.
+ * @param appType Application type to get notification for.
+ * @throws CarNotConnectedException
+ */
+ public abstract void registerFocusListener(AppFocusChangeListener listener, int appType)
+ throws CarNotConnectedException;
+
+ /**
+ * Unregister listener for app type and stop listening focus change events.
+ * @param listener Listener to unregister from focus events.
+ * @param appType Application type to get notification for.
+ * @throws CarNotConnectedException
+ */
+ public abstract void unregisterFocusListener(AppFocusChangeListener listener, int appType)
+ throws CarNotConnectedException;
+
+ /**
+ * Unregister listener for all app types and stop listening focus change events.
+ * @param listener Listener to unregister from focus events.
+ * @throws CarNotConnectedException
+ */
+ public abstract void unregisterFocusListener(AppFocusChangeListener listener)
+ throws CarNotConnectedException;
+
+ /**
+ * Retrieve currently active application types.
+ * @return Currently active application types.
+ * @throws CarNotConnectedException
+ */
+ public abstract int[] getActiveAppTypes() throws CarNotConnectedException;
+
+ /**
+ * Check if the current process owns the given focus.
+ * @param listener Listener that was used to request ownership.
+ * @param appType Application type.
+ * @return True if current listener owns focus for application type.
+ * @throws CarNotConnectedException
+ */
+ public abstract boolean isOwningFocus(AppFocusOwnershipChangeListener listener, int appType)
+ throws CarNotConnectedException;
+
+ /**
+ * Requests application focus.
+ * By requesting this, the app gains the focus for this appType.
+ * {@link AppFocusOwnershipChangeListener#onAppFocusOwnershipLoss(int)} will be sent to
+ * the app that currently holds focus.
+ * Fore-ground app will have higher priority and other app cannot set the same focus while
+ * owner is in fore-ground.
+ * @param ownershipListener Ownership listener to request app focus for. Cannot be null.
+ * @param appType Application type to request focus for.
+ * @return {@link #APP_FOCUS_REQUEST_FAILED} or {@link #APP_FOCUS_REQUEST_GRANTED}
+ * @throws IllegalStateException If listener was not registered.
+ * @throws SecurityException If owner cannot be changed.
+ * @throws CarNotConnectedException
+ */
+ public abstract int requestAppFocus(AppFocusOwnershipChangeListener ownershipListener,
+ int appType) throws IllegalStateException, SecurityException, CarNotConnectedException;
+
+ /**
+ * Abandon the given focus, i.e. mark it as inactive.
+ * @param ownershipListener Ownership listener to abandon app focus for. Cannot be null.
+ * @param appType Application type to abandon focus for.
+ * @throws CarNotConnectedException
+ */
+ public abstract void abandonAppFocus(AppFocusOwnershipChangeListener ownershipListener,
+ int appType) throws CarNotConnectedException;
+
+ /**
+ * Abandon all focuses, i.e. mark thme as inactive.
+ * @param ownershipListener Ownership listener to abandon focus for. Cannot be null.
+ * @throws CarNotConnectedException
+ */
+ public abstract void abandonAppFocus(AppFocusOwnershipChangeListener ownershipListener)
+ throws CarNotConnectedException;
+}
diff --git a/car-support-lib/src/android/support/car/CarAppFocusManagerEmbedded.java b/car-support-lib/src/android/support/car/CarAppFocusManagerEmbedded.java
new file mode 100644
index 0000000000..e6a99416b0
--- /dev/null
+++ b/car-support-lib/src/android/support/car/CarAppFocusManagerEmbedded.java
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+
+package android.support.car;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Implementation of {@link CarAppFocusManager} for embedded.
+ * @hide
+ */
+public class CarAppFocusManagerEmbedded extends CarAppFocusManager {
+
+ private final android.car.CarAppFocusManager mManager;
+
+ private final Map<AppFocusChangeListener, AppFocusChangeListenerProxy>
+ mChangeListeners = new HashMap<>();
+ private final Map<AppFocusOwnershipChangeListener, AppFocusOwnershipChangeListenerProxy>
+ mOwnershipListeners = new HashMap<>();
+
+ /**
+ * @hide
+ */
+ CarAppFocusManagerEmbedded(Object manager) {
+ mManager = (android.car.CarAppFocusManager) manager;
+ }
+
+ @Override
+ public void registerFocusListener(AppFocusChangeListener listener, int appType)
+ throws CarNotConnectedException {
+ if (listener == null) {
+ throw new IllegalArgumentException("null listener");
+ }
+ AppFocusChangeListenerProxy proxy;
+ synchronized (this) {
+ proxy = mChangeListeners.get(listener);
+ if (proxy == null) {
+ proxy = new AppFocusChangeListenerProxy(listener);
+ mChangeListeners.put(listener, proxy);
+ }
+ }
+ try {
+ mManager.registerFocusListener(proxy, appType);
+ } catch (android.car.CarNotConnectedException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ @Override
+ public void unregisterFocusListener(AppFocusChangeListener listener, int appType)
+ throws CarNotConnectedException {
+ AppFocusChangeListenerProxy proxy;
+ synchronized (this) {
+ proxy = mChangeListeners.get(listener);
+ if (proxy == null) {
+ return;
+ }
+ }
+ try {
+ mManager.unregisterFocusListener(proxy, appType);
+ } catch (android.car.CarNotConnectedException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ @Override
+ public void unregisterFocusListener(AppFocusChangeListener listener)
+ throws CarNotConnectedException {
+ AppFocusChangeListenerProxy proxy;
+ synchronized (this) {
+ proxy = mChangeListeners.remove(listener);
+ if (proxy == null) {
+ return;
+ }
+ }
+ try {
+ mManager.unregisterFocusListener(proxy);
+ } catch (android.car.CarNotConnectedException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ @Override
+ public int[] getActiveAppTypes() throws CarNotConnectedException {
+ try {
+ return mManager.getActiveAppTypes();
+ } catch (android.car.CarNotConnectedException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ @Override
+ public boolean isOwningFocus(AppFocusOwnershipChangeListener listener, int appType)
+ throws CarNotConnectedException {
+ AppFocusOwnershipChangeListenerProxy proxy;
+ synchronized (this) {
+ proxy = mOwnershipListeners.get(listener);
+ if (proxy == null) {
+ return false;
+ }
+ }
+ try {
+ return mManager.isOwningFocus(proxy, appType);
+ } catch (android.car.CarNotConnectedException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ @Override
+ public int requestAppFocus(AppFocusOwnershipChangeListener ownershipListener, int appType)
+ throws IllegalStateException, SecurityException, CarNotConnectedException {
+ if (ownershipListener == null) {
+ throw new IllegalArgumentException("null listener");
+ }
+ AppFocusOwnershipChangeListenerProxy proxy;
+ synchronized (this) {
+ proxy = mOwnershipListeners.get(ownershipListener);
+ if (proxy == null) {
+ proxy = new AppFocusOwnershipChangeListenerProxy(ownershipListener);
+ mOwnershipListeners.put(ownershipListener, proxy);
+ }
+ }
+ try {
+ return mManager.requestAppFocus(proxy, appType);
+ } catch (android.car.CarNotConnectedException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ @Override
+ public void abandonAppFocus(AppFocusOwnershipChangeListener ownershipListener, int appType)
+ throws CarNotConnectedException {
+ if (ownershipListener == null) {
+ throw new IllegalArgumentException("null listener");
+ }
+ AppFocusOwnershipChangeListenerProxy proxy;
+ synchronized (this) {
+ proxy = mOwnershipListeners.get(ownershipListener);
+ if (proxy == null) {
+ return;
+ }
+ }
+ try {
+ mManager.abandonAppFocus(proxy, appType);
+ } catch (android.car.CarNotConnectedException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ @Override
+ public void abandonAppFocus(AppFocusOwnershipChangeListener ownershipListener)
+ throws CarNotConnectedException {
+ if (ownershipListener == null) {
+ throw new IllegalArgumentException("null listener");
+ }
+ AppFocusOwnershipChangeListenerProxy proxy;
+ synchronized (this) {
+ proxy = mOwnershipListeners.get(ownershipListener);
+ if (proxy == null) {
+ return;
+ }
+ }
+ try {
+ mManager.abandonAppFocus(proxy);
+ } catch (android.car.CarNotConnectedException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ @Override
+ public void onCarDisconnected() {
+ // nothing to do
+ }
+
+ private static class AppFocusChangeListenerProxy implements
+ android.car.CarAppFocusManager.AppFocusChangeListener {
+
+ private final AppFocusChangeListener mListener;
+
+ AppFocusChangeListenerProxy(AppFocusChangeListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onAppFocusChange(int appType, boolean active) {
+ mListener.onAppFocusChange(appType, active);
+ }
+ }
+
+ private static class AppFocusOwnershipChangeListenerProxy implements
+ android.car.CarAppFocusManager.AppFocusOwnershipChangeListener {
+
+ private final AppFocusOwnershipChangeListener mListener;
+
+ AppFocusOwnershipChangeListenerProxy(AppFocusOwnershipChangeListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onAppFocusOwnershipLoss(int focus) {
+ mListener.onAppFocusOwnershipLoss(focus);
+ }
+ }
+}
diff --git a/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java b/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java
index f22b95a218..84a2911785 100644
--- a/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java
+++ b/car-support-lib/src/android/support/car/CarServiceLoaderEmbedded.java
@@ -26,7 +26,7 @@ import android.os.Message;
import android.support.car.content.pm.CarPackageManagerEmbedded;
import android.support.car.hardware.CarSensorManagerEmbedded;
import android.support.car.media.CarAudioManagerEmbedded;
-import android.support.car.navigation.CarNavigationManagerEmbedded;
+import android.support.car.navigation.CarNavigationStatusManagerEmbedded;
import java.util.LinkedList;
@@ -121,12 +121,16 @@ public class CarServiceLoaderEmbedded extends CarServiceLoader {
return new CarSensorManagerEmbedded(manager, getContext());
case Car.INFO_SERVICE:
return new CarInfoManagerEmbedded(manager);
- case Car.APP_CONTEXT_SERVICE:
- return new CarAppContextManagerEmbedded(manager);
+ case Car.APP_FOCUS_SERVICE:
+ return new CarAppFocusManagerEmbedded(manager);
case Car.PACKAGE_SERVICE:
return new CarPackageManagerEmbedded(manager);
case Car.CAR_NAVIGATION_SERVICE:
- return new CarNavigationManagerEmbedded(manager);
+ try {
+ return new CarNavigationStatusManagerEmbedded(manager);
+ } catch (CarNotSupportedException e) {
+ return null;
+ }
default:
return manager;
}
diff --git a/car-support-lib/src/android/support/car/media/CarAudioManager.java b/car-support-lib/src/android/support/car/media/CarAudioManager.java
index 864f5ac49c..16809c1d15 100644
--- a/car-support-lib/src/android/support/car/media/CarAudioManager.java
+++ b/car-support-lib/src/android/support/car/media/CarAudioManager.java
@@ -23,6 +23,7 @@ import android.support.annotation.IntDef;
import android.support.annotation.RequiresPermission;
import android.support.car.CarManagerBase;
import android.support.car.CarNotConnectedException;
+import android.support.car.CarNotSupportedException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -119,30 +120,37 @@ public abstract class CarAudioManager implements CarManagerBase {
/**
* Get minimum buffer size for {@link CarAudioRecord}.
+ *
* @return buffer size in bytes.
*/
- public abstract int getAudioRecordMinBufferSize();
+ public abstract int getAudioRecordMinBufferSize()
+ throws CarNotConnectedException, CarNotSupportedException;
/**
* Get maximum buffer size for {@link CarAudioRecord}.
+ *
* @return buffer size in bytes.
*/
- public abstract int getAudioRecordMaxBufferSize();
+ public abstract int getAudioRecordMaxBufferSize()
+ throws CarNotConnectedException, CarNotSupportedException;
/**
* Create a {@link CarAudioRecord} for the current {@link CarAudioManager}. There can be
- * multiple instances of {@link CarAudioRecord}.
- * This requires {@link android.Manifest.permission#RECORD_AUDIO} permission.
- * @param bufferSize It should be a multiple of minimum buffer size acquired from
- * {@link #getAudioRecordMinBufferSize()}. This cannot exceed
- * {@link #getAudioRecordMaxBufferSize()}.
+ * multiple instances of {@link CarAudioRecord}. This requires {@link
+ * android.Manifest.permission#RECORD_AUDIO} permission.
+ *
+ * @param bufferSize It should be a multiple of minimum buffer size acquired from {@link
+ * #getAudioRecordMinBufferSize()}. This cannot exceed {@link #getAudioRecordMaxBufferSize()}.
+ *
* @return {@link CarAudioRecord} instance for the given stream.
* @throws IllegalArgumentException if passed parameter like bufferSize is wrong.
* @throws SecurityException if client does not have
- * {@link android.Manifest.permission#RECORD_AUDIO} permission.
+ * {@link android.Manifest.permission#RECORD_AUDIO}
+ * permission.
*/
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
- public abstract CarAudioRecord createCarAudioRecord(int bufferSize) throws SecurityException;
+ public abstract CarAudioRecord createCarAudioRecord(int bufferSize)
+ throws SecurityException, CarNotConnectedException, CarNotSupportedException;
/**
* Check if media audio is muted or not. This will include music and radio. Any application
diff --git a/car-support-lib/src/android/support/car/media/CarAudioRecord.java b/car-support-lib/src/android/support/car/media/CarAudioRecord.java
index 5e8e945526..98d3535dce 100644
--- a/car-support-lib/src/android/support/car/media/CarAudioRecord.java
+++ b/car-support-lib/src/android/support/car/media/CarAudioRecord.java
@@ -15,6 +15,8 @@
*/
package android.support.car.media;
+import android.support.car.CarNotConnectedException;
+
/**
* CarAudioRecord allows apps to use microphone.
*/
@@ -28,7 +30,7 @@ public interface CarAudioRecord {
/**
* Start audio recording.
*/
- void startRecording();
+ void startRecording() throws CarNotConnectedException;
/**
* Stop audio recording. Calling stop multiple times will be a safe operation.
@@ -60,5 +62,5 @@ public interface CarAudioRecord {
* @throws IllegalStateException if audio recording was not started.
*/
int read(byte[] audioData, int offsetInBytes, int sizeInBytes)
- throws IllegalStateException;
+ throws IllegalStateException, CarNotConnectedException;
}
diff --git a/car-support-lib/src/android/support/car/navigation/CarNavigationManager.java b/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManager.java
index d723bc0719..0142fc5987 100644
--- a/car-support-lib/src/android/support/car/navigation/CarNavigationManager.java
+++ b/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManager.java
@@ -23,7 +23,7 @@ import android.support.car.CarNotConnectedException;
* API for providing navigation status for instrument cluster.
* @hide
*/
-public abstract class CarNavigationManager implements CarManagerBase {
+public interface CarNavigationStatusManager extends CarManagerBase {
/**
* Listener navigation related events.
@@ -64,12 +64,27 @@ public abstract class CarNavigationManager implements CarManagerBase {
public static final int TURN_SIDE_RIGHT = 2;
public static final int TURN_SIDE_UNSPECIFIED = 3;
+
+ /**
+ * Distance units for use in {@link #sendNavigationTurnDistanceEvent(int, int, int, int)}.
+ * DISTANCE_KILOMETERS_P1 and DISTANCE_MILES_P1 are the same as their respective
+ * units, except they require that the head unit display at least 1 digit after the
+ * decimal (e.g. 2.0).
+ */
+ public static final int DISTANCE_METERS = 1;
+ public static final int DISTANCE_KILOMETERS = 2;
+ public static final int DISTANCE_KILOMETERS_P1 = 3;
+ public static final int DISTANCE_MILES = 4;
+ public static final int DISTANCE_MILES_P1 = 5;
+ public static final int DISTANCE_FEET = 6;
+ public static final int DISTANCE_YARDS = 7;
+
/**
* @param status new instrument cluster navigation status.
* @return true if successful.
* @throws CarNotConnectedException
*/
- public abstract boolean sendNavigationStatus(int status) throws CarNotConnectedException;
+ boolean sendNavigationStatus(int status) throws CarNotConnectedException;
/**
* Sends a Navigation Next Step event to the car.
@@ -88,9 +103,9 @@ public abstract class CarNavigationManager implements CarManagerBase {
* used for event type {@link #TURN_ROUNDABOUT_ENTER_AND_EXIT}. -1 if unused.
* @param turnNumber turn number, counting around from the roundabout entry to the exit. Only
* used for event type {@link #TURN_ROUNDABOUT_ENTER_AND_EXIT}. -1 if unused.
- * @param image image to be shown in the instrument cluster (PNG format). Null if instrument
- * cluster type is {@link #INSTRUMENT_CLUSTER_TYPE_ENUM}, or if
- * the image parameters are malformed (length or width non-positive, or illegal
+ * @param image image to be shown in the instrument cluster. Null if instrument
+ * cluster type is {@link CarNavigationInstrumentCluster.ClusterType#IMAGE_CODES_ONLY},
+ * or if the image parameters are malformed (length or width non-positive, or illegal
* imageColorDepthBits) in the initial NavigationStatusService call.
* @param turnSide turn side ({@link #TURN_SIDE_LEFT}, {@link #TURN_SIDE_RIGHT} or
* {@link #TURN_SIDE_UNSPECIFIED}).
@@ -98,32 +113,34 @@ public abstract class CarNavigationManager implements CarManagerBase {
* @throws CarNotConnectedException
*
*/
- public abstract boolean sendNavigationTurnEvent(int event, String road, int turnAngle,
+ boolean sendNavigationTurnEvent(int event, String road, int turnAngle,
int turnNumber, Bitmap image, int turnSide) throws CarNotConnectedException;
/**
* Sends a Navigation Next Step Distance event to the car.
*
- * @param distanceMeters Distance to next event in meters.
- * @param timeSeconds Time to next event in seconds.
+ * @param distanceMeters distance to next event in meters.
+ * @param timeSeconds time to next event in seconds.
+ * @param displayDistanceMillis distance to the next event formatted as it will be displayed
+ * by the calling app, in milli-units. For example, 1.25 should be supplied as 1250
+ * @param displayDistanceUnit the unit type to use on of the DISTANCE_* types defined in this
+ * file.
* @return true if successful.
* @throws CarNotConnectedException
*/
- public abstract boolean sendNavigationTurnDistanceEvent(int distanceMeters, int timeSeconds)
- throws CarNotConnectedException;
-
- public abstract boolean isInstrumentClusterSupported() throws CarNotConnectedException;
+ boolean sendNavigationTurnDistanceEvent(int distanceMeters, int timeSeconds,
+ int displayDistanceMillis, int displayDistanceUnit) throws CarNotConnectedException;
/**
* @param listener {@link CarNavigationListener} to be registered, replacing any existing
* listeners.
* @throws CarNotConnectedException
*/
- public abstract void registerListener(CarNavigationListener listener)
+ void registerListener(CarNavigationListener listener)
throws CarNotConnectedException;
/**
* Unregisters {@link CarNavigationListener}.
*/
- public abstract void unregisterListener();
+ void unregisterListener();
}
diff --git a/car-support-lib/src/android/support/car/navigation/CarNavigationManagerEmbedded.java b/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManagerEmbedded.java
index 65b52113a3..32ded94a11 100644
--- a/car-support-lib/src/android/support/car/navigation/CarNavigationManagerEmbedded.java
+++ b/car-support-lib/src/android/support/car/navigation/CarNavigationStatusManagerEmbedded.java
@@ -17,17 +17,25 @@ package android.support.car.navigation;
import android.graphics.Bitmap;
import android.support.car.CarNotConnectedException;
+import android.support.car.CarNotSupportedException;
/**
* @hide
*/
-public class CarNavigationManagerEmbedded extends CarNavigationManager {
+public class CarNavigationStatusManagerEmbedded implements CarNavigationStatusManager {
private final android.car.navigation.CarNavigationManager mManager;
- private CarNavigationListenerProxy mListener;
- public CarNavigationManagerEmbedded(Object manager) {
+ public CarNavigationStatusManagerEmbedded(Object manager)
+ throws CarNotSupportedException, CarNotConnectedException {
mManager = (android.car.navigation.CarNavigationManager) manager;
+ try {
+ if (!mManager.isInstrumentClusterSupported()){
+ throw new CarNotSupportedException();
+ }
+ } catch (android.car.CarNotConnectedException e) {
+ throw new CarNotConnectedException(e);
+ }
}
/**
@@ -56,8 +64,8 @@ public class CarNavigationManagerEmbedded extends CarNavigationManager {
}
@Override
- public boolean sendNavigationTurnDistanceEvent(int distanceMeters, int timeSeconds)
- throws CarNotConnectedException {
+ public boolean sendNavigationTurnDistanceEvent(int distanceMeters, int timeSeconds,
+ int displayDistanceMillis, int displayDistanceUnit) throws CarNotConnectedException {
try {
return mManager.sendNavigationTurnDistanceEvent(distanceMeters, timeSeconds);
} catch (android.car.CarNotConnectedException e) {
@@ -66,29 +74,22 @@ public class CarNavigationManagerEmbedded extends CarNavigationManager {
}
@Override
- public boolean isInstrumentClusterSupported() throws CarNotConnectedException {
- try {
- return mManager.isInstrumentClusterSupported();
- } catch (android.car.CarNotConnectedException e) {
- throw new CarNotConnectedException(e);
- }
- }
-
- @Override
public void onCarDisconnected() {
//nothing to do
}
+ /**
+ * In this implementation we just immediately call {@code listener#onInstrumentClusterStart} as
+ * we expect instrument cluster to be working all the time.
+ *
+ * @throws CarNotConnectedException
+ */
@Override
public void registerListener(CarNavigationListener listener)
throws CarNotConnectedException {
- CarNavigationListenerProxy proxy = null;
- synchronized (this) {
- proxy = new CarNavigationListenerProxy(listener);
- mListener = proxy;
- }
+
try {
- mManager.registerListener(proxy);
+ listener.onInstrumentClusterStart(convert(mManager.getInstrumentClusterInfo()));
} catch (android.car.CarNotConnectedException e) {
throw new CarNotConnectedException(e);
}
@@ -96,10 +97,7 @@ public class CarNavigationManagerEmbedded extends CarNavigationManager {
@Override
public void unregisterListener() {
- synchronized (this) {
- mListener = null;
- }
- mManager.unregisterListener();
+ // Nothing to do.
}
private static CarNavigationInstrumentCluster convert(
@@ -110,25 +108,4 @@ public class CarNavigationManagerEmbedded extends CarNavigationManager {
return new CarNavigationInstrumentCluster(ic.getMinIntervalMs(), ic.getType(),
ic.getImageWidth(), ic.getImageHeight(), ic.getImageColorDepthBits());
}
-
- private static class CarNavigationListenerProxy implements
- android.car.navigation.CarNavigationManager.CarNavigationListener {
-
- private final CarNavigationListener mListener;
-
- private CarNavigationListenerProxy(CarNavigationListener listener) {
- mListener = listener;
- }
-
- @Override
- public void onInstrumentClusterStart(
- android.car.navigation.CarNavigationInstrumentCluster instrumentCluster) {
- mListener.onInstrumentClusterStart(convert(instrumentCluster));
- }
-
- @Override
- public void onInstrumentClusterStop() {
- mListener.onInstrumentClusterStop();
- }
- }
}
diff --git a/car-systemtest-lib/src/android/car/test/VehicleHalEmulator.java b/car-systemtest-lib/src/android/car/test/VehicleHalEmulator.java
index fdb81074c4..afe3f4c210 100644
--- a/car-systemtest-lib/src/android/car/test/VehicleHalEmulator.java
+++ b/car-systemtest-lib/src/android/car/test/VehicleHalEmulator.java
@@ -202,6 +202,7 @@ public class VehicleHalEmulator {
switch (property) {
case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME:
case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS:
+ case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT:
case VehicleNetworkConsts.VEHICLE_PROPERTY_AP_POWER_STATE:
case VehicleNetworkConsts.VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON:
continue;
diff --git a/car_product/build/car.mk b/car_product/build/car.mk
index cf7b9a4b3d..aa8a597cbe 100644
--- a/car_product/build/car.mk
+++ b/car_product/build/car.mk
@@ -18,7 +18,6 @@
PRODUCT_PACKAGES += \
Bluetooth \
- BluetoothMidiService \
bt-map-service \
OneTimeInitializer \
Provision \
diff --git a/car_product/build/car_base.mk b/car_product/build/car_base.mk
index 590a7e7b94..d192a81581 100644
--- a/car_product/build/car_base.mk
+++ b/car_product/build/car_base.mk
@@ -26,7 +26,6 @@ PRODUCT_PACKAGES += \
BasicDreams \
CaptivePortalLogin \
CertInstaller \
- DeskClock \
DocumentsUI \
DownloadProviderUi \
FusedLocation \
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/config.xml b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
index 141550ba56..584961a1a6 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
@@ -41,6 +41,8 @@
<!-- Allow smart unlock immediately after boot because the user shouldn't have to enter a pin
code to unlock their car head unit. -->
<bool name="config_strongAuthRequiredOnBoot">false</bool>
+ <!-- Show Navigation Bar -->
+ <bool name="config_showNavigationBar">true</bool>
<integer name="config_jobSchedulerInactivityIdleThreshold">0</integer>
<integer name="config_jobSchedulerIdleWindowSlop">0</integer>
diff --git a/car_product/sepolicy/atfwd.te b/car_product/sepolicy/atfwd.te
deleted file mode 100644
index f9c5eb1b0c..0000000000
--- a/car_product/sepolicy/atfwd.te
+++ /dev/null
@@ -1,14 +0,0 @@
-type atfwd, domain;
-type atfwd_exec, exec_type, file_type;
-
-init_daemon_domain(atfwd)
-
-# Creates/Talks to qmuxd via the qmux_radio socket.
-qmux_socket(atfwd)
-
-# Set radio.atfwd.* properties.
-set_prop(atfwd, radio_atfwd_prop)
-
-userdebug_or_eng(`
- allow atfwd diag_device:chr_file rw_file_perms;
-')
diff --git a/car_product/sepolicy/bluetooth.te b/car_product/sepolicy/bluetooth.te
deleted file mode 100644
index f93e040eca..0000000000
--- a/car_product/sepolicy/bluetooth.te
+++ /dev/null
@@ -1,18 +0,0 @@
-# Allow access to wc_transport.* properties.
-set_prop(bluetooth, wc_transport_prop)
-
-# Allow access to /dev/ttyHS0
-allow bluetooth serial_device:chr_file rw_file_perms;
-
-# Connect to start_hci_filter service.
-allow bluetooth start_hci_filter:unix_stream_socket connectto;
-
-# Allow access to /persist/.bt_nv.bin.
-allow bluetooth persist_file:file rw_file_perms;
-
-# Allow access to /bt_firmware files.
-allow bluetooth bt_firmware_file:file r_file_perms;
-
-# Allow access to bt_power sysfs nodes.
-r_dir_file(bluetooth, sysfs_bt_power);
-allow bluetooth sysfs_bt_power:file w_file_perms;
diff --git a/car_product/sepolicy/can.te b/car_product/sepolicy/can.te
deleted file mode 100644
index e18b839bec..0000000000
--- a/car_product/sepolicy/can.te
+++ /dev/null
@@ -1,18 +0,0 @@
-# CAN service
-type can, domain;
-type can_exec, exec_type, file_type;
-
-# Started by init
-init_daemon_domain(can)
-
-allow can self:capability net_admin;
-
-allow can self:netlink_route_socket nlmsg_write;
-
-allow can shell_exec:file r_file_perms;
-
-# Allow execution of /system/bin/ip.
-allow can system_file:file rx_file_perms;
-
-# Allow can operations
-allow can self:capability { net_raw };
diff --git a/car_product/sepolicy/config_bluetooth.te b/car_product/sepolicy/config_bluetooth.te
deleted file mode 100644
index f0c8789589..0000000000
--- a/car_product/sepolicy/config_bluetooth.te
+++ /dev/null
@@ -1,22 +0,0 @@
-# config_bluetooth service
-type config_bluetooth, domain;
-type config_bluetooth_exec, exec_type, file_type;
-
-# Started by init
-init_daemon_domain(config_bluetooth)
-
-# Set bluetooth.* and qualcomm.bluetooth.* properties.
-set_prop(config_bluetooth, bluetooth_prop);
-
-# Allow execution of /system/bin/btnvtool.
-allow config_bluetooth btnvtool_exec:file rx_file_perms;
-
-# Allow access to /persist/.bt_nv.bin.
-allow config_bluetooth persist_file:dir w_dir_perms;
-allow config_bluetooth persist_file:file create_file_perms;
-
-allow config_bluetooth shell_exec:file r_file_perms;
-
-allow config_bluetooth toolbox_exec:file rx_file_perms;
-
-allow config_bluetooth sysfs:file r_file_perms;
diff --git a/car_product/sepolicy/device.te b/car_product/sepolicy/device.te
deleted file mode 100644
index 38d26183d1..0000000000
--- a/car_product/sepolicy/device.te
+++ /dev/null
@@ -1,14 +0,0 @@
-# Define the logging device type
-type diag_device, dev_type, mlstrustedobject;
-
-#device type for gss device nodes, ie /dev/gss
-type gss_device, dev_type;
-
-type hsic_device, dev_type;
-type latency_device, dev_type;
-type modem_block_device, dev_type;
-type persist_block_device, dev_type;
-type qmuxd_socket, dev_type;
-type smem_log_device, dev_type;
-type ssd_block_device, dev_type;
-type wcnss_device, dev_type;
diff --git a/car_product/sepolicy/domain.te b/car_product/sepolicy/domain.te
index 67cbce6937..bb451f322e 100644
--- a/car_product/sepolicy/domain.te
+++ b/car_product/sepolicy/domain.te
@@ -1,48 +1,3 @@
# Ignore personality-8 denials.
-dontaudit adbd kernel:system module_request;
-dontaudit atfwd kernel:system module_request;
-dontaudit bootanim kernel:system module_request;
-dontaudit can kernel:system module_request;
-dontaudit config_bluetooth kernel:system module_request;
-dontaudit debuggerd kernel:system module_request;
-dontaudit drmserver kernel:system module_request;
-dontaudit fsck kernel:system module_request;
-dontaudit gatekeeperd kernel:system module_request;
-dontaudit healthd kernel:system module_request;
-dontaudit init kernel:system module_request;
-dontaudit installd kernel:system module_request;
-dontaudit irsc_util kernel:system module_request;
-dontaudit keystore kernel:system module_request;
-dontaudit lmkd kernel:system module_request;
-dontaudit logd kernel:system module_request;
-dontaudit mm-pp-daemon kernel:system module_request;
-dontaudit modem-sh kernel:system module_request;
-dontaudit mpdecision kernel:system module_request;
-dontaudit netd kernel:system module_request;
-dontaudit netmgrd kernel:system module_request;
-dontaudit qcom-c_core-sh kernel:system module_request;
-dontaudit qcom-c_main-sh kernel:system module_request;
-dontaudit qcom-post-boot kernel:system module_request;
-dontaudit qcom-sh kernel:system module_request;
-dontaudit qcom-usb-sh kernel:system module_request;
-dontaudit qmuxd kernel:system module_request;
-dontaudit rmt_storage kernel:system module_request;
-dontaudit sdcardd kernel:system module_request;
-dontaudit servicemanager kernel:system module_request;
-dontaudit shell kernel:system module_request;
-dontaudit start_hci_filter kernel:system module_request;
-dontaudit surfaceflinger kernel:system module_request;
-dontaudit thermal-engine kernel:system module_request;
-dontaudit time_daemon kernel:system module_request;
-dontaudit tzdatacheck kernel:system module_request;
-dontaudit ueventd kernel:system module_request;
-dontaudit untrusted_app kernel:system module_request;
-dontaudit usf-post-boot kernel:system module_request;
-dontaudit vns kernel:system module_request;
-dontaudit vold kernel:system module_request;
-dontaudit wcnss_service kernel:system module_request;
-dontaudit zygote kernel:system module_request;
+dontaudit domain kernel:system module_request;
-userdebug_or_eng(`
- dontaudit perfprofd kernel:system module_request;
-')
diff --git a/car_product/sepolicy/file.te b/car_product/sepolicy/file.te
deleted file mode 100644
index 2aaa844c70..0000000000
--- a/car_product/sepolicy/file.te
+++ /dev/null
@@ -1,21 +0,0 @@
-type btnvtool_exec, exec_type, file_type;
-
-# Bluetooth firmware file types
-type bt_firmware_file, contextmount_type, fs_type;
-
-# Default type for anything under /firmware.
-type firmware_file, contextmount_type, fs_type;
-
-type mpdecision_socket, file_type;
-type perfd_data_file, file_type, data_file_type;
-type persist_file, file_type;
-type pps_socket, file_type;
-type sysfs_bt_power, sysfs_type, fs_type;
-type sysfs_dcvs, sysfs_type, fs_type;
-type sysfs_hsic_modem_wait, sysfs_type, fs_type;
-type sysfs_mpdecision, sysfs_type, fs_type;
-type sysfs_rpm_resources, sysfs_type, fs_type;
-type sysfs_smd_open_timeout, sysfs_type, fs_type;
-type sysfs_usb, sysfs_type, fs_type;
-type thermal_socket, file_type;
-type time_data_file, file_type, data_file_type;
diff --git a/car_product/sepolicy/file_contexts b/car_product/sepolicy/file_contexts
index 55b32761e5..e95a012c09 100644
--- a/car_product/sepolicy/file_contexts
+++ b/car_product/sepolicy/file_contexts
@@ -1,97 +1,9 @@
-###################################
-# Data files
-#
-/data/misc/perfd(/.*)? u:object_r:perfd_data_file:s0
-/data/system/perfd(/.*)? u:object_r:perfd_data_file:s0
-/data/time(/.*)? u:object_r:time_data_file:s0
-
-###################################
-# Dev nodes
-#
-/dev/cpu_dma_latency u:object_r:latency_device:s0
-/dev/diag u:object_r:diag_device:s0
-/dev/kgsl-3d0 u:object_r:gpu_device:s0
-/dev/gss u:object_r:gss_device:s0
-/dev/hsicctl[0-3] u:object_r:hsic_device:s0
-/dev/mdp_arb u:object_r:graphics_device:s0
-/dev/media([0-9])+ u:object_r:video_device:s0
-/dev/msm_acdb u:object_r:audio_device:s0
-/dev/msm_camera(/.*)? u:object_r:video_device:s0
-/dev/msm_rotator u:object_r:video_device:s0
-/dev/msm_vidc_.* u:object_r:video_device:s0
-/dev/smem_log u:object_r:smem_log_device:s0
-/dev/socket/mpdecision u:object_r:mpdecision_socket:s0
-/dev/socket/pps u:object_r:pps_socket:s0
-/dev/socket/thermal-recv-client u:object_r:thermal_socket:s0
-/dev/socket/thermal-send-client u:object_r:thermal_socket:s0
-/dev/socket/qmux_radio(/.*)? u:object_r:qmuxd_socket:s0
-/dev/ttyHS[0-9]* u:object_r:serial_device:s0
-/dev/v4l-subdev.* u:object_r:video_device:s0
-/dev/wcnss_ctrl u:object_r:wcnss_device:s0
-/dev/wcnss_wlan u:object_r:wcnss_device:s0
-
-###################################
-# Dev block nodes
-#
-/dev/block/platform/msm_sdcc\.1/by-name/boot u:object_r:boot_block_device:s0
-/dev/block/platform/msm_sdcc\.1/by-name/cache u:object_r:cache_block_device:s0
-/dev/block/platform/msm_sdcc\.1/by-name/fsg u:object_r:modem_block_device:s0
-/dev/block/platform/msm_sdcc\.1/by-name/modemst1 u:object_r:modem_block_device:s0
-/dev/block/platform/msm_sdcc\.1/by-name/modemst2 u:object_r:modem_block_device:s0
-/dev/block/platform/msm_sdcc\.1/by-name/persist u:object_r:persist_block_device:s0
-/dev/block/platform/msm_sdcc\.1/by-name/recovery u:object_r:recovery_block_device:s0
-/dev/block/platform/msm_sdcc\.1/by-name/ssd u:object_r:ssd_block_device:s0
-/dev/block/platform/msm_sdcc\.1/by-name/system u:object_r:system_block_device:s0
-/dev/block/platform/msm_sdcc\.1/by-name/userdata u:object_r:userdata_block_device:s0
-/dev/block/mmcblk0 u:object_r:root_block_device:s0
-
-###################################
-# Persist files
-#
-/persist(/.*)? u:object_r:persist_file:s0
###################################
# System files
#
-/system/bin/ATFWD-daemon u:object_r:atfwd_exec:s0
-/system/bin/btnvtool u:object_r:btnvtool_exec:s0
-/system/bin/init\.class_main\.sh u:object_r:qcom-c_main-sh_exec:s0
-/system/bin/init\.qcom\.sh u:object_r:qcom-sh_exec:s0
-/system/bin/init\.qcom\.bt\.sh u:object_r:config_bluetooth_exec:s0
-/system/bin/init\.qcom\.class_core\.sh u:object_r:qcom-c_core-sh_exec:s0
-/system/bin/init\.qcom\.modem_links\.sh u:object_r:modem-sh_exec:s0
-/system/bin/init\.qcom\.post_boot\.sh u:object_r:qcom-post-boot_exec:s0
-/system/bin/init\.qcom\.usb\.sh u:object_r:qcom-usb-sh_exec:s0
-/system/bin/init\.qti\.can\.sh u:object_r:can_exec:s0
-/system/bin/usf_post_boot\.sh u:object_r:usf-post-boot_exec:s0
-/system/bin/irsc_util u:object_r:irsc_util_exec:s0
-/system/bin/mm-pp-daemon u:object_r:mm-pp-daemon_exec:s0
-/system/bin/mpdecision u:object_r:mpdecision_exec:s0
-/system/bin/netmgrd u:object_r:netmgrd_exec:s0
-/system/bin/qmuxd u:object_r:qmuxd_exec:s0
-/system/bin/rmt_storage u:object_r:rmt_storage_exec:s0
-/system/bin/thermal-engine u:object_r:thermal-engine_exec:s0
-/system/bin/time_daemon u:object_r:time_daemon_exec:s0
/system/bin/vehicle_network_service u:object_r:vns_exec:s0
-/system/bin/wcnss_filter u:object_r:start_hci_filter_exec:s0
-/system/bin/wcnss_service u:object_r:wcnss_service_exec:s0
-/system/bin/wpa_cli u:object_r:wcnss_service_exec:s0
###################################
-# sysfs files
-#
-/sys/class/thermal(/.*)? u:object_r:sysfs_thermal:s0
-/sys/class/android_usb(/.*)? u:object_r:sysfs_usb:s0
-/sys/devices/virtual/thermal(/.*)? u:object_r:sysfs_thermal:s0
-/sys/module/msm_dcvs(/.*)? u:object_r:sysfs_dcvs:s0
-/sys/module/msm_mpdecision(/.*)? u:object_r:sysfs_mpdecision:s0
-/sys/module/msm_thermal(/.*)? u:object_r:sysfs_thermal:s0
-/sys/module/pm_8x60(/.*)? u:object_r:sysfs_devices_system_cpu:s0
-/sys/module/rmnet_usb(/.*)? u:object_r:sysfs_usb:s0
-/sys/module/rpm_resources(/.*)? u:object_r:sysfs_rpm_resources:s0
-/sys/module/spm_v2(/.*)? u:object_r:sysfs_thermal:s0
-/sys/devices/platform/bt_power(/.*)? u:object_r:sysfs_bt_power:s0
-/sys/devices/platform/msm_hsusb\.[1-4](/.*)? u:object_r:sysfs_usb:s0
-/sys/devices/virtual/android_usb(/.*)? u:object_r:sysfs_usb:s0
-/sys/devices/virtual/hsicctl/hsicctl[0-9]+/modem_wait u:object_r:sysfs_hsic_modem_wait:s0
-/sys/devices/virtual/smdpkt/smdcntl[0-9]+/open_timeout u:object_r:sysfs_smd_open_timeout:s0
+
+
diff --git a/car_product/sepolicy/init.te b/car_product/sepolicy/init.te
index c909543071..a80ab0e917 100644
--- a/car_product/sepolicy/init.te
+++ b/car_product/sepolicy/init.te
@@ -1,4 +1,2 @@
# Allow legacy sdcard for creating directory symlinks
allow init tmpfs:lnk_file create_file_perms;
-
-r_dir_file(init, sysfs_usb);
diff --git a/car_product/sepolicy/irsc_util.te b/car_product/sepolicy/irsc_util.te
deleted file mode 100644
index 148134eea7..0000000000
--- a/car_product/sepolicy/irsc_util.te
+++ /dev/null
@@ -1,6 +0,0 @@
-type irsc_util, domain;
-type irsc_util_exec, exec_type, file_type;
-
-init_daemon_domain(irsc_util)
-
-allow irsc_util self:socket create_socket_perms;
diff --git a/car_product/sepolicy/mm-pp-daemon.te b/car_product/sepolicy/mm-pp-daemon.te
deleted file mode 100644
index c463509151..0000000000
--- a/car_product/sepolicy/mm-pp-daemon.te
+++ /dev/null
@@ -1,13 +0,0 @@
-type mm-pp-daemon, domain;
-type mm-pp-daemon_exec, exec_type, file_type;
-
-init_daemon_domain(mm-pp-daemon)
-
-# Need to use fb ioctls to communicate with kernel
-allow mm-pp-daemon graphics_device:chr_file rw_file_perms;
-
-# Allow socket calls in mm-pp-daemon
-allow mm-pp-daemon pps_socket:sock_file rw_file_perms;
-
-# Set hw.cabl.* properties.
-set_prop(mm-pp-daemon, hw_cabl_prop)
diff --git a/car_product/sepolicy/modem-sh.te b/car_product/sepolicy/modem-sh.te
deleted file mode 100644
index 0a06dc9570..0000000000
--- a/car_product/sepolicy/modem-sh.te
+++ /dev/null
@@ -1,8 +0,0 @@
-# modem-sh service
-type modem-sh, domain;
-type modem-sh_exec, exec_type, file_type;
-
-# Started by init
-init_daemon_domain(modem-sh)
-
-allow modem-sh shell_exec:file r_file_perms;
diff --git a/car_product/sepolicy/mpdecision.te b/car_product/sepolicy/mpdecision.te
deleted file mode 100644
index 432f4ee034..0000000000
--- a/car_product/sepolicy/mpdecision.te
+++ /dev/null
@@ -1,36 +0,0 @@
-# MpDecision service
-type mpdecision, domain;
-type mpdecision_exec, exec_type, file_type;
-
-init_daemon_domain(mpdecision)
-
-allow mpdecision self:capability { net_admin fsetid };
-
-allow mpdecision self:netlink_kobject_uevent_socket create_socket_perms;
-
-# Access to /dev/cpu_dma_latency.
-allow mpdecision latency_device:chr_file w_file_perms;
-
-# Create and access to /dev/socket/mpdecision
-allow mpdecision mpdecision_socket:sock_file rw_file_perms;
-
-# Access to /sys/devices/system/cpu/*.
-allow mpdecision sysfs_devices_system_cpu:file rw_file_perms;
-
-# Access to sysfs_thermal nodes.
-allow mpdecision sysfs_thermal:dir r_dir_perms;
-allow mpdecision sysfs_thermal:file r_file_perms;
-
-# Access to mpctl data files and sockets.
-allow mpdecision perfd_data_file:dir w_dir_perms;
-allow mpdecision perfd_data_file:file create_file_perms;
-allow mpdecision perfd_data_file:sock_file create_file_perms;
-
-# Access to some dynamically generated files under /sys/devices/system/cpu/.
-allow mpdecision sysfs:file write;
-
-allow mpdecision self:capability dac_override;
-
-allow mpdecision sysfs:file r_file_perms;
-
-allow mpdecision proc:file rw_file_perms;
diff --git a/car_product/sepolicy/netmgrd.te b/car_product/sepolicy/netmgrd.te
deleted file mode 100644
index d87f6905e2..0000000000
--- a/car_product/sepolicy/netmgrd.te
+++ /dev/null
@@ -1,28 +0,0 @@
-type netmgrd, domain;
-type netmgrd_exec, exec_type, file_type;
-
-init_daemon_domain(netmgrd)
-net_domain(netmgrd)
-
-# Creates/Talks to qmuxd via the qmux_radio socket.
-qmux_socket(netmgrd)
-
-# Allow writing of ipv6 network properties
-allow netmgrd proc_net:file w_file_perms;
-
-# Allow netmgrd operations
-allow netmgrd self:capability { net_admin net_raw fsetid };
-
-# Allow execution of /system/bin/sh.
-allow netmgrd shell_exec:file rx_file_perms;
-
-# Allow execution of /system/bin/*.
-allow netmgrd system_file:file rx_file_perms;
-
-allow netmgrd toolbox_exec:file rx_file_perms;
-
-userdebug_or_eng(`
- allow netmgrd diag_device:chr_file rw_file_perms;
-')
-
-dontaudit netmgrd self:capability sys_module;
diff --git a/car_product/sepolicy/priv_app.te b/car_product/sepolicy/priv_app.te
new file mode 100644
index 0000000000..5ecd537775
--- /dev/null
+++ b/car_product/sepolicy/priv_app.te
@@ -0,0 +1 @@
+get_prop(priv_app, opengles_prop)
diff --git a/car_product/sepolicy/property.te b/car_product/sepolicy/property.te
index 6282856d0c..0f4e53faf6 100644
--- a/car_product/sepolicy/property.te
+++ b/car_product/sepolicy/property.te
@@ -1,10 +1,6 @@
-type ctl_mpdecision_prop, property_type;
-type ctl_netmgrd_prop, property_type;
-type ctl_qmuxd_prop, property_type;
-type ctl_quipc_igsn_prop, property_type;
-type ctl_quipc_main_prop, property_type;
-type ctl_thermal-engine_prop, property_type;
type hw_cabl_prop, property_type;
-type wc_transport_prop, property_type;
type wlan_driver_prop, property_type;
-type radio_atfwd_prop, property_type;
+
+type opengles_prop, property_type;
+
+type car_prop, property_type;
diff --git a/car_product/sepolicy/property_contexts b/car_product/sepolicy/property_contexts
index 4afc2ac473..67b90f6764 100644
--- a/car_product/sepolicy/property_contexts
+++ b/car_product/sepolicy/property_contexts
@@ -1,11 +1,5 @@
-ctl.mpdecision u:object_r:ctl_mpdecision_prop:s0
-ctl.netmgrd u:object_r:ctl_netmgrd_prop:s0
-ctl.qmuxd u:object_r:ctl_qmuxd_prop:s0
-ctl.quipc_igsn u:object_r:ctl_quipc_igsn_prop:s0
-ctl.quipc_main u:object_r:ctl_quipc_main_prop:s0
-ctl.thermal-engine u:object_r:ctl_thermal-engine_prop:s0
hw.cabl. u:object_r:hw_cabl_prop:s0
-qualcomm.bluetooth. u:object_r:bluetooth_prop:s0
-radio.atfwd. u:object_r:radio_atfwd_prop:s0
-wc_transport. u:object_r:wc_transport_prop:s0
wlan.driver. u:object_r:wlan_driver_prop:s0
+
+boot.car_service_created u:object_r:car_prop:s0
+ro.opengles. u:object_r:opengles_prop:s0 \ No newline at end of file
diff --git a/car_product/sepolicy/qcom-c_core-sh.te b/car_product/sepolicy/qcom-c_core-sh.te
deleted file mode 100644
index 689282010b..0000000000
--- a/car_product/sepolicy/qcom-c_core-sh.te
+++ /dev/null
@@ -1,15 +0,0 @@
-# qcom-c_core-sh service
-type qcom-c_core-sh, domain;
-type qcom-c_core-sh_exec, exec_type, file_type;
-
-# Started by init
-init_daemon_domain(qcom-c_core-sh)
-
-# Set ctl.console properties.
-set_prop(qcom-c_core-sh, ctl_console_prop)
-
-allow qcom-c_core-sh shell_exec:file r_file_perms;
-
-allow qcom-c_core-sh toolbox_exec:file rx_file_perms;
-
-allow qcom-c_core-sh sysfs:file r_file_perms;
diff --git a/car_product/sepolicy/qcom-c_main-sh.te b/car_product/sepolicy/qcom-c_main-sh.te
deleted file mode 100644
index c8a1eed493..0000000000
--- a/car_product/sepolicy/qcom-c_main-sh.te
+++ /dev/null
@@ -1,16 +0,0 @@
-# qcom-c_main-sh service
-type qcom-c_main-sh, domain;
-type qcom-c_main-sh_exec, exec_type, file_type;
-
-# Started by init
-init_daemon_domain(qcom-c_main-sh)
-
-# Set ctl.qmuxd property.
-set_prop(qcom-c_main-sh, ctl_qmuxd_prop)
-
-# Set ctl.netmgrd property.
-set_prop(qcom-c_main-sh, ctl_netmgrd_prop)
-
-allow qcom-c_main-sh shell_exec:file r_file_perms;
-
-allow qcom-c_main-sh toolbox_exec:file rx_file_perms;
diff --git a/car_product/sepolicy/qcom-post-boot.te b/car_product/sepolicy/qcom-post-boot.te
deleted file mode 100644
index 3d54b843aa..0000000000
--- a/car_product/sepolicy/qcom-post-boot.te
+++ /dev/null
@@ -1,50 +0,0 @@
-# qcom-post-boot service
-type qcom-post-boot, domain;
-type qcom-post-boot_exec, exec_type, file_type;
-
-# Started by init
-init_daemon_domain(qcom-post-boot)
-
-# Set ctl.thermal-engine property.
-set_prop(qcom-post-boot, ctl_thermal-engine_prop);
-
-# Set ctl.mpdecision property.
-set_prop(qcom-post-boot, ctl_mpdecision_prop);
-
-# Allow access to /dev/ttyHS0.
-allow qcom-post-boot serial_device:chr_file { getattr setattr };
-
-allow qcom-post-boot shell_exec:file r_file_perms;
-
-# Write access to thermal related sysfs nodes.
-allow qcom-post-boot sysfs_thermal:dir search;
-allow qcom-post-boot sysfs_thermal:file w_file_perms;
-
-# Access to /sys/module/rpm_resources/*.
-allow qcom-post-boot sysfs_rpm_resources:dir search;
-allow qcom-post-boot sysfs_rpm_resources:file w_file_perms;
-
-# Write access to mpdecision related sysfs nodes.
-allow qcom-post-boot sysfs_mpdecision:dir search;
-allow qcom-post-boot sysfs_mpdecision:file { rw_file_perms setattr };
-
-# Access to /sys/module/msm_dcvs/*.
-allow qcom-post-boot sysfs_dcvs:dir search;
-allow qcom-post-boot sysfs_dcvs:file { rw_file_perms setattr };
-
-# Chown /sys/devices/platform/bt_power/*.
-allow qcom-post-boot sysfs_bt_power:dir search;
-allow qcom-post-boot sysfs_bt_power:file { getattr setattr };
-
-# Write access to /sys/devices/system/cpu/*.
-allow qcom-post-boot sysfs_devices_system_cpu:file { w_file_perms setattr };
-
-# Write access to dynamically generated files under /sys/devices/system/cpufreq/ondemand/*.
-allow qcom-post-boot sysfs:file { w_file_perms setattr };
-
-# Allow changing the owner of the above sysfs nodes.
-allow qcom-post-boot self:capability { fowner chown fsetid };
-
-allow qcom-post-boot sysfs:file r_file_perms;
-
-allow qcom-post-boot toolbox_exec:file rx_file_perms;
diff --git a/car_product/sepolicy/qcom-sh.te b/car_product/sepolicy/qcom-sh.te
deleted file mode 100644
index 1ae6c4fb3b..0000000000
--- a/car_product/sepolicy/qcom-sh.te
+++ /dev/null
@@ -1,21 +0,0 @@
-# qcom-sh service
-type qcom-sh, domain;
-type qcom-sh_exec, exec_type, file_type;
-
-# Started by init
-init_daemon_domain(qcom-sh)
-
-# Set ctl.quipc_* property.
-set_prop(qcom-sh, ctl_quipc_igsn_prop)
-set_prop(qcom-sh, ctl_quipc_main_prop)
-
-allow qcom-sh self:capability net_admin;
-
-# Allow writing of ipv6 network properties
-allow qcom-sh proc_net:file w_file_perms;
-
-allow qcom-sh shell_exec:file r_file_perms;
-
-allow qcom-sh toolbox_exec:file rx_file_perms;
-
-allow qcom-sh sysfs:file r_file_perms;
diff --git a/car_product/sepolicy/qcom-usb-sh.te b/car_product/sepolicy/qcom-usb-sh.te
deleted file mode 100644
index 8d637821b5..0000000000
--- a/car_product/sepolicy/qcom-usb-sh.te
+++ /dev/null
@@ -1,29 +0,0 @@
-# qcom-usb-sh service
-type qcom-usb-sh, domain;
-type qcom-usb-sh_exec, exec_type, file_type;
-
-# Started by init
-init_daemon_domain(qcom-usb-sh)
-
-# Allow changing the owner of /sys/devices/virtual/hsicctl/hsicctl0/modem_wait.
-allow qcom-usb-sh sysfs_hsic_modem_wait:file { getattr setattr };
-
-# qcom.usb.sh needs to restore the context of /sys/devices/virtual/hsicctl/hsicctl0/modem_wait
-# as it may not be properly labeled when accessed.
-allow qcom-usb-sh sysfs:file relabelfrom;
-allow qcom-usb-sh sysfs_hsic_modem_wait:file relabelto;
-
-# Follow links from /sys/class/android_usb/* to /sys/devices/virtual/android_usb/*.
-allow qcom-usb-sh sysfs_usb:lnk_file read;
-
-# Allow write access to USB related sysfs nodes.
-allow qcom-usb-sh sysfs_usb:dir search;
-allow qcom-usb-sh sysfs_usb:file w_file_perms;
-
-allow qcom-usb-sh self:capability chown;
-
-allow qcom-usb-sh shell_exec:file r_file_perms;
-
-allow qcom-usb-sh toolbox_exec:file rx_file_perms;
-
-allow qcom-usb-sh rootfs:file r_file_perms;
diff --git a/car_product/sepolicy/qmuxd.te b/car_product/sepolicy/qmuxd.te
deleted file mode 100644
index e0b6dabd0e..0000000000
--- a/car_product/sepolicy/qmuxd.te
+++ /dev/null
@@ -1,22 +0,0 @@
-# qmuxd daemon
-type qmuxd, domain;
-type qmuxd_exec, exec_type, file_type;
-
-init_daemon_domain(qmuxd)
-net_domain(qmuxd)
-
-# Allow access to /dev/hsicctl*.
-allow qmuxd hsic_device:chr_file rw_file_perms;
-
-# Allow access to /sys/devices/virtual/smdpkt/smdcntl*/open_timeout.
-allow qmuxd sysfs_smd_open_timeout:file w_file_perms;
-
-# Allow access to /sys/devices/virtual/hsicctl/hsicctl*/modem_wait.
-allow qmuxd sysfs_hsic_modem_wait:file w_file_perms;
-
-userdebug_or_eng(`
- allow qmuxd diag_device:chr_file rw_file_perms;
-')
-
-# Allow qmuxd to have the CAP_BLOCK_SUSPEND capability
-wakelock_use(qmuxd)
diff --git a/car_product/sepolicy/rild.te b/car_product/sepolicy/rild.te
deleted file mode 100644
index ea450be2ef..0000000000
--- a/car_product/sepolicy/rild.te
+++ /dev/null
@@ -1,6 +0,0 @@
-# Creates/Talks to qmuxd via the qmux_radio socket.
-qmux_socket(rild);
-
-userdebug_or_eng(`
- allow rild diag_device:chr_file rw_file_perms;
-')
diff --git a/car_product/sepolicy/rmt_storage.te b/car_product/sepolicy/rmt_storage.te
deleted file mode 100644
index d67a2cee3e..0000000000
--- a/car_product/sepolicy/rmt_storage.te
+++ /dev/null
@@ -1,32 +0,0 @@
-# rmt_storage daemon
-type rmt_storage, domain;
-type rmt_storage_exec, exec_type, file_type;
-
-init_daemon_domain(rmt_storage)
-
-# Drop (user, group) to (nobody, nobody)
-allow rmt_storage self:capability { setuid setgid };
-
-# Opens and reads /dev/block/mmcblk0.
-allow rmt_storage root_block_device:blk_file r_file_perms;
-
-# Allow access to /dev/uio0.
-allow rmt_storage uio_device:chr_file rw_file_perms;
-
-# Allow access to /dev/smem_log.
-allow rmt_storage smem_log_device:chr_file rw_file_perms;
-
-# Allow access to modem related block devices.
-allow rmt_storage modem_block_device:blk_file rw_file_perms;
-
-# Allow access to SSD related block devices.
-allow rmt_storage ssd_block_device:blk_file rw_file_perms;
-
-allow rmt_storage self:socket create_socket_perms;
-
-allow rmt_storage sysfs:file r_file_perms;
-
-allow rmt_storage sysfs:dir r_dir_perms;
-
-# Wake lock access.
-wakelock_use(rmt_storage)
diff --git a/car_product/sepolicy/service.te b/car_product/sepolicy/service.te
new file mode 100644
index 0000000000..b92ed0aa81
--- /dev/null
+++ b/car_product/sepolicy/service.te
@@ -0,0 +1 @@
+type car_service, service_manager_type;
diff --git a/car_product/sepolicy/service_contexts b/car_product/sepolicy/service_contexts
new file mode 100644
index 0000000000..63aff466c0
--- /dev/null
+++ b/car_product/sepolicy/service_contexts
@@ -0,0 +1 @@
+com.android.car.vehiclenetwork.IVehicleNetwork u:object_r:car_service:s0
diff --git a/car_product/sepolicy/start_hci_filter.te b/car_product/sepolicy/start_hci_filter.te
deleted file mode 100644
index 95ce9e406d..0000000000
--- a/car_product/sepolicy/start_hci_filter.te
+++ /dev/null
@@ -1,10 +0,0 @@
-type start_hci_filter, domain;
-type start_hci_filter_exec, exec_type, file_type;
-
-init_daemon_domain(start_hci_filter);
-
-# Allow access to /dev/ttyHS0
-allow start_hci_filter serial_device:chr_file rw_file_perms;
-
-# Allow access to wc_transport.* properties.
-set_prop(start_hci_filter, wc_transport_prop)
diff --git a/car_product/sepolicy/system_app.te b/car_product/sepolicy/system_app.te
index aaa99c23f4..5dffcf2569 100644
--- a/car_product/sepolicy/system_app.te
+++ b/car_product/sepolicy/system_app.te
@@ -1 +1,3 @@
binder_call(system_app, vns);
+
+set_prop(system_app, car_prop)
diff --git a/car_product/sepolicy/system_server.te b/car_product/sepolicy/system_server.te
index 507f8ca8d0..4e0da641b3 100644
--- a/car_product/sepolicy/system_server.te
+++ b/car_product/sepolicy/system_server.te
@@ -1,17 +1,6 @@
# Set wlan.driver.* properties.
set_prop(system_server, wlan_driver_prop)
-# Creates/Talks to qmuxd via the qmux_radio socket.
-qmux_socket(system_server)
-
-# For gss
-allow system_server gss_device:chr_file rw_file_perms;
-
-userdebug_or_eng(`
- allow system_server diag_device:chr_file rw_file_perms;
-')
-
-r_dir_file(system_server, sysfs_usb)
-allow system_server sysfs_usb:file w_file_perms;
+get_prop(system_server opengles_prop)
dontaudit system_server self:capability sys_module;
diff --git a/car_product/sepolicy/te_macros b/car_product/sepolicy/te_macros
deleted file mode 100644
index 485bfb7d93..0000000000
--- a/car_product/sepolicy/te_macros
+++ /dev/null
@@ -1,11 +0,0 @@
-#####################################
-# qmux_socket(clientdomain)
-# Allow client domain to connecto and send
-# via a local socket to the qmux domain.
-# Also allow the client domain to remove
-# its own socket.
-define(`qmux_socket', `
-allow $1 qmuxd_socket:dir create_dir_perms;
-unix_socket_connect($1, qmuxd, qmuxd)
-allow $1 qmuxd_socket:sock_file { read getattr write setattr create unlink };
-')
diff --git a/car_product/sepolicy/thermal-engine.te b/car_product/sepolicy/thermal-engine.te
deleted file mode 100644
index aa9224fc2b..0000000000
--- a/car_product/sepolicy/thermal-engine.te
+++ /dev/null
@@ -1,38 +0,0 @@
-# Thermal-engine daemon
-type thermal-engine, domain;
-type thermal-engine_exec, exec_type, file_type;
-
-init_daemon_domain(thermal-engine)
-
-userdebug_or_eng(`
- allow thermal-engine diag_device:chr_file rw_file_perms;
-')
-
-allow thermal-engine self:capability { net_admin fsetid };
-
-allow thermal-engine self:netlink_kobject_uevent_socket create_socket_perms;
-
-# Allow access to /dev/smem_log.
-allow thermal-engine smem_log_device:chr_file rw_file_perms;
-
-# Access to /dev/socket/thermal-.*
-allow thermal-engine thermal_socket:sock_file rw_file_perms;
-
-# Access to /dev/socket/mpdecision.
-unix_socket_connect(thermal-engine, mpdecision, mpdecision);
-
-# Allow access to /dev/uio0.
-#allow rmt_storage uio_device:chr_file rw_file_perms;
-
-# Write access to thermal related sysfs nodes.
-r_dir_file(thermal-engine, sysfs_thermal)
-allow thermal-engine sysfs_thermal:file w_file_perms;
-
-# Creates/Talks to qmuxd via the qmux_radio socket.
-qmux_socket(thermal-engine);
-
-allow thermal-engine self:socket create_socket_perms;
-
-allow thermal-engine sysfs_thermal:file r_file_perms;
-
-allow thermal-engine sysfs_thermal:dir r_dir_perms;
diff --git a/car_product/sepolicy/time_daemon.te b/car_product/sepolicy/time_daemon.te
deleted file mode 100644
index 782da98f0f..0000000000
--- a/car_product/sepolicy/time_daemon.te
+++ /dev/null
@@ -1,19 +0,0 @@
-# Policies for time daemon
-type time_daemon, domain;
-type time_daemon_exec, exec_type, file_type;
-
-init_daemon_domain(time_daemon)
-
-# Allow access to /dev/smem_log.
-allow time_daemon smem_log_device:chr_file rw_file_perms;
-
-# Add rules for access permissions
-allow time_daemon rtc_device:chr_file r_file_perms;
-allow time_daemon alarm_device:chr_file rw_file_perms;
-
-# Allo access to /data/time/*.
-allow time_daemon time_data_file:file r_file_perms;
-
-allow time_daemon self:socket create_socket_perms;
-
-allow time_daemon self:capability { setuid setgid };
diff --git a/car_product/sepolicy/ueventd.te b/car_product/sepolicy/ueventd.te
deleted file mode 100644
index f898e27641..0000000000
--- a/car_product/sepolicy/ueventd.te
+++ /dev/null
@@ -1,11 +0,0 @@
-# Allow read access to firmware related files.
-r_dir_file(ueventd, firmware_file);
-
-# Write access to thermal related sysfs nodes.
-allow ueventd sysfs_thermal:file w_file_perms;
-
-# Allow write access to usb related sysfs nodes.
-allow ueventd sysfs_usb:file w_file_perms;
-
-# Allow write access to bt_power sysfs nodes.
-allow ueventd sysfs_bt_power:file w_file_perms;
diff --git a/car_product/sepolicy/usf-post-boot.te b/car_product/sepolicy/usf-post-boot.te
deleted file mode 100644
index e21f583965..0000000000
--- a/car_product/sepolicy/usf-post-boot.te
+++ /dev/null
@@ -1,8 +0,0 @@
-# usf-post-boot service
-type usf-post-boot, domain;
-type usf-post-boot_exec, exec_type, file_type;
-
-# Started by init
-init_daemon_domain(usf-post-boot)
-
-allow usf-post-boot shell_exec:file r_file_perms;
diff --git a/car_product/sepolicy/vehicle_network_service.te b/car_product/sepolicy/vehicle_network_service.te
new file mode 100644
index 0000000000..cef863a3f7
--- /dev/null
+++ b/car_product/sepolicy/vehicle_network_service.te
@@ -0,0 +1,4 @@
+type vehicle_network_service_exec, exec_type, file_type;
+type vehicle_network_service, domain;
+
+init_daemon_domain(vehicle_network_service)
diff --git a/car_product/sepolicy/vns.te b/car_product/sepolicy/vns.te
index ff5f1f8d62..6d663f8b03 100644
--- a/car_product/sepolicy/vns.te
+++ b/car_product/sepolicy/vns.te
@@ -2,6 +2,10 @@
type vns, domain;
type vns_exec, exec_type, file_type;
+allow vns system_app:binder { call };
+allow vns car_service:service_manager { add };
+allow vns priv_app:binder { call };
+
init_daemon_domain(vns)
binder_use(vns);
diff --git a/car_product/sepolicy/wcnss_service.te b/car_product/sepolicy/wcnss_service.te
deleted file mode 100644
index fb97c43f9a..0000000000
--- a/car_product/sepolicy/wcnss_service.te
+++ /dev/null
@@ -1,17 +0,0 @@
-# WCNSS service
-type wcnss_service, domain;
-type wcnss_service_exec, exec_type, file_type;
-
-init_daemon_domain(wcnss_service)
-net_domain(wcnss_service)
-
-unix_socket_connect(wcnss_service, property, init)
-
-# Allow creation and modification of wifi data files.
-allow wcnss_service wifi_data_file:file create_file_perms;
-
-# Allow modifications of /dev/wcnss_* devices.
-allow wcnss_service wcnss_device:chr_file rw_file_perms;
-
-# Set wlan.driver.* properties.
-set_prop(wcnss_service, wlan_driver_prop)
diff --git a/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetwork.java b/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetwork.java
index f7ef2c4449..a54282a1da 100644
--- a/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetwork.java
+++ b/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetwork.java
@@ -216,6 +216,12 @@ public class VehicleNetwork {
setProperty(v);
}
+ public void setStringProperty(int property, String value)
+ throws IllegalArgumentException, ServiceSpecificException {
+ VehiclePropValue v = VehiclePropValueUtil.createStringValue(property, value, 0);
+ setProperty(v);
+ }
+
/**
* Set zoned boolean type property
*
diff --git a/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetworkConsts.java b/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetworkConsts.java
index cd7531e677..4cbbeeaf9d 100644
--- a/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetworkConsts.java
+++ b/libvehiclenetwork/java/src/com/android/car/vehiclenetwork/VehicleNetworkConsts.java
@@ -50,6 +50,7 @@ public static final int VEHICLE_PROPERTY_HVAC_MAX_DEFROST_ON = 0x00000507;
public static final int VEHICLE_PROPERTY_HVAC_RECIRC_ON = 0x00000508;
public static final int VEHICLE_PROPERTY_HVAC_DUAL_ON = 0x00000509;
public static final int VEHICLE_PROPERTY_HVAC_AUTO_ON = 0x0000050A;
+public static final int VEHICLE_PROPERTY_HVAC_SEAT_TEMPERATURE = 0x0000050B;
public static final int VEHICLE_PROPERTY_HVAC_POWER_ON = 0x00000510;
public static final int VEHICLE_PROPERTY_ENV_OUTSIDE_TEMPERATURE = 0x00000703;
public static final int VEHICLE_PROPERTY_ENV_CABIN_TEMPERATURE = 0x00000704;
@@ -59,6 +60,7 @@ public static final int VEHICLE_PROPERTY_AUDIO_VOLUME = 0x00000901;
public static final int VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT = 0x00000902;
public static final int VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY = 0x00000903;
public static final int VEHICLE_PROPERTY_AUDIO_HW_VARIANT = 0x00000904;
+public static final int VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT = 0x00000905;
public static final int VEHICLE_PROPERTY_AP_POWER_STATE = 0x00000A00;
public static final int VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS = 0x00000A01;
public static final int VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON = 0x00000A02;
@@ -66,9 +68,48 @@ public static final int VEHICLE_PROPERTY_HW_KEY_INPUT = 0x00000A10;
public static final int VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO = 0x00000A20;
public static final int VEHICLE_PROPERTY_UNIX_TIME = 0x00000A30;
public static final int VEHICLE_PROPERTY_CURRENT_TIME_IN_SECONDS = 0x00000A31;
-public static final int VEHICLE_PROPERTY_SEAT_TEMPERATURE = 0x00000B00;
-public static final int VEHICLE_PROPERTY_SEAT_MEMORY_SELECT = 0x00000B01;
-public static final int VEHICLE_PROPERTY_SEAT_MEMORY_SET = 0x00000B02;
+public static final int VEHICLE_PROPERTY_DOOR_POS = 0x00000B00;
+public static final int VEHICLE_PROPERTY_DOOR_MOVE = 0x00000B01;
+public static final int VEHICLE_PROPERTY_DOOR_LOCK = 0x00000B02;
+public static final int VEHICLE_PROPERTY_MIRROR_Z_POS = 0x00000B40;
+public static final int VEHICLE_PROPERTY_MIRROR_Z_MOVE = 0x00000B41;
+public static final int VEHICLE_PROPERTY_MIRROR_Y_POS = 0x00000B42;
+public static final int VEHICLE_PROPERTY_MIRROR_Y_MOVE = 0x00000B43;
+public static final int VEHICLE_PROPERTY_MIRROR_LOCK = 0x00000B44;
+public static final int VEHICLE_PROPERTY_MIRROR_HEAT = 0x00000B45;
+public static final int VEHICLE_PROPERTY_MIRROR_FOLD = 0x00000B46;
+public static final int VEHICLE_PROPERTY_SEAT_MEMORY_SELECT = 0x00000B80;
+public static final int VEHICLE_PROPERTY_SEAT_MEMORY_SET = 0x00000B81;
+public static final int VEHICLE_PROPERTY_SEAT_BELT_BUCKLED = 0x00000B82;
+public static final int VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_POS = 0x00000B83;
+public static final int VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_MOVE = 0x00000B84;
+public static final int VEHICLE_PROPERTY_SEAT_FORE_AFT_POS = 0x00000B85;
+public static final int VEHICLE_PROPERTY_SEAT_FORE_AFT_MOVE = 0x00000B86;
+public static final int VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_POS = 0x00000B87;
+public static final int VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_MOVE = 0x00000B88;
+public static final int VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_POS = 0x00000B89;
+public static final int VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_MOVE = 0x00000B8A;
+public static final int VEHICLE_PROPERTY_SEAT_HEIGHT_POS = 0x00000B8B;
+public static final int VEHICLE_PROPERTY_SEAT_HEIGHT_MOVE = 0x00000B8C;
+public static final int VEHICLE_PROPERTY_SEAT_DEPTH_POS = 0x00000B8D;
+public static final int VEHICLE_PROPERTY_SEAT_DEPTH_MOVE = 0x00000B8E;
+public static final int VEHICLE_PROPERTY_SEAT_TILT_POS = 0x00000B8F;
+public static final int VEHICLE_PROPERTY_SEAT_TILT_MOVE = 0x00000B90;
+public static final int VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_POS = 0x00000B91;
+public static final int VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_MOVE = 0x00000B92;
+public static final int VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_POS = 0x00000B93;
+public static final int VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_MOVE = 0x00000B94;
+public static final int VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_POS = 0x00000B95;
+public static final int VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_MOVE = 0x00000B96;
+public static final int VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_POS = 0x00000B97;
+public static final int VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_MOVE = 0x00000B98;
+public static final int VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_POS = 0x00000B99;
+public static final int VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_MOVE = 0x00000B9A;
+public static final int VEHICLE_PROPERTY_WINDOW_POS = 0x00000BC0;
+public static final int VEHICLE_PROPERTY_WINDOW_MOVE = 0x00000BC1;
+public static final int VEHICLE_PROPERTY_WINDOW_VENT_POS = 0x00000BC2;
+public static final int VEHICLE_PROPERTY_WINDOW_VENT_MOVE = 0x00000BC3;
+public static final int VEHICLE_PROPERTY_WINDOW_LOCK = 0x00000BC4;
public static final int VEHICLE_PROPERTY_CUSTOM_START = 0x70000000;
public static final int VEHICLE_PROPERTY_CUSTOM_END = 0x73ffffff;
public static final int VEHICLE_PROPERTY_INTERNAL_START = 0x74000000;
@@ -103,6 +144,7 @@ case VEHICLE_PROPERTY_HVAC_MAX_DEFROST_ON: return VehicleValueType.VEHICLE_VALUE
case VEHICLE_PROPERTY_HVAC_RECIRC_ON: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_BOOLEAN;
case VEHICLE_PROPERTY_HVAC_DUAL_ON: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_BOOLEAN;
case VEHICLE_PROPERTY_HVAC_AUTO_ON: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_BOOLEAN;
+case VEHICLE_PROPERTY_HVAC_SEAT_TEMPERATURE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
case VEHICLE_PROPERTY_HVAC_POWER_ON: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_BOOLEAN;
case VEHICLE_PROPERTY_ENV_OUTSIDE_TEMPERATURE: return VehicleValueType.VEHICLE_VALUE_TYPE_FLOAT;
case VEHICLE_PROPERTY_ENV_CABIN_TEMPERATURE: return VehicleValueType.VEHICLE_VALUE_TYPE_FLOAT;
@@ -112,6 +154,7 @@ case VEHICLE_PROPERTY_AUDIO_VOLUME: return VehicleValueType.VEHICLE_VALUE_TYPE_I
case VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2;
case VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2;
case VEHICLE_PROPERTY_AUDIO_HW_VARIANT: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
+case VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4;
case VEHICLE_PROPERTY_AP_POWER_STATE: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2;
case VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
case VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
@@ -119,9 +162,48 @@ case VEHICLE_PROPERTY_HW_KEY_INPUT: return VehicleValueType.VEHICLE_VALUE_TYPE_I
case VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4;
case VEHICLE_PROPERTY_UNIX_TIME: return VehicleValueType.VEHICLE_VALUE_TYPE_INT64;
case VEHICLE_PROPERTY_CURRENT_TIME_IN_SECONDS: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
-case VEHICLE_PROPERTY_SEAT_TEMPERATURE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_DOOR_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_DOOR_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_DOOR_LOCK: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_BOOLEAN;
+case VEHICLE_PROPERTY_MIRROR_Z_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_MIRROR_Z_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_MIRROR_Y_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_MIRROR_Y_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_MIRROR_LOCK: return VehicleValueType.VEHICLE_VALUE_TYPE_BOOLEAN;
+case VEHICLE_PROPERTY_MIRROR_HEAT: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32;
+case VEHICLE_PROPERTY_MIRROR_FOLD: return VehicleValueType.VEHICLE_VALUE_TYPE_BOOLEAN;
case VEHICLE_PROPERTY_SEAT_MEMORY_SELECT: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
case VEHICLE_PROPERTY_SEAT_MEMORY_SET: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_BELT_BUCKLED: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_BOOLEAN;
+case VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_FORE_AFT_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_FORE_AFT_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_HEIGHT_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_HEIGHT_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_DEPTH_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_DEPTH_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_TILT_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_TILT_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_WINDOW_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_WINDOW_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_WINDOW_VENT_POS: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_WINDOW_VENT_MOVE: return VehicleValueType.VEHICLE_VALUE_TYPE_ZONED_INT32;
+case VEHICLE_PROPERTY_WINDOW_LOCK: return VehicleValueType.VEHICLE_VALUE_TYPE_BOOLEAN;
case VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: return VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2;
default: return VehicleValueType.VEHICLE_VALUE_TYPE_SHOUD_NOT_USE;
}
@@ -156,6 +238,7 @@ case VEHICLE_PROPERTY_HVAC_MAX_DEFROST_ON: return "VEHICLE_PROPERTY_HVAC_MAX_DEF
case VEHICLE_PROPERTY_HVAC_RECIRC_ON: return "VEHICLE_PROPERTY_HVAC_RECIRC_ON";
case VEHICLE_PROPERTY_HVAC_DUAL_ON: return "VEHICLE_PROPERTY_HVAC_DUAL_ON";
case VEHICLE_PROPERTY_HVAC_AUTO_ON: return "VEHICLE_PROPERTY_HVAC_AUTO_ON";
+case VEHICLE_PROPERTY_HVAC_SEAT_TEMPERATURE: return "VEHICLE_PROPERTY_HVAC_SEAT_TEMPERATURE";
case VEHICLE_PROPERTY_HVAC_POWER_ON: return "VEHICLE_PROPERTY_HVAC_POWER_ON";
case VEHICLE_PROPERTY_ENV_OUTSIDE_TEMPERATURE: return "VEHICLE_PROPERTY_ENV_OUTSIDE_TEMPERATURE";
case VEHICLE_PROPERTY_ENV_CABIN_TEMPERATURE: return "VEHICLE_PROPERTY_ENV_CABIN_TEMPERATURE";
@@ -165,6 +248,7 @@ case VEHICLE_PROPERTY_AUDIO_VOLUME: return "VEHICLE_PROPERTY_AUDIO_VOLUME";
case VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: return "VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT";
case VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY: return "VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY";
case VEHICLE_PROPERTY_AUDIO_HW_VARIANT: return "VEHICLE_PROPERTY_AUDIO_HW_VARIANT";
+case VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT: return "VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT";
case VEHICLE_PROPERTY_AP_POWER_STATE: return "VEHICLE_PROPERTY_AP_POWER_STATE";
case VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS: return "VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS";
case VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON: return "VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON";
@@ -172,9 +256,48 @@ case VEHICLE_PROPERTY_HW_KEY_INPUT: return "VEHICLE_PROPERTY_HW_KEY_INPUT";
case VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO: return "VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO";
case VEHICLE_PROPERTY_UNIX_TIME: return "VEHICLE_PROPERTY_UNIX_TIME";
case VEHICLE_PROPERTY_CURRENT_TIME_IN_SECONDS: return "VEHICLE_PROPERTY_CURRENT_TIME_IN_SECONDS";
-case VEHICLE_PROPERTY_SEAT_TEMPERATURE: return "VEHICLE_PROPERTY_SEAT_TEMPERATURE";
+case VEHICLE_PROPERTY_DOOR_POS: return "VEHICLE_PROPERTY_DOOR_POS";
+case VEHICLE_PROPERTY_DOOR_MOVE: return "VEHICLE_PROPERTY_DOOR_MOVE";
+case VEHICLE_PROPERTY_DOOR_LOCK: return "VEHICLE_PROPERTY_DOOR_LOCK";
+case VEHICLE_PROPERTY_MIRROR_Z_POS: return "VEHICLE_PROPERTY_MIRROR_Z_POS";
+case VEHICLE_PROPERTY_MIRROR_Z_MOVE: return "VEHICLE_PROPERTY_MIRROR_Z_MOVE";
+case VEHICLE_PROPERTY_MIRROR_Y_POS: return "VEHICLE_PROPERTY_MIRROR_Y_POS";
+case VEHICLE_PROPERTY_MIRROR_Y_MOVE: return "VEHICLE_PROPERTY_MIRROR_Y_MOVE";
+case VEHICLE_PROPERTY_MIRROR_LOCK: return "VEHICLE_PROPERTY_MIRROR_LOCK";
+case VEHICLE_PROPERTY_MIRROR_HEAT: return "VEHICLE_PROPERTY_MIRROR_HEAT";
+case VEHICLE_PROPERTY_MIRROR_FOLD: return "VEHICLE_PROPERTY_MIRROR_FOLD";
case VEHICLE_PROPERTY_SEAT_MEMORY_SELECT: return "VEHICLE_PROPERTY_SEAT_MEMORY_SELECT";
case VEHICLE_PROPERTY_SEAT_MEMORY_SET: return "VEHICLE_PROPERTY_SEAT_MEMORY_SET";
+case VEHICLE_PROPERTY_SEAT_BELT_BUCKLED: return "VEHICLE_PROPERTY_SEAT_BELT_BUCKLED";
+case VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_POS: return "VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_POS";
+case VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_MOVE: return "VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_MOVE";
+case VEHICLE_PROPERTY_SEAT_FORE_AFT_POS: return "VEHICLE_PROPERTY_SEAT_FORE_AFT_POS";
+case VEHICLE_PROPERTY_SEAT_FORE_AFT_MOVE: return "VEHICLE_PROPERTY_SEAT_FORE_AFT_MOVE";
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_POS: return "VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_POS";
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_MOVE: return "VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_MOVE";
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_POS: return "VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_POS";
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_MOVE: return "VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_MOVE";
+case VEHICLE_PROPERTY_SEAT_HEIGHT_POS: return "VEHICLE_PROPERTY_SEAT_HEIGHT_POS";
+case VEHICLE_PROPERTY_SEAT_HEIGHT_MOVE: return "VEHICLE_PROPERTY_SEAT_HEIGHT_MOVE";
+case VEHICLE_PROPERTY_SEAT_DEPTH_POS: return "VEHICLE_PROPERTY_SEAT_DEPTH_POS";
+case VEHICLE_PROPERTY_SEAT_DEPTH_MOVE: return "VEHICLE_PROPERTY_SEAT_DEPTH_MOVE";
+case VEHICLE_PROPERTY_SEAT_TILT_POS: return "VEHICLE_PROPERTY_SEAT_TILT_POS";
+case VEHICLE_PROPERTY_SEAT_TILT_MOVE: return "VEHICLE_PROPERTY_SEAT_TILT_MOVE";
+case VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_POS: return "VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_POS";
+case VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_MOVE: return "VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_MOVE";
+case VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_POS: return "VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_POS";
+case VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_MOVE: return "VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_MOVE";
+case VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_POS: return "VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_POS";
+case VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_MOVE: return "VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_MOVE";
+case VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_POS: return "VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_POS";
+case VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_MOVE: return "VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_MOVE";
+case VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_POS: return "VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_POS";
+case VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_MOVE: return "VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_MOVE";
+case VEHICLE_PROPERTY_WINDOW_POS: return "VEHICLE_PROPERTY_WINDOW_POS";
+case VEHICLE_PROPERTY_WINDOW_MOVE: return "VEHICLE_PROPERTY_WINDOW_MOVE";
+case VEHICLE_PROPERTY_WINDOW_VENT_POS: return "VEHICLE_PROPERTY_WINDOW_VENT_POS";
+case VEHICLE_PROPERTY_WINDOW_VENT_MOVE: return "VEHICLE_PROPERTY_WINDOW_VENT_MOVE";
+case VEHICLE_PROPERTY_WINDOW_LOCK: return "VEHICLE_PROPERTY_WINDOW_LOCK";
case VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: return "VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE";
default: return "UNKNOWN_PROPERTY";
}
@@ -209,6 +332,7 @@ case VEHICLE_PROPERTY_HVAC_MAX_DEFROST_ON: return new int[] { VehiclePropChangeM
case VEHICLE_PROPERTY_HVAC_RECIRC_ON: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_HVAC_DUAL_ON: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_HVAC_AUTO_ON: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_HVAC_SEAT_TEMPERATURE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_HVAC_POWER_ON: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_ENV_OUTSIDE_TEMPERATURE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE , VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_CONTINUOUS };
case VEHICLE_PROPERTY_ENV_CABIN_TEMPERATURE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE , VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_CONTINUOUS };
@@ -218,6 +342,7 @@ case VEHICLE_PROPERTY_AUDIO_VOLUME: return new int[] { VehiclePropChangeMode.VEH
case VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_AUDIO_HW_VARIANT: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC };
+case VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_AP_POWER_STATE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC };
@@ -225,9 +350,48 @@ case VEHICLE_PROPERTY_HW_KEY_INPUT: return new int[] { VehiclePropChangeMode.VEH
case VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_UNIX_TIME: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_SET };
case VEHICLE_PROPERTY_CURRENT_TIME_IN_SECONDS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_SET };
-case VEHICLE_PROPERTY_SEAT_TEMPERATURE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_DOOR_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_DOOR_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_DOOR_LOCK: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_MIRROR_Z_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_MIRROR_Z_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_MIRROR_Y_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_MIRROR_Y_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_MIRROR_LOCK: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_MIRROR_HEAT: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_MIRROR_FOLD: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_SEAT_MEMORY_SELECT: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_SEAT_MEMORY_SET: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_BELT_BUCKLED: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_FORE_AFT_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_FORE_AFT_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_HEIGHT_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_HEIGHT_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_DEPTH_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_DEPTH_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_TILT_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_TILT_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_WINDOW_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_WINDOW_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_WINDOW_VENT_POS: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_WINDOW_VENT_MOVE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
+case VEHICLE_PROPERTY_WINDOW_LOCK: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
case VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: return new int[] { VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE };
default: return null;
}
@@ -262,6 +426,7 @@ case VEHICLE_PROPERTY_HVAC_MAX_DEFROST_ON: return new int[] { VehiclePropAccess.
case VEHICLE_PROPERTY_HVAC_RECIRC_ON: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_HVAC_DUAL_ON: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_HVAC_AUTO_ON: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_HVAC_SEAT_TEMPERATURE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
case VEHICLE_PROPERTY_HVAC_POWER_ON: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_ENV_OUTSIDE_TEMPERATURE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ };
case VEHICLE_PROPERTY_ENV_CABIN_TEMPERATURE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ };
@@ -271,6 +436,7 @@ case VEHICLE_PROPERTY_AUDIO_VOLUME: return new int[] { VehiclePropAccess.VEHICLE
case VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
case VEHICLE_PROPERTY_AUDIO_HW_VARIANT: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ };
+case VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_AP_POWER_STATE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_DISPLAY_BRIGHTNESS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ , VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_AP_POWER_BOOTUP_REASON: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ };
@@ -278,14 +444,70 @@ case VEHICLE_PROPERTY_HW_KEY_INPUT: return new int[] { VehiclePropAccess.VEHICLE
case VEHICLE_PROPERTY_INSTRUMENT_CLUSTER_INFO: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_UNIX_TIME: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_CURRENT_TIME_IN_SECONDS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
-case VEHICLE_PROPERTY_SEAT_TEMPERATURE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_DOOR_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_DOOR_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_DOOR_LOCK: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_MIRROR_Z_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_MIRROR_Z_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_MIRROR_Y_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_MIRROR_Y_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_MIRROR_LOCK: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_MIRROR_HEAT: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_MIRROR_FOLD: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_SEAT_MEMORY_SELECT: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
case VEHICLE_PROPERTY_SEAT_MEMORY_SET: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_SEAT_BELT_BUCKLED: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_SEAT_BELT_HEIGHT_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_SEAT_FORE_AFT_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_SEAT_FORE_AFT_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_1_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_SEAT_BACKREST_ANGLE_2_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_SEAT_HEIGHT_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_SEAT_HEIGHT_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_SEAT_DEPTH_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_SEAT_DEPTH_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_SEAT_TILT_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_SEAT_TILT_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_SEAT_LUMBAR_FORE_AFT_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_SEAT_LUMBAR_SIDE_SUPPORT_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_SEAT_HEADREST_HEIGHT_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_SEAT_HEADREST_ANGLE_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_SEAT_HEADREST_FORE_AFT_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_WINDOW_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_WINDOW_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_WINDOW_VENT_POS: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
+case VEHICLE_PROPERTY_WINDOW_VENT_MOVE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE , VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE };
+case VEHICLE_PROPERTY_WINDOW_LOCK: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
case VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE: return new int[] { VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE };
default: return null;
}
}
+
+
+
+public static final String VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_RADIO_AM_FM = "RADIO_AM_FM";
+public static final String VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_RADIO_AM_FM_HD = "RADIO_AM_FM_HD";
+public static final String VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_RADIO_DAB = "RADIO_DAB";
+public static final String VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_RADIO_SATELLITE = "RADIO_SATELLITE";
+public static final String VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_CD_DVD = "CD_DVD";
+public static final String VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_AUX_IN0 = "AUX_IN0";
+public static final String VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_AUX_IN1 = "AUX_IN1";
+public static final String VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_EXT_NAV_GUIDANCE = "EXT_NAV_GUIDANCE";
+public static final String VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_EXT_VOICE_COMMAND = "EXT_VOICE_COMMAND";
+public static final String VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_EXT_VOICE_CALL = "EXT_VOICE_CALL";
+public static final String VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_EXT_SAFETY_ALERT = "EXT_SAFETY_ALERT";
+
+
+
public static class VehicleHvacFanDirection {
public static final int VEHICLE_HVAC_FAN_DIRECTION_FACE = 0x1;
public static final int VEHICLE_HVAC_FAN_DIRECTION_FLOOR = 0x2;
@@ -425,6 +647,7 @@ public static final int VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG = 0x100;
public static final int VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG = 0x200;
public static final int VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG = 0x400;
public static final int VEHICLE_AUDIO_CONTEXT_RADIO_FLAG = 0x800;
+public static final int VEHICLE_AUDIO_CONTEXT_EXT_SOURCE_FLAG = 0x1000;
public static String enumToString(int v) {
switch(v) {
case VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG: return "VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG";
@@ -439,6 +662,7 @@ case VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG: return "VEHICLE_AUDIO_CONTEXT_CD_ROM_FLA
case VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG: return "VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG";
case VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG: return "VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG";
case VEHICLE_AUDIO_CONTEXT_RADIO_FLAG: return "VEHICLE_AUDIO_CONTEXT_RADIO_FLAG";
+case VEHICLE_AUDIO_CONTEXT_EXT_SOURCE_FLAG: return "VEHICLE_AUDIO_CONTEXT_EXT_SOURCE_FLAG";
default: return "UNKNOWN";
}
}
@@ -446,9 +670,11 @@ default: return "UNKNOWN";
public static class VehicleAudioVolumeCapabilityFlag {
public static final int VEHICLE_AUDIO_VOLUME_CAPABILITY_PERSISTENT_STORAGE = 0x1;
+public static final int VEHICLE_AUDIO_VOLUME_CAPABILITY_MASTER_VOLUME_ONLY = 0x2;
public static String enumToString(int v) {
switch(v) {
case VEHICLE_AUDIO_VOLUME_CAPABILITY_PERSISTENT_STORAGE: return "VEHICLE_AUDIO_VOLUME_CAPABILITY_PERSISTENT_STORAGE";
+case VEHICLE_AUDIO_VOLUME_CAPABILITY_MASTER_VOLUME_ONLY: return "VEHICLE_AUDIO_VOLUME_CAPABILITY_MASTER_VOLUME_ONLY";
default: return "UNKNOWN";
}
}
diff --git a/libvehiclenetwork/native/VehicleNetwork.cpp b/libvehiclenetwork/native/VehicleNetwork.cpp
index 8b28bc7a59..5e57962556 100644
--- a/libvehiclenetwork/native/VehicleNetwork.cpp
+++ b/libvehiclenetwork/native/VehicleNetwork.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "VehicleNetwork.Lib"
+#include <assert.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <utils/threads.h>
@@ -130,18 +131,29 @@ void VehicleNetworkEventMessageHandler::handleMessage(const Message& message) {
// ----------------------------------------------------------------------------
+static const int MAX_SERVICE_RETRY = 4;
+
sp<VehicleNetwork> VehicleNetwork::createVehicleNetwork(sp<VehicleNetworkListener>& listener) {
- sp<IBinder> binder = defaultServiceManager()->getService(
- String16(IVehicleNetwork::SERVICE_NAME));
- sp<VehicleNetwork> vn;
- if (binder != NULL) {
- sp<IVehicleNetwork> ivn(interface_cast<IVehicleNetwork>(binder));
- vn = new VehicleNetwork(ivn, listener);
- if (vn != NULL) {
- // in case thread pool is not started, start it.
- ProcessState::self()->startThreadPool();
+ sp<IBinder> binder;
+ int retry = 0;
+ while (true) {
+ binder = defaultServiceManager()->getService(String16(IVehicleNetwork::SERVICE_NAME));
+ if (binder.get() != NULL) {
+ break;
+ }
+ retry++;
+ if (retry > MAX_SERVICE_RETRY) {
+ ALOGE("cannot get VNS, will crash");
+ break;
}
}
+ ASSERT_ALWAYS_ON_NO_MEMORY(binder.get());
+ sp<IVehicleNetwork> ivn(interface_cast<IVehicleNetwork>(binder));
+ sp<VehicleNetwork> vn;
+ vn = new VehicleNetwork(ivn, listener);
+ ASSERT_ALWAYS_ON_NO_MEMORY(vn.get());
+ // in case thread pool is not started, start it.
+ ProcessState::self()->startThreadPool();
return vn;
}
diff --git a/libvehiclenetwork/tool/vehiclehal_code_gen.py b/libvehiclenetwork/tool/vehiclehal_code_gen.py
index e35654fe44..dfabd7dccd 100755
--- a/libvehiclenetwork/tool/vehiclehal_code_gen.py
+++ b/libvehiclenetwork/tool/vehiclehal_code_gen.py
@@ -50,9 +50,10 @@ JAVA_TRAIL = \
}
"""
-RE_PROPERTY_PATTERN = r'/\*\*(.*?)\*/\n\#define\s+VEHICLE_PROPERTY_(\S+)\s+(\S+)'
+RE_PROPERTY_PATTERN = r'/\*\*(.*?)\*/\n\#define\s+VEHICLE_PROPERTY_(\S+)\s+(\(0x\S+\))'
RE_ENUM_PATTERN = r'enum\s+(\S+)\s+\{\S*(.*?)\}'
RE_ENUM_ENTRY_PATTERN = r'(\S+)\s*=\s*(.*?)[,\n]'
+RE_AUDIO_EXT_ROUTING_PATTERN = r'\n\#define\s+VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_(\S+)\s+\"(\S+)\"'
class PropertyInfo(object):
def __init__(self, value, name):
@@ -188,6 +189,10 @@ def printEnums(enums):
for e in enums:
printEnum(e)
+def printExtAudioRoutingSources(audio_ext_routing):
+ for r in audio_ext_routing:
+ print "public static final String VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_SOURCE_" + r + ' = "' + r + '";'
+
def main(argv):
vehicle_h_path = os.path.dirname(os.path.abspath(__file__)) + "/../../../../../hardware/libhardware/include/hardware/vehicle.h"
#print vehicle_h_path
@@ -245,10 +250,26 @@ def main(argv):
enums.append(info)
#for e in enums:
# print e
+
+ audio_ext_routing = []
+ audio_ext_routing_re = re.compile(RE_AUDIO_EXT_ROUTING_PATTERN, re.MULTILINE | re.DOTALL)
+ for match in audio_ext_routing_re.finditer(text):
+ #print match
+ name = match.group(1)
+ value = match.group(2)
+ if name != value:
+ print "Warning, AUDIO_EXT_ROUTING_SOURCE_" + name + " does not match " + value
+ else:
+ audio_ext_routing.append(name)
+
print JAVA_HEADER
printProperties(props)
+ print "\n\n"
+ printExtAudioRoutingSources(audio_ext_routing)
+ print "\n\n"
printEnums(enums)
print JAVA_TRAIL
+
if __name__ == '__main__':
main(sys.argv)
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index 9bed93f08d..9168239f6b 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -94,24 +94,32 @@
android:label="@string/car_permission_label_audio_volume"
android:description="@string/car_permission_desc_audio_volume" />
- <uses-permission android:name="android.permission.WRITE_SETTINGS" />
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <permission
+ android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"
+ android:protectionLevel="signature"
+ android:label="@string/car_permission_label_bind_instrument_cluster_rendering"
+ android:description="@string/car_permission_desc_bind_instrument_cluster_rendering"/>
+
+ <uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
- <uses-permission android:name="android.permission.REBOOT" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
<uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE" />
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
- <uses-permission android:name="android.permission.CALL_PHONE" />
- <uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+ <uses-permission android:name="android.permission.REBOOT" />
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.REMOVE_TASKS" />
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application android:label="Car service"
android:directBootAware="true"
android:allowBackup="false"
- android:persistent="true"
- android:process="android.car.service">
+ android:persistent="true">
<service android:name=".CarService"
android:singleUser="true">
@@ -125,5 +133,12 @@
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
+ <activity android:name="com.android.car.pm.ActivityBlockingActivity"
+ android:excludeFromRecents="true"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/service/res/layout/activity_blocking.xml b/service/res/layout/activity_blocking.xml
new file mode 100644
index 0000000000..14dcce36ae
--- /dev/null
+++ b/service/res/layout/activity_blocking.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+ <TextView
+ android:id="@+id/activity_blocked_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/activity_blocked_string" />
+ <Button
+ android:id="@+id/botton_exit_now"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/exit_now" />
+</LinearLayout>
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index 1c80579f9b..92aae54945 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -60,6 +60,18 @@
from vehicle hal -->
<string name="inputInjectionDeviceNode">/dev/input/event2</string>
- <string name="instrumentClusterRendererPackage">android.car.cluster.loggingrenderer</string>
- <string name="instrumentClusterRendererFactoryClass">android.car.cluster.loggingrenderer.InstrumentClusterRendererFactory</string>
+ <string name="instrumentClusterRendererService">android.car.cluster.loggingrenderer/.LoggingClusterRenderingService</string>
+
+ <!-- Whether to enable Avtivity blocking for safety. When Activity blocking is enabled,
+ only whitelisted safe Activities will be allowed while car is not parked. -->
+ <bool name="enableActivityBlockingForSafety">true</bool>
+ <!-- Activity to be presented when un-safe activity is launched. Take a look at the javadoc of the
+ default implementation. -->
+ <string name="activityBlockingActivity">com.android.car/com.android.car.pm.ActivityBlockingActivity</string>
+ <!-- Comma separated list of activities that will be allowed by default. This only applies to
+ system apps which is included into system image and non-system app in the list will be
+ ignored. Format of each entry is either to specify package name to whitelist the whole
+ package or use format of "packagename/activity_classname" for tagging each activities.
+ Besides this, system apps with car app meta data will be auto whitelisted. -->
+ <string name="defauiltActivityWhitelist">com.android.systemui</string>
</resources>
diff --git a/service/res/values/strings.xml b/service/res/values/strings.xml
index 3721fb3498..f97efc4369 100644
--- a/service/res/values/strings.xml
+++ b/service/res/values/strings.xml
@@ -69,6 +69,8 @@
cluster [CHAR LIMIT=NONE] -->
<string name="car_permission_desc_car_navigation_manager">Report navigation data to instrument
cluster</string>
+ <string name="car_permission_label_bind_instrument_cluster_rendering">Instrument Cluster Rendering</string>
+ <string name="car_permission_desc_bind_instrument_cluster_rendering">Receive instrument cluster data</string>
<!-- Notification messages -->
<!-- Notification text: Notification shown to the user when vehicle CAN bus fails -->
@@ -76,4 +78,9 @@
<!-- Notification text: Notification description shown to the user when vehicle CAN bus fails -->
<string name="car_can_bus_failure_desc">CAN bus does not respond. Unplug and plug back headunit
box and restart the car</string>
+
+ <!-- Blocking activity: Message to show to user when an application is not allowed. [CHAR LIMIT=NONE] -->
+ <string name="activity_blocked_string">The application is not allowed while driving.</string>
+ <!-- Blocking activity: Message on exit button. [CHAR LIMIT=NONE] -->
+ <string name="exit_now">Exit</string>
</resources>
diff --git a/service/src/com/android/car/AppContextService.java b/service/src/com/android/car/AppContextService.java
deleted file mode 100644
index edc10bd026..0000000000
--- a/service/src/com/android/car/AppContextService.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2015 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.car;
-
-import android.car.CarAppContextManager;
-import android.car.IAppContext;
-import android.car.IAppContextListener;
-import android.content.Context;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.car.hal.VehicleHal;
-
-import java.io.PrintWriter;
-import java.util.HashMap;
-
-public class AppContextService extends IAppContext.Stub implements CarServiceBase,
- BinderInterfaceContainer.BinderEventHandler<IAppContextListener> {
- private static final boolean DBG = true;
- private static final boolean DBG_EVENT = false;
-
- private final ClientHolder mAllClients;
- /** K: context flag, V: client owning it */
- private final HashMap<Integer, ClientInfo> mContextOwners = new HashMap<>();
- private int mActiveContexts;
-
- private final HandlerThread mHandlerThread;
- private final DispatchHandler mDispatchHandler;
-
- public AppContextService(Context context) {
- mAllClients = new ClientHolder(this);
- mHandlerThread = new HandlerThread(AppContextService.class.getSimpleName());
- mHandlerThread.start();
- mDispatchHandler = new DispatchHandler(mHandlerThread.getLooper());
- }
-
- @Override
- public void registerContextListener(IAppContextListener listener, int filter) {
- synchronized (this) {
- ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
- if (info == null) {
- info = new ClientInfo(mAllClients, listener, Binder.getCallingUid(),
- Binder.getCallingPid(), filter);
- mAllClients.addBinderInterface(info);
- } else {
- info.setFilter(filter);
- }
- }
- }
-
- @Override
- public void unregisterContextListener(IAppContextListener listener) {
- synchronized (this) {
- ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
- if (info == null) {
- return;
- }
- resetActiveContexts(listener, info.getOwnedContexts());
- mAllClients.removeBinder(listener);
- }
- }
-
- @Override
- public int getActiveAppContexts() {
- synchronized (this) {
- return mActiveContexts;
- }
- }
-
- @Override
- public boolean isOwningContext(IAppContextListener listener, int context) {
- synchronized (this) {
- ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
- if (info == null) {
- return false;
- }
- int ownedContexts = info.getOwnedContexts();
- return (ownedContexts & context) == context;
- }
- }
-
- @Override
- public void setActiveContexts(IAppContextListener listener, int contexts) {
- synchronized (this) {
- ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
- if (info == null) {
- throw new IllegalStateException("listener not registered");
- }
- int alreadyOwnedContexts = info.getOwnedContexts();
- int addedContexts = 0;
- for (int c = CarAppContextManager.APP_CONTEXT_START_FLAG;
- c <= CarAppContextManager.APP_CONTEXT_END_FLAG; c = (c << 1)) {
- if ((c & contexts) != 0 && (c & alreadyOwnedContexts) == 0) {
- ClientInfo ownerInfo = mContextOwners.get(c);
- if (ownerInfo != null && ownerInfo != info) {
- //TODO check if current owner is having fore-ground activity. If yes,
- //reject request. Always grant if requestor is fore-ground activity.
- ownerInfo.setOwnedContexts(ownerInfo.getOwnedContexts() & ~c);
- mDispatchHandler.requestAppContextOwnershipLossDispatch(
- ownerInfo.binderInterface, c);
- if (DBG) {
- Log.i(CarLog.TAG_APP_CONTEXT, "losing context " +
- Integer.toHexString(c) + "," + ownerInfo.toString());
- }
- } else {
- addedContexts |= c;
- }
- mContextOwners.put(c, info);
- }
- }
- info.setOwnedContexts(alreadyOwnedContexts | contexts);
- mActiveContexts |= addedContexts;
- if (addedContexts != 0) {
- if (DBG) {
- Log.i(CarLog.TAG_APP_CONTEXT, "setting context " +
- Integer.toHexString(addedContexts) + "," + info.toString());
- }
- for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
- mAllClients.getInterfaces()) {
- ClientInfo clientInfo = (ClientInfo) client;
- // dispatch events only when there is change after filter and the listener
- // is not coming from the current caller.
- int clientFilter = clientInfo.getFilter();
- if ((addedContexts & clientFilter) != 0 && clientInfo != info) {
- mDispatchHandler.requestAppContextChangeDispatch(clientInfo.binderInterface,
- mActiveContexts & clientFilter);
- }
- }
- }
- }
- }
-
- @Override
- public void resetActiveContexts(IAppContextListener listener, int contexts) {
- synchronized (this) {
- ClientInfo info = (ClientInfo) mAllClients.getBinderInterface(listener);
- if (info == null) {
- // ignore as this client cannot have owned anything.
- return;
- }
- if ((contexts & mActiveContexts) == 0) {
- // ignore as none of them are active;
- return;
- }
- int removedContexts = 0;
- int currentlyOwnedContexts = info.getOwnedContexts();
- for (int c = CarAppContextManager.APP_CONTEXT_START_FLAG;
- c <= CarAppContextManager.APP_CONTEXT_END_FLAG; c = (c << 1)) {
- if ((c & contexts) != 0 && (c & currentlyOwnedContexts) != 0) {
- removedContexts |= c;
- mContextOwners.remove(c);
- }
- }
- if (removedContexts != 0) {
- mActiveContexts &= ~removedContexts;
- info.setOwnedContexts(currentlyOwnedContexts & ~removedContexts);
- if (DBG) {
- Log.i(CarLog.TAG_APP_CONTEXT, "resetting context " +
- Integer.toHexString(removedContexts) + "," + info.toString());
- }
- for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
- mAllClients.getInterfaces()) {
- ClientInfo clientInfo = (ClientInfo) client;
- int clientFilter = clientInfo.getFilter();
- if ((removedContexts & clientFilter) != 0 && clientInfo != info) {
- mDispatchHandler.requestAppContextChangeDispatch(clientInfo.binderInterface,
- mActiveContexts & clientFilter);
- }
- }
- }
- }
- }
-
- @Override
- public void init() {
- // nothing to do
- }
-
- @Override
- public void release() {
- synchronized (this) {
- mAllClients.clear();
- mContextOwners.clear();
- mActiveContexts = 0;
- }
- }
-
- @Override
- public void onBinderDeath(
- BinderInterfaceContainer.BinderInterface<IAppContextListener> bInterface) {
- ClientInfo info = (ClientInfo) bInterface;
- int ownedContexts = info.getOwnedContexts();
- if (ownedContexts != 0) {
- resetActiveContexts(bInterface.binderInterface, ownedContexts);
- }
- }
-
- @Override
- public void dump(PrintWriter writer) {
- writer.println("**AppContextService**");
- synchronized (this) {
- writer.println("mActiveContexts:" + Integer.toHexString(mActiveContexts));
- for (BinderInterfaceContainer.BinderInterface<IAppContextListener> client :
- mAllClients.getInterfaces()) {
- ClientInfo clientInfo = (ClientInfo) client;
- writer.println(clientInfo.toString());
- }
- }
- }
-
- /**
- * Returns true if process with given uid and pid owns provided context.
- */
- public boolean isContextOwner(int uid, int pid, int context) {
- synchronized (this) {
- if (mContextOwners.containsKey(context)) {
- ClientInfo clientInfo = mContextOwners.get(context);
- return clientInfo.uid == uid && clientInfo.pid == pid;
- }
- }
- return false;
- }
-
- private void dispatchAppContextOwnershipLoss(IAppContextListener listener, int contexts) {
- try {
- listener.onAppContextOwnershipLoss(contexts);
- } catch (RemoteException e) {
- }
- }
-
- private void dispatchAppContextChange(IAppContextListener listener, int contexts) {
- try {
- listener.onAppContextChange(contexts);
- } catch (RemoteException e) {
- }
- }
-
- private static class ClientHolder extends BinderInterfaceContainer<IAppContextListener> {
- private ClientHolder(AppContextService service) {
- super(service);
- }
- }
-
- private static class ClientInfo extends
- BinderInterfaceContainer.BinderInterface<IAppContextListener> {
- private final int uid;
- private final int pid;
- private int mFilter;
- /** contexts owned by this client */
- private int mOwnedContexts;
-
- private ClientInfo(ClientHolder holder, IAppContextListener binder, int uid, int pid,
- int filter) {
- super(holder, binder);
- this.uid = uid;
- this.pid = pid;
- this.mFilter = filter;
- }
-
- private synchronized int getFilter() {
- return mFilter;
- }
-
- private synchronized void setFilter(int filter) {
- mFilter = filter;
- }
-
- private synchronized int getOwnedContexts() {
- if (DBG_EVENT) {
- Log.i(CarLog.TAG_APP_CONTEXT, "getOwnedContexts " +
- Integer.toHexString(mOwnedContexts));
- }
- return mOwnedContexts;
- }
-
- private synchronized void setOwnedContexts(int contexts) {
- if (DBG_EVENT) {
- Log.i(CarLog.TAG_APP_CONTEXT, "setOwnedContexts " + Integer.toHexString(contexts));
- }
- mOwnedContexts = contexts;
- }
-
- @Override
- public String toString() {
- synchronized (this) {
- return "ClientInfo{uid=" + uid + ",pid=" + pid +
- ",filter=" + Integer.toHexString(mFilter) +
- ",owned=" + Integer.toHexString(mOwnedContexts) + "}";
- }
- }
- }
-
- private class DispatchHandler extends Handler {
- private static final int MSG_DISPATCH_OWNERSHIP_LOSS = 0;
- private static final int MSG_DISPATCH_CONTEXT_CHANGE = 1;
-
- private DispatchHandler(Looper looper) {
- super(looper);
- }
-
- private void requestAppContextOwnershipLossDispatch(IAppContextListener listener,
- int contexts) {
- Message msg = obtainMessage(MSG_DISPATCH_OWNERSHIP_LOSS, contexts, 0, listener);
- sendMessage(msg);
- }
-
- private void requestAppContextChangeDispatch(IAppContextListener listener, int contexts) {
- Message msg = obtainMessage(MSG_DISPATCH_CONTEXT_CHANGE, contexts, 0, listener);
- sendMessage(msg);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_DISPATCH_OWNERSHIP_LOSS:
- dispatchAppContextOwnershipLoss((IAppContextListener) msg.obj, msg.arg1);
- break;
- case MSG_DISPATCH_CONTEXT_CHANGE:
- dispatchAppContextChange((IAppContextListener) msg.obj, msg.arg1);
- break;
- }
- }
- }
-}
diff --git a/service/src/com/android/car/AppFocusService.java b/service/src/com/android/car/AppFocusService.java
new file mode 100644
index 0000000000..ad463ba9ff
--- /dev/null
+++ b/service/src/com/android/car/AppFocusService.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2015 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.car;
+
+import android.car.CarAppFocusManager;
+import android.car.IAppFocus;
+import android.car.IAppFocusListener;
+import android.car.IAppFocusOwnershipListener;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * App focus service ensures only one instance of application type is active at a time.
+ */
+public class AppFocusService extends IAppFocus.Stub implements CarServiceBase,
+ BinderInterfaceContainer.BinderEventHandler<IAppFocusOwnershipListener> {
+ private static final boolean DBG = true;
+ private static final boolean DBG_EVENT = false;
+
+ private final ClientHolder mAllChangeClients;
+ private final OwnershipClientHolder mAllOwnershipClients;
+ /** K: appType, V: client owning it */
+ private final HashMap<Integer, OwnershipClientInfo> mFocusOwners = new HashMap<>();
+ private final Set<Integer> mActiveAppTypes = new HashSet<>();
+ private final HandlerThread mHandlerThread;
+ private final DispatchHandler mDispatchHandler;
+ private final CopyOnWriteArrayList<FocusOwnershipListener> mFocusOwnershipListeners =
+ new CopyOnWriteArrayList<>();
+ private final BinderInterfaceContainer.BinderEventHandler<IAppFocusListener>
+ mAllBinderEventHandler = bInterface -> { /* nothing to do.*/ };
+
+ public AppFocusService(Context context) {
+ mAllChangeClients = new ClientHolder(mAllBinderEventHandler);
+ mAllOwnershipClients = new OwnershipClientHolder(this);
+ mHandlerThread = new HandlerThread(AppFocusService.class.getSimpleName());
+ mHandlerThread.start();
+ mDispatchHandler = new DispatchHandler(mHandlerThread.getLooper());
+ }
+
+ @Override
+ public void registerFocusListener(IAppFocusListener listener, int appType) {
+ synchronized (this) {
+ ClientInfo info = (ClientInfo) mAllChangeClients.getBinderInterface(listener);
+ if (info == null) {
+ info = new ClientInfo(mAllChangeClients, listener, Binder.getCallingUid(),
+ Binder.getCallingPid(), appType);
+ mAllChangeClients.addBinderInterface(info);
+ } else {
+ info.addAppType(appType);
+ }
+ }
+ }
+
+ @Override
+ public void unregisterFocusListener(IAppFocusListener listener, int appType) {
+ synchronized (this) {
+ ClientInfo info = (ClientInfo) mAllChangeClients.getBinderInterface(listener);
+ if (info == null) {
+ return;
+ }
+ info.removeAppType(appType);
+ if (info.getAppTypes().isEmpty()) {
+ mAllChangeClients.removeBinder(listener);
+ }
+ }
+ }
+
+ @Override
+ public int[] getActiveAppTypes() {
+ synchronized (this) {
+ return toIntArray(mActiveAppTypes);
+ }
+ }
+
+ @Override
+ public boolean isOwningFocus(IAppFocusOwnershipListener listener, int appType) {
+ synchronized (this) {
+ OwnershipClientInfo info =
+ (OwnershipClientInfo) mAllOwnershipClients.getBinderInterface(listener);
+ if (info == null) {
+ return false;
+ }
+ return info.getOwnedAppTypes().contains(appType);
+ }
+ }
+
+ @Override
+ public int requestAppFocus(IAppFocusOwnershipListener listener, int appType) {
+ synchronized (this) {
+ OwnershipClientInfo info =
+ (OwnershipClientInfo) mAllOwnershipClients.getBinderInterface(listener);
+ if (info == null) {
+ info = new OwnershipClientInfo(mAllOwnershipClients, listener,
+ Binder.getCallingUid(), Binder.getCallingPid());
+ mAllOwnershipClients.addBinderInterface(info);
+ }
+ Set<Integer> alreadyOwnedAppTypes = info.getOwnedAppTypes();
+ if (!alreadyOwnedAppTypes.contains(appType)) {
+ OwnershipClientInfo ownerInfo = mFocusOwners.get(appType);
+ if (ownerInfo != null && ownerInfo != info) {
+ //TODO check if current owner is having fore-ground activity. If yes,
+ //reject request. Always grant if requester is fore-ground activity.
+ ownerInfo.removeOwnedAppType(appType);
+ mDispatchHandler.requestAppFocusOwnershipLossDispatch(
+ ownerInfo.binderInterface, appType);
+ if (DBG) {
+ Log.i(CarLog.TAG_APP_FOCUS, "losing app type "
+ + appType + "," + ownerInfo.toString());
+ }
+ }
+ updateFocusOwner(appType, info);
+ }
+ info.addOwnedAppType(appType);
+ if (mActiveAppTypes.add(appType)) {
+ if (DBG) {
+ Log.i(CarLog.TAG_APP_FOCUS, "adding active app type " + appType + ","
+ + info.toString());
+ }
+ for (BinderInterfaceContainer.BinderInterface<IAppFocusListener> client :
+ mAllChangeClients.getInterfaces()) {
+ ClientInfo clientInfo = (ClientInfo) client;
+ // dispatch events only when there is change after filter and the listener
+ // is not coming from the current caller.
+ if (clientInfo.getAppTypes().contains(appType)) {
+ mDispatchHandler.requestAppFocusChangeDispatch(clientInfo.binderInterface,
+ appType, true);
+ }
+ }
+ }
+ }
+ return CarAppFocusManager.APP_FOCUS_REQUEST_GRANTED;
+ }
+
+ @Override
+ public void abandonAppFocus(IAppFocusOwnershipListener listener, int appType) {
+ synchronized (this) {
+ OwnershipClientInfo info =
+ (OwnershipClientInfo) mAllOwnershipClients.getBinderInterface(listener);
+ if (info == null) {
+ // ignore as this client cannot have owned anything.
+ return;
+ }
+ if (!mActiveAppTypes.contains(appType)) {
+ // ignore as none of them are active;
+ return;
+ }
+ Set<Integer> currentlyOwnedAppTypes = info.getOwnedAppTypes();
+ if (!currentlyOwnedAppTypes.contains(appType)) {
+ // ignore as listener doesn't own focus.
+ return;
+ }
+ if (mFocusOwners.remove(appType) != null) {
+ mActiveAppTypes.remove(appType);
+ info.removeOwnedAppType(appType);
+ if (DBG) {
+ Log.i(CarLog.TAG_APP_FOCUS, "abandoning focus " + appType
+ + "," + info.toString());
+ }
+ for (FocusOwnershipListener ownershipListener : mFocusOwnershipListeners) {
+ ownershipListener.onFocusAbandoned(appType, info.mUid, info.mPid);
+ }
+ for (BinderInterfaceContainer.BinderInterface<IAppFocusListener> client :
+ mAllChangeClients.getInterfaces()) {
+ ClientInfo clientInfo = (ClientInfo) client;
+ if (clientInfo.getAppTypes().contains(appType)) {
+ mDispatchHandler.requestAppFocusChangeDispatch(clientInfo.binderInterface,
+ appType, false);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void init() {
+ // nothing to do
+ }
+
+ @Override
+ public void release() {
+ synchronized (this) {
+ mAllChangeClients.clear();
+ mAllOwnershipClients.clear();
+ mFocusOwners.clear();
+ mActiveAppTypes.clear();
+ }
+ }
+
+ @Override
+ public void onBinderDeath(
+ BinderInterfaceContainer.BinderInterface<IAppFocusOwnershipListener> bInterface) {
+ OwnershipClientInfo info = (OwnershipClientInfo) bInterface;
+ for (Integer appType : info.getOwnedAppTypes()) {
+ abandonAppFocus(bInterface.binderInterface, appType);
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ writer.println("**AppFocusService**");
+ synchronized (this) {
+ writer.println("mActiveAppTypes:" + mActiveAppTypes);
+ for (BinderInterfaceContainer.BinderInterface<IAppFocusOwnershipListener> client :
+ mAllOwnershipClients.getInterfaces()) {
+ OwnershipClientInfo clientInfo = (OwnershipClientInfo) client;
+ writer.println(clientInfo.toString());
+ }
+ }
+ }
+
+ /**
+ * Returns true if process with given uid and pid owns provided focus.
+ */
+ public boolean isFocusOwner(int uid, int pid, int appType) {
+ synchronized (this) {
+ if (mFocusOwners.containsKey(appType)) {
+ OwnershipClientInfo clientInfo = mFocusOwners.get(appType);
+ return clientInfo.getUid() == uid && clientInfo.getPid() == pid;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Defines callback functions that will be called when ownership has been changed.
+ */
+ public interface FocusOwnershipListener {
+ void onFocusAcquired(int appType, int uid, int pid);
+ void onFocusAbandoned(int appType, int uid, int pid);
+ }
+
+ /**
+ * Registers listener.
+ *
+ * If any focus already acquired it will trigger
+ * {@link FocusOwnershipListener#onFocusAcquired} call immediately in the same thread.
+ */
+ public void registerContextOwnerChangedListener(FocusOwnershipListener listener) {
+ mFocusOwnershipListeners.add(listener);
+
+ HashSet<Map.Entry<Integer, OwnershipClientInfo>> owners;
+ synchronized (this) {
+ owners = new HashSet<>(mFocusOwners.entrySet());
+ }
+
+ for (Map.Entry<Integer, OwnershipClientInfo> entry : owners) {
+ OwnershipClientInfo clientInfo = entry.getValue();
+ listener.onFocusAcquired(entry.getKey(), clientInfo.getUid(), clientInfo.getPid());
+ }
+ }
+
+ /**
+ * Unregisters provided listener.
+ */
+ public void unregisterContextOwnerChangedListener(FocusOwnershipListener listener) {
+ mFocusOwnershipListeners.remove(listener);
+ }
+
+ private void updateFocusOwner(int appType, OwnershipClientInfo owner) {
+ synchronized (this) {
+ mFocusOwners.put(appType, owner);
+ }
+
+ CarServiceUtils.runOnMain(() -> {
+ for (FocusOwnershipListener listener : mFocusOwnershipListeners) {
+ listener.onFocusAcquired(appType, owner.getUid(), owner.getPid());
+ }
+ });
+ }
+
+ private void dispatchAppFocusOwnershipLoss(IAppFocusOwnershipListener listener, int appType) {
+ try {
+ listener.onAppFocusOwnershipLoss(appType);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private void dispatchAppFocusChange(IAppFocusListener listener, int appType, boolean active) {
+ try {
+ listener.onAppFocusChange(appType, active);
+ } catch (RemoteException e) {
+ }
+ }
+
+ private static class ClientHolder extends BinderInterfaceContainer<IAppFocusListener> {
+ private ClientHolder(BinderEventHandler<IAppFocusListener> holder) {
+ super(holder);
+ }
+ }
+
+ private static class OwnershipClientHolder extends
+ BinderInterfaceContainer<IAppFocusOwnershipListener> {
+ private OwnershipClientHolder(AppFocusService service) {
+ super(service);
+ }
+ }
+
+ private static class ClientInfo extends
+ BinderInterfaceContainer.BinderInterface<IAppFocusListener> {
+ private final int mUid;
+ private final int mPid;
+ private final Set<Integer> mAppTypes = new HashSet<>();
+
+ private ClientInfo(ClientHolder holder, IAppFocusListener binder, int uid, int pid,
+ int appType) {
+ super(holder, binder);
+ this.mUid = uid;
+ this.mPid = pid;
+ this.mAppTypes.add(appType);
+ }
+
+ private synchronized Set<Integer> getAppTypes() {
+ return mAppTypes;
+ }
+
+ private synchronized boolean addAppType(Integer appType) {
+ return mAppTypes.add(appType);
+ }
+
+ private synchronized boolean removeAppType(Integer appType) {
+ return mAppTypes.remove(appType);
+ }
+
+ @Override
+ public String toString() {
+ synchronized (this) {
+ return "ClientInfo{mUid=" + mUid + ",mPid=" + mPid
+ + ",appTypes=" + mAppTypes + "}";
+ }
+ }
+ }
+
+ private static class OwnershipClientInfo extends
+ BinderInterfaceContainer.BinderInterface<IAppFocusOwnershipListener> {
+ private final int mUid;
+ private final int mPid;
+ private final Set<Integer> mOwnedAppTypes = new HashSet<>();
+
+ private OwnershipClientInfo(OwnershipClientHolder holder, IAppFocusOwnershipListener binder,
+ int uid, int pid) {
+ super(holder, binder);
+ this.mUid = uid;
+ this.mPid = pid;
+ }
+
+ private synchronized Set<Integer> getOwnedAppTypes() {
+ if (DBG_EVENT) {
+ Log.i(CarLog.TAG_APP_FOCUS, "getOwnedAppTypes " + mOwnedAppTypes);
+ }
+ return mOwnedAppTypes;
+ }
+
+ private synchronized boolean addOwnedAppType(Integer appType) {
+ if (DBG_EVENT) {
+ Log.i(CarLog.TAG_APP_FOCUS, "addOwnedAppType " + appType);
+ }
+ return mOwnedAppTypes.add(appType);
+ }
+
+ private synchronized boolean removeOwnedAppType(Integer appType) {
+ if (DBG_EVENT) {
+ Log.i(CarLog.TAG_APP_FOCUS, "removeOwnedAppType " + appType);
+ }
+ return mOwnedAppTypes.remove(appType);
+ }
+
+ int getUid() {
+ return mUid;
+ }
+
+ int getPid() {
+ return mPid;
+ }
+
+ @Override
+ public String toString() {
+ synchronized (this) {
+ return "ClientInfo{mUid=" + mUid + ",mPid=" + mPid
+ + ",owned=" + mOwnedAppTypes + "}";
+ }
+ }
+ }
+
+ private class DispatchHandler extends Handler {
+ private static final int MSG_DISPATCH_OWNERSHIP_LOSS = 0;
+ private static final int MSG_DISPATCH_FOCUS_CHANGE = 1;
+
+ private DispatchHandler(Looper looper) {
+ super(looper);
+ }
+
+ private void requestAppFocusOwnershipLossDispatch(IAppFocusOwnershipListener listener,
+ int appType) {
+ Message msg = obtainMessage(MSG_DISPATCH_OWNERSHIP_LOSS, appType, 0, listener);
+ sendMessage(msg);
+ }
+
+ private void requestAppFocusChangeDispatch(IAppFocusListener listener, int appType,
+ boolean active) {
+ Message msg = obtainMessage(MSG_DISPATCH_FOCUS_CHANGE, appType, active ? 1 : 0,
+ listener);
+ sendMessage(msg);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_DISPATCH_OWNERSHIP_LOSS:
+ dispatchAppFocusOwnershipLoss((IAppFocusOwnershipListener) msg.obj, msg.arg1);
+ break;
+ case MSG_DISPATCH_FOCUS_CHANGE:
+ dispatchAppFocusChange((IAppFocusListener) msg.obj, msg.arg1, msg.arg2 == 1);
+ break;
+ }
+ }
+ }
+
+ private static int[] toIntArray(Set<Integer> intSet) {
+ int[] intArr = new int[intSet.size()];
+ int index = 0;
+ for (Integer value : intSet) {
+ intArr[index++] = value;
+ }
+ return intArr;
+ }
+}
diff --git a/service/src/com/android/car/AudioRoutingPolicy.java b/service/src/com/android/car/AudioRoutingPolicy.java
index fdad5aaeb9..0a5951976a 100644
--- a/service/src/com/android/car/AudioRoutingPolicy.java
+++ b/service/src/com/android/car/AudioRoutingPolicy.java
@@ -40,6 +40,11 @@ public class AudioRoutingPolicy {
public static AudioRoutingPolicy create(Context context, int policyNumber) {
final Resources res = context.getResources();
String[] policies = res.getStringArray(R.array.audioRoutingPolicy);
+ if (policyNumber > (policies.length - 1)) {
+ Log.e(CarLog.TAG_AUDIO, "AudioRoutingPolicy.create got wrong policy number:" +
+ policyNumber + ", num of avaiable policies:" + policies.length);
+ policyNumber = 0;
+ }
return new AudioRoutingPolicy(policies[policyNumber]);
}
diff --git a/service/src/com/android/car/CarAudioAttributesUtil.java b/service/src/com/android/car/CarAudioAttributesUtil.java
index 1d0cb00ebe..54ed2c638c 100644
--- a/service/src/com/android/car/CarAudioAttributesUtil.java
+++ b/service/src/com/android/car/CarAudioAttributesUtil.java
@@ -28,7 +28,7 @@ public class CarAudioAttributesUtil {
public static final int CAR_AUDIO_USAGE_CARSERVICE_CAR_PROXY = 101;
public static final int CAR_AUDIO_USAGE_CARSERVICE_MEDIA_MUTE = 102;
- /** Bundle key for storing media type */
+ /** Bundle key for storing media type. */
public static final String KEY_CAR_AUDIO_TYPE = "car_audio_type";
private static final int CAR_AUDIO_TYPE_DEFAULT = 0;
@@ -38,12 +38,17 @@ public class CarAudioAttributesUtil {
private static final int CAR_AUDIO_TYPE_CARSERVICE_BOTTOM = 4;
private static final int CAR_AUDIO_TYPE_CARSERVICE_CAR_PROXY = 5;
private static final int CAR_AUDIO_TYPE_CARSERVICE_MEDIA_MUTE = 6;
+ private static final int CAR_AUDIO_TYPE_EXTERNAL_SOURCE = 7;
+
+ /** Bundle key for storing routing type which is String. */
+ public static final String KEY_EXT_ROUTING_TYPE = "ext_routing_type";
public static AudioAttributes getAudioAttributesForCarUsage(int carUsage) {
switch (carUsage) {
case CarAudioManager.CAR_AUDIO_USAGE_MUSIC:
return createAudioAttributes(AudioAttributes.CONTENT_TYPE_MUSIC,
AudioAttributes.USAGE_MEDIA);
+ case CarAudioManager.CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE: // default to radio
case CarAudioManager.CAR_AUDIO_USAGE_RADIO:
return createCustomAudioAttributes(CAR_AUDIO_TYPE_RADIO,
AudioAttributes.CONTENT_TYPE_MUSIC, AudioAttributes.USAGE_MEDIA);
@@ -98,10 +103,13 @@ public class CarAudioAttributesUtil {
}
switch (usage) {
case AudioAttributes.USAGE_MEDIA:
- if (type == CAR_AUDIO_TYPE_RADIO) {
- return CarAudioManager.CAR_AUDIO_USAGE_RADIO;
- } else {
- return CarAudioManager.CAR_AUDIO_USAGE_MUSIC;
+ switch (type) {
+ case CAR_AUDIO_TYPE_RADIO:
+ return CarAudioManager.CAR_AUDIO_USAGE_RADIO;
+ case CAR_AUDIO_TYPE_EXTERNAL_SOURCE:
+ return CarAudioManager.CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE;
+ default:
+ return CarAudioManager.CAR_AUDIO_USAGE_MUSIC;
}
case AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
if (type == CAR_AUDIO_TYPE_VOICE_COMMAND) {
@@ -149,4 +157,36 @@ public class CarAudioAttributesUtil {
bundle.putInt(KEY_CAR_AUDIO_TYPE, carAudioType);
return builder.setContentType(contentType).setUsage(usage).addBundle(bundle).build();
}
+
+ public static AudioAttributes getCarRadioAttributes(String radioType) {
+ AudioAttributes.Builder builder = new AudioAttributes.Builder();
+ Bundle bundle = new Bundle();
+ bundle.putInt(KEY_CAR_AUDIO_TYPE, CAR_AUDIO_TYPE_RADIO);
+ bundle.putString(KEY_EXT_ROUTING_TYPE, radioType);
+ return builder.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).
+ setUsage(AudioAttributes.USAGE_MEDIA).addBundle(bundle).build();
+ }
+
+ public static AudioAttributes getCarExtSourceAttributes(String externalSourceType) {
+ AudioAttributes.Builder builder = new AudioAttributes.Builder();
+ Bundle bundle = new Bundle();
+ bundle.putInt(KEY_CAR_AUDIO_TYPE, CAR_AUDIO_TYPE_EXTERNAL_SOURCE);
+ bundle.putString(KEY_EXT_ROUTING_TYPE, externalSourceType);
+ return builder.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).
+ setUsage(AudioAttributes.USAGE_MEDIA).addBundle(bundle).build();
+ }
+
+ /**
+ * Get ext routing type from given AudioAttributes.
+ * @param attr
+ * @return {@link CarAudioManager#CAR_RADIO_TYPE_AM_FM} if ext routing info does not exist.
+ */
+ public static String getExtRouting(AudioAttributes attr) {
+ Bundle bundle = attr.getBundle();
+ String extRouting = CarAudioManager.CAR_RADIO_TYPE_AM_FM;
+ if (bundle != null) {
+ extRouting = bundle.getString(KEY_EXT_ROUTING_TYPE);
+ }
+ return extRouting;
+ }
}
diff --git a/service/src/com/android/car/CarAudioService.java b/service/src/com/android/car/CarAudioService.java
index c6f09a990c..a6d278500a 100644
--- a/service/src/com/android/car/CarAudioService.java
+++ b/service/src/com/android/car/CarAudioService.java
@@ -40,6 +40,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Pair;
import com.android.car.hal.AudioHalService;
import com.android.car.hal.AudioHalService.AudioHalFocusListener;
@@ -47,7 +48,13 @@ import com.android.car.hal.VehicleHal;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedList;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
AudioHalFocusListener {
@@ -107,7 +114,7 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
@GuardedBy("mLock")
private int mBottomFocusState;
@GuardedBy("mLock")
- private boolean mRadioActive = false;
+ private boolean mRadioOrExtSourceActive = false;
@GuardedBy("mLock")
private boolean mCallActive = false;
@GuardedBy("mLock")
@@ -125,6 +132,24 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
@GuardedBy("mLock")
private int mNumConsecutiveHalFailures;
+ @GuardedBy("mLock")
+ private boolean mExternalRoutingHintSupported;
+ @GuardedBy("mLock")
+ private Map<String, AudioHalService.ExtRoutingSourceInfo> mExternalRoutingTypes;
+ @GuardedBy("mLock")
+ private Set<String> mExternalRadioRoutingTypes;
+ @GuardedBy("mLock")
+ private String mDefaultRadioRoutingType;
+ @GuardedBy("mLock")
+ private Set<String> mExternalNonRadioRoutingTypes;
+ @GuardedBy("mLock")
+ private int mRadioPhysicalStream;
+ @GuardedBy("mLock")
+ private int[] mExternalRoutings = {0, 0, 0, 0};
+ private int[] mExternalRoutingsScratch = {0, 0, 0, 0};
+ private final int[] mExternalRoutingsForFocusRelease = {0, 0, 0, 0};
+ private final ExtSourceInfo mExtSourceInfoScratch = new ExtSourceInfo();
+
private final boolean mUseDynamicRouting;
private final AudioAttributes mAttributeBottom =
@@ -191,12 +216,61 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
}
mAudioHal.setFocusListener(this);
mAudioHal.setAudioRoutingPolicy(audioRoutingPolicy);
+ // get call outside lock as it can take time
+ HashSet<String> externalRadioRoutingTypes = new HashSet<>();
+ HashSet<String> externalNonRadioRoutingTypes = new HashSet<>();
+ Map<String, AudioHalService.ExtRoutingSourceInfo> externalRoutingTypes =
+ mAudioHal.getExternalAudioRoutingTypes();
+ if (externalRoutingTypes != null) {
+ for (String routingType : externalRoutingTypes.keySet()) {
+ if (routingType.startsWith("RADIO_")) {
+ externalRadioRoutingTypes.add(routingType);
+ } else {
+ externalNonRadioRoutingTypes.add(routingType);
+ }
+ }
+ }
+ // select default radio routing. AM_FM -> AM_FM_HD -> whatever with AM or FM -> first one
+ String defaultRadioRouting = null;
+ if (externalRadioRoutingTypes.contains(CarAudioManager.CAR_RADIO_TYPE_AM_FM)) {
+ defaultRadioRouting = CarAudioManager.CAR_RADIO_TYPE_AM_FM;
+ } else if (externalRadioRoutingTypes.contains(CarAudioManager.CAR_RADIO_TYPE_AM_FM_HD)) {
+ defaultRadioRouting = CarAudioManager.CAR_RADIO_TYPE_AM_FM_HD;
+ } else {
+ for (String radioType : externalRadioRoutingTypes) {
+ // set to 1st one
+ if (defaultRadioRouting == null) {
+ defaultRadioRouting = radioType;
+ }
+ if (radioType.contains("AM") || radioType.contains("FM")) {
+ defaultRadioRouting = radioType;
+ break;
+ }
+ }
+ }
+ if (defaultRadioRouting == null) { // no radio type defined. fall back to AM_FM
+ defaultRadioRouting = CarAudioManager.CAR_RADIO_TYPE_AM_FM;
+ }
+ int radioPhysicalStream = audioRoutingPolicy.getPhysicalStreamForLogicalStream(
+ CarAudioManager.CAR_AUDIO_USAGE_RADIO);
synchronized (mLock) {
if (audioPolicy != null) {
mAudioPolicy = audioPolicy;
}
+ mRadioPhysicalStream = radioPhysicalStream;
mAudioRoutingPolicy = audioRoutingPolicy;
mIsRadioExternal = mAudioHal.isRadioExternal();
+ if (externalRoutingTypes != null) {
+ mExternalRoutingHintSupported = true;
+ mExternalRoutingTypes = externalRoutingTypes;
+ } else {
+ mExternalRoutingHintSupported = false;
+ mExternalRoutingTypes = new HashMap<>();
+ }
+ mExternalRadioRoutingTypes = externalRadioRoutingTypes;
+ mExternalNonRadioRoutingTypes = externalNonRadioRoutingTypes;
+ mDefaultRadioRoutingType = defaultRadioRouting;
+ Arrays.fill(mExternalRoutings, 0);
}
mVolumeService.init();
}
@@ -322,7 +396,7 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
mLastFocusRequestToCar = null;
mTopFocusInfo = null;
mPendingFocusChanges.clear();
- mRadioActive = false;
+ mRadioOrExtSourceActive = false;
if (mCarAudioContextChangeHandler != null) {
mCarAudioContextChangeHandler.cancelAll();
mCarAudioContextChangeHandler = null;
@@ -331,6 +405,9 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
mCurrentPrimaryAudioContext = 0;
audioPolicy = mAudioPolicy;
mAudioPolicy = null;
+ mExternalRoutingTypes.clear();
+ mExternalRadioRoutingTypes.clear();
+ mExternalNonRadioRoutingTypes.clear();
}
if (audioPolicy != null) {
mAudioManager.unregisterAudioPolicyAsync(audioPolicy);
@@ -351,18 +428,33 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
@Override
public void dump(PrintWriter writer) {
- writer.println("*CarAudioService*");
- writer.println(" mCurrentFocusState:" + mCurrentFocusState +
- " mLastFocusRequestToCar:" + mLastFocusRequestToCar);
- writer.println(" mCurrentAudioContexts:0x" + Integer.toHexString(mCurrentAudioContexts));
- writer.println(" mCallActive:" + mCallActive + " mRadioActive:" + mRadioActive);
- writer.println(" mCurrentPrimaryAudioContext:" + mCurrentPrimaryAudioContext +
- " mCurrentPrimaryPhysicalStream:" + mCurrentPrimaryPhysicalStream);
- writer.println(" mIsRadioExternal:" + mIsRadioExternal);
- writer.println(" mNumConsecutiveHalFailures:" + mNumConsecutiveHalFailures);
- writer.println(" media muted:" + mMediaMuteAudioFocusListener.isMuted());
- writer.println(" mAudioPolicy:" + mAudioPolicy);
- mAudioRoutingPolicy.dump(writer);
+ synchronized (mLock) {
+ writer.println("*CarAudioService*");
+ writer.println(" mCurrentFocusState:" + mCurrentFocusState +
+ " mLastFocusRequestToCar:" + mLastFocusRequestToCar);
+ writer.println(" mCurrentAudioContexts:0x" +
+ Integer.toHexString(mCurrentAudioContexts));
+ writer.println(" mCallActive:" + mCallActive + " mRadioOrExtSourceActive:" +
+ mRadioOrExtSourceActive);
+ writer.println(" mCurrentPrimaryAudioContext:" + mCurrentPrimaryAudioContext +
+ " mCurrentPrimaryPhysicalStream:" + mCurrentPrimaryPhysicalStream);
+ writer.println(" mIsRadioExternal:" + mIsRadioExternal);
+ writer.println(" mNumConsecutiveHalFailures:" + mNumConsecutiveHalFailures);
+ writer.println(" media muted:" + mMediaMuteAudioFocusListener.isMuted());
+ writer.println(" mAudioPolicy:" + mAudioPolicy);
+ mAudioRoutingPolicy.dump(writer);
+ writer.println(" mExternalRoutingHintSupported:" + mExternalRoutingHintSupported);
+ if (mExternalRoutingHintSupported) {
+ writer.println(" mDefaultRadioRoutingType:" + mDefaultRadioRoutingType);
+ writer.println(" Routing Types:");
+ for (Entry<String, AudioHalService.ExtRoutingSourceInfo> entry :
+ mExternalRoutingTypes.entrySet()) {
+ writer.println(" type:" + entry.getKey() + " info:" + entry.getValue());
+ }
+ }
+ }
+ writer.println("** Dump CarVolumeService**");
+ mVolumeService.dump(writer);
}
@Override
@@ -429,6 +521,44 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
}
}
+ @Override
+ public AudioAttributes getAudioAttributesForRadio(String radioType) {
+ synchronized (mLock) {
+ if (!mExternalRadioRoutingTypes.contains(radioType)) { // type not exist
+ throw new IllegalArgumentException("Specified radio type is not available:" +
+ radioType);
+ }
+ }
+ return CarAudioAttributesUtil.getCarRadioAttributes(radioType);
+ }
+
+ @Override
+ public AudioAttributes getAudioAttributesForExternalSource(String externalSourceType) {
+ synchronized (mLock) {
+ if (!mExternalNonRadioRoutingTypes.contains(externalSourceType)) { // type not exist
+ throw new IllegalArgumentException("Specified ext source type is not available:" +
+ externalSourceType);
+ }
+ }
+ return CarAudioAttributesUtil.getCarExtSourceAttributes(externalSourceType);
+ }
+
+ @Override
+ public String[] getSupportedExternalSourceTypes() {
+ synchronized (mLock) {
+ return mExternalNonRadioRoutingTypes.toArray(
+ new String[mExternalNonRadioRoutingTypes.size()]);
+ }
+ }
+
+ @Override
+ public String[] getSupportedRadioTypes() {
+ synchronized (mLock) {
+ return mExternalRadioRoutingTypes.toArray(
+ new String[mExternalRadioRoutingTypes.size()]);
+ }
+ }
+
/**
* API for system to control mute with lock.
* @param mute
@@ -495,12 +625,12 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
newFocusState = AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
}
mLastFocusRequestToCar = null;
- if (mRadioActive &&
+ if (mRadioOrExtSourceActive &&
(mCurrentFocusState.externalFocus &
AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG) == 0) {
// radio flag dropped
newFocusState = AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
- mRadioActive = false;
+ mRadioOrExtSourceActive = false;
}
if (newFocusState == AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS ||
newFocusState == AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT ||
@@ -627,18 +757,20 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
return false;
}
- private boolean isFocusFromExternalRadio(AudioFocusInfo info) {
- if (!mIsRadioExternal) {
- // if radio is not external, no special handling of radio is necessary.
- return false;
- }
+ private boolean isFocusFromExternalRadioOrExternalSource(AudioFocusInfo info) {
if (info == null) {
return false;
}
AudioAttributes attrib = info.getAttributes();
- if (attrib != null &&
- CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attrib) ==
- CarAudioManager.CAR_AUDIO_USAGE_RADIO) {
+ if (attrib == null) {
+ return false;
+ }
+ // if radio is not external, no special handling of radio is necessary.
+ if (CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attrib) ==
+ CarAudioManager.CAR_AUDIO_USAGE_RADIO && mIsRadioExternal) {
+ return true;
+ } else if (CarAudioAttributesUtil.getCarUsageFromAudioAttributes(attrib) ==
+ CarAudioManager.CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE) {
return true;
}
return false;
@@ -679,8 +811,8 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
doHandleFocusLossTransientExclusiveFromCar(mCurrentFocusState);
break;
}
- if (mRadioActive) { // radio is no longer active.
- mRadioActive = false;
+ if (mRadioOrExtSourceActive) { // radio is no longer active.
+ mRadioOrExtSourceActive = false;
}
return false;
}
@@ -692,53 +824,53 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
? logicalStreamTypeForTop : CarAudioManager.CAR_AUDIO_USAGE_MUSIC);
boolean muteMedia = false;
+ String primaryExtSource = CarAudioAttributesUtil.getExtRouting(attrib);
// update primary context and notify if necessary
- int primaryContext = AudioHalService.logicalStreamToHalContextType(logicalStreamTypeForTop);
- switch (logicalStreamTypeForTop) {
- case CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_MEDIA_MUTE:
+ int primaryContext = AudioHalService.logicalStreamWithExtTypeToHalContextType(
+ logicalStreamTypeForTop, primaryExtSource);
+ if (logicalStreamTypeForTop ==
+ CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_MEDIA_MUTE) {
muteMedia = true;
- // remaining parts the same with other cases. fall through.
- case CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_BOTTOM:
- case CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_CAR_PROXY:
- primaryContext = 0;
- break;
- }
- // save the current context now but it is sent to context change listener after focus
- // response from car
- if (mCurrentPrimaryAudioContext != primaryContext) {
- mCurrentPrimaryAudioContext = primaryContext;
- mCurrentPrimaryPhysicalStream = physicalStreamTypeForTop;
}
-
- int audioContexts = 0;
if (logicalStreamTypeForTop == CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL) {
- if (!mCallActive) {
- mCallActive = true;
- audioContexts |= AudioHalService.AUDIO_CONTEXT_CALL_FLAG;
- }
+ mCallActive = true;
} else {
- if (mCallActive) {
- mCallActive = false;
- }
- audioContexts = primaryContext;
+ mCallActive = false;
}
// other apps having focus
int focusToRequest = AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE;
int extFocus = AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG;
int streamsToRequest = 0x1 << physicalStreamTypeForTop;
+ boolean primaryIsExternal = false;
+ if (isFocusFromExternalRadioOrExternalSource(mTopFocusInfo)) {
+ streamsToRequest = 0;
+ mRadioOrExtSourceActive = true;
+ primaryIsExternal = true;
+ if (fixExtSourceAndContext(
+ mExtSourceInfoScratch.set(primaryExtSource, primaryContext))) {
+ primaryExtSource = mExtSourceInfoScratch.source;
+ primaryContext = mExtSourceInfoScratch.context;
+ }
+ } else {
+ mRadioOrExtSourceActive = false;
+ primaryExtSource = null;
+ }
+ // save the current context now but it is sent to context change listener after focus
+ // response from car
+ if (mCurrentPrimaryAudioContext != primaryContext) {
+ mCurrentPrimaryAudioContext = primaryContext;
+ mCurrentPrimaryPhysicalStream = physicalStreamTypeForTop;
+ }
+
+ boolean secondaryIsExternal = false;
+ int secondaryContext = 0;
+ String secondaryExtSource = null;
switch (mTopFocusInfo.getGainRequest()) {
case AudioManager.AUDIOFOCUS_GAIN:
- if (isFocusFromExternalRadio(mTopFocusInfo)) {
- mRadioActive = true;
- } else {
- mRadioActive = false;
- }
focusToRequest = AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN;
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
- // radio cannot be active
- mRadioActive = false;
focusToRequest = AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT;
break;
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
@@ -758,9 +890,30 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
muteMedia = true;
break;
}
- int secondContext = AudioHalService.logicalStreamToHalContextType(
- logicalStreamTypeForSecond);
- audioContexts |= secondContext;
+ if (isFocusFromExternalRadioOrExternalSource(mSecondFocusInfo)) {
+ secondaryIsExternal = true;
+ secondaryExtSource = CarAudioAttributesUtil.getExtRouting(secondAttrib);
+ secondaryContext = AudioHalService.logicalStreamWithExtTypeToHalContextType(
+ logicalStreamTypeForSecond, secondaryExtSource);
+ if (fixExtSourceAndContext(
+ mExtSourceInfoScratch.set(secondaryExtSource, secondaryContext))) {
+ secondaryExtSource = mExtSourceInfoScratch.source;
+ secondaryContext = mExtSourceInfoScratch.context;
+ }
+ int secondaryExtPhysicalStreamFlag =
+ getPhysicalStreamFlagForExtSourceLocked(secondaryExtSource);
+ if ((secondaryExtPhysicalStreamFlag & streamsToRequest) != 0) {
+ // secondary stream is the same as primary. cannot keep secondary
+ secondaryIsExternal = false;
+ secondaryContext = 0;
+ secondaryExtSource = null;
+ break;
+ }
+ mRadioOrExtSourceActive = true;
+ } else {
+ secondaryContext = AudioHalService.logicalStreamWithExtTypeToHalContextType(
+ logicalStreamTypeForSecond, null);
+ }
switch (mCurrentFocusState.focusState) {
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_GAIN:
streamsToRequest |= mCurrentFocusState.streams;
@@ -783,45 +936,155 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
streamsToRequest = 0;
break;
}
+ int audioContexts = 0;
if (muteMedia) {
- mRadioActive = false;
- audioContexts &= ~(AudioHalService.AUDIO_CONTEXT_RADIO_FLAG |
- AudioHalService.AUDIO_CONTEXT_MUSIC_FLAG);
- extFocus = AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_MUTE_MEDIA_FLAG;
- int radioPhysicalStream = mAudioRoutingPolicy.getPhysicalStreamForLogicalStream(
- CarAudioManager.CAR_AUDIO_USAGE_RADIO);
- streamsToRequest &= ~(0x1 << radioPhysicalStream);
- } else if (mRadioActive) {
- // TODO any need to keep media stream while radio is active?
- // Most cars do not allow that, but if mixing is possible, it can take media stream.
- // For now, assume no mixing capability.
- int radioPhysicalStream = mAudioRoutingPolicy.getPhysicalStreamForLogicalStream(
- CarAudioManager.CAR_AUDIO_USAGE_RADIO);
- if (!isFocusFromExternalRadio(mTopFocusInfo) &&
- (physicalStreamTypeForTop == radioPhysicalStream) && mIsRadioExternal) {
- Log.i(CarLog.TAG_AUDIO, "Top stream is taking the same stream:" +
- physicalStreamTypeForTop + " as radio, stopping radio");
- // stream conflict here. radio cannot be played
- extFocus = 0;
- mRadioActive = false;
- audioContexts &= ~AudioHalService.AUDIO_CONTEXT_RADIO_FLAG;
+ boolean addMute = true;
+ if (primaryIsExternal) {
+ if ((getPhysicalStreamFlagForExtSourceLocked(primaryExtSource) &
+ (0x1 << mRadioPhysicalStream)) != 0) {
+ // cannot mute as primary is media
+ addMute = false;
+ }
+ } else if (secondaryIsExternal) {
+ if ((getPhysicalStreamFlagForExtSourceLocked(secondaryExtSource) &
+ (0x1 << mRadioPhysicalStream)) != 0) {
+ mRadioOrExtSourceActive = false;
+ }
} else {
+ mRadioOrExtSourceActive = false;
+ }
+ audioContexts = primaryContext | secondaryContext;
+ if (addMute) {
+ audioContexts &= ~(AudioHalService.AUDIO_CONTEXT_RADIO_FLAG |
+ AudioHalService.AUDIO_CONTEXT_MUSIC_FLAG |
+ AudioHalService.AUDIO_CONTEXT_CD_ROM_FLAG |
+ AudioHalService.AUDIO_CONTEXT_AUX_AUDIO_FLAG);
+ extFocus = AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_MUTE_MEDIA_FLAG;
+ streamsToRequest &= ~(0x1 << mRadioPhysicalStream);
+ }
+ } else if (mRadioOrExtSourceActive) {
+ boolean addExtFocusFlag = true;
+ if (primaryIsExternal) {
+ int primaryExtPhysicalStreamFlag =
+ getPhysicalStreamFlagForExtSourceLocked(primaryExtSource);
+ if (secondaryIsExternal) {
+ int secondaryPhysicalStreamFlag =
+ getPhysicalStreamFlagForExtSourceLocked(secondaryExtSource);
+ if (primaryExtPhysicalStreamFlag == secondaryPhysicalStreamFlag) {
+ // overlap, drop secondary
+ audioContexts &= ~secondaryContext;
+ secondaryContext = 0;
+ secondaryExtSource = null;
+ }
+ streamsToRequest = 0;
+ } else { // primary only
+ if (streamsToRequest == primaryExtPhysicalStreamFlag) {
+ // cannot keep secondary
+ secondaryContext = 0;
+ }
+ streamsToRequest &= ~primaryExtPhysicalStreamFlag;
+ }
+ }
+ if (addExtFocusFlag) {
extFocus = AudioHalService.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG;
- streamsToRequest &= ~(0x1 << radioPhysicalStream);
}
+ audioContexts = primaryContext | secondaryContext;
} else if (streamsToRequest == 0) {
mCurrentAudioContexts = 0;
mFocusHandler.handleFocusReleaseRequest();
return false;
+ } else {
+ audioContexts = primaryContext | secondaryContext;
}
+ boolean routingHintChanged = sendExtRoutingHintToCarIfNecessaryLocked(primaryExtSource,
+ secondaryExtSource);
return sendFocusRequestToCarIfNecessaryLocked(focusToRequest, streamsToRequest, extFocus,
- audioContexts);
+ audioContexts, routingHintChanged);
+ }
+
+ /**
+ * Fix external source info if it is not valid.
+ * @param extSourceInfo
+ * @return true if value is not valid and was updated.
+ */
+ private boolean fixExtSourceAndContext(ExtSourceInfo extSourceInfo) {
+ if (!mExternalRoutingTypes.containsKey(extSourceInfo.source)) {
+ Log.w(CarLog.TAG_AUDIO, "External source not available:" + extSourceInfo.source);
+ // fall back to radio
+ extSourceInfo.source = mDefaultRadioRoutingType;
+ extSourceInfo.context = AudioHalService.AUDIO_CONTEXT_RADIO_FLAG;
+ return true;
+ }
+ if (extSourceInfo.context == AudioHalService.AUDIO_CONTEXT_RADIO_FLAG &&
+ !extSourceInfo.source.startsWith("RADIO_")) {
+ Log.w(CarLog.TAG_AUDIO, "Expecting Radio source:" + extSourceInfo.source);
+ extSourceInfo.source = mDefaultRadioRoutingType;
+ return true;
+ }
+ return false;
+ }
+
+ private int getPhysicalStreamFlagForExtSourceLocked(String extSource) {
+ AudioHalService.ExtRoutingSourceInfo info = mExternalRoutingTypes.get(
+ extSource);
+ if (info != null) {
+ return 0x1 << info.physicalStreamNumber;
+ } else {
+ return 0x1 << mRadioPhysicalStream;
+ }
+ }
+
+ private boolean sendExtRoutingHintToCarIfNecessaryLocked(String primarySource,
+ String secondarySource) {
+ if (!mExternalRoutingHintSupported) {
+ return false;
+ }
+ if (DBG) {
+ Log.d(TAG_FOCUS, "Setting external routing hint, primary:" + primarySource +
+ " secondary:" + secondarySource);
+ }
+ Arrays.fill(mExternalRoutingsScratch, 0);
+ fillExtRoutingPositionLocked(mExternalRoutingsScratch, primarySource);
+ fillExtRoutingPositionLocked(mExternalRoutingsScratch, secondarySource);
+ if (Arrays.equals(mExternalRoutingsScratch, mExternalRoutings)) {
+ return false;
+ }
+ System.arraycopy(mExternalRoutingsScratch, 0, mExternalRoutings, 0,
+ mExternalRoutingsScratch.length);
+ if (DBG) {
+ Log.d(TAG_FOCUS, "Set values:" + Arrays.toString(mExternalRoutingsScratch));
+ }
+ try {
+ mAudioHal.setExternalRoutingSource(mExternalRoutings);
+ } catch (IllegalArgumentException e) {
+ //ignore. can happen with mocking.
+ return false;
+ }
+ return true;
+ }
+
+ private void fillExtRoutingPositionLocked(int[] array, String extSource) {
+ if (extSource == null) {
+ return;
+ }
+ AudioHalService.ExtRoutingSourceInfo info = mExternalRoutingTypes.get(
+ extSource);
+ if (info == null) {
+ return;
+ }
+ int pos = info.bitPosition;
+ if (pos < 0) {
+ return;
+ }
+ int index = pos / 32;
+ int bitPosInInt = pos % 32;
+ array[index] |= (0x1 << bitPosInInt);
}
private boolean sendFocusRequestToCarIfNecessaryLocked(int focusToRequest,
- int streamsToRequest, int extFocus, int audioContexts) {
+ int streamsToRequest, int extFocus, int audioContexts, boolean forceSend) {
if (needsToSendFocusRequestLocked(focusToRequest, streamsToRequest, extFocus,
- audioContexts)) {
+ audioContexts) || forceSend) {
mLastFocusRequestToCar = FocusRequest.create(focusToRequest, streamsToRequest,
extFocus);
mCurrentAudioContexts = audioContexts;
@@ -963,6 +1226,9 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
mLastFocusRequestToCar = FocusRequest.STATE_RELEASE;
sent = true;
try {
+ if (mExternalRoutingHintSupported) {
+ mAudioHal.setExternalRoutingSource(mExternalRoutingsForFocusRelease);
+ }
mAudioHal.requestAudioFocusChange(
AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, 0, 0);
} catch (IllegalArgumentException e) {
@@ -1355,4 +1621,16 @@ public class CarAudioService extends ICarAudio.Stub implements CarServiceBase,
public static FocusRequest STATE_RELEASE =
new FocusRequest(AudioHalService.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, 0, 0);
}
+
+ private static class ExtSourceInfo {
+
+ public String source;
+ public int context;
+
+ public ExtSourceInfo set(String source, int context) {
+ this.source = source;
+ this.context = context;
+ return this;
+ }
+ }
}
diff --git a/service/src/com/android/car/CarHvacService.java b/service/src/com/android/car/CarHvacService.java
index ab09f606ab..173e3077e5 100644
--- a/service/src/com/android/car/CarHvacService.java
+++ b/service/src/com/android/car/CarHvacService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -17,182 +17,13 @@
package com.android.car;
import android.car.Car;
-import android.car.hardware.hvac.CarHvacEvent;
-import android.car.hardware.CarPropertyConfig;
-import android.car.hardware.CarPropertyValue;
-import android.car.hardware.hvac.ICarHvac;
-import android.car.hardware.hvac.ICarHvacEventListener;
import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.car.hal.HvacHalService;
+import com.android.car.CarLog;
import com.android.car.hal.VehicleHal;
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class CarHvacService extends ICarHvac.Stub
- implements CarServiceBase, HvacHalService.HvacHalListener {
- public static final boolean DBG = true;
- public static final String TAG = CarLog.TAG_HVAC + ".CarHvacService";
-
- private HvacHalService mHvacHal;
- private final Map<IBinder, ICarHvacEventListener> mListenersMap = new HashMap<>();
- private final Map<IBinder, HvacDeathRecipient> mDeathRecipientMap = new HashMap<>();
- private final Context mContext;
-
+public class CarHvacService extends CarPropertyServiceBase {
public CarHvacService(Context context) {
- mHvacHal = VehicleHal.getInstance().getHvacHal();
- mContext = context;
- }
-
- class HvacDeathRecipient implements IBinder.DeathRecipient {
- private static final String TAG = CarHvacService.TAG + ".HvacDeathRecipient";
- private IBinder mListenerBinder;
-
- HvacDeathRecipient(IBinder listenerBinder) {
- mListenerBinder = listenerBinder;
- }
-
- /**
- * Client died. Remove the listener from HAL service and unregister if this is the last
- * client.
- */
- @Override
- public void binderDied() {
- if (DBG) {
- Log.d(TAG, "binderDied " + mListenerBinder);
- }
- CarHvacService.this.unregisterListenerLocked(mListenerBinder);
- }
-
- void release() {
- mListenerBinder.unlinkToDeath(this, 0);
- }
- }
-
- @Override
- public synchronized void init() {
- }
-
- @Override
- public synchronized void release() {
- for (HvacDeathRecipient recipient : mDeathRecipientMap.values()) {
- recipient.release();
- }
- mDeathRecipientMap.clear();
- mListenersMap.clear();
- }
-
- @Override
- public void dump(PrintWriter writer) {
- // TODO
- }
-
- @Override
- public synchronized void registerListener(ICarHvacEventListener listener) {
- if (DBG) {
- Log.d(TAG, "registerListener");
- }
- ICarImpl.assertHvacPermission(mContext);
- if (listener == null) {
- Log.e(TAG, "registerListener: Listener is null.");
- throw new IllegalArgumentException("listener cannot be null.");
- }
-
- IBinder listenerBinder = listener.asBinder();
- if (mListenersMap.containsKey(listenerBinder)) {
- // Already registered, nothing to do.
- return;
- }
-
- HvacDeathRecipient deathRecipient = new HvacDeathRecipient(listenerBinder);
- try {
- listenerBinder.linkToDeath(deathRecipient, 0);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to link death for recipient. " + e);
- throw new IllegalStateException(Car.CAR_NOT_CONNECTED_EXCEPTION_MSG);
- }
- mDeathRecipientMap.put(listenerBinder, deathRecipient);
-
- if (mListenersMap.isEmpty()) {
- mHvacHal.setListener(this);
- }
-
- mListenersMap.put(listenerBinder, listener);
- }
-
- @Override
- public synchronized void unregisterListener(ICarHvacEventListener listener) {
- if (DBG) {
- Log.d(TAG, "unregisterListener");
- }
- ICarImpl.assertHvacPermission(mContext);
- if (listener == null) {
- Log.e(TAG, "unregisterListener: Listener is null.");
- throw new IllegalArgumentException("Listener is null");
- }
-
- IBinder listenerBinder = listener.asBinder();
- if (!mListenersMap.containsKey(listenerBinder)) {
- Log.e(TAG, "unregisterListener: Listener was not previously registered.");
- }
- unregisterListenerLocked(listenerBinder);
- }
-
- // Removes the listenerBinder from the current state.
- // The function assumes that the binder will exist both in listeners and death recipients list.
- private void unregisterListenerLocked(IBinder listenerBinder) {
- Object status = mListenersMap.remove(listenerBinder);
-
- if (status != null) {
- mDeathRecipientMap.get(listenerBinder).release();
- mDeathRecipientMap.remove(listenerBinder);
- }
-
- if (mListenersMap.isEmpty()) {
- mHvacHal.setListener(null);
- }
- }
-
- @Override
- public synchronized List<CarPropertyConfig> getHvacProperties() {
- ICarImpl.assertHvacPermission(mContext);
- return mHvacHal.getHvacProperties();
- }
-
- @Override
- public synchronized CarPropertyValue getProperty(int prop, int zone) {
- ICarImpl.assertHvacPermission(mContext);
- return mHvacHal.getHvacProperty(prop, zone);
- }
-
- @Override
- public synchronized void setProperty(CarPropertyValue prop) {
- ICarImpl.assertHvacPermission(mContext);
- mHvacHal.setHvacProperty(prop);
- }
-
- // Implement HvacHalListener interface
- @Override
- public synchronized void onPropertyChange(CarHvacEvent event) {
- for (ICarHvacEventListener l : mListenersMap.values()) {
- try {
- l.onEvent(event);
- } catch (RemoteException ex) {
- // If we could not send a record, its likely the connection snapped. Let the binder
- // death handle the situation.
- Log.e(TAG, "onEvent calling failed: " + ex);
- }
- }
- }
-
- @Override
- public synchronized void onError(int zone, int property) {
- // TODO:
+ super(context, VehicleHal.getInstance().getHvacHal(), Car.PERMISSION_CAR_HVAC, true,
+ CarLog.TAG_HVAC);
}
}
diff --git a/service/src/com/android/car/CarLog.java b/service/src/com/android/car/CarLog.java
index 79ed6060ea..6b2a2e404e 100644
--- a/service/src/com/android/car/CarLog.java
+++ b/service/src/com/android/car/CarLog.java
@@ -17,22 +17,24 @@
package com.android.car;
public class CarLog {
- public static final String TAG_APP_CONTEXT = "CAR.APP_CONTEXT";
+ public static final String TAG_AM = "CAR.AM";
+ public static final String TAG_APP_FOCUS = "CAR.APP_FOCUS";
public static final String TAG_AUDIO = "CAR.AUDIO";
public static final String TAG_CAMERA = "CAR.CAMERA";
+ public static final String TAG_CAN_BUS = "CAR.CAN_BUS";
+ public static final String TAG_CLUSTER = "CAR.CLUSTER";
public static final String TAG_HAL = "CAR.HAL";
public static final String TAG_HVAC = "CAR.HVAC";
public static final String TAG_INFO = "CAR.INFO";
+ public static final String TAG_INPUT = "CAR.INPUT";
+ public static final String TAG_NAV = "CAR.NAV";
public static final String TAG_PACKAGE = "CAR.PACKAGE";
public static final String TAG_POWER = "CAR.POWER";
+ public static final String TAG_PROJECTION = "CAR.PROJECTION";
+ public static final String TAG_PROPERTY = "CAR.PROPERTY";
public static final String TAG_RADIO = "CAR.RADIO";
public static final String TAG_SENSOR = "CAR.SENSOR";
public static final String TAG_SERVICE = "CAR.SERVICE";
- public static final String TAG_NAV = "CAR.NAV";
- public static final String TAG_TEST = "CAR.TEST";
- public static final String TAG_INPUT = "CAR.INPUT";
- public static final String TAG_PROJECTION = "CAR.PROJECTION";
- public static final String TAG_CLUSTER = "CAR.CLUSTER";
- public static final String TAG_CAN_BUS = "CAR.CAN_BUS";
public static final String TAG_SYS = "CAR.SYS";
+ public static final String TAG_TEST = "CAR.TEST";
}
diff --git a/service/src/com/android/car/CarPropertyServiceBase.java b/service/src/com/android/car/CarPropertyServiceBase.java
new file mode 100644
index 0000000000..4c2028ad70
--- /dev/null
+++ b/service/src/com/android/car/CarPropertyServiceBase.java
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ */
+
+package com.android.car;
+
+import android.car.Car;
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyEvent;
+import android.car.hardware.property.ICarProperty;
+import android.car.hardware.property.ICarPropertyEventListener;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.car.hal.PropertyHalServiceBase;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class implements the binder interface for ICarProperty.aidl to make it easier to create
+ * multiple managers that deal with Vehicle Properties. To create a new service, simply extend
+ * this class and call the super() constructor with the appropriate arguments for the new service.
+ * CarHvacService.java shows the basic usage.
+ */
+public class CarPropertyServiceBase extends ICarProperty.Stub
+ implements CarServiceBase, PropertyHalServiceBase.PropertyHalListener {
+ private final Context mContext;
+ private final boolean mDbg;
+ private final Map<IBinder, PropertyDeathRecipient> mDeathRecipientMap = new HashMap<>();
+ private final PropertyHalServiceBase mHal;
+ private final Map<IBinder, ICarPropertyEventListener> mListenersMap = new HashMap<>();
+ private final String mPermission;
+ private final String mTag;
+
+ public CarPropertyServiceBase(Context context, PropertyHalServiceBase hal, String permission,
+ boolean dbg, String tag) {
+ mContext = context;
+ mHal = hal;
+ mPermission = permission;
+ mDbg = dbg;
+ mTag = tag + ".service";
+ }
+
+ class PropertyDeathRecipient implements IBinder.DeathRecipient {
+ private IBinder mListenerBinder;
+
+ PropertyDeathRecipient(IBinder listenerBinder) {
+ mListenerBinder = listenerBinder;
+ }
+
+ /**
+ * Client died. Remove the listener from HAL service and unregister if this is the last
+ * client.
+ */
+ @Override
+ public void binderDied() {
+ if (mDbg) {
+ Log.d(mTag, "binderDied " + mListenerBinder);
+ }
+ CarPropertyServiceBase.this.unregisterListenerLocked(mListenerBinder);
+ }
+
+ void release() {
+ mListenerBinder.unlinkToDeath(this, 0);
+ }
+ }
+
+ @Override
+ public synchronized void init() {
+ }
+
+ @Override
+ public synchronized void release() {
+ for (PropertyDeathRecipient recipient : mDeathRecipientMap.values()) {
+ recipient.release();
+ }
+ mDeathRecipientMap.clear();
+ mListenersMap.clear();
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ // TODO
+ }
+
+ @Override
+ public synchronized void registerListener(ICarPropertyEventListener listener) {
+ if (mDbg) {
+ Log.d(mTag, "registerListener");
+ }
+ ICarImpl.assertPermission(mContext, mPermission);
+ if (listener == null) {
+ Log.e(mTag, "registerListener: Listener is null.");
+ throw new IllegalArgumentException("listener cannot be null.");
+ }
+
+ IBinder listenerBinder = listener.asBinder();
+ if (mListenersMap.containsKey(listenerBinder)) {
+ // Already registered, nothing to do.
+ return;
+ }
+
+ PropertyDeathRecipient deathRecipient = new PropertyDeathRecipient(listenerBinder);
+ try {
+ listenerBinder.linkToDeath(deathRecipient, 0);
+ } catch (RemoteException e) {
+ Log.e(mTag, "Failed to link death for recipient. " + e);
+ throw new IllegalStateException(Car.CAR_NOT_CONNECTED_EXCEPTION_MSG);
+ }
+ mDeathRecipientMap.put(listenerBinder, deathRecipient);
+
+ if (mListenersMap.isEmpty()) {
+ mHal.setListener(this);
+ }
+
+ mListenersMap.put(listenerBinder, listener);
+ }
+
+ @Override
+ public synchronized void unregisterListener(ICarPropertyEventListener listener) {
+ if (mDbg) {
+ Log.d(mTag, "unregisterListener");
+ }
+ ICarImpl.assertPermission(mContext, mPermission);
+ if (listener == null) {
+ Log.e(mTag, "unregisterListener: Listener is null.");
+ throw new IllegalArgumentException("Listener is null");
+ }
+
+ IBinder listenerBinder = listener.asBinder();
+ if (!mListenersMap.containsKey(listenerBinder)) {
+ Log.e(mTag, "unregisterListener: Listener was not previously registered.");
+ }
+ unregisterListenerLocked(listenerBinder);
+ }
+
+ // Removes the listenerBinder from the current state.
+ // The function assumes that binder will exist both in listeners and death recipients list.
+ private void unregisterListenerLocked(IBinder listenerBinder) {
+ boolean found = mListenersMap.remove(listenerBinder) != null;
+
+ if (found) {
+ mDeathRecipientMap.get(listenerBinder).release();
+ mDeathRecipientMap.remove(listenerBinder);
+ }
+
+ if (mListenersMap.isEmpty()) {
+ mHal.setListener(null);
+ }
+ }
+
+ @Override
+ public synchronized List<CarPropertyConfig> getPropertyList() {
+ ICarImpl.assertPermission(mContext, mPermission);
+ return mHal.getPropertyList();
+ }
+
+ @Override
+ public synchronized CarPropertyValue getProperty(int prop, int zone) {
+ ICarImpl.assertPermission(mContext, mPermission);
+ return mHal.getProperty(prop, zone);
+ }
+
+ @Override
+ public synchronized void setProperty(CarPropertyValue prop) {
+ ICarImpl.assertPermission(mContext, mPermission);
+ mHal.setProperty(prop);
+ }
+
+ // Implement PropertyHalListener interface
+ @Override
+ public synchronized void onPropertyChange(CarPropertyEvent event) {
+ for (ICarPropertyEventListener l : mListenersMap.values()) {
+ try {
+ l.onEvent(event);
+ } catch (RemoteException ex) {
+ // If we could not send a record, its likely the connection snapped. Let the binder
+ // death handle the situation.
+ Log.e(mTag, "onEvent calling failed: " + ex);
+ }
+ }
+ }
+
+ @Override
+ public synchronized void onError(int zone, int property) {
+ // TODO:
+ }
+}
diff --git a/service/src/com/android/car/CarVolumeControllerFactory.java b/service/src/com/android/car/CarVolumeControllerFactory.java
index b126f0cb18..ef4a5e1141 100644
--- a/service/src/com/android/car/CarVolumeControllerFactory.java
+++ b/service/src/com/android/car/CarVolumeControllerFactory.java
@@ -36,6 +36,7 @@ import com.android.car.CarVolumeService.CarVolumeController;
import com.android.car.hal.AudioHalService;
import com.android.internal.annotations.GuardedBy;
+import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
@@ -136,6 +137,12 @@ public class CarVolumeControllerFactory {
return true;
}
+ @Override
+ public void dump(PrintWriter writer) {
+ writer.println("Volume controller:" + SimpleCarVolumeController.class.getSimpleName());
+ // nothing else to dump
+ }
+
private void handleVolumeKeyDefault(KeyEvent event) {
if (event.getAction() != KeyEvent.ACTION_DOWN
|| interceptVolKeyBeforeDispatching(mContext)) {
@@ -200,6 +207,7 @@ public class CarVolumeControllerFactory {
private int mSupportedAudioContext;
private boolean mHasExternalMemory;
+ private boolean mMasterVolumeOnly;
@GuardedBy("this")
private int mCurrentContext = CarVolumeService.DEFAULT_CAR_AUDIO_CONTEXT;
@@ -266,6 +274,11 @@ public class CarVolumeControllerFactory {
case MSG_UPDATE_HAL:
stream = msg.arg1;
volume = msg.arg2;
+ synchronized (CarExternalVolumeController.this) {
+ if (mMasterVolumeOnly) {
+ stream = 0;
+ }
+ }
mHal.setStreamVolume(stream, volume);
break;
default:
@@ -287,6 +300,7 @@ public class CarVolumeControllerFactory {
void init() {
mSupportedAudioContext = mHal.getSupportedAudioVolumeContexts();
mHasExternalMemory = mHal.isExternalAudioVolumePersistent();
+ mMasterVolumeOnly = mHal.isAudioVolumeMasterOnly();
synchronized (this) {
initVolumeLimitLocked();
initCurrentVolumeLocked();
@@ -328,7 +342,8 @@ public class CarVolumeControllerFactory {
int carStream = carContextToCarStream(i);
Integer volume = volumesPerCarStream.get(carStream);
if (volume == null) {
- volume = Integer.valueOf(mHal.getStreamVolume(carStream));
+ volume = Integer.valueOf(mHal.getStreamVolume(mMasterVolumeOnly ? 0 :
+ carStream));
volumesPerCarStream.put(carStream, volume);
}
mCurrentCarContextVolume.put(i, volume);
@@ -422,11 +437,14 @@ public class CarVolumeControllerFactory {
synchronized (this) {
if (DBG) {
Log.d(TAG, "onVolumeChange carStream:" + carStream + " volume: " + volume
- + "volumeState: " + volumeState);
+ + " volumeState: " + volumeState);
}
// Assume single channel here.
int currentLogicalStream = VolumeUtils.carContextToAndroidStream(mCurrentContext);
int currentCarStream = carContextToCarStream(mCurrentContext);
+ if (mMasterVolumeOnly) { //for master volume only H/W, always assume current stream
+ carStream = currentCarStream;
+ }
if (currentCarStream == carStream) {
mCurrentCarContextVolume.put(mCurrentContext, volume);
mHandler.sendMessage(
@@ -537,7 +555,7 @@ public class CarVolumeControllerFactory {
// Otherwise, we need to tell Hal what the correct volume is for the new context.
int currentVolume = mCurrentCarContextVolume.get(primaryFocusContext);
- int carStreamNumber = mSupportedAudioContext == 0 ? primaryFocusPhysicalStream :
+ int carStreamNumber = (mSupportedAudioContext == 0) ? primaryFocusPhysicalStream :
primaryFocusContext;
if (DBG) {
Log.d(TAG, "Change volume from: "
@@ -547,5 +565,32 @@ public class CarVolumeControllerFactory {
updateHalVolumeLocked(carStreamNumber, currentVolume);
}
}
+
+ @Override
+ public void dump(PrintWriter writer) {
+ writer.println("Volume controller:" +
+ CarExternalVolumeController.class.getSimpleName());
+ synchronized (this) {
+ writer.println("mSupportedAudioContext:0x" +
+ Integer.toHexString(mSupportedAudioContext) +
+ ",mHasExternalMemory:" + mHasExternalMemory +
+ ",mMasterVolumeOnly:" + mMasterVolumeOnly);
+ writer.println("mCurrentContext:0x" + Integer.toHexString(mCurrentContext));
+ writer.println("mCurrentCarContextVolume:");
+ dumpVolumes(writer, mCurrentCarContextVolume);
+ writer.println("mCarContextVolumeMax:");
+ dumpVolumes(writer, mCarContextVolumeMax);
+ writer.println("mCarContextVolumeMin:");
+ dumpVolumes(writer, mCarContextVolumeMin);
+ writer.println("Number of volume controllers:" +
+ mVolumeControllers.getRegisteredCallbackCount());
+ }
+ }
+
+ private void dumpVolumes(PrintWriter writer, SparseArray<Integer> array) {
+ for (int i = 0; i < array.size(); i++) {
+ writer.println("0x" + Integer.toHexString(array.keyAt(i)) + ":" + array.valueAt(i));
+ }
+ }
}
}
diff --git a/service/src/com/android/car/CarVolumeService.java b/service/src/com/android/car/CarVolumeService.java
index b9bc5829bd..c8bfc9053e 100644
--- a/service/src/com/android/car/CarVolumeService.java
+++ b/service/src/com/android/car/CarVolumeService.java
@@ -24,6 +24,8 @@ import android.view.KeyEvent;
import com.android.car.hal.AudioHalService;
import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioContextFlag;
+import java.io.PrintWriter;
+
/**
* Handles car volume controls.
*
@@ -87,6 +89,10 @@ public class CarVolumeService {
return getController().getStreamVolume(stream);
}
+ public void dump(PrintWriter writer) {
+ mCarVolumeController.dump(writer);
+ }
+
private synchronized CarVolumeController getController() {
return mCarVolumeController;
}
@@ -102,5 +108,6 @@ public class CarVolumeService {
abstract public int getStreamMaxVolume(int stream);
abstract public int getStreamMinVolume(int stream);
abstract public int getStreamVolume(int stream);
+ abstract public void dump(PrintWriter writer);
}
}
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 580e5e96a1..05e8f592e2 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -18,12 +18,12 @@ package com.android.car;
import android.car.Car;
import android.car.ICar;
+import android.car.cluster.renderer.IInstrumentClusterNavigation;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.util.Log;
-import com.android.car.cluster.CarNavigationService;
import com.android.car.cluster.InstrumentClusterService;
import com.android.car.hal.VehicleHal;
import com.android.car.pm.CarPackageManagerService;
@@ -34,6 +34,8 @@ import java.io.PrintWriter;
public class ICarImpl extends ICar.Stub {
public static final String INTERNAL_INPUT_SERVICE = "internal_input";
+ public static final String INTERNAL_SYSTEM_ACTIVITY_MONITORING_SERVICE =
+ "system_activity_monitoring";
// load jni for all services here
static {
@@ -46,6 +48,7 @@ public class ICarImpl extends ICar.Stub {
private final Context mContext;
private final VehicleHal mHal;
+ private final SystemActivityMonitoringService mSystemActivityMonitoringService;
private final CarPowerManagementService mCarPowerManagementService;
private final CarPackageManagerService mCarPackageManagerService;
private final CarInputService mCarInputService;
@@ -57,9 +60,8 @@ public class ICarImpl extends ICar.Stub {
private final CarHvacService mCarHvacService;
private final CarRadioService mCarRadioService;
private final CarNightService mCarNightService;
- private final AppContextService mAppContextService;
+ private final AppFocusService mAppFocusService;
private final GarageModeService mGarageModeService;
- private final CarNavigationService mCarNavigationService;
private final InstrumentClusterService mInstrumentClusterService;
private final SystemStateControllerService mSystemStateControllerService;
@@ -87,34 +89,36 @@ public class ICarImpl extends ICar.Stub {
public ICarImpl(Context serviceContext) {
mContext = serviceContext;
mHal = VehicleHal.getInstance();
+ mSystemActivityMonitoringService = new SystemActivityMonitoringService(serviceContext);
mCarPowerManagementService = new CarPowerManagementService(serviceContext);
+ mCarSensorService = new CarSensorService(serviceContext);
+ mCarPackageManagerService = new CarPackageManagerService(serviceContext, mCarSensorService,
+ mSystemActivityMonitoringService);
mCarInputService = new CarInputService(serviceContext);
mCarProjectionService = new CarProjectionService(serviceContext, mCarInputService);
mGarageModeService = new GarageModeService(mContext, mCarPowerManagementService);
mCarInfoService = new CarInfoService(serviceContext);
- mAppContextService = new AppContextService(serviceContext);
- mCarSensorService = new CarSensorService(serviceContext);
+ mAppFocusService = new AppFocusService(serviceContext);
mCarAudioService = new CarAudioService(serviceContext, mCarInputService);
mCarHvacService = new CarHvacService(serviceContext);
mCarRadioService = new CarRadioService(serviceContext);
mCarCameraService = new CarCameraService(serviceContext);
mCarNightService = new CarNightService(serviceContext);
- mCarPackageManagerService = new CarPackageManagerService(serviceContext);
- mInstrumentClusterService = new InstrumentClusterService(serviceContext);
- mCarNavigationService = new CarNavigationService(
- mAppContextService, mInstrumentClusterService);
+ mInstrumentClusterService = new InstrumentClusterService(serviceContext,
+ mAppFocusService, mCarInputService);
mSystemStateControllerService = new SystemStateControllerService(serviceContext,
mCarPowerManagementService, mCarAudioService, this);
// Be careful with order. Service depending on other service should be inited later.
mAllServices = new CarServiceBase[] {
+ mSystemActivityMonitoringService,
mCarPowerManagementService,
+ mCarSensorService,
mCarPackageManagerService,
mCarInputService,
mGarageModeService,
mCarInfoService,
- mAppContextService,
- mCarSensorService,
+ mAppFocusService,
mCarAudioService,
mCarHvacService,
mCarRadioService,
@@ -122,7 +126,6 @@ public class ICarImpl extends ICar.Stub {
mCarNightService,
mInstrumentClusterService,
mCarProjectionService,
- mCarNavigationService,
mSystemStateControllerService
};
}
@@ -172,8 +175,8 @@ public class ICarImpl extends ICar.Stub {
return mCarSensorService;
case Car.INFO_SERVICE:
return mCarInfoService;
- case Car.APP_CONTEXT_SERVICE:
- return mAppContextService;
+ case Car.APP_FOCUS_SERVICE:
+ return mAppFocusService;
case Car.PACKAGE_SERVICE:
return mCarPackageManagerService;
case Car.CAMERA_SERVICE:
@@ -187,7 +190,9 @@ public class ICarImpl extends ICar.Stub {
return mCarRadioService;
case Car.CAR_NAVIGATION_SERVICE:
assertNavigationManagerPermission(mContext);
- return mCarNavigationService;
+ IInstrumentClusterNavigation navService =
+ mInstrumentClusterService.getNavigationService();
+ return navService == null ? null : navService.asBinder();
case Car.PROJECTION_SERVICE:
assertProjectionPermission(mContext);
return mCarProjectionService;
@@ -219,6 +224,8 @@ public class ICarImpl extends ICar.Stub {
switch (serviceName) {
case INTERNAL_INPUT_SERVICE:
return mCarInputService;
+ case INTERNAL_SYSTEM_ACTIVITY_MONITORING_SERVICE:
+ return mSystemActivityMonitoringService;
default:
Log.w(CarLog.TAG_SERVICE, "getCarInternalService for unknown service:" +
serviceName);
@@ -238,49 +245,32 @@ public class ICarImpl extends ICar.Stub {
}
public static void assertVehicleHalMockPermission(Context context) {
- if (context.checkCallingOrSelfPermission(Car.PERMISSION_MOCK_VEHICLE_HAL)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("requires CAR_MOCK_VEHICLE_HAL permission");
- }
+ assertPermission(context, Car.PERMISSION_MOCK_VEHICLE_HAL);
}
public static void assertCameraPermission(Context context) {
- if (context.checkCallingOrSelfPermission(Car.PERMISSION_CAR_CAMERA)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(
- "requires " + Car.PERMISSION_CAR_CAMERA);
- }
+ assertPermission(context, Car.PERMISSION_CAR_CAMERA);
}
public static void assertNavigationManagerPermission(Context context) {
- if (context.checkCallingOrSelfPermission(Car.PERMISSION_CAR_NAVIGATION_MANAGER)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(
- "requires " + Car.PERMISSION_CAR_NAVIGATION_MANAGER);
- }
+ assertPermission(context, Car.PERMISSION_CAR_NAVIGATION_MANAGER);
}
public static void assertHvacPermission(Context context) {
- if (context.checkCallingOrSelfPermission(Car.PERMISSION_CAR_HVAC)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(
- "requires " + Car.PERMISSION_CAR_HVAC);
- }
+ assertPermission(context, Car.PERMISSION_CAR_HVAC);
}
private static void assertRadioPermission(Context context) {
- if (context.checkCallingOrSelfPermission(Car.PERMISSION_CAR_RADIO)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(
- "requires permission " + Car.PERMISSION_CAR_RADIO);
- }
+ assertPermission(context, Car.PERMISSION_CAR_RADIO);
}
public static void assertProjectionPermission(Context context) {
- if (context.checkCallingOrSelfPermission(Car.PERMISSION_CAR_PROJECTION)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(
- "requires " + Car.PERMISSION_CAR_PROJECTION);
+ assertPermission(context, Car.PERMISSION_CAR_PROJECTION);
+ }
+
+ public static void assertPermission(Context context, String permission) {
+ if (context.checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("requires " + permission);
}
}
diff --git a/service/src/com/android/car/SystemActivityMonitoringService.java b/service/src/com/android/car/SystemActivityMonitoringService.java
new file mode 100644
index 0000000000..f647993a1e
--- /dev/null
+++ b/service/src/com/android/car/SystemActivityMonitoringService.java
@@ -0,0 +1,462 @@
+/*
+ * 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.
+ */
+package com.android.car;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.IProcessObserver;
+import android.app.ITaskStackListener;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Service to monitor AMS for new Activity or Service launching.
+ */
+public class SystemActivityMonitoringService implements CarServiceBase {
+
+ /**
+ * Container to hold info on top task in an Activity stack
+ */
+ public static class TopTaskInfoContainer {
+ public final ComponentName topActivity;
+ public final int taskId;
+ public final StackInfo stackInfo;
+
+ private TopTaskInfoContainer(ComponentName topActivity, int taskId, StackInfo stackInfo) {
+ this.topActivity = topActivity;
+ this.taskId = taskId;
+ this.stackInfo = stackInfo;
+ }
+
+ public boolean isMatching(ComponentName topActivity, int taskId, StackInfo stackInfo) {
+ return this.topActivity.equals(topActivity) && this.taskId == taskId &&
+ this.stackInfo.stackId == stackInfo.stackId &&
+ this.stackInfo.userId == stackInfo.userId;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "TaskInfoContainer [topActivity=%s, taskId=%d, stackId=%d, userId=%d",
+ topActivity, taskId, stackInfo.stackId, stackInfo.userId);
+ }
+ }
+
+ public interface ActivityLaunchListener {
+ /**
+ * Notify launch of activity.
+ * @param topTask Task information for what is currently launched.
+ */
+ void onActivityLaunch(TopTaskInfoContainer topTask);
+ }
+
+ private static final boolean DBG = true;
+
+ private static final int NUM_MAX_TASK_TO_FETCH = 10;
+
+ private final Context mContext;
+ private final IActivityManager mAm;
+ private final ProcessObserver mProcessObserver;
+ private final TaskListener mTaskListener;
+
+ private final HandlerThread mMonitorHandlerThread;
+ private final ActivityMonitorHandler mHandler;
+
+ /** K: stack id, V: top task */
+ private final SparseArray<TopTaskInfoContainer> mTopTasks = new SparseArray<>();
+ /** K: uid, V : list of pid */
+ private final Map<Integer, Set<Integer>> mForegroundUidPids = new HashMap<>();
+ private int mFocusedStackId = -1;
+
+ /**
+ * Temporary container to dispatch tasks for onActivityLaunch. Only used in handler thread.
+ * can be accessed without lock. */
+ private final List<TopTaskInfoContainer> mTasksToDispatch = new LinkedList<>();
+ private ActivityLaunchListener mActivityLaunchListener;
+
+ public SystemActivityMonitoringService(Context context) {
+ mContext = context;
+ mMonitorHandlerThread = new HandlerThread(CarLog.TAG_AM);
+ mMonitorHandlerThread.start();
+ mHandler = new ActivityMonitorHandler(mMonitorHandlerThread.getLooper());
+ mProcessObserver = new ProcessObserver();
+ mTaskListener = new TaskListener();
+ mAm = ActivityManagerNative.getDefault();
+ // Monitoring both listeners are necessary as there are cases where one listener cannot
+ // monitor activity change.
+ try {
+ mAm.registerProcessObserver(mProcessObserver);
+ mAm.registerTaskStackListener(mTaskListener);
+ } catch (RemoteException e) {
+ Log.e(CarLog.TAG_AM, "cannot register activity monitoring", e);
+ throw new RuntimeException(e);
+ }
+ updateTasks();
+ }
+
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public void release() {
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ writer.println("*SystemActivityMonitoringService*");
+ writer.println(" Top Tasks:");
+ synchronized (this) {
+ for (int i = 0; i < mTopTasks.size(); i++) {
+ TopTaskInfoContainer info = mTopTasks.valueAt(i);
+ if (info != null) {
+ writer.println(info);
+ }
+ }
+ writer.println(" Foregroud uid-pids:");
+ for (Integer key : mForegroundUidPids.keySet()) {
+ Set<Integer> pids = mForegroundUidPids.get(key);
+ if (pids == null) {
+ continue;
+ }
+ writer.println("uid:" + key + ", pids:" + Arrays.toString(pids.toArray()));
+ }
+ writer.println(" focused stack:" + mFocusedStackId);
+ }
+ }
+
+ /**
+ * Block the current task: Launch new activity with given Intent and finish the current task.
+ * @param currentTask task to finish
+ * @param newActivityIntent Intent for new Activity
+ */
+ public void blockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent) {
+ mHandler.requestBlockActivity(currentTask, newActivityIntent);
+ }
+
+ public List<TopTaskInfoContainer> getTopTasks() {
+ LinkedList<TopTaskInfoContainer> tasks = new LinkedList<>();
+ synchronized (this) {
+ for (int i = 0; i < mTopTasks.size(); i++) {
+ tasks.add(mTopTasks.valueAt(i));
+ }
+ }
+ return tasks;
+ }
+
+ public boolean isInForeground(int pid, int uid) {
+ synchronized (this) {
+ Set<Integer> pids = mForegroundUidPids.get(uid);
+ if (pids == null) {
+ return false;
+ }
+ if (pids.contains(pid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void registerActivityLaunchListener(ActivityLaunchListener listener) {
+ synchronized (this) {
+ mActivityLaunchListener = listener;
+ }
+ }
+
+ private void updateTasks() {
+ List<StackInfo> infos;
+ try {
+ infos = mAm.getAllStackInfos();
+ } catch (RemoteException e) {
+ Log.e(CarLog.TAG_AM, "cannot getTasks", e);
+ return;
+ }
+ int focusedStackId = -1;
+ try {
+ focusedStackId = mAm.getFocusedStackId();
+ } catch (RemoteException e) {
+ Log.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
+ return;
+ }
+ mTasksToDispatch.clear();
+ ActivityLaunchListener listener;
+ synchronized (this) {
+ listener = mActivityLaunchListener;
+ for (StackInfo info : infos) {
+ int stackId = info.stackId;
+ if (info.taskNames.length == 0 || !info.visible) { // empty stack or not shown
+ mTopTasks.remove(stackId);
+ continue;
+ }
+ // Assume last activity as top activity. StackInfo.topAcvitiy does not represent
+ // visible Activity correctly. Things will break if this assumption does not work.
+ int topActivityTaskId = info.taskIds[info.taskIds.length -1];
+ String topActivityName = info.taskNames[info.taskNames.length -1];
+ ComponentName topActivity = ComponentName.unflattenFromString(topActivityName);
+ TopTaskInfoContainer currentTopTaskInfo = mTopTasks.get(stackId);
+ // if a new task is added to stack or focused stack changes, should notify
+ if (currentTopTaskInfo == null ||
+ !currentTopTaskInfo.isMatching(topActivity, topActivityTaskId, info) ||
+ (focusedStackId == stackId && focusedStackId != mFocusedStackId)) {
+ currentTopTaskInfo = new TopTaskInfoContainer(topActivity,
+ topActivityTaskId, info);
+ mTopTasks.put(stackId, currentTopTaskInfo);
+ mTasksToDispatch.add(currentTopTaskInfo);
+ if (DBG) {
+ Log.i(CarLog.TAG_AM, "top activity:" + topActivityName + " stack:" + info);
+ }
+ }
+ }
+ mFocusedStackId = focusedStackId;
+ }
+ if (listener != null) {
+ for (TopTaskInfoContainer topTask : mTasksToDispatch) {
+ if (DBG) {
+ Log.i(CarLog.TAG_AM, "activity launched:" + topTask.toString());
+ }
+ listener.onActivityLaunch(topTask);
+ }
+ }
+ }
+
+ public StackInfo getFocusedStackForTopActivity(ComponentName activity) {
+ int focusedStackId = -1;
+ try {
+ focusedStackId = mAm.getFocusedStackId();
+ } catch (RemoteException e) {
+ Log.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
+ return null;
+ }
+ StackInfo focusedStack;
+ try {
+ focusedStack = mAm.getStackInfo(focusedStackId);
+ } catch (RemoteException e) {
+ Log.e(CarLog.TAG_AM, "cannot getFocusedStackId", e);
+ return null;
+ }
+ if (focusedStack.taskNames.length == 0) { // nothing in focused stack
+ return null;
+ }
+ ComponentName topActivity = ComponentName.unflattenFromString(
+ focusedStack.taskNames[focusedStack.taskNames.length - 1]);
+ if (topActivity.equals(activity)) {
+ return focusedStack;
+ } else {
+ return null;
+ }
+ }
+
+ private void handleForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+ synchronized (this) {
+ if (foregroundActivities) {
+ Set<Integer> pids = mForegroundUidPids.get(uid);
+ if (pids == null) {
+ pids = new ArraySet<Integer>();
+ mForegroundUidPids.put(uid, pids);
+ }
+ pids.add(pid);
+ } else {
+ doHandlePidGoneLocked(pid, uid);
+ }
+ }
+ }
+
+ private void handleProcessDied(int pid, int uid) {
+ synchronized (this) {
+ doHandlePidGoneLocked(pid, uid);
+ }
+ }
+
+ private void doHandlePidGoneLocked(int pid, int uid) {
+ Set<Integer> pids = mForegroundUidPids.get(uid);
+ if (pids != null) {
+ pids.remove(pid);
+ if (pids.isEmpty()) {
+ mForegroundUidPids.remove(uid);
+ }
+ }
+ }
+
+ private void handleBlockActivity(TopTaskInfoContainer currentTask, Intent newActivityIntent) {
+ Log.i(CarLog.TAG_AM, String.format("stopping activity %s with taskid:%d",
+ currentTask.topActivity, currentTask.taskId));
+ newActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivityAsUser(newActivityIntent,
+ new UserHandle(currentTask.stackInfo.userId));
+ // now make stack with new activity focused.
+ findTaskAndGrantFocus(newActivityIntent.getComponent());
+ try {
+ mAm.removeTask(currentTask.taskId);
+ } catch (RemoteException e) {
+ Log.w(CarLog.TAG_AM, "cannot remove task:" + currentTask.taskId, e);
+ }
+ }
+
+ private void findTaskAndGrantFocus(ComponentName activity) {
+ List<StackInfo> infos;
+ try {
+ infos = mAm.getAllStackInfos();
+ } catch (RemoteException e) {
+ Log.e(CarLog.TAG_AM, "cannot getTasks", e);
+ return;
+ }
+ for (StackInfo info : infos) {
+ if (info.taskNames.length == 0) {
+ continue;
+ }
+ ComponentName topActivity = ComponentName.unflattenFromString(
+ info.taskNames[info.taskNames.length - 1]);
+ if (activity.equals(topActivity)) {
+ try {
+ mAm.setFocusedStack(info.stackId);
+ } catch (RemoteException e) {
+ Log.e(CarLog.TAG_AM, "cannot setFocusedStack to stack:" + info.stackId, e);
+ }
+ return;
+ }
+ }
+ Log.i(CarLog.TAG_AM, "cannot give focus, cannot find Activity:" + activity);
+ }
+
+ private class ProcessObserver extends IProcessObserver.Stub {
+ @Override
+ public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+ if (DBG) {
+ Log.i(CarLog.TAG_AM,
+ String.format("onForegroundActivitiesChanged uid %d pid %d fg %b",
+ uid, pid, foregroundActivities));
+ }
+ mHandler.requestForegroundActivitiesChanged(pid, uid, foregroundActivities);
+ }
+
+ @Override
+ public void onProcessStateChanged(int pid, int uid, int procState) {
+ // ignore
+ }
+
+ @Override
+ public void onProcessDied(int pid, int uid) {
+ mHandler.requestProcessDied(pid, uid);
+ }
+ }
+
+ private class TaskListener extends ITaskStackListener.Stub {
+ @Override
+ public void onTaskStackChanged() {
+ if (DBG) {
+ Log.i(CarLog.TAG_AM, "onTaskStackChanged");
+ }
+ mHandler.requestUpdatingTask();
+ }
+
+ @Override
+ public void onActivityPinned() {
+ }
+
+ @Override
+ public void onPinnedActivityRestartAttempt() {
+ }
+
+ @Override
+ public void onPinnedStackAnimationEnded() {
+ }
+
+ @Override
+ public void onActivityForcedResizable(String packageName, int taskId) {
+ }
+
+ @Override
+ public void onActivityDismissingDockedStack() {
+ }
+ }
+
+ private class ActivityMonitorHandler extends Handler {
+ private static final int MSG_UPDATE_TASKS = 0;
+ private static final int MSG_FOREGROUND_ACTIVITIES_CHANGED = 1;
+ private static final int MSG_PROCESS_DIED = 2;
+ private static final int MSG_BLOCK_ACTIVITY = 3;
+
+ private ActivityMonitorHandler(Looper looper) {
+ super(looper);
+ }
+
+ private void requestUpdatingTask() {
+ Message msg = obtainMessage(MSG_UPDATE_TASKS);
+ sendMessage(msg);
+ }
+
+ private void requestForegroundActivitiesChanged(int pid, int uid,
+ boolean foregroundActivities) {
+ Message msg = obtainMessage(MSG_FOREGROUND_ACTIVITIES_CHANGED, pid, uid,
+ Boolean.valueOf(foregroundActivities));
+ sendMessage(msg);
+ }
+
+ private void requestProcessDied(int pid, int uid) {
+ Message msg = obtainMessage(MSG_PROCESS_DIED, pid, uid);
+ sendMessage(msg);
+ }
+
+ private void requestBlockActivity(TopTaskInfoContainer currentTask,
+ Intent newActivityIntent) {
+ Message msg = obtainMessage(MSG_BLOCK_ACTIVITY,
+ new Pair<TopTaskInfoContainer, Intent>(currentTask, newActivityIntent));
+ sendMessage(msg);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_TASKS:
+ updateTasks();
+ break;
+ case MSG_FOREGROUND_ACTIVITIES_CHANGED:
+ handleForegroundActivitiesChanged(msg.arg1, msg.arg2, (Boolean) msg.obj);
+ updateTasks();
+ break;
+ case MSG_PROCESS_DIED:
+ handleProcessDied(msg.arg1, msg.arg2);
+ break;
+ case MSG_BLOCK_ACTIVITY:
+ Pair<TopTaskInfoContainer, Intent> pair =
+ (Pair<TopTaskInfoContainer, Intent>) msg.obj;
+ handleBlockActivity(pair.first, pair.second);
+ break;
+ }
+ }
+ }
+}
diff --git a/service/src/com/android/car/cluster/CarNavigationService.java b/service/src/com/android/car/cluster/CarNavigationService.java
deleted file mode 100644
index 1e2887df95..0000000000
--- a/service/src/com/android/car/cluster/CarNavigationService.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2015 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.car.cluster;
-
-import android.car.CarAppContextManager;
-import android.car.cluster.renderer.NavigationRenderer;
-import android.car.navigation.CarNavigationInstrumentCluster;
-import android.car.navigation.CarNavigationManager;
-import android.car.navigation.ICarNavigation;
-import android.car.navigation.ICarNavigationEventListener;
-import android.graphics.Bitmap;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.car.AppContextService;
-import com.android.car.CarLog;
-import com.android.car.CarServiceBase;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Service that will push navigation event to navigation renderer in instrument cluster.
- */
-public class CarNavigationService extends ICarNavigation.Stub implements CarServiceBase {
- private static final String TAG = CarLog.TAG_NAV;
-
- private final List<CarNavigationEventListener> mListeners = new ArrayList<>();
- private final AppContextService mAppContextService;
- private final InstrumentClusterService mInstrumentClusterService;
-
- private volatile CarNavigationInstrumentCluster mInstrumentClusterInfo = null;
- private volatile NavigationRenderer mNavigationRenderer;
-
- public CarNavigationService(AppContextService appContextService,
- InstrumentClusterService instrumentClusterService) {
- mAppContextService = appContextService;
- mInstrumentClusterService = instrumentClusterService;
- }
-
- @Override
- public void init() {
- Log.d(TAG, "init");
- mNavigationRenderer = mInstrumentClusterService.getNavigationRenderer();
- mInstrumentClusterInfo = mNavigationRenderer != null
- ? mNavigationRenderer.getNavigationProperties() : null;
- }
-
- @Override
- public void release() {
- synchronized(mListeners) {
- mListeners.clear();
- }
- }
-
- @Override
- public void sendNavigationStatus(int status) {
- Log.d(TAG, "sendNavigationStatus, status: " + status);
- if (!isRendererAvailable()) {
- return;
- }
- verifyNavigationContextOwner();
-
- if (status == CarNavigationManager.STATUS_ACTIVE) {
- mNavigationRenderer.onStartNavigation();
- } else if (status == CarNavigationManager.STATUS_INACTIVE
- || status == CarNavigationManager.STATUS_UNAVAILABLE) {
- mNavigationRenderer.onStopNavigation();
- } else {
- throw new IllegalArgumentException("Unknown navigation status: " + status);
- }
- }
-
- @Override
- public void sendNavigationTurnEvent(
- int event, String road, int turnAngle, int turnNumber, Bitmap image, int turnSide) {
- Log.d(TAG, "sendNavigationTurnEvent, event:" + event + ", turnAngle: " + turnAngle + ", "
- + "turnNumber: " + turnNumber + ", " + "turnSide: " + turnSide);
- if (!isRendererAvailable()) {
- return;
- }
- verifyNavigationContextOwner();
-
- mNavigationRenderer.onNextTurnChanged(event, road, turnAngle, turnNumber, image, turnSide);
- }
-
- @Override
- public void sendNavigationTurnDistanceEvent(int distanceMeters, int timeSeconds) {
- Log.d(TAG, "sendNavigationTurnDistanceEvent, distanceMeters:" + distanceMeters + ", "
- + "timeSeconds: " + timeSeconds);
- if (!isRendererAvailable()) {
- return;
- }
- verifyNavigationContextOwner();
-
- mNavigationRenderer.onNextTurnDistanceChanged(distanceMeters, timeSeconds);
- }
-
- @Override
- public boolean registerEventListener(ICarNavigationEventListener listener) {
- CarNavigationEventListener eventListener;
- synchronized(mListeners) {
- if (findClientLocked(listener) != null) {
- return true;
- }
-
- eventListener = new CarNavigationEventListener(listener);
- try {
- listener.asBinder().linkToDeath(eventListener, 0);
- } catch (RemoteException e) {
- Log.w(TAG, "Adding listener failed.", e);
- return false;
- }
- mListeners.add(eventListener);
- }
-
- // The new listener needs to be told the instrument cluster parameters.
- if (isRendererAvailable()) {
- return eventListener.onInstrumentClusterStart(mInstrumentClusterInfo);
- }
- return true;
- }
-
- @Override
- public boolean unregisterEventListener(ICarNavigationEventListener listener) {
- CarNavigationEventListener client;
- synchronized (mListeners) {
- client = findClientLocked(listener);
- }
- return client != null && removeClient(client);
- }
-
- @Override
- public CarNavigationInstrumentCluster getInstrumentClusterInfo() {
- return mInstrumentClusterInfo;
- }
-
- @Override
- public boolean isInstrumentClusterSupported() {
- return mInstrumentClusterInfo != null;
- }
-
- private void verifyNavigationContextOwner() {
- if (!mAppContextService.isContextOwner(
- Binder.getCallingUid(),
- Binder.getCallingPid(),
- CarAppContextManager.APP_CONTEXT_NAVIGATION)) {
- throw new IllegalStateException(
- "Client is not an owner of APP_CONTEXT_NAVIGATION.");
- }
- }
-
- private boolean removeClient(CarNavigationEventListener listener) {
- synchronized(mListeners) {
- for (CarNavigationEventListener currentListener : mListeners) {
- // Use asBinder() for comparison.
- if (currentListener == listener) {
- currentListener.listener.asBinder().unlinkToDeath(currentListener, 0);
- return mListeners.remove(currentListener);
- }
- }
- }
- return false;
- }
-
- private CarNavigationEventListener findClientLocked(
- ICarNavigationEventListener listener) {
- for (CarNavigationEventListener existingListener : mListeners) {
- if (existingListener.listener.asBinder() == listener.asBinder()) {
- return existingListener;
- }
- }
- return null;
- }
-
- private class CarNavigationEventListener implements IBinder.DeathRecipient {
- final ICarNavigationEventListener listener;
-
- public CarNavigationEventListener(ICarNavigationEventListener listener) {
- this.listener = listener;
- }
-
- @Override
- public void binderDied() {
- listener.asBinder().unlinkToDeath(this, 0);
- removeClient(this);
- }
-
- /** Returns true if event sent successfully */
- public boolean onInstrumentClusterStart(CarNavigationInstrumentCluster clusterInfo) {
- try {
- listener.onInstrumentClusterStart(clusterInfo);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to call onInstrumentClusterStart for listener: " + listener, e);
- return false;
- }
- return true;
- }
- }
-
- @Override
- public void dump(PrintWriter writer) {
- // TODO Auto-generated method stub
- }
-
- private boolean isRendererAvailable() {
- boolean available = mNavigationRenderer != null && mInstrumentClusterInfo != null;
- if (!available) {
- Log.w(TAG, "Instrument cluster renderer is not available.");
- }
- return available;
- }
-}
diff --git a/service/src/com/android/car/cluster/InstrumentClusterRendererLoader.java b/service/src/com/android/car/cluster/InstrumentClusterRendererLoader.java
deleted file mode 100644
index 6d0a590e5e..0000000000
--- a/service/src/com/android/car/cluster/InstrumentClusterRendererLoader.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2015 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.car.cluster;
-
-import android.car.cluster.renderer.InstrumentClusterRenderer;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.car.CarLog;
-import com.android.car.R;
-
-import dalvik.system.PathClassLoader;
-
-import java.lang.reflect.Method;
-
-/**
- * Responsible for loading {@link InstrumentClusterRenderer} from separate android.car.cluster APK
- * library.
- */
-public class InstrumentClusterRendererLoader {
- private final static String TAG = CarLog.TAG_CLUSTER;
-
- private final static String sCreateRendererMethod = "createRenderer";
-
- /**
- * Returns true if instrument cluster renderer installed.
- */
- public static boolean isRendererAvailable(Context context) {
- String packageName = getRendererPackageName(context);
- if (TextUtils.isEmpty(packageName)) {
- Log.d(TAG, "Instrument cluster renderer was not configured.");
- return false;
- }
-
- PackageManager packageManager = context.getPackageManager();
- try {
- packageManager.getPackageInfo(packageName, 0);
- return true;
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Package not found: " + packageName);
- return false;
- }
- }
-
- /** Dynamically load renderer APK and creates {@link InstrumentClusterRenderer}. */
- public static InstrumentClusterRenderer createRenderer(Context context) {
- final String packageName = getRendererPackageName(context);
- try {
- return load(context, packageName, getRendererFactoryClassName(context));
- } catch (Exception e) {
- Log.e(TAG, "Failed to load renderer class: " + e.getMessage(), e);
- throw new RuntimeException(e);
- }
- }
-
- public static Context createRendererPackageContext(Context context) {
- return createPackageContext(context, getRendererPackageName(context));
- }
-
- /** To prevent instantiation of a singleton class. */
- private InstrumentClusterRendererLoader() {}
-
- /**
- * Creates package context for given package name. It is necessary to get renderer's context
- * so appropriate resources will be loaded in the renderer code.
- */
- private static Context createPackageContext(Context currentContext, String packageName) {
- try {
- return new PackageContextWrapper(currentContext.createPackageContext(packageName,
- Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY));
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Package not found: " + packageName, e);
- throw new IllegalStateException(e);
- }
- }
-
- /** Returns instrument cluster renderer or null if renderer package is not found */
- private static InstrumentClusterRenderer load(Context context, String packageName,
- String factoryClassName) throws Exception {
- PackageManager packageManager = context.getPackageManager();
-
- assertSignature(context.getApplicationContext(), packageManager, packageName);
-
- String clusterRendererApk = packageManager.getApplicationInfo(packageName, 0).sourceDir;
-
- PathClassLoader pathClassLoader = new dalvik.system.PathClassLoader(
- clusterRendererApk, ClassLoader.getSystemClassLoader());
-
- Class<?> factoryClass = Class.forName(factoryClassName, true, pathClassLoader);
-
- Method createRendererMethod = factoryClass.getMethod(sCreateRendererMethod);
-
- Object rendererObject = createRendererMethod.invoke(null /* static */);
-
- if (rendererObject == null) {
- Log.e(TAG, factoryClassName + "#" + sCreateRendererMethod + " returned null.");
- throw new IllegalStateException();
- }
-
- if (!(rendererObject instanceof InstrumentClusterRenderer)) {
- Log.e(TAG, factoryClassName + "#" + sCreateRendererMethod + " returned unexpected"
- + " object of class " + rendererObject.getClass().getCanonicalName());
- throw new IllegalStateException();
- }
- return (InstrumentClusterRenderer) rendererObject;
- }
-
- /** Asserts that signature of a given alienPackageName matches with the current application. */
- private static void assertSignature(Context applicationContext, PackageManager packageManager,
- String alienPackageName) {
- String carServicePackage = applicationContext.getPackageName();
- int signatureMatch = packageManager.checkSignatures(carServicePackage, alienPackageName);
- if (signatureMatch != PackageManager.SIGNATURE_MATCH) {
- throw new IllegalArgumentException(
- "Signature doesn't match for package: " + alienPackageName
- + ", signatureMatch: " + signatureMatch);
- }
- }
-
- private static String getRendererFactoryClassName(Context context) {
- return context.getString(R.string.instrumentClusterRendererFactoryClass);
- }
-
- private static String getRendererPackageName(Context context) {
- return context.getString(R.string.instrumentClusterRendererPackage);
- }
-
- /**
- * The context returned by Context.createPackageContext returns null in getApplicationContext
- * method which causes some problems later, e.g. when CursorLoader class is being used.
- */
- private static class PackageContextWrapper extends ContextWrapper {
- PackageContextWrapper(Context packageContext) {
- super(packageContext);
- }
-
- @Override
- public Context getApplicationContext() {
- return this;
- }
- }
-}
diff --git a/service/src/com/android/car/cluster/InstrumentClusterService.java b/service/src/com/android/car/cluster/InstrumentClusterService.java
index a6bd7f9fc3..425c65f146 100644
--- a/service/src/com/android/car/cluster/InstrumentClusterService.java
+++ b/service/src/com/android/car/cluster/InstrumentClusterService.java
@@ -15,15 +15,29 @@
*/
package com.android.car.cluster;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.car.cluster.renderer.InstrumentClusterRenderer;
-import android.car.cluster.renderer.NavigationRenderer;
+import android.car.CarAppFocusManager;
+import android.car.cluster.renderer.IInstrumentCluster;
+import android.car.cluster.renderer.IInstrumentClusterNavigation;
+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.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
+import android.view.KeyEvent;
+import com.android.car.AppFocusService;
+import com.android.car.AppFocusService.FocusOwnershipListener;
+import com.android.car.CarInputService;
+import com.android.car.CarInputService.KeyEventListener;
import com.android.car.CarLog;
import com.android.car.CarServiceBase;
+import com.android.car.CarServiceUtils;
+import com.android.car.R;
import java.io.PrintWriter;
@@ -33,54 +47,160 @@ import java.io.PrintWriter;
* @hide
*/
@SystemApi
-public class InstrumentClusterService implements CarServiceBase {
+public class InstrumentClusterService implements CarServiceBase,
+ FocusOwnershipListener, KeyEventListener {
- private static final String TAG = CarLog.TAG_CLUSTER + "."
- + InstrumentClusterService.class.getSimpleName();
+ private static final String TAG = CarLog.TAG_CLUSTER;
+ private static final Boolean DBG = true;
private final Context mContext;
+ private final AppFocusService mAppFocusService;
+ private final CarInputService mCarInputService;
- private InstrumentClusterRenderer mRenderer;
+ private Pair<Integer, Integer> mNavContextOwner;
- public InstrumentClusterService(Context context) {
+ private IInstrumentCluster mRendererService;
+ private boolean mRendererBound = false;
+
+ private final ServiceConnection mRendererServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ if (DBG) {
+ Log.d(TAG, "onServiceConnected, name: " + name + ", binder: " + binder);
+ }
+ mRendererService = IInstrumentCluster.Stub.asInterface(binder);
+
+ if (mNavContextOwner != null) {
+ notifyNavContextOwnerChanged(mNavContextOwner.first, mNavContextOwner.second);
+ }
+
+ try {
+ binder.linkToDeath(() -> CarServiceUtils.runOnMainSync(() -> {
+ Log.w(TAG, "Instrument cluster renderer died, trying to rebind");
+ mRendererService = null;
+ // Try to rebind with instrument cluster.
+ mRendererBound = bindInstrumentClusterRendererService();
+ }), 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.d(TAG, "onServiceDisconnected, name: " + name);
+ }
+ };
+
+ public InstrumentClusterService(Context context, AppFocusService appFocusService,
+ CarInputService carInputService) {
mContext = context;
+ mAppFocusService = appFocusService;
+ mCarInputService = carInputService;
}
@Override
public void init() {
- Log.d(TAG, "init");
-
- boolean rendererFound = InstrumentClusterRendererLoader.isRendererAvailable(mContext);
-
- if (rendererFound) {
- mRenderer = InstrumentClusterRendererLoader.createRenderer(mContext);
- Context packageContext = InstrumentClusterRendererLoader
- .createRendererPackageContext(mContext);
- mRenderer.onCreate(packageContext);
- mRenderer.initialize();
- mRenderer.onStart();
+ if (DBG) {
+ Log.d(TAG, "init");
}
+
+ mAppFocusService.registerContextOwnerChangedListener(this /* FocusOwnershipListener */);
+ mCarInputService.setInstrumentClusterKeyListener(this /* KeyEventListener */);
+ mRendererBound = bindInstrumentClusterRendererService();
}
@Override
public void release() {
- Log.d(TAG, "release");
- if (mRenderer != null) {
- mRenderer.onStop();
- mRenderer = null;
+ if (DBG) {
+ Log.d(TAG, "release");
+ }
+
+ mAppFocusService.unregisterContextOwnerChangedListener(this);
+ if (mRendererBound) {
+ mContext.unbindService(mRendererServiceConnection);
+ mRendererBound = false;
}
}
@Override
public void dump(PrintWriter writer) {
writer.println("**" + getClass().getSimpleName() + "**");
- writer.println("InstrumentClusterRenderer: " + mRenderer);
- writer.println("NavigationRenderer: "
- + (mRenderer != null ? mRenderer.getNavigationRenderer() : null));
+ writer.println("bound with renderer: " + mRendererBound);
+ writer.println("renderer service: " + mRendererService);
+ }
+
+ @Override
+ public void onFocusAcquired(int appType, int uid, int pid) {
+ if (appType != CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION) {
+ return;
+ }
+
+ mNavContextOwner = new Pair<>(uid, pid);
+
+ notifyNavContextOwnerChanged(uid, pid);
+ }
+
+ @Override
+ public void onFocusAbandoned(int appType, int uid, int pid) {
+ if (appType != CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION) {
+ return;
+ }
+
+ if (mNavContextOwner != null
+ && mNavContextOwner.first == uid
+ && mNavContextOwner.second == pid) {
+ notifyNavContextOwnerChanged(0, 0); // Reset focus ownership
+ }
}
- @Nullable
- public NavigationRenderer getNavigationRenderer() {
- return mRenderer != null ? mRenderer.getNavigationRenderer() : null;
+ private void notifyNavContextOwnerChanged(int uid, int pid) {
+ if (mRendererService != null) {
+ try {
+ mRendererService.setNavigationContextOwner(uid, pid);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call setNavigationContextOwner", e);
+ }
+ }
+ }
+
+ private boolean bindInstrumentClusterRendererService() {
+ String rendererService = mContext.getString(R.string.instrumentClusterRendererService);
+ if (TextUtils.isEmpty(rendererService)) {
+ Log.i(TAG, "Instrument cluster renderer was not configured");
+ return false;
+ }
+
+ Log.d(TAG, "bindInstrumentClusterRendererService, component: " + rendererService);
+
+ Intent intent = new Intent();
+ intent.setComponent(ComponentName.unflattenFromString(rendererService));
+ // Explicitly start service as we do not use BIND_AUTO_CREATE flag to handle renderer crash.
+ mContext.startService(intent);
+ return mContext.bindService(intent, mRendererServiceConnection, Context.BIND_IMPORTANT);
+ }
+
+ public IInstrumentClusterNavigation getNavigationService() {
+ try {
+ return mRendererService == null ? null : mRendererService.getNavigationService();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getNavigationServiceBinder" , e);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean onKeyEvent(KeyEvent event) {
+ if (DBG) {
+ Log.d(TAG, "InstrumentClusterService#onKeyEvent: " + event);
+ }
+ if (mRendererService != null) {
+ try {
+ mRendererService.onKeyEvent(event);
+ } catch (RemoteException e) {
+ Log.e(TAG, "onKeyEvent", e);
+ }
+ }
+ return true;
}
}
diff --git a/service/src/com/android/car/hal/AudioHalService.java b/service/src/com/android/car/hal/AudioHalService.java
index d60479d0b8..5470f9700a 100644
--- a/service/src/com/android/car/hal/AudioHalService.java
+++ b/service/src/com/android/car/hal/AudioHalService.java
@@ -47,6 +47,8 @@ import java.io.PrintWriter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
public class AudioHalService extends HalServiceBase {
public static final int VEHICLE_AUDIO_FOCUS_REQUEST_INVALID = -1;
@@ -131,6 +133,8 @@ public class AudioHalService extends HalServiceBase {
VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG;
public static final int AUDIO_CONTEXT_SYSTEM_SOUND_FLAG =
VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG;
+ public static final int AUDIO_CONTEXT_EXT_SOURCE_FLAG =
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_EXT_SOURCE_FLAG;
public interface AudioHalFocusListener {
/**
@@ -165,6 +169,8 @@ public class AudioHalService extends HalServiceBase {
void onVolumeLimitChange(int streamNumber, int volume);
}
+ private static final boolean DBG = true;
+
private final VehicleHal mVehicleHal;
private AudioHalFocusListener mFocusListener;
private AudioHalVolumeListener mVolumeListener;
@@ -215,7 +221,7 @@ public class AudioHalService extends HalServiceBase {
/**
* Returns the volume limits of a stream in the form <min, max>.
*/
- public Pair<Integer, Integer> getStreamVolumeLimit(int stream) {
+ public synchronized Pair<Integer, Integer> getStreamVolumeLimit(int stream) {
if (!isPropertySupportedLocked(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME)) {
throw new IllegalStateException("VEHICLE_PROPERTY_AUDIO_VOLUME not supported");
}
@@ -254,6 +260,10 @@ public class AudioHalService extends HalServiceBase {
* Convert car audio manager stream type (usage) into audio context type.
*/
public static int logicalStreamToHalContextType(int logicalStream) {
+ return logicalStreamWithExtTypeToHalContextType(logicalStream, null);
+ }
+
+ public static int logicalStreamWithExtTypeToHalContextType(int logicalStream, String extType) {
switch (logicalStream) {
case CarAudioManager.CAR_AUDIO_USAGE_RADIO:
return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG;
@@ -275,6 +285,24 @@ public class AudioHalService extends HalServiceBase {
return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG;
case CarAudioManager.CAR_AUDIO_USAGE_DEFAULT:
return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG;
+ case CarAudioManager.CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE:
+ if (extType != null) {
+ switch (extType) {
+ case CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_CD_DVD:
+ return AudioHalService.AUDIO_CONTEXT_CD_ROM_FLAG;
+ case CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_AUX_IN0:
+ case CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_AUX_IN1:
+ return AudioHalService.AUDIO_CONTEXT_AUX_AUDIO_FLAG;
+ default:
+ if (extType.startsWith("RADIO_")) {
+ return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG;
+ } else {
+ return AudioHalService.AUDIO_CONTEXT_EXT_SOURCE_FLAG;
+ }
+ }
+ } else { // no external source specified. fall back to radio
+ return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG;
+ }
case CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_BOTTOM:
case CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_CAR_PROXY:
case CarAudioAttributesUtil.CAR_AUDIO_USAGE_CARSERVICE_MEDIA_MUTE:
@@ -300,11 +328,11 @@ public class AudioHalService extends HalServiceBase {
case VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG:
return CarAudioManager.CAR_AUDIO_USAGE_VOICE_COMMAND;
case VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG:
- return CarAudioManager.CAR_AUDIO_USAGE_MUSIC;
+ return CarAudioManager.CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE;
case VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CALL_FLAG:
return CarAudioManager.CAR_AUDIO_USAGE_VOICE_CALL;
case VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG:
- return CarAudioManager.CAR_AUDIO_USAGE_MUSIC;
+ return CarAudioManager.CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE;
case VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG:
return CarAudioManager.CAR_AUDIO_USAGE_NOTIFICATION;
case VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG:
@@ -315,6 +343,8 @@ public class AudioHalService extends HalServiceBase {
return CarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SOUND;
case VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG:
return CarAudioManager.CAR_AUDIO_USAGE_DEFAULT;
+ case VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_EXT_SOURCE_FLAG:
+ return CarAudioManager.CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE;
default:
Log.w(CarLog.TAG_AUDIO, "Unknown car context:" + carContext);
return 0;
@@ -435,6 +465,20 @@ public class AudioHalService extends HalServiceBase {
return isPropertySupportedLocked(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT);
}
+ public synchronized boolean isAudioVolumeMasterOnly() {
+ if (!isPropertySupportedLocked(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME)) {
+ throw new IllegalStateException("VEHICLE_PROPERTY_AUDIO_VOLUME not supported");
+ }
+ VehiclePropConfig config = mProperties.get(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME);
+ if ((config.getConfigArray(1) &
+ VehicleAudioVolumeCapabilityFlag.VEHICLE_AUDIO_VOLUME_CAPABILITY_MASTER_VOLUME_ONLY)
+ != 0) {
+ return true;
+ }
+ return false;
+ }
+
/**
* Get the current audio focus state.
* @return 0: focusState, 1: streams, 2: externalFocus
@@ -452,6 +496,87 @@ public class AudioHalService extends HalServiceBase {
}
}
+ public static class ExtRoutingSourceInfo {
+ /** Represents an external route which will not disable any physical stream in android side.
+ */
+ public static final int NO_DISABLED_PHYSICAL_STREAM = -1;
+
+ /** Bit position of this source in vhal */
+ public final int bitPosition;
+ /**
+ * Physical stream replaced by this routing. will be {@link #NO_DISABLED_PHYSICAL_STREAM}
+ * if no physical stream for android is replaced by this routing.
+ */
+ public final int physicalStreamNumber;
+
+ public ExtRoutingSourceInfo(int bitPosition, int physycalStreamNumber) {
+ this.bitPosition = bitPosition;
+ this.physicalStreamNumber = physycalStreamNumber;
+ }
+
+ @Override
+ public String toString() {
+ return "[bitPosition=" + bitPosition + ", physycalStreamNumber="
+ + physicalStreamNumber + "]";
+ }
+ }
+
+ /**
+ * Get external audio routing types from AUDIO_EXT_ROUTING_HINT property.
+ *
+ * @return null if AUDIO_EXT_ROUTING_HINT is not supported.
+ */
+ public Map<String, ExtRoutingSourceInfo> getExternalAudioRoutingTypes() {
+ VehiclePropConfig config;
+ synchronized (this) {
+ if (!isPropertySupportedLocked(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT)) {
+ return null;
+ }
+ config = mProperties.get(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT);
+ }
+ if (!config.hasConfigString()) {
+ Log.w(CarLog.TAG_HAL, "AUDIO_EXT_ROUTING_HINT with empty config string");
+ return null;
+ }
+ Map<String, ExtRoutingSourceInfo> routingTypes = new HashMap<>();
+ String configString = config.getConfigString();
+ if (DBG) {
+ Log.i(CarLog.TAG_HAL, "AUDIO_EXT_ROUTING_HINT config string:" + configString);
+ }
+ String[] routes = configString.split(",");
+ for (String routeString : routes) {
+ String[] tokens = routeString.split(":");
+ int bitPosition = 0;
+ String name = null;
+ int physicalStreamNumber = ExtRoutingSourceInfo.NO_DISABLED_PHYSICAL_STREAM;
+ if (tokens.length == 2) {
+ bitPosition = Integer.parseInt(tokens[0]);
+ name = tokens[1];
+ } else if (tokens.length == 3) {
+ bitPosition = Integer.parseInt(tokens[0]);
+ name = tokens[1];
+ physicalStreamNumber = Integer.parseInt(tokens[2]);
+ } else {
+ Log.w(CarLog.TAG_AUDIO, "VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT has wrong entry:" +
+ routeString);
+ continue;
+ }
+ routingTypes.put(name, new ExtRoutingSourceInfo(bitPosition, physicalStreamNumber));
+ }
+ return routingTypes;
+ }
+
+ public void setExternalRoutingSource(int[] externalRoutings) {
+ try {
+ mVehicleHal.getVehicleNetwork().setIntVectorProperty(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT, externalRoutings);
+ } catch (ServiceSpecificException e) {
+ Log.e(CarLog.TAG_AUDIO, "Cannot write to VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT", e);
+ }
+ }
+
private boolean isPropertySupportedLocked(int property) {
VehiclePropConfig config = mProperties.get(property);
return config != null;
@@ -495,6 +620,7 @@ public class AudioHalService extends HalServiceBase {
case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME:
case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT:
case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_HW_VARIANT:
+ case VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT:
case VehicleNetworkConsts.VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE:
mProperties.put(p.getProp(), p);
break;
diff --git a/service/src/com/android/car/hal/HvacHalService.java b/service/src/com/android/car/hal/HvacHalService.java
index 98a15d1de3..1341506649 100644
--- a/service/src/com/android/car/hal/HvacHalService.java
+++ b/service/src/com/android/car/hal/HvacHalService.java
@@ -15,193 +15,21 @@
*/
package com.android.car.hal;
-import static com.android.car.hal.CarPropertyUtils.toCarPropertyValue;
-import static com.android.car.hal.CarPropertyUtils.toVehiclePropValue;
-import static java.lang.Integer.toHexString;
-
-import android.car.hardware.CarPropertyConfig;
-import android.car.hardware.CarPropertyValue;
-import android.car.hardware.hvac.CarHvacEvent;
import android.car.hardware.hvac.CarHvacManager.HvacPropertyId;
-import android.os.ServiceSpecificException;
-import android.util.Log;
-import android.util.SparseIntArray;
-import com.android.car.CarLog;
-import com.android.car.vehiclenetwork.VehicleNetwork;
import com.android.car.vehiclenetwork.VehicleNetworkConsts;
-import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig;
-import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-public class HvacHalService extends HalServiceBase {
- private static final boolean DBG = true;
- private static final String TAG = CarLog.TAG_HVAC + ".HvacHalService";
- private HvacHalListener mListener;
- private final VehicleHal mVehicleHal;
-
- private final HashMap<Integer, CarPropertyConfig<?>> mProps = new HashMap<>();
- private final SparseIntArray mHalPropToValueType = new SparseIntArray();
-
- public interface HvacHalListener {
- void onPropertyChange(CarHvacEvent event);
- void onError(int zone, int property);
- }
+public class HvacHalService extends PropertyHalServiceBase {
+ private static final boolean DBG = true;
+ private static final String TAG = "HvacHalService";
public HvacHalService(VehicleHal vehicleHal) {
- mVehicleHal = vehicleHal;
- if (DBG) {
- Log.d(TAG, "started HvacHalService!");
- }
- }
-
- public void setListener(HvacHalListener listener) {
- synchronized (this) {
- mListener = listener;
- }
- }
-
- public List<CarPropertyConfig> getHvacProperties() {
- List<CarPropertyConfig> propList;
- synchronized (mProps) {
- propList = new ArrayList<>(mProps.values());
- }
- return propList;
- }
-
- public CarPropertyValue getHvacProperty(int hvacPropertyId, int areaId) {
- int halProp = hvacToHalPropId(hvacPropertyId);
-
- VehiclePropValue value = null;
- try {
- VehiclePropValue valueRequest = VehiclePropValue.newBuilder()
- .setProp(halProp)
- .setZone(areaId)
- .setValueType(mHalPropToValueType.get(halProp))
- .build();
-
- value = mVehicleHal.getVehicleNetwork().getProperty(valueRequest);
- } catch (ServiceSpecificException e) {
- Log.e(CarLog.TAG_HVAC, "get, property not ready 0x" + toHexString(halProp), e);
- }
-
- return value == null ? null : toCarPropertyValue(value, hvacPropertyId);
- }
-
- public void setHvacProperty(CarPropertyValue prop) {
- VehiclePropValue halProp = toVehiclePropValue(prop, hvacToHalPropId(prop.getPropertyId()));
- try {
- mVehicleHal.getVehicleNetwork().setProperty(halProp);
- } catch (ServiceSpecificException e) {
- Log.e(CarLog.TAG_HVAC, "set, property not ready 0x" + toHexString(halProp.getProp()), e);
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public void init() {
- if (DBG) {
- Log.d(TAG, "init()");
- }
- synchronized (mProps) {
- // Subscribe to each of the HVAC properties
- for (Integer prop : mProps.keySet()) {
- mVehicleHal.subscribeProperty(this, prop, 0);
- }
- }
- }
-
- @Override
- public void release() {
- if (DBG) {
- Log.d(TAG, "release()");
- }
- synchronized (mProps) {
- for (Integer prop : mProps.keySet()) {
- mVehicleHal.unsubscribeProperty(this, prop);
- }
-
- // Clear the property list
- mProps.clear();
- }
- mListener = null;
- }
-
- @Override
- public synchronized List<VehiclePropConfig> takeSupportedProperties(
- List<VehiclePropConfig> allProperties) {
- List<VehiclePropConfig> taken = new LinkedList<>();
-
- for (VehiclePropConfig p : allProperties) {
- int hvacPropId;
- try {
- hvacPropId = halToHvacPropId(p.getProp());
- } catch (IllegalArgumentException e) {
- continue;
- }
- CarPropertyConfig hvacConfig = CarPropertyUtils.toCarPropertyConfig(p, hvacPropId);
-
- taken.add(p);
- mProps.put(p.getProp(), hvacConfig);
- mHalPropToValueType.put(p.getProp(), p.getValueType());
-
- if (DBG) {
- Log.d(TAG, "takeSupportedProperties: " + toHexString(p.getProp()));
- }
- }
- return taken;
- }
-
- @Override
- public void handleHalEvents(List<VehiclePropValue> values) {
- HvacHalListener listener;
- synchronized (this) {
- listener = mListener;
- }
- if (listener != null) {
- dispatchEventToListener(listener, values);
- }
- }
-
- private void dispatchEventToListener(HvacHalListener listener, List<VehiclePropValue> values) {
- for (VehiclePropValue v : values) {
- int prop = v.getProp();
-
- int hvacPropId;
- try {
- hvacPropId = halToHvacPropId(prop);
- } catch (IllegalArgumentException ex) {
- Log.e(TAG, "Property is not supported: 0x" + toHexString(prop), ex);
- continue;
- }
-
- CarHvacEvent event;
- CarPropertyValue<?> hvacProperty = toCarPropertyValue(v, hvacPropId);
- event = new CarHvacEvent(CarHvacEvent.HVAC_EVENT_PROPERTY_CHANGE, hvacProperty);
-
- listener.onPropertyChange(event);
- if (DBG) {
- Log.d(TAG, "dispatchEventToListener event: " + event);
- }
- }
- }
-
- @Override
- public void dump(PrintWriter writer) {
- writer.println("*HVAC HAL*");
- writer.println(" Properties available:");
- for (CarPropertyConfig prop : mProps.values()) {
- writer.println(" " + prop.toString());
- }
+ super(vehicleHal, TAG, DBG);
}
// Convert the HVAC public API property ID to HAL property ID
- private static int hvacToHalPropId(int hvacPropId) {
+ @Override
+ protected int managerToHalPropId(int hvacPropId) {
switch (hvacPropId) {
case HvacPropertyId.ZONED_FAN_SPEED_SETPOINT:
return VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_FAN_SPEED;
@@ -228,12 +56,13 @@ public class HvacHalService extends HalServiceBase {
case HvacPropertyId.ZONED_MAX_DEFROST_ON:
return VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_MAX_DEFROST_ON;
default:
- throw new IllegalArgumentException("hvacPropId " + hvacPropId + " is not supported");
+ throw new IllegalArgumentException("hvacPropId " + hvacPropId + " not supported");
}
}
// Convert he HAL specific property ID to HVAC public API
- private static int halToHvacPropId(int halPropId) {
+ @Override
+ protected int halToManagerPropId(int halPropId) {
switch (halPropId) {
case VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_FAN_SPEED:
return HvacPropertyId.ZONED_FAN_SPEED_SETPOINT;
@@ -260,7 +89,7 @@ public class HvacHalService extends HalServiceBase {
case VehicleNetworkConsts.VEHICLE_PROPERTY_HVAC_MAX_DEFROST_ON:
return HvacPropertyId.ZONED_MAX_DEFROST_ON;
default:
- throw new IllegalArgumentException("halPropId " + halPropId + " is not supported");
+ throw new IllegalArgumentException("halPropId " + halPropId + " not supported");
}
}
}
diff --git a/service/src/com/android/car/hal/PropertyHalServiceBase.java b/service/src/com/android/car/hal/PropertyHalServiceBase.java
new file mode 100644
index 0000000000..bc93e56847
--- /dev/null
+++ b/service/src/com/android/car/hal/PropertyHalServiceBase.java
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+package com.android.car.hal;
+
+import static com.android.car.hal.CarPropertyUtils.toCarPropertyValue;
+import static com.android.car.hal.CarPropertyUtils.toVehiclePropValue;
+import static java.lang.Integer.toHexString;
+
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.property.CarPropertyEvent;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.car.CarLog;
+import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropConfig;
+import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Common interface for HAL services that send Vehicle Properties back and forth via ICarProperty.
+ * Services that communicate by passing vehicle properties back and forth via ICarProperty should
+ * extend this class.
+ */
+public abstract class PropertyHalServiceBase extends HalServiceBase {
+ private final boolean mDbg;
+ private final SparseIntArray mHalPropToValueType = new SparseIntArray();
+ private PropertyHalListener mListener;
+ private final ConcurrentHashMap<Integer, CarPropertyConfig<?>> mProps =
+ new ConcurrentHashMap<>();
+ private final String mTag;
+ private final VehicleHal mVehicleHal;
+
+ public interface PropertyHalListener {
+ void onPropertyChange(CarPropertyEvent event);
+ void onError(int zone, int property);
+ }
+
+ public PropertyHalServiceBase(VehicleHal vehicleHal, String tag, boolean dbg) {
+ mVehicleHal = vehicleHal;
+ mTag = "PropertyHalServiceBase." + tag;
+ mDbg = dbg;
+
+ if (mDbg) {
+ Log.d(mTag, "started PropertyHalServiceBase!");
+ }
+ }
+
+ public void setListener(PropertyHalListener listener) {
+ synchronized (this) {
+ mListener = listener;
+ }
+ }
+
+ public List<CarPropertyConfig> getPropertyList() {
+ return new ArrayList<>(mProps.values());
+ }
+
+ public CarPropertyValue getProperty(int mgrPropId, int areaId) {
+ int halPropId = managerToHalPropId(mgrPropId);
+
+ VehiclePropValue value = null;
+ try {
+ VehiclePropValue valueRequest = VehiclePropValue.newBuilder()
+ .setProp(halPropId)
+ .setZone(areaId)
+ .setValueType(mHalPropToValueType.get(halPropId))
+ .build();
+
+ value = mVehicleHal.getVehicleNetwork().getProperty(valueRequest);
+ } catch (ServiceSpecificException e) {
+ Log.e(CarLog.TAG_PROPERTY, "get, property not ready 0x" + toHexString(halPropId), e);
+ }
+
+ return value == null ? null : toCarPropertyValue(value, mgrPropId);
+ }
+
+ public void setProperty(CarPropertyValue prop) {
+ int halPropId = managerToHalPropId(prop.getPropertyId());
+ VehiclePropValue halProp = toVehiclePropValue(prop, halPropId);
+ try {
+ mVehicleHal.getVehicleNetwork().setProperty(halProp);
+ } catch (ServiceSpecificException e) {
+ Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(halPropId), e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void init() {
+ if (mDbg) {
+ Log.d(mTag, "init()");
+ }
+ // Subscribe to each of the properties
+ for (Integer prop : mProps.keySet()) {
+ mVehicleHal.subscribeProperty(this, prop, 0);
+ }
+ }
+
+ @Override
+ public void release() {
+ if (mDbg) {
+ Log.d(mTag, "release()");
+ }
+
+ for (Integer prop : mProps.keySet()) {
+ mVehicleHal.unsubscribeProperty(this, prop);
+ }
+
+ // Clear the property list
+ mProps.clear();
+
+ synchronized (this) {
+ mListener = null;
+ }
+ }
+
+ @Override
+ public List<VehiclePropConfig> takeSupportedProperties(
+ List<VehiclePropConfig> allProperties) {
+ List<VehiclePropConfig> taken = new LinkedList<>();
+
+ for (VehiclePropConfig p : allProperties) {
+ int mgrPropId;
+ int halPropId;
+
+ try {
+ // See if the property is handled by this HAL
+ mgrPropId = halToManagerPropId(p.getProp());
+ halPropId = managerToHalPropId(mgrPropId);
+ if (halPropId != p.getProp()) {
+ throw new IllegalArgumentException("propId " + p.getProp() + " becomes " +
+ halPropId);
+ }
+ } catch (IllegalArgumentException e) {
+ continue;
+ }
+ CarPropertyConfig config = CarPropertyUtils.toCarPropertyConfig(p, mgrPropId);
+
+ taken.add(p);
+ mProps.put(p.getProp(), config);
+ mHalPropToValueType.put(p.getProp(), p.getValueType());
+
+ if (mDbg) {
+ Log.d(mTag, "takeSupportedProperties: " + toHexString(p.getProp()));
+ }
+ }
+ return taken;
+ }
+
+ @Override
+ public void handleHalEvents(List<VehiclePropValue> values) {
+ PropertyHalListener listener;
+ synchronized (this) {
+ listener = mListener;
+ }
+ if (listener != null) {
+ for (VehiclePropValue v : values) {
+ int prop = v.getProp();
+ int mgrPropId;
+
+ try {
+ mgrPropId = halToManagerPropId(prop);
+ } catch (IllegalArgumentException ex) {
+ Log.e(mTag, "Property is not supported: 0x" + toHexString(prop), ex);
+ continue;
+ }
+
+ CarPropertyEvent event;
+ CarPropertyValue<?> propVal = toCarPropertyValue(v, mgrPropId);
+ event = new CarPropertyEvent(CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE,
+ propVal);
+
+ listener.onPropertyChange(event);
+ if (mDbg) {
+ Log.d(mTag, "handleHalEvents event: " + event);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ writer.println(mTag);
+ writer.println(" Properties available:");
+ for (CarPropertyConfig prop : mProps.values()) {
+ writer.println(" " + prop.toString());
+ }
+ }
+
+ /**
+ * Convert manager property ID to Vehicle HAL property ID
+ */
+ abstract protected int managerToHalPropId(int managerPropId);
+
+ /**
+ * Convert Vehicle HAL property ID to manager property ID
+ */
+ abstract protected int halToManagerPropId(int halPropId);
+}
diff --git a/service/src/com/android/car/hal/VehicleHal.java b/service/src/com/android/car/hal/VehicleHal.java
index 181252417f..305f9aedf3 100644
--- a/service/src/com/android/car/hal/VehicleHal.java
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -247,9 +247,9 @@ public class VehicleHal implements VehicleNetworkListener {
}
public static boolean isPropertySubscribable(VehiclePropConfig config) {
- if (config.hasAccess() & VehiclePropAccess.VEHICLE_PROP_ACCESS_READ == 0 ||
- config.getChangeMode() ==
- VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC) {
+ if ((config.getAccess() & VehiclePropAccess.VEHICLE_PROP_ACCESS_READ) == 0 ||
+ (config.getChangeMode() ==
+ VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_STATIC)) {
return false;
}
return true;
diff --git a/service/src/com/android/car/pm/ActivityBlockingActivity.java b/service/src/com/android/car/pm/ActivityBlockingActivity.java
new file mode 100644
index 0000000000..fc6d9976a4
--- /dev/null
+++ b/service/src/com/android/car/pm/ActivityBlockingActivity.java
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+package com.android.car.pm;
+
+import android.app.Activity;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.content.pm.CarPackageManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+
+import com.android.car.CarLog;
+import com.android.car.R;
+
+/**
+ * Default activity that will be launched when the current foreground activity is not allowed.
+ * Additional information on blocked Activity will be passed as extra in Intent
+ * via {@link #INTENT_KEY_BLOCKED_ACTIVITY} key. *
+ */
+public class ActivityBlockingActivity extends Activity {
+
+ public static final String INTENT_KEY_BLOCKED_ACTIVITY = "blocked_activity";
+
+ private static final long AUTO_DISMISS_TIME_MS = 3000;
+
+ private Handler mHandler;
+
+ private Button mExitButton;
+
+ private Car mCar;
+
+ private boolean mExitRequested;
+
+ private final Runnable mFinishRunnable = () -> handleFinish();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_blocking);
+ mHandler = new Handler(Looper.getMainLooper());
+ mExitButton = (Button) findViewById(R.id.botton_exit_now);
+ mExitButton.setOnClickListener((View v) -> handleFinish());
+ mCar = Car.createCar(this, new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (mExitRequested) {
+ handleFinish();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+
+ });
+ mCar.connect();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mHandler.postDelayed(mFinishRunnable, AUTO_DISMISS_TIME_MS);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mHandler.removeCallbacks(mFinishRunnable);
+ }
+
+ private void handleFinish() {
+ if (!mCar.isConnected()) {
+ mExitRequested = true;
+ return;
+ }
+ if (isFinishing()) {
+ return;
+ }
+ try {
+ CarPackageManager carPm = (CarPackageManager) mCar.getCarManager(Car.PACKAGE_SERVICE);
+
+ // finish itself only when it will not lead into another blocking
+ if (carPm.isActivityBackedBySafeActivity(getComponentName())) {
+ finish();
+ return;
+ }
+ // back activity is not safe either. Now try home
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_HOME);
+ PackageManager pm = getPackageManager();
+ ComponentName homeComponent = homeIntent.resolveActivity(pm);
+ if (carPm.isActivityAllowedWhileDriving(homeComponent.getPackageName(),
+ homeComponent.getClassName())) {
+ startActivity(homeIntent);
+ finish();
+ return;
+ } else {
+ Log.w(CarLog.TAG_AM, "Home activity is not in white list. Keep blocking activity. "
+ + ", Home Activity:" + homeComponent);
+ }
+ } catch (CarNotConnectedException e) {
+ Log.w(CarLog.TAG_AM, "Car service not avaiable, will finish", e);
+ finish();
+ }
+ }
+}
diff --git a/service/src/com/android/car/pm/CarPackageManagerService.java b/service/src/com/android/car/pm/CarPackageManagerService.java
index 881650271b..e9298d472d 100644
--- a/service/src/com/android/car/pm/CarPackageManagerService.java
+++ b/service/src/com/android/car/pm/CarPackageManagerService.java
@@ -15,12 +15,17 @@
*/
package com.android.car.pm;
+import android.app.ActivityManager.StackInfo;
import android.car.Car;
import android.car.content.pm.AppBlockingPackageInfo;
import android.car.content.pm.CarAppBlockingPolicy;
import android.car.content.pm.CarAppBlockingPolicyService;
import android.car.content.pm.CarPackageManager;
import android.car.content.pm.ICarPackageManager;
+import android.car.hardware.CarSensorEvent;
+import android.car.hardware.CarSensorManager;
+import android.car.hardware.ICarSensorEventListener;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
@@ -29,6 +34,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
+import android.content.res.Resources;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -38,8 +44,12 @@ import android.util.Log;
import android.util.Pair;
import com.android.car.CarLog;
+import com.android.car.CarSensorService;
import com.android.car.CarServiceBase;
import com.android.car.CarServiceUtils;
+import com.android.car.R;
+import com.android.car.SystemActivityMonitoringService;
+import com.android.car.SystemActivityMonitoringService.TopTaskInfoContainer;
import com.android.car.pm.CarAppMetadataReader.CarAppMetadataInfo;
import com.android.internal.annotations.GuardedBy;
@@ -49,14 +59,18 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
+import java.util.Set;
//TODO monitor app installing and refresh policy
public class CarPackageManagerService extends ICarPackageManager.Stub implements CarServiceBase {
static final boolean DBG_POLICY_SET = true;
static final boolean DBG_POLICY_CHECK = false;
+ static final boolean DBG_POLICY_ENFORCEMENT = true;
private final Context mContext;
+ private final SystemActivityMonitoringService mSystemActivityMonitoringService;
+ private final CarSensorService mSensorService;
private final PackageManager mPackageManager;
private final HandlerThread mHandlerThread;
@@ -79,12 +93,25 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
@GuardedBy("this")
private final LinkedList<CarAppBlockingPolicy> mWaitingPolicies = new LinkedList<>();
- public CarPackageManagerService(Context context) {
+ private final boolean mEnableActivityBlocking;
+ private final ComponentName mActivityBlockingActivity;
+
+ private final ActivityLaunchListener mActivityLaunchListener = new ActivityLaunchListener();
+ private final SensorListener mDrivingStateListener = new SensorListener();
+
+ public CarPackageManagerService(Context context, CarSensorService sensorService,
+ SystemActivityMonitoringService systemActivityMonitoringService) {
mContext = context;
+ mSensorService = sensorService;
+ mSystemActivityMonitoringService = systemActivityMonitoringService;
mPackageManager = mContext.getPackageManager();
mHandlerThread = new HandlerThread(CarLog.TAG_PACKAGE);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
+ Resources res = context.getResources();
+ mEnableActivityBlocking = res.getBoolean(R.bool.enableActivityBlockingForSafety);
+ String blockingActivity = res.getString(R.string.activityBlockingActivity);
+ mActivityBlockingActivity = ComponentName.unflattenFromString(blockingActivity);
}
@Override
@@ -163,6 +190,25 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
return false;
}
+ @Override
+ public boolean isActivityBackedBySafeActivity(ComponentName activityName) {
+ if (!mEnableActivityBlocking || !mDrivingStateListener.isRestricted()) {
+ return true;
+ }
+ StackInfo info = mSystemActivityMonitoringService.getFocusedStackForTopActivity(
+ activityName);
+ if (info == null) { // not top in focused stack
+ return true;
+ }
+ if (info.taskNames.length <= 1) { // nothing below this.
+ return false;
+ }
+ ComponentName activityBehind = ComponentName.unflattenFromString(
+ info.taskNames[info.taskNames.length - 2]);
+ return isActivityAllowedWhileDriving(activityBehind.getPackageName(),
+ activityBehind.getClassName());
+ }
+
public Looper getLooper() {
return mHandlerThread.getLooper();
}
@@ -221,6 +267,13 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
mReleased = false;
mHandler.requestInit();
}
+ if (mEnableActivityBlocking) {
+ mSensorService.registerOrUpdateSensorListener(
+ CarSensorManager.SENSOR_TYPE_DRIVING_STATUS, 0, mDrivingStateListener);
+ mDrivingStateListener.resetState();
+ mSystemActivityMonitoringService.registerActivityLaunchListener(
+ mActivityLaunchListener);
+ }
}
@Override
@@ -238,6 +291,11 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
}
wakeupClientsWaitingForPolicySetitngLocked();
}
+ if (mEnableActivityBlocking) {
+ mSensorService.unregisterSensorListener(CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
+ mDrivingStateListener);
+ mSystemActivityMonitoringService.registerActivityLaunchListener(null);
+ }
}
// run from HandlerThread
@@ -256,11 +314,10 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
}
private void doSetPolicy() {
- //TODO should set policy to AMS
- // waiting for framework API to be ready
synchronized (this) {
wakeupClientsWaitingForPolicySetitngLocked();
}
+ blockTopActivitiesIfNecessary();
}
private void doUpdatePolicy(String packageName, CarAppBlockingPolicy policy, int flags) {
@@ -293,6 +350,7 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
Log.i(CarLog.TAG_PACKAGE, "policy set:" + dumpPoliciesLocked(false));
}
}
+ blockTopActivitiesIfNecessary();
}
private AppBlockingPackageInfoWrapper[] verifyList(AppBlockingPackageInfo[] list) {
@@ -369,22 +427,76 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
return false;
}
+ /**
+ * Return list of whitelist including default activity. Key is package name while
+ * value is list of activities. If list is empty, whole activities in the package
+ * are whitelisted.
+ * @return
+ */
+ private HashMap<String, Set<String>> parseConfigWhitelist() {
+ HashMap<String, Set<String>> packageToActivityMap = new HashMap<>();
+ Set<String> defaultActivity = new ArraySet<>();
+ defaultActivity.add(mActivityBlockingActivity.getClassName());
+ packageToActivityMap.put(mActivityBlockingActivity.getPackageName(), defaultActivity);
+ Resources res = mContext.getResources();
+ String whitelist = res.getString(R.string.defauiltActivityWhitelist);
+ String[] entries = whitelist.split(",");
+ for (String entry : entries) {
+ String[] packageActivityPair = entry.split("/");
+ Set<String> activities = packageToActivityMap.get(packageActivityPair[0]);
+ boolean newPackage = false;
+ if (activities == null) {
+ activities = new ArraySet<>();
+ newPackage = true;
+ packageToActivityMap.put(packageActivityPair[0], activities);
+ }
+ if (packageActivityPair.length == 1) { // whole package
+ activities.clear();
+ } else if (packageActivityPair.length == 2){
+ // add class name only when the whole package is not whitelisted.
+ if (newPackage || (activities.size() > 0)) {
+ activities.add(packageActivityPair[1]);
+ }
+ }
+ }
+ return packageToActivityMap;
+ }
+
private void generateSystemWhitelists() {
HashMap<String, AppBlockingPackageInfoWrapper> systemWhitelists = new HashMap<>();
+ HashMap<String, Set<String>> configWhitelist = parseConfigWhitelist();
// trust all system apps for services and trust all activities with car app meta-data.
List<PackageInfo> packages = mPackageManager.getInstalledPackages(0);
for (PackageInfo info : packages) {
if (info.applicationInfo != null && (info.applicationInfo.isSystemApp() ||
info.applicationInfo.isUpdatedSystemApp())) {
- CarAppMetadataInfo metadataInfo = CarAppMetadataReader.parseMetadata(mContext,
- info.packageName);
int flags = AppBlockingPackageInfo.FLAG_SYSTEM_APP;
- String[] activities = null;
- if (metadataInfo != null) {
- if (metadataInfo.useAllActivities) {
+ Set<String> configActivitiesForPackage =
+ configWhitelist.get(info.packageName);
+ if (configActivitiesForPackage != null) {
+ if(configActivitiesForPackage.size() == 0) {
flags |= AppBlockingPackageInfo.FLAG_WHOLE_ACTIVITY;
- } else {
- activities = metadataInfo.activities;
+ }
+ } else {
+ configActivitiesForPackage = new ArraySet<>();
+ }
+ String[] activities = null;
+ // Go through meta data if whole activities are allowed already
+ if ((flags & AppBlockingPackageInfo.FLAG_WHOLE_ACTIVITY) == 0) {
+ CarAppMetadataInfo metadataInfo = CarAppMetadataReader.parseMetadata(mContext,
+ info.packageName);
+ if (metadataInfo != null) {
+ if (metadataInfo.useAllActivities) {
+ flags |= AppBlockingPackageInfo.FLAG_WHOLE_ACTIVITY;
+ } else if(metadataInfo.activities != null) {
+ for (String activity : metadataInfo.activities) {
+ configActivitiesForPackage.add(activity);
+ }
+ }
+ }
+ if (configActivitiesForPackage.size() > 0) {
+ activities = configActivitiesForPackage.toArray(
+ new String[configActivitiesForPackage.size()]);
}
}
AppBlockingPackageInfo appBlockingInfo = new AppBlockingPackageInfo(
@@ -473,6 +585,8 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
public void dump(PrintWriter writer) {
synchronized (this) {
writer.println("*PackageManagementService*");
+ writer.println("mEnableActivityBlocking:" + mEnableActivityBlocking);
+ writer.println("ActivityRestricted:" + mDrivingStateListener.isRestricted());
writer.print(dumpPoliciesLocked(true));
}
}
@@ -506,6 +620,45 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
return sb.toString();
}
+ private void blockTopActivityIfNecessary(TopTaskInfoContainer topTask) {
+ boolean restricted = mDrivingStateListener.isRestricted();
+ if (!restricted) {
+ return;
+ }
+ doBlockTopActivityIfNotAllowed(topTask);
+ }
+
+ private void doBlockTopActivityIfNotAllowed(TopTaskInfoContainer topTask) {
+ boolean allowed = isActivityAllowedWhileDriving(
+ topTask.topActivity.getPackageName(),
+ topTask.topActivity.getClassName());
+ if (DBG_POLICY_ENFORCEMENT) {
+ Log.i(CarLog.TAG_PACKAGE, "new activity:" + topTask.toString() + " allowed:" + allowed);
+ }
+ if (!allowed) {
+ Log.i(CarLog.TAG_PACKAGE, "Current activity " + topTask.topActivity +
+ " not allowed, will block, number of tasks in stack:" +
+ topTask.stackInfo.taskIds.length);
+ Intent newActivityIntent = new Intent();
+ newActivityIntent.setComponent(mActivityBlockingActivity);
+ newActivityIntent.putExtra(
+ ActivityBlockingActivity.INTENT_KEY_BLOCKED_ACTIVITY,
+ topTask.topActivity.flattenToString());
+ mSystemActivityMonitoringService.blockActivity(topTask, newActivityIntent);
+ }
+ }
+
+ private void blockTopActivitiesIfNecessary() {
+ boolean restricted = mDrivingStateListener.isRestricted();
+ if (!restricted) {
+ return;
+ }
+ List<TopTaskInfoContainer> topTasks = mSystemActivityMonitoringService.getTopTasks();
+ for (TopTaskInfoContainer topTask : topTasks) {
+ doBlockTopActivityIfNotAllowed(topTask);
+ }
+ }
+
/**
* Reading policy and setting policy can take time. Run it in a separate handler thread.
*/
@@ -643,4 +796,46 @@ public class CarPackageManagerService extends ICarPackageManager.Stub implements
}
}
}
+
+ private class ActivityLaunchListener
+ implements SystemActivityMonitoringService.ActivityLaunchListener {
+ @Override
+ public void onActivityLaunch(TopTaskInfoContainer topTask) {
+ blockTopActivityIfNecessary(topTask);
+ }
+ }
+
+ private class SensorListener extends ICarSensorEventListener.Stub {
+ private int mLatestDrivingState;
+
+ private void resetState() {
+ CarSensorEvent lastEvent = mSensorService.getLatestSensorEvent(
+ CarSensorManager.SENSOR_TYPE_DRIVING_STATUS);
+ boolean shouldBlock = false;
+ synchronized (this) {
+ if (lastEvent == null) {
+ // When driving status is not available yet, do not block.
+ // This happens during bootup.
+ mLatestDrivingState = CarSensorEvent.DRIVE_STATUS_UNRESTRICTED;
+ } else {
+ mLatestDrivingState = lastEvent.intValues[0];
+ }
+ if (mLatestDrivingState != CarSensorEvent.DRIVE_STATUS_UNRESTRICTED) {
+ shouldBlock = true;
+ }
+ }
+ if (shouldBlock) {
+ blockTopActivitiesIfNecessary();
+ }
+ }
+
+ private synchronized boolean isRestricted() {
+ return mLatestDrivingState != CarSensorEvent.DRIVE_STATUS_UNRESTRICTED;
+ }
+
+ @Override
+ public void onSensorChanged(List<CarSensorEvent> events) {
+ resetState();
+ }
+ }
}
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/hvac_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/hvac_test.xml
index f5fb340cd5..063a6f9edb 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/hvac_test.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/hvac_test.xml
@@ -17,7 +17,7 @@
android:gravity="center"
android:layout_height="match_parent"
android:layout_width="match_parent"
- android:layout_marginTop="60dp"
+ android:layout_marginTop="160dp"
android:orientation="vertical">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml b/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml
index a92f608356..71c7fca195 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/instrument_cluster.xml
@@ -17,8 +17,8 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginTop="100dp"
- android:layout_marginLeft="40dp">
+ android:layout_marginTop="160dp"
+ android:layout_marginStart="40dp">
<LinearLayout
android:orientation="vertical"
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
index ce9e43e5aa..d22de6b02f 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -24,7 +24,7 @@ import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.support.car.Car;
-import android.support.car.CarAppContextManager;
+import android.support.car.CarAppFocusManager;
import android.support.car.CarNotConnectedException;
import android.support.car.CarNotSupportedException;
import android.support.car.ServiceConnectionListener;
@@ -34,7 +34,6 @@ import android.support.car.app.menu.CarMenuCallbacks;
import android.support.car.app.menu.RootMenu;
import android.support.car.hardware.CarSensorEvent;
import android.support.car.hardware.CarSensorManager;
-import android.support.car.navigation.CarNavigationManager;
import android.util.Log;
import com.google.android.car.kitchensink.audio.AudioTestFragment;
@@ -74,9 +73,7 @@ public class KitchenSinkActivity extends CarDrawerActivity {
private CarCameraManager mCameraManager;
private CarHvacManager mHvacManager;
private CarSensorManager mCarSensorManager;
- private CarNavigationManager mCarNavigationManager;
- private CarAppContextManager mCarAppContextManager;
-
+ private CarAppFocusManager mCarAppFocusManager;
private AudioTestFragment mAudioTestFragment;
private RadioTestFragment mRadioTestFragment;
@@ -183,14 +180,12 @@ public class KitchenSinkActivity extends CarDrawerActivity {
mCameraManager = (CarCameraManager) mCarApi.getCarManager(android.car.Car
.CAMERA_SERVICE);
mHvacManager = (CarHvacManager) mCarApi.getCarManager(android.car.Car.HVAC_SERVICE);
- mCarNavigationManager = (CarNavigationManager) mCarApi.getCarManager(
- android.car.Car.CAR_NAVIGATION_SERVICE);
mCarSensorManager = (CarSensorManager) mCarApi.getCarManager(Car.SENSOR_SERVICE);
mCarSensorManager.registerListener(mListener,
CarSensorManager.SENSOR_TYPE_DRIVING_STATUS,
CarSensorManager.SENSOR_RATE_NORMAL);
- mCarAppContextManager =
- (CarAppContextManager) mCarApi.getCarManager(Car.APP_CONTEXT_SERVICE);
+ mCarAppFocusManager =
+ (CarAppFocusManager) mCarApi.getCarManager(Car.APP_FOCUS_SERVICE);
} catch (CarNotConnectedException e) {
Log.e(TAG, "Car is not connected!", e);
} catch (CarNotSupportedException e) {
@@ -288,8 +283,6 @@ public class KitchenSinkActivity extends CarDrawerActivity {
} else if (id.equals(MENU_CLUSTER)) {
if (mInstrumentClusterFragment == null) {
mInstrumentClusterFragment = new InstrumentClusterFragment();
- mInstrumentClusterFragment.setCarNavigationManager(mCarNavigationManager);
- mInstrumentClusterFragment.setCarAppContextManager(mCarAppContextManager);
}
setContentFragment(mInstrumentClusterFragment);
} else if (id.equals(MENU_INPUT_TEST)) {
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
index bed33e2a8b..a040533187 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/audio/AudioTestFragment.java
@@ -17,9 +17,9 @@
package com.google.android.car.kitchensink.audio;
import android.car.Car;
-import android.car.CarAppContextManager;
-import android.car.CarAppContextManager.AppContextChangeListener;
-import android.car.CarAppContextManager.AppContextOwnershipChangeListener;
+import android.car.CarAppFocusManager;
+import android.car.CarAppFocusManager.AppFocusChangeListener;
+import android.car.CarAppFocusManager.AppFocusOwnershipChangeListener;
import android.car.CarNotConnectedException;
import android.car.media.CarAudioManager;
import android.content.ComponentName;
@@ -88,7 +88,7 @@ public class AudioTestFragment extends Fragment {
private Context mContext;
private Car mCar;
- private CarAppContextManager mAppContextManager;
+ private CarAppFocusManager mAppFocusManager;
private CarAudioManager mCarAudioManager;
private AudioAttributes mMusicAudioAttrib;
private AudioAttributes mNavAudioAttrib;
@@ -119,10 +119,10 @@ public class AudioTestFragment extends Fragment {
}
};
- private final AppContextOwnershipChangeListener mOwnershipListener =
- new AppContextOwnershipChangeListener() {
+ private final AppFocusOwnershipChangeListener mOwnershipListener =
+ new AppFocusOwnershipChangeListener() {
@Override
- public void onAppContextOwnershipLoss(int context) {
+ public void onAppFocusOwnershipLoss(int focus) {
}
};
@@ -133,20 +133,23 @@ public class AudioTestFragment extends Fragment {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
- mAppContextManager =
- (CarAppContextManager) mCar.getCarManager(Car.APP_CONTEXT_SERVICE);
+ mAppFocusManager =
+ (CarAppFocusManager) mCar.getCarManager(Car.APP_FOCUS_SERVICE);
} catch (CarNotConnectedException e) {
- throw new RuntimeException("Failed to create app context manager", e);
+ throw new RuntimeException("Failed to create app focus manager", e);
}
try {
- mAppContextManager.registerContextListener(new AppContextChangeListener() {
+ AppFocusChangeListener listener = new AppFocusChangeListener() {
@Override
- public void onAppContextChange(int activeContexts) {
+ public void onAppFocusChange(int appType, boolean active) {
}
- }, CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ };
+ mAppFocusManager.registerFocusListener(listener,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mAppFocusManager.registerFocusListener(listener,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to register context listener", e);
+ Log.e(TAG, "Failed to register focus listener", e);
}
try {
mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
@@ -250,7 +253,7 @@ public class AudioTestFragment extends Fragment {
mNavPlayOnce.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- if (mAppContextManager == null) {
+ if (mAppFocusManager == null) {
return;
}
if (DBG) {
@@ -258,10 +261,10 @@ public class AudioTestFragment extends Fragment {
}
if (!mNavGuidancePlayer.isPlaying()) {
try {
- mAppContextManager.setActiveContexts(mOwnershipListener,
- CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ mAppFocusManager.requestAppFocus(mOwnershipListener,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to set active context", e);
+ Log.e(TAG, "Failed to set active focus", e);
}
mNavGuidancePlayer.start(true, false,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
@@ -269,10 +272,10 @@ public class AudioTestFragment extends Fragment {
@Override
public void onCompletion() {
try {
- mAppContextManager.resetActiveContexts(
- CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ mAppFocusManager.abandonAppFocus(mOwnershipListener,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to reset active context", e);
+ Log.e(TAG, "Failed to reset active focus", e);
}
}
});
@@ -283,17 +286,17 @@ public class AudioTestFragment extends Fragment {
mVrPlayOnce.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- if (mAppContextManager == null) {
+ if (mAppFocusManager == null) {
return;
}
if (DBG) {
Log.i(TAG, "VR start");
}
try {
- mAppContextManager.setActiveContexts(mOwnershipListener,
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ mAppFocusManager.requestAppFocus(mOwnershipListener,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to set active context", e);
+ Log.e(TAG, "Failed to set active focus", e);
}
if (!mVrPlayer.isPlaying()) {
mVrPlayer.start(true, false, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
@@ -301,10 +304,10 @@ public class AudioTestFragment extends Fragment {
@Override
public void onCompletion() {
try {
- mAppContextManager.resetActiveContexts(
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ mAppFocusManager.abandonAppFocus(mOwnershipListener,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to reset active context", e);
+ Log.e(TAG, "Failed to reset active focus", e);
}
}
});
@@ -464,18 +467,17 @@ public class AudioTestFragment extends Fragment {
mAudioFocusHandler.release();
mAudioFocusHandler = null;
}
- if (mAppContextManager != null) {
+ if (mAppFocusManager != null) {
try {
- mAppContextManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ mAppFocusManager.abandonAppFocus(mOwnershipListener);
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to reset active context", e);
+ Log.e(TAG, "Failed to reset active focus", e);
}
}
}
private void handleNavStart() {
- if (mAppContextManager == null) {
+ if (mAppFocusManager == null) {
return;
}
if (mCarAudioManager == null) {
@@ -485,17 +487,17 @@ public class AudioTestFragment extends Fragment {
Log.i(TAG, "Nav start");
}
try {
- mAppContextManager.setActiveContexts(mOwnershipListener,
- CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ mAppFocusManager.requestAppFocus(mOwnershipListener,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to set active context", e);
+ Log.e(TAG, "Failed to set active focus", e);
}
mCarAudioManager.requestAudioFocus(mNavFocusListener, mNavAudioAttrib,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
}
private void handleNavEnd() {
- if (mAppContextManager == null) {
+ if (mAppFocusManager == null) {
return;
}
if (mCarAudioManager == null) {
@@ -505,16 +507,16 @@ public class AudioTestFragment extends Fragment {
Log.i(TAG, "Nav end");
}
try {
- mAppContextManager.resetActiveContexts(
- CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ mAppFocusManager.abandonAppFocus(mOwnershipListener,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to reset active context", e);
+ Log.e(TAG, "Failed to reset active focus", e);
}
mCarAudioManager.abandonAudioFocus(mNavFocusListener, mNavAudioAttrib);
}
private void handleVrStart() {
- if (mAppContextManager == null) {
+ if (mAppFocusManager == null) {
return;
}
if (mCarAudioManager == null) {
@@ -524,17 +526,17 @@ public class AudioTestFragment extends Fragment {
Log.i(TAG, "VR start");
}
try {
- mAppContextManager.setActiveContexts(mOwnershipListener,
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ mAppFocusManager.requestAppFocus(mOwnershipListener,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to set active context", e);
+ Log.e(TAG, "Failed to set active focus", e);
}
mCarAudioManager.requestAudioFocus(mVrFocusListener, mVrAudioAttrib,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, 0);
}
private void handleVrEnd() {
- if (mAppContextManager == null) {
+ if (mAppFocusManager == null) {
return;
}
if (mCarAudioManager == null) {
@@ -544,10 +546,10 @@ public class AudioTestFragment extends Fragment {
Log.i(TAG, "VR end");
}
try {
- mAppContextManager.resetActiveContexts(
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
+ mAppFocusManager.abandonAppFocus(mOwnershipListener,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to reset active context", e);
+ Log.e(TAG, "Failed to reset active focus", e);
}
mCarAudioManager.abandonAudioFocus(mVrFocusListener, mVrAudioAttrib);
}
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
index 421bb422bc..a24c122bc9 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/cluster/InstrumentClusterFragment.java
@@ -16,13 +16,17 @@
package com.google.android.car.kitchensink.cluster;
import android.app.AlertDialog;
+import android.content.ComponentName;
import android.os.Bundle;
import android.support.annotation.Nullable;
-import android.support.car.CarAppContextManager;
-import android.support.car.CarAppContextManager.AppContextChangeListener;
-import android.support.car.CarAppContextManager.AppContextOwnershipChangeListener;
+import android.support.car.Car;
+import android.support.car.CarAppFocusManager;
+import android.support.car.CarAppFocusManager.AppFocusChangeListener;
+import android.support.car.CarAppFocusManager.AppFocusOwnershipChangeListener;
import android.support.car.CarNotConnectedException;
-import android.support.car.navigation.CarNavigationManager;
+import android.support.car.CarNotSupportedException;
+import android.support.car.ServiceConnectionListener;
+import android.support.car.navigation.CarNavigationStatusManager;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
@@ -37,15 +41,51 @@ import com.google.android.car.kitchensink.R;
public class InstrumentClusterFragment extends Fragment {
private static final String TAG = InstrumentClusterFragment.class.getSimpleName();
- private CarNavigationManager mCarNavigationManager;
- private CarAppContextManager mCarAppContextManager;
+ private CarNavigationStatusManager mCarNavigationStatusManager;
+ private CarAppFocusManager mCarAppFocusManager;
+ private Car mCarApi;
- public void setCarNavigationManager(CarNavigationManager carNavigationManager) {
- mCarNavigationManager = carNavigationManager;
- }
+ private final ServiceConnectionListener mServiceConnectionListener =
+ new ServiceConnectionListener() {
+ @Override
+ public void onServiceConnected(ComponentName name) {
+ Log.d(TAG, "Connected to Car Service");
+ try {
+ mCarNavigationStatusManager = (CarNavigationStatusManager) mCarApi.getCarManager(
+ android.car.Car.CAR_NAVIGATION_SERVICE);
+ mCarAppFocusManager =
+ (CarAppFocusManager) mCarApi.getCarManager(Car.APP_FOCUS_SERVICE);
+ } catch (CarNotConnectedException e) {
+ Log.e(TAG, "Car is not connected!", e);
+ } catch (CarNotSupportedException e) {
+ Log.e(TAG, "Car is not supported!", e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.d(TAG, "Disconnect from Car Service");
+ }
- public void setCarAppContextManager(CarAppContextManager carAppContextManager) {
- mCarAppContextManager = carAppContextManager;
+ @Override
+ public void onServiceSuspended(int cause) {
+ Log.d(TAG, "Car Service connection suspended");
+ }
+
+ @Override
+ public void onServiceConnectionFailed(int cause) {
+ Log.d(TAG, "Car Service connection failed");
+ }
+ };
+
+ private void initCarApi() {
+ if (mCarApi != null && mCarApi.isConnected()) {
+ mCarApi.disconnect();
+ mCarApi = null;
+ }
+
+ mCarApi = Car.createCar(getContext(), mServiceConnectionListener);
+ mCarApi.connect();
}
@Nullable
@@ -57,62 +97,75 @@ public class InstrumentClusterFragment extends Fragment {
view.findViewById(R.id.cluster_start_button).setOnClickListener(v -> initCluster());
view.findViewById(R.id.cluster_turn_left_button).setOnClickListener(v -> turnLeft());
- return super.onCreateView(inflater, container, savedInstanceState);
+ return view;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ initCarApi();
+
+ super.onCreate(savedInstanceState);
}
private void turnLeft() {
try {
- mCarNavigationManager.sendNavigationTurnEvent(CarNavigationManager.TURN_TURN,
- "Huff Ave", 90, -1, null, CarNavigationManager.TURN_SIDE_LEFT);
- mCarNavigationManager.sendNavigationTurnDistanceEvent(500, 10);
+ mCarNavigationStatusManager
+ .sendNavigationTurnEvent(CarNavigationStatusManager.TURN_TURN, "Huff Ave", 90,
+ -1, null, CarNavigationStatusManager.TURN_SIDE_LEFT);
+ mCarNavigationStatusManager.sendNavigationTurnDistanceEvent(500, 10, 500,
+ CarNavigationStatusManager.DISTANCE_METERS);
} catch (CarNotConnectedException e) {
e.printStackTrace();
+ initCarApi(); // This might happen due to inst cluster renderer crash.
}
}
private void initCluster() {
try {
- mCarAppContextManager.registerContextListener(new AppContextChangeListener() {
+ mCarAppFocusManager.registerFocusListener(new AppFocusChangeListener() {
@Override
- public void onAppContextChange(int activeContexts) {
- Log.d(TAG, "onAppContextChange, activeContexts: " + activeContexts);
+ public void onAppFocusChange(int appType, boolean active) {
+ Log.d(TAG, "onAppFocusChange, appType: " + appType + " active: " + active);
}
- }, CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ }, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to register context listener", e);
+ Log.e(TAG, "Failed to register focus listener", e);
}
+ AppFocusOwnershipChangeListener focusListener = new AppFocusOwnershipChangeListener() {
+ @Override
+ public void onAppFocusOwnershipLoss(int focus) {
+ Log.w(TAG, "onAppFocusOwnershipLoss, focus: " + focus);
+ new AlertDialog.Builder(getContext())
+ .setTitle(getContext().getApplicationInfo().name)
+ .setMessage(R.string.cluster_nav_app_context_loss)
+ .show();
+ }
+ };
try {
- mCarAppContextManager.setActiveContexts(new AppContextOwnershipChangeListener() {
- @Override
- public void onAppContextOwnershipLoss(int context) {
- Log.w(TAG, "onAppContextOwnershipLoss, context: " + context);
- new AlertDialog.Builder(getContext())
- .setTitle(getContext().getApplicationInfo().name)
- .setMessage(R.string.cluster_nav_app_context_loss)
- .show();
- }
- }, CarAppContextManager.APP_CONTEXT_NAVIGATION);
+ mCarAppFocusManager.requestAppFocus(focusListener,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to set active context", e);
+ Log.e(TAG, "Failed to set active focus", e);
}
try {
- boolean ownsContext =
- mCarAppContextManager.isOwningContext(
- CarAppContextManager.APP_CONTEXT_NAVIGATION);
- Log.d(TAG, "Owns APP_CONTEXT_NAVIGATION: " + ownsContext);
- if (!ownsContext) {
- throw new RuntimeException("Context was not acquired.");
+ boolean ownsFocus = mCarAppFocusManager.isOwningFocus(focusListener,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ Log.d(TAG, "Owns APP_FOCUS_TYPE_NAVIGATION: " + ownsFocus);
+ if (!ownsFocus) {
+ throw new RuntimeException("Focus was not acquired.");
}
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to get owned context", e);
+ Log.e(TAG, "Failed to get owned focus", e);
}
try {
- mCarNavigationManager.sendNavigationStatus(CarNavigationManager.STATUS_ACTIVE);
+ mCarNavigationStatusManager
+ .sendNavigationStatus(CarNavigationStatusManager.STATUS_ACTIVE);
} catch (CarNotConnectedException e) {
- Log.e(TAG, "Failed to set navigation status", e);
+ Log.e(TAG, "Failed to set navigation status, reconnecting to the car", e);
+ initCarApi(); // This might happen due to inst cluster renderer crash.
}
}
}
diff --git a/tests/android_car_api_test/Android.mk b/tests/android_car_api_test/Android.mk
index aec4c71cb5..4048564325 100644
--- a/tests/android_car_api_test/Android.mk
+++ b/tests/android_car_api_test/Android.mk
@@ -32,7 +32,7 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_STATIC_JAVA_LIBRARIES += car-systemtest
+LOCAL_STATIC_JAVA_LIBRARIES += car-systemtest android-support-test
LOCAL_JAVA_LIBRARIES := android.car android.test.runner
diff --git a/tests/android_car_api_test/src/com/android/car/apitest/CarAppContextManagerTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarAppContextManagerTest.java
deleted file mode 100644
index 84e1c50f5d..0000000000
--- a/tests/android_car_api_test/src/com/android/car/apitest/CarAppContextManagerTest.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
- * Copyright (C) 2015 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.car.apitest;
-
-import android.car.Car;
-import android.car.CarAppContextManager;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-@MediumTest
-public class CarAppContextManagerTest extends CarApiTestBase {
- private static final String TAG = CarAppContextManager.class.getSimpleName();
- private CarAppContextManager mManager;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mManager = (CarAppContextManager) getCar().getCarManager(Car.APP_CONTEXT_SERVICE);
- assertNotNull(mManager);
- }
-
- public void testSetActiveNullListener() throws Exception {
- try {
- mManager.setActiveContexts(null, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- fail();
- } catch (IllegalArgumentException e) {
- // expected
- }
- }
-
- public void testRegisterNull() throws Exception {
- try {
- mManager.registerContextListener(null, 0);
- fail();
- } catch (IllegalArgumentException e) {
- // expected
- }
- }
-
- public void testRegisterUnregister() throws Exception {
- ContextChangeListerner listener = new ContextChangeListerner();
- ContextChangeListerner listener2 = new ContextChangeListerner();
- mManager.registerContextListener(listener, 0);
- mManager.registerContextListener(listener2, 0);
- mManager.unregisterContextListener();
- // this one is no-op
- mManager.unregisterContextListener();
- }
-
- public void testContextChange() throws Exception {
- DefaultServiceConnectionListener connectionListener =
- new DefaultServiceConnectionListener();
- Car car2 = Car.createCar(getContext(), connectionListener, null);
- car2.connect();
- connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
- CarAppContextManager manager2 = (CarAppContextManager)
- car2.getCarManager(Car.APP_CONTEXT_SERVICE);
- assertNotNull(manager2);
-
- assertEquals(0, mManager.getActiveAppContexts());
- ContextChangeListerner change = new ContextChangeListerner();
- ContextChangeListerner change2 = new ContextChangeListerner();
- ContextOwnershipChangeListerner owner = new ContextOwnershipChangeListerner();
- ContextOwnershipChangeListerner owner2 = new ContextOwnershipChangeListerner();
- mManager.registerContextListener(change, CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- manager2.registerContextListener(change2, CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
-
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- int expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION;
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(mManager.isOwningContext(expectedContexts));
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(change2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- expectedContexts));
- // change should not get notification for its own change
- assertFalse(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
-
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND;
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(change2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- expectedContexts));
- // change should not get notification for its own change
- assertFalse(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
-
- // this should be no-op
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertFalse(change2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- assertFalse(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
-
- manager2.setActiveContexts(owner2, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(owner.waitForOwnershipLossAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- CarAppContextManager.APP_CONTEXT_NAVIGATION));
-
- // no-op as it is not owning it
- mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
-
- mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION;
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(change2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
-
- manager2.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- expectedContexts = 0;
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- mManager.unregisterContextListener();
- manager2.unregisterContextListener();
- }
-
- public void testFilter() throws Exception {
- DefaultServiceConnectionListener connectionListener =
- new DefaultServiceConnectionListener();
- Car car2 = Car.createCar(getContext(), connectionListener);
- car2.connect();
- connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
- CarAppContextManager manager2 = (CarAppContextManager)
- car2.getCarManager(Car.APP_CONTEXT_SERVICE);
- assertNotNull(manager2);
-
- assertEquals(0, mManager.getActiveAppContexts());
- ContextChangeListerner change = new ContextChangeListerner();
- ContextChangeListerner listener = new ContextChangeListerner();
- ContextOwnershipChangeListerner owner = new ContextOwnershipChangeListerner();
- mManager.registerContextListener(change, CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- manager2.registerContextListener(listener, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertTrue(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- CarAppContextManager.APP_CONTEXT_NAVIGATION));
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- assertFalse(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- assertFalse(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertTrue(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- }
-
- private class ContextChangeListerner implements CarAppContextManager.AppContextChangeListener {
- private int mLastChangeEvent;
- private final Semaphore mChangeWait = new Semaphore(0);
-
- public boolean waitForContextChangeAndAssert(long timeoutMs, int expectedContexts)
- throws Exception {
- if (!mChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
- return false;
- }
- assertEquals(expectedContexts, mLastChangeEvent);
- return true;
- }
-
- @Override
- public void onAppContextChange(int activeContexts) {
- Log.i(TAG, "onAppContextChange " + Integer.toHexString(activeContexts));
- assertMainThread();
- mLastChangeEvent = activeContexts;
- mChangeWait.release();
- }
- }
-
- private class ContextOwnershipChangeListerner
- implements CarAppContextManager.AppContextOwnershipChangeListener {
- private int mLastLossEvent;
- private final Semaphore mLossEventWait = new Semaphore(0);
-
- public boolean waitForOwnershipLossAndAssert(long timeoutMs, int expectedContexts)
- throws Exception {
- if (!mLossEventWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
- return false;
- }
- assertEquals(expectedContexts, mLastLossEvent);
- return true;
- }
-
- @Override
- public void onAppContextOwnershipLoss(int context) {
- Log.i(TAG, "onAppContextOwnershipLoss " + Integer.toHexString(context));
- assertMainThread();
- mLastLossEvent = context;
- mLossEventWait.release();
- }
- }
-}
diff --git a/tests/android_car_api_test/src/com/android/car/apitest/CarAppFocusManagerTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarAppFocusManagerTest.java
new file mode 100644
index 0000000000..e234916f0c
--- /dev/null
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarAppFocusManagerTest.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2015 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.car.apitest;
+
+import android.car.Car;
+import android.car.CarAppFocusManager;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+import org.junit.Assert;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+public class CarAppFocusManagerTest extends CarApiTestBase {
+ private static final String TAG = CarAppFocusManagerTest.class.getSimpleName();
+ private CarAppFocusManager mManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mManager = (CarAppFocusManager) getCar().getCarManager(Car.APP_FOCUS_SERVICE);
+ assertNotNull(mManager);
+
+ // Request all application focuses and abandon them to ensure no active context is present
+ // when test starts.
+ FocusOwnershipChangeListerner owner = new FocusOwnershipChangeListerner();
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ mManager.abandonAppFocus(owner);
+ }
+
+ public void testSetActiveNullListener() throws Exception {
+ try {
+ mManager.requestAppFocus(null, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void testRegisterNull() throws Exception {
+ try {
+ mManager.registerFocusListener(null, 0);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void testRegisterUnregister() throws Exception {
+ FocusChangeListerner listener = new FocusChangeListerner();
+ FocusChangeListerner listener2 = new FocusChangeListerner();
+ mManager.registerFocusListener(listener, 1);
+ mManager.registerFocusListener(listener2, 1);
+ mManager.unregisterFocusListener(listener);
+ mManager.unregisterFocusListener(listener2);
+ }
+
+ public void testFocusChange() throws Exception {
+ DefaultServiceConnectionListener connectionListener =
+ new DefaultServiceConnectionListener();
+ Car car2 = Car.createCar(getContext(), connectionListener, null);
+ car2.connect();
+ connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+ CarAppFocusManager manager2 = (CarAppFocusManager)
+ car2.getCarManager(Car.APP_FOCUS_SERVICE);
+ assertNotNull(manager2);
+ final int[] emptyFocus = new int[0];
+
+ Assert.assertArrayEquals(emptyFocus, mManager.getActiveAppTypes());
+ FocusChangeListerner change = new FocusChangeListerner();
+ FocusChangeListerner change2 = new FocusChangeListerner();
+ FocusOwnershipChangeListerner owner = new FocusOwnershipChangeListerner();
+ FocusOwnershipChangeListerner owner2 = new FocusOwnershipChangeListerner();
+ mManager.registerFocusListener(change, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mManager.registerFocusListener(change, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ manager2.registerFocusListener(change2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ manager2.registerFocusListener(change2, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ int[] expectedFocuses = new int[] {CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION};
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertFalse(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertTrue(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertTrue(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ expectedFocuses = new int[] {
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND };
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertFalse(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+ assertTrue(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+
+ // this should be no-op
+ change.reset();
+ change2.reset();
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertFalse(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertFalse(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+
+ manager2.requestAppFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertTrue(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(owner.waitForOwnershipLossAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+
+ // no-op as it is not owning it
+ change.reset();
+ change2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertTrue(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+
+ change.reset();
+ change2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertTrue(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ expectedFocuses = new int[] {CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION};
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+ assertTrue(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+
+ change.reset();
+ change2.reset();
+ manager2.abandonAppFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertFalse(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ expectedFocuses = emptyFocus;
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ mManager.unregisterFocusListener(change);
+ manager2.unregisterFocusListener(change2);
+ }
+
+ public void testFilter() throws Exception {
+ DefaultServiceConnectionListener connectionListener =
+ new DefaultServiceConnectionListener();
+ Car car2 = Car.createCar(getContext(), connectionListener);
+ car2.connect();
+ connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+ CarAppFocusManager manager2 = (CarAppFocusManager)
+ car2.getCarManager(Car.APP_FOCUS_SERVICE);
+ assertNotNull(manager2);
+
+ Assert.assertArrayEquals(new int[0], mManager.getActiveAppTypes());
+
+ FocusChangeListerner listener = new FocusChangeListerner();
+ FocusChangeListerner listener2 = new FocusChangeListerner();
+ FocusOwnershipChangeListerner owner = new FocusOwnershipChangeListerner();
+ mManager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mManager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ manager2.registerFocusListener(listener2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertTrue(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+
+ listener.reset();
+ listener2.reset();
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+ assertFalse(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+
+ listener.reset();
+ listener2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+ assertFalse(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+
+ listener.reset();
+ listener2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ assertTrue(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ }
+
+ public void testMultipleChangeListenersPerManager() throws Exception {
+ FocusChangeListerner listener = new FocusChangeListerner();
+ FocusChangeListerner listener2 = new FocusChangeListerner();
+ FocusOwnershipChangeListerner owner = new FocusOwnershipChangeListerner();
+ mManager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mManager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ mManager.registerFocusListener(listener2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertTrue(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+
+ listener.reset();
+ listener2.reset();
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+ assertFalse(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+
+ listener.reset();
+ listener2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+ assertFalse(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+
+ listener.reset();
+ listener2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ assertTrue(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ }
+
+ private class FocusChangeListerner implements CarAppFocusManager.AppFocusChangeListener {
+ private int mLastChangeAppType;
+ private boolean mLastChangeAppActive;
+ private final Semaphore mChangeWait = new Semaphore(0);
+
+ public boolean waitForFocusChangeAndAssert(long timeoutMs, int expectedAppType,
+ boolean expectedAppActive) throws Exception {
+ if (!mChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ assertEquals(expectedAppType, mLastChangeAppType);
+ assertEquals(expectedAppActive, mLastChangeAppActive);
+ return true;
+ }
+
+ public void reset() {
+ mLastChangeAppType = 0;
+ mLastChangeAppActive = false;
+ }
+
+ @Override
+ public void onAppFocusChange(int appType, boolean active) {
+ Log.i(TAG, "onAppFocusChange appType=" + appType + " active=" + active);
+ assertMainThread();
+ mLastChangeAppType = appType;
+ mLastChangeAppActive = active;
+ mChangeWait.release();
+ }
+ }
+
+ private class FocusOwnershipChangeListerner
+ implements CarAppFocusManager.AppFocusOwnershipChangeListener {
+ private int mLastLossEvent;
+ private final Semaphore mLossEventWait = new Semaphore(0);
+
+ public boolean waitForOwnershipLossAndAssert(long timeoutMs, int expectedAppType)
+ throws Exception {
+ if (!mLossEventWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ assertEquals(expectedAppType, mLastLossEvent);
+ return true;
+ }
+
+ @Override
+ public void onAppFocusOwnershipLoss(int appType) {
+ Log.i(TAG, "onAppFocusOwnershipLoss " + appType);
+ assertMainThread();
+ mLastLossEvent = appType;
+ mLossEventWait.release();
+ }
+ }
+}
diff --git a/tests/android_car_api_test/src/com/android/car/apitest/CarNavigationManagerTest.java b/tests/android_car_api_test/src/com/android/car/apitest/CarNavigationManagerTest.java
index 2eb37eba86..a92b3804a3 100644
--- a/tests/android_car_api_test/src/com/android/car/apitest/CarNavigationManagerTest.java
+++ b/tests/android_car_api_test/src/com/android/car/apitest/CarNavigationManagerTest.java
@@ -16,18 +16,13 @@
package com.android.car.apitest;
import android.car.Car;
-import android.car.CarAppContextManager;
-import android.car.CarAppContextManager.AppContextChangeListener;
-import android.car.CarAppContextManager.AppContextOwnershipChangeListener;
-import android.car.navigation.CarNavigationInstrumentCluster;
+import android.car.CarAppFocusManager;
+import android.car.CarAppFocusManager.AppFocusChangeListener;
+import android.car.CarAppFocusManager.AppFocusOwnershipChangeListener;
import android.car.navigation.CarNavigationManager;
-import android.car.navigation.CarNavigationManager.CarNavigationListener;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
/**
* Unit tests for {@link CarNavigationManager}
*/
@@ -37,7 +32,7 @@ public class CarNavigationManagerTest extends CarApiTestBase {
private static final String TAG = CarNavigationManagerTest.class.getSimpleName();
private CarNavigationManager mCarNavigationManager;
- private CarAppContextManager mCarAppContextManager;
+ private CarAppFocusManager mCarAppFocusManager;
@Override
protected void setUp() throws Exception {
@@ -45,9 +40,9 @@ public class CarNavigationManagerTest extends CarApiTestBase {
mCarNavigationManager =
(CarNavigationManager) getCar().getCarManager(Car.CAR_NAVIGATION_SERVICE);
assertNotNull(mCarNavigationManager);
- mCarAppContextManager =
- (CarAppContextManager) getCar().getCarManager(Car.APP_CONTEXT_SERVICE);
- assertNotNull(mCarAppContextManager);
+ mCarAppFocusManager =
+ (CarAppFocusManager) getCar().getCarManager(Car.APP_FOCUS_SERVICE);
+ assertNotNull(mCarAppFocusManager);
}
public void testStart() throws Exception {
@@ -56,47 +51,29 @@ public class CarNavigationManagerTest extends CarApiTestBase {
return;
}
- final CountDownLatch onStartLatch = new CountDownLatch(1);
-
- mCarNavigationManager.registerListener(new CarNavigationListener() {
- @Override
- public void onInstrumentClusterStart(CarNavigationInstrumentCluster instrumentCluster) {
- // TODO: we should use VehicleHalMock once we implement HAL support in
- // CarNavigationStatusService.
- assertFalse(instrumentCluster.supportsCustomImages());
- assertEquals(1000, instrumentCluster.getMinIntervalMs());
- onStartLatch.countDown();
- }
-
- @Override
- public void onInstrumentClusterStop() {
- // TODO
- }
- });
-
- assertTrue(onStartLatch.await(DEFAULT_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
-
try {
mCarNavigationManager.sendNavigationStatus(1);
fail();
} catch (IllegalStateException expected) {
- // Expected. Client should acquire context ownership for APP_CONTEXT_NAVIGATION.
+ // Expected. Client should acquire focus ownership for APP_FOCUS_TYPE_NAVIGATION.
}
- mCarAppContextManager.registerContextListener(new AppContextChangeListener() {
+ mCarAppFocusManager.registerFocusListener(new AppFocusChangeListener() {
@Override
- public void onAppContextChange(int activeContexts) {
+ public void onAppFocusChange(int appType, boolean active) {
// Nothing to do here.
}
- }, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- mCarAppContextManager.setActiveContexts(new AppContextOwnershipChangeListener() {
+ }, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ AppFocusOwnershipChangeListener ownershipListener = new AppFocusOwnershipChangeListener() {
@Override
- public void onAppContextOwnershipLoss(int context) {
+ public void onAppFocusOwnershipLoss(int focus) {
// Nothing to do here.
}
- }, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertTrue(mCarAppContextManager.isOwningContext(
- CarAppContextManager.APP_CONTEXT_NAVIGATION));
+ };
+ mCarAppFocusManager.requestAppFocus(ownershipListener,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(mCarAppFocusManager.isOwningFocus(ownershipListener,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
// TODO: we should use mocked HAL to be able to verify this, right now just make sure that
// it is not crashing and logcat has appropriate traces.
diff --git a/tests/android_support_car_api_test/Android.mk b/tests/android_support_car_api_test/Android.mk
index fb6629626f..98f0f96196 100644
--- a/tests/android_support_car_api_test/Android.mk
+++ b/tests/android_support_car_api_test/Android.mk
@@ -32,7 +32,7 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_STATIC_JAVA_LIBRARIES += android.support.car
+LOCAL_STATIC_JAVA_LIBRARIES += android.support.car android-support-test
LOCAL_JAVA_LIBRARIES := android.car android.test.runner
diff --git a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarAppContextManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarAppContextManagerTest.java
deleted file mode 100644
index 9d164b40b8..0000000000
--- a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarAppContextManagerTest.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2015 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.support.car.apitest;
-
-import android.support.car.Car;
-import android.support.car.CarAppContextManager;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-@MediumTest
-public class CarAppContextManagerTest extends CarApiTestBase {
- private static final String TAG = CarAppContextManager.class.getSimpleName();
- private CarAppContextManager mManager;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mManager = (CarAppContextManager) getCar().getCarManager(Car.APP_CONTEXT_SERVICE);
- assertNotNull(mManager);
- }
-
- public void testSetActiveNullListener() throws Exception {
- try {
- mManager.setActiveContexts(null, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- fail();
- } catch (IllegalArgumentException e) {
- // expected
- }
- }
-
- public void testRegisterNull() throws Exception {
- try {
- mManager.registerContextListener(null, 0);
- fail();
- } catch (IllegalArgumentException e) {
- // expected
- }
- }
-
- public void testRegisterUnregister() throws Exception {
- ContextChangeListerner listener = new ContextChangeListerner();
- ContextChangeListerner listener2 = new ContextChangeListerner();
- mManager.registerContextListener(listener, 0);
- mManager.registerContextListener(listener2, 0);
- mManager.unregisterContextListener();
- // this one is no-op
- mManager.unregisterContextListener();
- }
-
- public void testContextChange() throws Exception {
- DefaultServiceConnectionListener connectionListener =
- new DefaultServiceConnectionListener();
- Car car2 = Car.createCar(getContext(), connectionListener, null);
- car2.connect();
- connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
- CarAppContextManager manager2 = (CarAppContextManager)
- car2.getCarManager(Car.APP_CONTEXT_SERVICE);
- assertNotNull(manager2);
-
- assertEquals(0, mManager.getActiveAppContexts());
- ContextChangeListerner change = new ContextChangeListerner();
- ContextChangeListerner change2 = new ContextChangeListerner();
- ContextOwnershipChangeListerner owner = new ContextOwnershipChangeListerner();
- ContextOwnershipChangeListerner owner2 = new ContextOwnershipChangeListerner();
- mManager.registerContextListener(change, CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- manager2.registerContextListener(change2, CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
-
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- int expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION;
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(mManager.isOwningContext(expectedContexts));
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(change2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- expectedContexts));
- // change should not get notification for its own change
- assertFalse(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
-
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND;
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(change2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- expectedContexts));
- // change should not get notification for its own change
- assertFalse(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
-
- // this should be no-op
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertFalse(change2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- assertFalse(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
-
- manager2.setActiveContexts(owner2, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(owner.waitForOwnershipLossAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- CarAppContextManager.APP_CONTEXT_NAVIGATION));
-
- // no-op as it is not owning it
- mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertTrue(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
-
- mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertTrue(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- expectedContexts = CarAppContextManager.APP_CONTEXT_NAVIGATION;
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(change2.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
-
- manager2.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(mManager.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_NAVIGATION));
- assertFalse(manager2.isOwningContext(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND));
- expectedContexts = 0;
- assertEquals(expectedContexts, mManager.getActiveAppContexts());
- assertEquals(expectedContexts, manager2.getActiveAppContexts());
- assertTrue(change.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- mManager.unregisterContextListener();
- manager2.unregisterContextListener();
- }
-
- public void testFilter() throws Exception {
- DefaultServiceConnectionListener connectionListener =
- new DefaultServiceConnectionListener();
- Car car2 = Car.createCar(getContext(), connectionListener);
- car2.connect();
- connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
- CarAppContextManager manager2 = (CarAppContextManager)
- car2.getCarManager(Car.APP_CONTEXT_SERVICE);
- assertNotNull(manager2);
-
- assertEquals(0, mManager.getActiveAppContexts());
- ContextChangeListerner change = new ContextChangeListerner();
- ContextChangeListerner listener = new ContextChangeListerner();
- ContextOwnershipChangeListerner owner = new ContextOwnershipChangeListerner();
- mManager.registerContextListener(change, CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- manager2.registerContextListener(listener, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertTrue(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
- CarAppContextManager.APP_CONTEXT_NAVIGATION));
- mManager.setActiveContexts(owner, CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- assertFalse(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- assertFalse(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- mManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
- assertTrue(listener.waitForContextChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS, 0));
- }
-
- private class ContextChangeListerner implements CarAppContextManager.AppContextChangeListener {
- private int mLastChangeEvent;
- private final Semaphore mChangeWait = new Semaphore(0);
-
- public boolean waitForContextChangeAndAssert(long timeoutMs, int expectedContexts)
- throws Exception {
- if (!mChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
- return false;
- }
- assertEquals(expectedContexts, mLastChangeEvent);
- return true;
- }
- @Override
- public void onAppContextChange(int activeContexts) {
- Log.i(TAG, "onAppContextChange " + Integer.toHexString(activeContexts));
- assertMainThread();
- mLastChangeEvent = activeContexts;
- mChangeWait.release();
- }
- }
-
- private class ContextOwnershipChangeListerner
- implements CarAppContextManager.AppContextOwnershipChangeListener {
- private int mLastLossEvent;
- private final Semaphore mLossEventWait = new Semaphore(0);
-
- public boolean waitForOwnershipLossAndAssert(long timeoutMs, int expectedContexts)
- throws Exception {
- if (!mLossEventWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
- return false;
- }
- assertEquals(expectedContexts, mLastLossEvent);
- return true;
- }
-
- @Override
- public void onAppContextOwnershipLoss(int context) {
- Log.i(TAG, "onAppContextOwnershipLoss " + Integer.toHexString(context));
- assertMainThread();
- mLastLossEvent = context;
- mLossEventWait.release();
- }
- }
-}
diff --git a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarAppFocusManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarAppFocusManagerTest.java
new file mode 100644
index 0000000000..1e5491c78a
--- /dev/null
+++ b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarAppFocusManagerTest.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2015 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.support.car.apitest;
+
+import android.support.car.Car;
+import android.support.car.CarAppFocusManager;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+import org.junit.Assert;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+public class CarAppFocusManagerTest extends CarApiTestBase {
+ private static final String TAG = CarAppFocusManagerTest.class.getSimpleName();
+ private CarAppFocusManager mManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mManager = (CarAppFocusManager) getCar().getCarManager(Car.APP_FOCUS_SERVICE);
+ assertNotNull(mManager);
+
+ // Request all application focuses and abandon them to ensure no active context is present
+ // when test starts.
+ FocusOwnershipChangeListerner owner = new FocusOwnershipChangeListerner();
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ mManager.abandonAppFocus(owner);
+ }
+
+ public void testSetActiveNullListener() throws Exception {
+ try {
+ mManager.requestAppFocus(null, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void testRegisterNull() throws Exception {
+ try {
+ mManager.registerFocusListener(null, 0);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void testRegisterUnregister() throws Exception {
+ FocusChangeListerner listener = new FocusChangeListerner();
+ FocusChangeListerner listener2 = new FocusChangeListerner();
+ mManager.registerFocusListener(listener, 1);
+ mManager.registerFocusListener(listener2, 1);
+ mManager.unregisterFocusListener(listener);
+ mManager.unregisterFocusListener(listener2);
+ }
+
+ public void testFocusChange() throws Exception {
+ DefaultServiceConnectionListener connectionListener =
+ new DefaultServiceConnectionListener();
+ Car car2 = Car.createCar(getContext(), connectionListener, null);
+ car2.connect();
+ connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+ CarAppFocusManager manager2 = (CarAppFocusManager)
+ car2.getCarManager(Car.APP_FOCUS_SERVICE);
+ assertNotNull(manager2);
+ final int[] emptyFocus = new int[0];
+
+ Assert.assertArrayEquals(emptyFocus, mManager.getActiveAppTypes());
+ FocusChangeListerner change = new FocusChangeListerner();
+ FocusChangeListerner change2 = new FocusChangeListerner();
+ FocusOwnershipChangeListerner owner = new FocusOwnershipChangeListerner();
+ FocusOwnershipChangeListerner owner2 = new FocusOwnershipChangeListerner();
+ mManager.registerFocusListener(change, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mManager.registerFocusListener(change, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ manager2.registerFocusListener(change2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ manager2.registerFocusListener(change2, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ int[] expectedFocuses = new int[] {CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION};
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertFalse(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertTrue(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertTrue(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ expectedFocuses = new int[] {
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND };
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertFalse(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+ assertTrue(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+
+ // this should be no-op
+ change.reset();
+ change2.reset();
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertFalse(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertFalse(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+
+ manager2.requestAppFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertTrue(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(owner.waitForOwnershipLossAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+
+ // no-op as it is not owning it
+ change.reset();
+ change2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertTrue(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertTrue(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+
+ change.reset();
+ change2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertTrue(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ expectedFocuses = new int[] {CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION};
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(change2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+ assertTrue(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+
+ change.reset();
+ change2.reset();
+ manager2.abandonAppFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(mManager.isOwningFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ assertFalse(manager2.isOwningFocus(owner2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION));
+ assertFalse(manager2.isOwningFocus(owner2,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND));
+ expectedFocuses = emptyFocus;
+ Assert.assertArrayEquals(expectedFocuses, mManager.getActiveAppTypes());
+ Assert.assertArrayEquals(expectedFocuses, manager2.getActiveAppTypes());
+ assertTrue(change.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ mManager.unregisterFocusListener(change);
+ manager2.unregisterFocusListener(change2);
+ }
+
+ public void testFilter() throws Exception {
+ DefaultServiceConnectionListener connectionListener =
+ new DefaultServiceConnectionListener();
+ Car car2 = Car.createCar(getContext(), connectionListener);
+ car2.connect();
+ connectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+ CarAppFocusManager manager2 = (CarAppFocusManager)
+ car2.getCarManager(Car.APP_FOCUS_SERVICE);
+ assertNotNull(manager2);
+
+ Assert.assertArrayEquals(new int[0], mManager.getActiveAppTypes());
+
+ FocusChangeListerner listener = new FocusChangeListerner();
+ FocusChangeListerner listener2 = new FocusChangeListerner();
+ FocusOwnershipChangeListerner owner = new FocusOwnershipChangeListerner();
+ mManager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mManager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ manager2.registerFocusListener(listener2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertTrue(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+
+ listener.reset();
+ listener2.reset();
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+ assertFalse(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+
+ listener.reset();
+ listener2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+ assertFalse(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+
+ listener.reset();
+ listener2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ assertTrue(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ }
+
+ public void testMultipleChangeListenersPerManager() throws Exception {
+ FocusChangeListerner listener = new FocusChangeListerner();
+ FocusChangeListerner listener2 = new FocusChangeListerner();
+ FocusOwnershipChangeListerner owner = new FocusOwnershipChangeListerner();
+ mManager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ mManager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ mManager.registerFocusListener(listener2, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+ assertTrue(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true));
+
+ listener.reset();
+ listener2.reset();
+ mManager.requestAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+ assertFalse(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true));
+
+ listener.reset();
+ listener2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+ assertFalse(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false));
+
+ listener.reset();
+ listener2.reset();
+ mManager.abandonAppFocus(owner, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ assertTrue(listener2.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false));
+ }
+
+ private class FocusChangeListerner implements CarAppFocusManager.AppFocusChangeListener {
+ private int mLastChangeAppType;
+ private boolean mLastChangeAppActive;
+ private final Semaphore mChangeWait = new Semaphore(0);
+
+ public boolean waitForFocusChangeAndAssert(long timeoutMs, int expectedAppType,
+ boolean expectedAppActive) throws Exception {
+ if (!mChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ assertEquals(expectedAppType, mLastChangeAppType);
+ assertEquals(expectedAppActive, mLastChangeAppActive);
+ return true;
+ }
+
+ public void reset() {
+ mLastChangeAppType = 0;
+ mLastChangeAppActive = false;
+ }
+
+ @Override
+ public void onAppFocusChange(int appType, boolean active) {
+ Log.i(TAG, "onAppFocusChange appType=" + appType + " active=" + active);
+ assertMainThread();
+ mLastChangeAppType = appType;
+ mLastChangeAppActive = active;
+ mChangeWait.release();
+ }
+ }
+
+ private class FocusOwnershipChangeListerner
+ implements CarAppFocusManager.AppFocusOwnershipChangeListener {
+ private int mLastLossEvent;
+ private final Semaphore mLossEventWait = new Semaphore(0);
+
+ public boolean waitForOwnershipLossAndAssert(long timeoutMs, int expectedAppType)
+ throws Exception {
+ if (!mLossEventWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ assertEquals(expectedAppType, mLastLossEvent);
+ return true;
+ }
+
+ @Override
+ public void onAppFocusOwnershipLoss(int appType) {
+ Log.i(TAG, "onAppFocusOwnershipLoss " + appType);
+ assertMainThread();
+ mLastLossEvent = appType;
+ mLossEventWait.release();
+ }
+ }
+}
diff --git a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationManagerTest.java b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationStatusManagerTest.java
index 1d1f47656b..1a1198d476 100644
--- a/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationManagerTest.java
+++ b/tests/android_support_car_api_test/src/com/android/support/car/apitest/CarNavigationStatusManagerTest.java
@@ -15,17 +15,15 @@
*/
package com.android.support.car.apitest;
-import static android.support.car.CarAppContextManager.APP_CONTEXT_NAVIGATION;
+import static android.support.car.CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION;
-import android.os.Looper;
import android.support.car.Car;
-import android.support.car.CarAppContextManager;
-import android.support.car.CarAppContextManager.AppContextChangeListener;
-import android.support.car.CarAppContextManager.AppContextOwnershipChangeListener;
+import android.support.car.CarAppFocusManager;
+import android.support.car.CarAppFocusManager.AppFocusChangeListener;
+import android.support.car.CarAppFocusManager.AppFocusOwnershipChangeListener;
import android.support.car.navigation.CarNavigationInstrumentCluster;
-import android.support.car.CarNotConnectedException;
-import android.support.car.navigation.CarNavigationManager;
-import android.support.car.navigation.CarNavigationManager.CarNavigationListener;
+import android.support.car.navigation.CarNavigationStatusManager;
+import android.support.car.navigation.CarNavigationStatusManager.CarNavigationListener;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -33,26 +31,26 @@ import java.util.concurrent.TimeUnit;
/**
* Unit tests for {@link android.support.car.navigation.CarNavigationStatusManager}
*/
-public class CarNavigationManagerTest extends CarApiTestBase {
+public class CarNavigationStatusManagerTest extends CarApiTestBase {
- private CarNavigationManager mCarNavigationManager;
- private CarAppContextManager mCarAppContextManager;
+ private CarNavigationStatusManager mCarNavigationStatusManager;
+ private CarAppFocusManager mCarAppFocusManager;
@Override
protected void setUp() throws Exception {
super.setUp();
- mCarNavigationManager =
- (CarNavigationManager) getCar().getCarManager(Car.CAR_NAVIGATION_SERVICE);
- assertNotNull(mCarNavigationManager);
- mCarAppContextManager =
- (CarAppContextManager) getCar().getCarManager(Car.APP_CONTEXT_SERVICE);
- assertNotNull(mCarAppContextManager);
+ mCarNavigationStatusManager =
+ (CarNavigationStatusManager) getCar().getCarManager(Car.CAR_NAVIGATION_SERVICE);
+ assertNotNull(mCarNavigationStatusManager);
+ mCarAppFocusManager =
+ (CarAppFocusManager) getCar().getCarManager(Car.APP_FOCUS_SERVICE);
+ assertNotNull(mCarAppFocusManager);
}
public void testStart() throws Exception {
final CountDownLatch onStartLatch = new CountDownLatch(1);
- mCarNavigationManager.registerListener(new CarNavigationListener() {
+ mCarNavigationStatusManager.registerListener(new CarNavigationListener() {
@Override
public void onInstrumentClusterStart(CarNavigationInstrumentCluster instrumentCluster) {
// TODO: we should use VehicleHalMock once we implement HAL support in
@@ -71,28 +69,29 @@ public class CarNavigationManagerTest extends CarApiTestBase {
assertTrue(onStartLatch.await(DEFAULT_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
try {
- mCarNavigationManager.sendNavigationStatus(1);
+ mCarNavigationStatusManager.sendNavigationStatus(1);
fail();
} catch (IllegalStateException expected) {
- // Expected. Client should acquire context ownership for APP_CONTEXT_NAVIGATION.
+ // Expected. Client should acquire focus ownership for APP_FOCUS_TYPE_NAVIGATION.
}
- mCarAppContextManager.registerContextListener(new AppContextChangeListener() {
+ mCarAppFocusManager.registerFocusListener(new AppFocusChangeListener() {
@Override
- public void onAppContextChange(int activeContexts) {
+ public void onAppFocusChange(int appType, boolean active) {
// Nothing to do here.
}
- }, APP_CONTEXT_NAVIGATION);
- mCarAppContextManager.setActiveContexts(new AppContextOwnershipChangeListener() {
+ }, APP_FOCUS_TYPE_NAVIGATION);
+ AppFocusOwnershipChangeListener ownershipListener = new AppFocusOwnershipChangeListener() {
@Override
- public void onAppContextOwnershipLoss(int context) {
+ public void onAppFocusOwnershipLoss(int focus) {
// Nothing to do here.
}
- }, APP_CONTEXT_NAVIGATION);
- assertTrue(mCarAppContextManager.isOwningContext(APP_CONTEXT_NAVIGATION));
+ };
+ mCarAppFocusManager.requestAppFocus(ownershipListener, APP_FOCUS_TYPE_NAVIGATION);
+ assertTrue(mCarAppFocusManager.isOwningFocus(ownershipListener, APP_FOCUS_TYPE_NAVIGATION));
// TODO: we should use mocked HAL to be able to verify this, right now just make sure that
// it is not crashing and logcat has appropriate traces.
- mCarNavigationManager.sendNavigationStatus(1);
+ mCarNavigationStatusManager.sendNavigationStatus(1);
}
}
diff --git a/tests/carservice_test/src/com/android/car/test/CarAudioExtFocusTest.java b/tests/carservice_test/src/com/android/car/test/CarAudioExtFocusTest.java
new file mode 100644
index 0000000000..0b6268b096
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/test/CarAudioExtFocusTest.java
@@ -0,0 +1,1047 @@
+/*
+ * 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.
+ */
+package com.android.car.test;
+
+import android.car.Car;
+import android.car.media.CarAudioManager;
+import android.car.test.VehicleHalEmulator.VehicleHalPropertyHandler;
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+import com.android.car.vehiclenetwork.VehicleNetworkConsts;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioContextFlag;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioExtFocusFlag;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusIndex;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusRequest;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioFocusState;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioStream;
+import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
+import com.android.car.vehiclenetwork.VehiclePropValueUtil;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePermissionModel;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropAccess;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropChangeMode;
+import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleValueType;
+import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+public class CarAudioExtFocusTest extends MockedCarTestBase {
+ private static final String TAG = CarAudioExtFocusTest.class.getSimpleName();
+
+ private static final long TIMEOUT_MS = 3000;
+
+ private final VehicleHalPropertyHandler mAudioRoutingPolicyPropertyHandler =
+ new VehicleHalPropertyHandler() {
+ @Override
+ public void onPropertySet(VehiclePropValue value) {
+ //TODO
+ }
+
+ @Override
+ public VehiclePropValue onPropertyGet(VehiclePropValue value) {
+ fail("cannot get");
+ return null;
+ }
+
+ @Override
+ public void onPropertySubscribe(int property, float sampleRate, int zones) {
+ fail("cannot subscribe");
+ }
+
+ @Override
+ public void onPropertyUnsubscribe(int property) {
+ fail("cannot unsubscribe");
+ }
+ };
+
+ private final FocusPropertyHandler mAudioFocusPropertyHandler =
+ new FocusPropertyHandler(this);
+
+ private final ExtRoutingHintPropertyHandler mExtRoutingHintPropertyHandler =
+ new ExtRoutingHintPropertyHandler();
+
+ private static final String EXT_ROUTING_CONFIG =
+ "0:RADIO_AM_FM:0,1:RADIO_SATELLITE:0,33:CD_DVD:0," +
+ "64:com.google.test.SOMETHING_SPECIAL," +
+ "4:EXT_NAV_GUIDANCE:1," +
+ "5:AUX_IN0:0";
+
+ private final Semaphore mWaitSemaphore = new Semaphore(0);
+ private final LinkedList<VehiclePropValue> mEvents = new LinkedList<VehiclePropValue>();
+ private AudioManager mAudioManager;
+ private CarAudioManager mCarAudioManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // AudioManager should be created in main thread to get focus event. :(
+ runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ }
+ });
+
+ getVehicleHalEmulator().addProperty(
+ VehiclePropConfigUtil.getBuilder(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_ROUTING_POLICY,
+ VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE,
+ VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
+ VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC2,
+ VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
+ 0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
+ mAudioRoutingPolicyPropertyHandler);
+ getVehicleHalEmulator().addProperty(
+ VehiclePropConfigUtil.getBuilder(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS,
+ VehiclePropAccess.VEHICLE_PROP_ACCESS_READ_WRITE,
+ VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
+ VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4,
+ VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
+ 0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).build(),
+ mAudioFocusPropertyHandler);
+ getVehicleHalEmulator().addStaticProperty(
+ VehiclePropConfigUtil.createStaticStringProperty(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_HW_VARIANT),
+ VehiclePropValueUtil.createIntValue(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_HW_VARIANT, 1, 0));
+ getVehicleHalEmulator().addProperty(
+ VehiclePropConfigUtil.getBuilder(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT,
+ VehiclePropAccess.VEHICLE_PROP_ACCESS_WRITE,
+ VehiclePropChangeMode.VEHICLE_PROP_CHANGE_MODE_ON_CHANGE,
+ VehicleValueType.VEHICLE_VALUE_TYPE_INT32_VEC4,
+ VehiclePermissionModel.VEHICLE_PERMISSION_SYSTEM_APP_ONLY,
+ 0 /*configFlags*/, 0 /*sampleRateMax*/, 0 /*sampleRateMin*/).
+ setConfigString(EXT_ROUTING_CONFIG).build(),
+ mExtRoutingHintPropertyHandler);
+ getVehicleHalEmulator().start();
+ mCarAudioManager = (CarAudioManager) getCar().getCarManager(Car.AUDIO_SERVICE);
+ assertNotNull(mCarAudioManager);
+ }
+
+ public void testExtRoutings() throws Exception {
+ String[] radioTypes = mCarAudioManager.getSupportedRadioTypes();
+ assertNotNull(radioTypes);
+ checkStringArrayContents(new String[] {"RADIO_AM_FM", "RADIO_SATELLITE"}, radioTypes);
+
+ String[] nonRadioTypes = mCarAudioManager.getSupportedExternalSourceTypes();
+ assertNotNull(nonRadioTypes);
+ checkStringArrayContents(new String[] {"CD_DVD", "com.google.test.SOMETHING_SPECIAL",
+ "EXT_NAV_GUIDANCE", "AUX_IN0"}, nonRadioTypes);
+ }
+
+ private void checkStringArrayContents(String[] expected, String[] actual) throws Exception {
+ Arrays.sort(expected);
+ Arrays.sort(actual);
+ assertEquals(expected.length, actual.length);
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals(expected[i], actual[i]);
+ }
+ }
+
+ public void testRadioAttributeCreation() throws Exception {
+ AudioAttributes attrb = mCarAudioManager.getAudioAttributesForRadio(
+ CarAudioManager.CAR_RADIO_TYPE_AM_FM);
+ assertNotNull(attrb);
+
+ attrb = mCarAudioManager.getAudioAttributesForRadio(
+ CarAudioManager.CAR_RADIO_TYPE_SATELLITE);
+ assertNotNull(attrb);
+
+ try {
+ attrb = mCarAudioManager.getAudioAttributesForRadio(
+ CarAudioManager.CAR_RADIO_TYPE_AM_FM_HD);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void testExtSourceAttributeCreation() throws Exception {
+ AudioAttributes attrb = mCarAudioManager.getAudioAttributesForExternalSource(
+ CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_CD_DVD);
+ assertNotNull(attrb);
+
+ attrb = mCarAudioManager.getAudioAttributesForExternalSource(
+ CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
+ assertNotNull(attrb);
+
+ attrb = mCarAudioManager.getAudioAttributesForExternalSource(
+ "com.google.test.SOMETHING_SPECIAL");
+ assertNotNull(attrb);
+
+ try {
+ attrb = mCarAudioManager.getAudioAttributesForExternalSource(
+ CarAudioManager.CAR_RADIO_TYPE_AM_FM_HD);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void testRadioAmFmGainFocus() throws Exception {
+ AudioAttributes attrb = mCarAudioManager.getAudioAttributesForRadio(
+ CarAudioManager.CAR_RADIO_TYPE_AM_FM);
+ assertNotNull(attrb);
+ checkSingleRequestRelease(attrb, AudioManager.AUDIOFOCUS_GAIN, new int[] {1, 0, 0, 0},
+ 0, VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG);
+ }
+
+ public void testRadioSatelliteGainFocus() throws Exception {
+ AudioAttributes attrb = mCarAudioManager.getAudioAttributesForRadio(
+ CarAudioManager.CAR_RADIO_TYPE_SATELLITE);
+ assertNotNull(attrb);
+ checkSingleRequestRelease(attrb, AudioManager.AUDIOFOCUS_GAIN, new int[] {2, 0, 0, 0},
+ 0, VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG);
+ }
+
+ public void testCdGainFocus() throws Exception {
+ AudioAttributes attrb = mCarAudioManager.getAudioAttributesForExternalSource(
+ CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_CD_DVD);
+ assertNotNull(attrb);
+ checkSingleRequestRelease(attrb, AudioManager.AUDIOFOCUS_GAIN, new int[] {0, 2, 0, 0},
+ 0, VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG);
+ }
+
+ public void testAuxInFocus() throws Exception {
+ AudioAttributes attrb = mCarAudioManager.getAudioAttributesForExternalSource(
+ CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_AUX_IN0);
+ assertNotNull(attrb);
+ checkSingleRequestRelease(attrb, AudioManager.AUDIOFOCUS_GAIN, new int[] {0x1<<5, 0, 0, 0},
+ 0, VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG);
+ }
+
+ public void testExtNavInFocus() throws Exception {
+ AudioAttributes attrb = mCarAudioManager.getAudioAttributesForExternalSource(
+ CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
+ assertNotNull(attrb);
+ checkSingleRequestRelease(attrb, AudioManager.AUDIOFOCUS_GAIN, new int[] {0x1<<4, 0, 0, 0},
+ 0, VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_EXT_SOURCE_FLAG);
+ }
+
+ public void testCustomInFocus() throws Exception {
+ AudioAttributes attrb = mCarAudioManager.getAudioAttributesForExternalSource(
+ "com.google.test.SOMETHING_SPECIAL");
+ assertNotNull(attrb);
+ checkSingleRequestRelease(attrb, AudioManager.AUDIOFOCUS_GAIN, new int[] {0, 0, 1, 0},
+ 0, VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_EXT_SOURCE_FLAG);
+ }
+
+ public void testMediaNavFocus() throws Exception {
+ //music start
+ AudioFocusListener listenerMusic = new AudioFocusListener();
+ int res = mAudioManager.requestAudioFocus(listenerMusic,
+ AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
+ assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ request[1],
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+
+ // nav guidance start
+ AudioFocusListener listenerNav = new AudioFocusListener();
+ AudioAttributes navAttrib = (new AudioAttributes.Builder()).
+ setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
+ setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
+ build();
+ res = mAudioManager.requestAudioFocus(listenerNav, navAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
+ assertEquals(0x3, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG |
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN, request[1],
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+
+ // nav guidance done
+ mAudioManager.abandonAudioFocus(listenerNav);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
+ assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN, request[1],
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+
+ // music done
+ mAudioManager.abandonAudioFocus(listenerMusic);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(0, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS, request[1],
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+ }
+
+ public void testMediaExternalMediaNavFocus() throws Exception {
+ // android music
+ AudioFocusListener listenerMusic = new AudioFocusListener();
+ int res = mAudioManager.requestAudioFocus(listenerMusic,
+ AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
+ assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ request[1],
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+
+ // car plays external media (=outside Android)
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
+ 0,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT_FLAG);
+ int focusChange = listenerMusic.waitAndGetFocusChange(TIMEOUT_MS);
+ assertEquals(AudioManager.AUDIOFOCUS_LOSS, focusChange);
+
+ // nav guidance start
+ AudioFocusListener listenerNav = new AudioFocusListener();
+ AudioAttributes navAttrib = (new AudioAttributes.Builder()).
+ setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
+ setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
+ build();
+ res = mAudioManager.requestAudioFocus(listenerNav, navAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK,
+ request[0]);
+ assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT,
+ 0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT_FLAG);
+
+ // nav guidance ends
+ mAudioManager.abandonAudioFocus(listenerNav);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(0, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
+ 0,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PERMANENT_FLAG);
+
+ // now ends external play
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
+ 0,
+ 0);
+ mAudioManager.abandonAudioFocus(listenerMusic);
+ //TODO how to check this?
+ }
+
+ public void testExternalRadioExternalNav() throws Exception {
+ // android radio
+ AudioFocusListener listenerRadio = new AudioFocusListener();
+ CarAudioManager carAudioManager = (CarAudioManager) getCar().getCarManager(
+ Car.AUDIO_SERVICE);
+ assertNotNull(carAudioManager);
+ AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
+ CarAudioManager.CAR_AUDIO_USAGE_RADIO);
+ int res = mAudioManager.requestAudioFocus(listenerRadio,
+ radioAttributes, AudioManager.AUDIOFOCUS_GAIN, 0);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG, request[3]);
+ assertArrayEquals(new int[] {1, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ 0,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG);
+
+ //external nav
+ AudioFocusListener listenerNav = new AudioFocusListener();
+ AudioAttributes extNavAttributes = mCarAudioManager.getAudioAttributesForExternalSource(
+ CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
+ res = mAudioManager.requestAudioFocus(listenerNav,
+ extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN,
+ request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG |
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_EXT_SOURCE_FLAG, request[3]);
+ assertArrayEquals(new int[] {1 | 1<<4, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ 0,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG);
+
+ mAudioManager.abandonAudioFocus(listenerNav);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG, request[3]);
+ assertArrayEquals(new int[] {1, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ 0,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG);
+
+ mAudioManager.abandonAudioFocus(listenerRadio);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(0, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
+ 0,
+ 0);
+ }
+
+ public void testMediaExternalNav() throws Exception {
+ // android music
+ AudioFocusListener listenerMusic = new AudioFocusListener();
+ int res = mAudioManager.requestAudioFocus(listenerMusic,
+ AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
+ assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ request[1],
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+
+ //external nav
+ AudioFocusListener listenerNav = new AudioFocusListener();
+ AudioAttributes extNavAttributes = mCarAudioManager.getAudioAttributesForExternalSource(
+ CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
+ res = mAudioManager.requestAudioFocus(listenerNav,
+ extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN,
+ request[0]);
+ assertEquals(0x1, request[1]);
+ assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG |
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_EXT_SOURCE_FLAG, request[3]);
+ assertArrayEquals(new int[] {1<<4, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ 0x1,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG);
+
+ mAudioManager.abandonAudioFocus(listenerNav);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
+ assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ request[1],
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+
+ mAudioManager.abandonAudioFocus(listenerMusic);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(0, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
+ 0,
+ 0);
+ }
+
+ /**
+ * Test internal nav - external nav case.
+ * External nav takes the same physical stream as internal nav. So internal nav
+ * will be lost while external nav is played. This should not happen in real case when
+ * AppFocus is used, but this test is to make sure that audio focus works as expected.
+ */
+ public void testNavExternalNav() throws Exception {
+ // android nav
+ AudioFocusListener listenerIntNav = new AudioFocusListener();
+ AudioAttributes intNavAttributes = mCarAudioManager.getAudioAttributesForCarUsage(
+ CarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE);
+ int res = mAudioManager.requestAudioFocus(listenerIntNav, intNavAttributes,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK,
+ request[0]);
+ assertEquals(0x2, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ request[1],
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+
+ //external nav
+ AudioFocusListener listenerExtNav = new AudioFocusListener();
+ AudioAttributes extNavAttributes = mCarAudioManager.getAudioAttributesForExternalSource(
+ CarAudioManager.CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE);
+ res = mAudioManager.requestAudioFocus(listenerExtNav,
+ extNavAttributes, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN,
+ request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_EXT_SOURCE_FLAG, request[3]);
+ assertArrayEquals(new int[] {1<<4, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ 0x1,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG);
+
+ mAudioManager.abandonAudioFocus(listenerExtNav);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK,
+ request[0]);
+ assertEquals(0x2, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ request[1],
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+
+ mAudioManager.abandonAudioFocus(listenerIntNav);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(0, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
+ 0,
+ 0);
+ }
+
+ public void testMediaExternalRadioNavMediaFocus() throws Exception {
+ // android music
+ AudioFocusListener listenerMusic = new AudioFocusListener();
+ int res = mAudioManager.requestAudioFocus(listenerMusic,
+ AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
+ assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ request[1],
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+
+ // android radio
+ AudioFocusListener listenerRadio = new AudioFocusListener();
+ CarAudioManager carAudioManager = (CarAudioManager) getCar().getCarManager(
+ Car.AUDIO_SERVICE);
+ assertNotNull(carAudioManager);
+ AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(
+ CarAudioManager.CAR_AUDIO_USAGE_RADIO);
+ res = mAudioManager.requestAudioFocus(listenerRadio,
+ radioAttributes, AudioManager.AUDIOFOCUS_GAIN, 0);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG, request[3]);
+ assertArrayEquals(new int[] {1, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ 0,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG);
+
+ // nav guidance start
+ AudioFocusListener listenerNav = new AudioFocusListener();
+ AudioAttributes navAttrib = (new AudioAttributes.Builder()).
+ setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
+ setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
+ build();
+ res = mAudioManager.requestAudioFocus(listenerNav, navAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN,
+ request[0]);
+ assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1, request[1]);
+ assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG |
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG, request[3]);
+ assertArrayEquals(new int[] {1, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ 0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG);
+
+ // nav guidance ends
+ mAudioManager.abandonAudioFocus(listenerNav);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN,
+ request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG, request[3]);
+ assertArrayEquals(new int[] {1, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ 0,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG);
+
+ // ends radio. music will get the focus GAIN.
+ // Music app is supposed to stop and release focus when it has lost focus, but here just
+ // check if focus is working.
+ mAudioManager.abandonAudioFocus(listenerRadio);
+ listenerMusic.waitForFocus(TIMEOUT_MS, AudioManager.AUDIOFOCUS_GAIN);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
+ assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ 0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM0,
+ 0);
+
+ // now music release focus.
+ mAudioManager.abandonAudioFocus(listenerMusic);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(0, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
+ 0,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+ }
+
+ private void checkSingleRequestRelease(AudioAttributes attrb, int androidFocusToRequest,
+ int[] expectedExtRouting, int expectedStreams,
+ int expectedExtState, int expectedContexts) throws Exception {
+ AudioFocusListener lister = new AudioFocusListener();
+ int res = mCarAudioManager.requestAudioFocus(lister, attrb, androidFocusToRequest, 0);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ int expectedFocusRequest = VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE;
+ int response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
+ switch (androidFocusToRequest) {
+ case AudioManager.AUDIOFOCUS_GAIN:
+ expectedFocusRequest = VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN;
+ response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN;
+ break;
+ case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
+ expectedFocusRequest =
+ VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT;
+ response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT;
+ break;
+ case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
+ expectedFocusRequest =
+ VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK;
+ response = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT;
+ break;
+ }
+ assertEquals(expectedFocusRequest, request[0]);
+ assertEquals(expectedStreams, request[1]);
+ assertEquals(expectedExtState, request[2]);
+ assertEquals(expectedContexts, request[3]);
+ assertArrayEquals(expectedExtRouting, mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ response,
+ request[1],
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+ mAudioManager.abandonAudioFocus(lister);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_RELEASE, request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(0, request[2]);
+ assertEquals(0, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS,
+ request[1],
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_NONE_FLAG);
+ }
+
+ public void testRadioMute() throws Exception {
+ testMediaMute(CarAudioManager.CAR_AUDIO_USAGE_RADIO,
+ 0,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_PLAY_ONLY_FLAG,
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG);
+ }
+
+ public void testMusicMute() throws Exception {
+ testMediaMute(CarAudioManager.CAR_AUDIO_USAGE_MUSIC,
+ 0x1,
+ 0,
+ VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG);
+ }
+
+ private void testMediaMute(int mediaUsage, int primaryStream, int extFocusFlag,
+ int mediaContext) throws Exception {
+ // android radio
+ AudioFocusListener listenerMedia = new AudioFocusListener();
+ CarAudioManager carAudioManager = (CarAudioManager) getCar().getCarManager(
+ Car.AUDIO_SERVICE);
+ assertNotNull(carAudioManager);
+ AudioAttributes radioAttributes = carAudioManager.getAudioAttributesForCarUsage(mediaUsage);
+ Log.i(TAG, "request media Focus");
+ int res = mAudioManager.requestAudioFocus(listenerMedia,
+ radioAttributes, AudioManager.AUDIOFOCUS_GAIN, 0);
+ assertEquals(AudioManager.AUDIOFOCUS_REQUEST_GRANTED, res);
+ int[] request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
+ assertEquals(primaryStream, request[1]);
+ assertEquals(extFocusFlag, request[2]);
+ assertEquals(mediaContext, request[3]);
+ if (mediaUsage == CarAudioManager.CAR_AUDIO_USAGE_RADIO) {
+ assertArrayEquals(new int[] {1, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ } else {
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ }
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ primaryStream,
+ extFocusFlag);
+ // now mute it.
+ assertFalse(carAudioManager.isMediaMuted());
+ Log.i(TAG, "mute media");
+ assertTrue(carAudioManager.setMediaMute(true));
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT,
+ request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_MUTE_MEDIA_FLAG,
+ request[2]);
+ assertEquals(0, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ 0,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_MUTE_MEDIA_FLAG);
+ assertTrue(carAudioManager.isMediaMuted());
+ // nav guidance on top of it
+ AudioFocusListener listenerNav = new AudioFocusListener();
+ AudioAttributes navAttrib = (new AudioAttributes.Builder()).
+ setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION).
+ setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE).
+ build();
+ Log.i(TAG, "request nav Focus");
+ res = mAudioManager.requestAudioFocus(listenerNav, navAttrib,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT_MAY_DUCK,
+ request[0]);
+ assertEquals(0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1, request[1]);
+ assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_MUTE_MEDIA_FLAG,
+ request[2]);
+ assertEquals(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ assertTrue(carAudioManager.isMediaMuted());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ 0x1 << VehicleAudioStream.VEHICLE_AUDIO_STREAM1,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_MUTE_MEDIA_FLAG);
+ assertTrue(carAudioManager.isMediaMuted());
+ // nav guidance ends
+ mAudioManager.abandonAudioFocus(listenerNav);
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN_TRANSIENT,
+ request[0]);
+ assertEquals(0, request[1]);
+ assertEquals(VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_MUTE_MEDIA_FLAG,
+ request[2]);
+ assertEquals(0, request[3]);
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ assertTrue(carAudioManager.isMediaMuted());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ 0,
+ VehicleAudioExtFocusFlag.VEHICLE_AUDIO_EXT_FOCUS_CAR_MUTE_MEDIA_FLAG);
+ // now unmute it. media should resume.
+ assertTrue(carAudioManager.isMediaMuted());
+ assertFalse(carAudioManager.setMediaMute(false));
+ assertFalse(carAudioManager.isMediaMuted());
+ request = mAudioFocusPropertyHandler.waitForAudioFocusRequest(TIMEOUT_MS);
+ assertEquals(VehicleAudioFocusRequest.VEHICLE_AUDIO_FOCUS_REQUEST_GAIN, request[0]);
+ assertEquals(primaryStream, request[1]);
+ assertEquals(extFocusFlag,
+ request[2]);
+ assertEquals(mediaContext, request[3]);
+ if (mediaUsage == CarAudioManager.CAR_AUDIO_USAGE_RADIO) {
+ assertArrayEquals(new int[] {1, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ } else {
+ assertArrayEquals(new int[] {0, 0, 0, 0},
+ mExtRoutingHintPropertyHandler.getLastHint());
+ }
+ assertFalse(carAudioManager.isMediaMuted());
+ mAudioFocusPropertyHandler.sendAudioFocusState(
+ VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_GAIN,
+ primaryStream,
+ extFocusFlag);
+ assertFalse(carAudioManager.isMediaMuted());
+ // release focus
+ mAudioManager.abandonAudioFocus(listenerMedia);
+ }
+
+ protected static class AudioFocusListener implements AudioManager.OnAudioFocusChangeListener {
+ private final Semaphore mFocusChangeWait = new Semaphore(0);
+ private int mLastFocusChange;
+
+ public int waitAndGetFocusChange(long timeoutMs) throws Exception {
+ if (!mFocusChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ fail("timeout waiting for focus change");
+ }
+ return mLastFocusChange;
+ }
+
+ public void waitForFocus(long timeoutMs, int expectedFocus) throws Exception {
+ while (mLastFocusChange != expectedFocus) {
+ if (!mFocusChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ fail("timeout waiting for focus change");
+ }
+ }
+ }
+
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ mLastFocusChange = focusChange;
+ mFocusChangeWait.release();
+ }
+ }
+
+ protected static class FocusPropertyHandler implements VehicleHalPropertyHandler {
+
+ private int mState = VehicleAudioFocusState.VEHICLE_AUDIO_FOCUS_STATE_LOSS;
+ private int mStreams = 0;
+ private int mExtFocus = 0;
+ private int mRequest;
+ private int mRequestedStreams;
+ private int mRequestedExtFocus;
+ private int mRequestedAudioContexts;
+ private final MockedCarTestBase mCarTest;
+
+ private final Semaphore mSetWaitSemaphore = new Semaphore(0);
+
+ public FocusPropertyHandler(MockedCarTestBase carTest) {
+ mCarTest = carTest;
+ }
+
+ public void sendAudioFocusState(int state, int streams, int extFocus) {
+ synchronized (this) {
+ mState = state;
+ mStreams = streams;
+ mExtFocus = extFocus;
+ }
+ int[] values = { state, streams, extFocus, 0 };
+ mCarTest.getVehicleHalEmulator().injectEvent(VehiclePropValueUtil.createIntVectorValue(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
+ SystemClock.elapsedRealtimeNanos()));
+ }
+
+ public int[] waitForAudioFocusRequest(long timeoutMs) throws Exception {
+ if (!mSetWaitSemaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ fail("timeout");
+ }
+ synchronized (this) {
+ return new int[] { mRequest, mRequestedStreams, mRequestedExtFocus,
+ mRequestedAudioContexts };
+ }
+ }
+
+ @Override
+ public void onPropertySet(VehiclePropValue value) {
+ assertEquals(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, value.getProp());
+ synchronized (this) {
+ mRequest = value.getInt32Values(
+ VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_FOCUS);
+ mRequestedStreams = value.getInt32Values(
+ VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_STREAMS);
+ mRequestedExtFocus = value.getInt32Values(
+ VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_EXTERNAL_FOCUS_STATE);
+ mRequestedAudioContexts = value.getInt32Values(
+ VehicleAudioFocusIndex.VEHICLE_AUDIO_FOCUS_INDEX_AUDIO_CONTEXTS);
+ }
+ mSetWaitSemaphore.release();
+ }
+
+ @Override
+ public VehiclePropValue onPropertyGet(VehiclePropValue value) {
+ assertEquals(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, value.getProp());
+ int state, streams, extFocus;
+ synchronized (this) {
+ state = mState;
+ streams = mStreams;
+ extFocus = mExtFocus;
+ }
+ int[] values = { state, streams, extFocus, 0 };
+ return VehiclePropValueUtil.createIntVectorValue(
+ VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, values,
+ SystemClock.elapsedRealtimeNanos());
+ }
+
+ @Override
+ public void onPropertySubscribe(int property, float sampleRate, int zones) {
+ assertEquals(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, property);
+ }
+
+ @Override
+ public void onPropertyUnsubscribe(int property) {
+ assertEquals(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_FOCUS, property);
+ }
+ }
+
+ private static class ExtRoutingHintPropertyHandler implements VehicleHalPropertyHandler {
+ private int[] mLastHint = {0, 0, 0, 0};
+
+ public int[] getLastHint() {
+ int[] lastHint = new int[mLastHint.length];
+ synchronized (this) {
+ System.arraycopy(mLastHint, 0, lastHint, 0, mLastHint.length);
+ }
+ return lastHint;
+ }
+
+ @Override
+ public void onPropertySet(VehiclePropValue value) {
+ assertEquals(VehicleNetworkConsts.VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT,
+ value.getProp());
+ assertEquals(mLastHint.length, value.getInt32ValuesCount());
+ synchronized (this) {
+ for (int i = 0; i < mLastHint.length; i++) {
+ mLastHint[i] = value.getInt32Values(i);
+ }
+ }
+ }
+
+ @Override
+ public VehiclePropValue onPropertyGet(VehiclePropValue value) {
+ fail("write only");
+ return null;
+ }
+
+ @Override
+ public void onPropertySubscribe(int property, float sampleRate, int zones) {
+ fail("cannot subsctibe");
+ }
+
+ @Override
+ public void onPropertyUnsubscribe(int property) {
+ fail("cannot subsctibe");
+ }
+ }
+}
diff --git a/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java b/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
index 5a978fd489..df1db9a128 100644
--- a/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
+++ b/tests/carservice_test/src/com/android/car/test/CarAudioFocusTest.java
@@ -77,34 +77,6 @@ public class CarAudioFocusTest extends MockedCarTestBase {
private final FocusPropertyHandler mAudioFocusPropertyHandler =
new FocusPropertyHandler(this);
- private final VehicleHalPropertyHandler mAppContextPropertyHandler =
- new VehicleHalPropertyHandler() {
-
- @Override
- public void onPropertySet(VehiclePropValue value) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public VehiclePropValue onPropertyGet(VehiclePropValue value) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public void onPropertySubscribe(int property, float sampleRate, int zones) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void onPropertyUnsubscribe(int property) {
- // TODO Auto-generated method stub
-
- }
- };
-
private final Semaphore mWaitSemaphore = new Semaphore(0);
private final LinkedList<VehiclePropValue> mEvents = new LinkedList<VehiclePropValue>();
private AudioManager mAudioManager;
diff --git a/tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java b/tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java
index 9c8ef3d7fb..adfc3f355a 100644
--- a/tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java
+++ b/tests/carservice_test/src/com/android/car/test/MockedCarTestBase.java
@@ -26,6 +26,7 @@ import android.support.car.ServiceConnectionListener;
import android.test.AndroidTestCase;
import android.util.Log;
+import java.util.Arrays;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@@ -68,6 +69,20 @@ public class MockedCarTestBase extends AndroidTestCase {
}
};
+ public static <T> void assertArrayEquals(T[] expected, T[] actual) {
+ if (!Arrays.equals(expected, actual)) {
+ fail("expected:<" + Arrays.toString(expected) +
+ "> but was:<" + Arrays.toString(actual) + ">");
+ }
+ }
+
+ public static void assertArrayEquals(int[] expected, int[] actual) {
+ if (!Arrays.equals(expected, actual)) {
+ fail("expected:<" + Arrays.toString(expected) +
+ "> but was:<" + Arrays.toString(actual) + ">");
+ }
+ }
+
@Override
protected synchronized void setUp() throws Exception {
super.setUp();
diff --git a/tests/carservice_test/src/com/android/support/car/test/AppContextTest.java b/tests/carservice_test/src/com/android/support/car/test/AppContextTest.java
deleted file mode 100644
index 7711d5038d..0000000000
--- a/tests/carservice_test/src/com/android/support/car/test/AppContextTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2015 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.support.car.test;
-
-import android.car.test.VehicleHalEmulator.VehicleHalPropertyHandler;
-import android.content.Context;
-import android.media.AudioManager;
-import android.support.car.Car;
-import android.support.car.CarAppContextManager;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-
-import com.android.car.test.MockedCarTestBase;
-import com.android.car.vehiclenetwork.VehicleNetworkConsts;
-import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioContextFlag;
-import com.android.car.vehiclenetwork.VehiclePropConfigUtil;
-import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePermissionModel;
-import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropAccess;
-import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehiclePropChangeMode;
-import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleValueType;
-import com.android.car.vehiclenetwork.VehicleNetworkProto.VehiclePropValue;
-
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-@MediumTest
-public class AppContextTest extends MockedCarTestBase {
- private static final String TAG = AppContextTest.class.getSimpleName();
- private static final long DEFAULT_WAIT_TIMEOUT_MS = 1000;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- getVehicleHalEmulator().start();
- }
-
- public void testContextChange() throws Exception {
- CarAppContextManager manager = (CarAppContextManager) getSupportCar().getCarManager(
- Car.APP_CONTEXT_SERVICE);
- ContextChangeListener listener = new ContextChangeListener();
- ContextOwnershipChangeListerner ownershipListener = new ContextOwnershipChangeListerner();
- manager.registerContextListener(listener, CarAppContextManager.APP_CONTEXT_NAVIGATION |
- CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- manager.setActiveContexts(ownershipListener, CarAppContextManager.APP_CONTEXT_NAVIGATION);
- manager.setActiveContexts(ownershipListener, CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- manager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION);
- manager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_VOICE_COMMAND);
- manager.unregisterContextListener();
- }
-
- private class ContextChangeListener implements CarAppContextManager.AppContextChangeListener {
- private int mLastChangeEvent;
- private final Semaphore mChangeWait = new Semaphore(0);
-
- public boolean waitForContextChangeAndAssert(long timeoutMs, int expectedContexts)
- throws Exception {
- if (!mChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
- return false;
- }
- assertEquals(expectedContexts, mLastChangeEvent);
- return true;
- }
-
- @Override
- public void onAppContextChange(int activeContexts) {
- Log.i(TAG, "onAppContextChange " + Integer.toHexString(activeContexts));
- mLastChangeEvent = activeContexts;
- mChangeWait.release();
- }
- }
-
- private class ContextOwnershipChangeListerner
- implements CarAppContextManager.AppContextOwnershipChangeListener {
- private int mLastLossEvent;
- private final Semaphore mLossEventWait = new Semaphore(0);
-
- public boolean waitForOwnershipLossAndAssert(long timeoutMs, int expectedContexts)
- throws Exception {
- if (!mLossEventWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
- return false;
- }
- assertEquals(expectedContexts, mLastLossEvent);
- return true;
- }
-
- @Override
- public void onAppContextOwnershipLoss(int context) {
- Log.i(TAG, "onAppContextOwnershipLoss " + Integer.toHexString(context));
- mLastLossEvent = context;
- mLossEventWait.release();
- }
- }
-}
diff --git a/tests/carservice_test/src/com/android/support/car/test/AppFocusTest.java b/tests/carservice_test/src/com/android/support/car/test/AppFocusTest.java
new file mode 100644
index 0000000000..d80063d0f6
--- /dev/null
+++ b/tests/carservice_test/src/com/android/support/car/test/AppFocusTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 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.support.car.test;
+
+import android.support.car.Car;
+import android.support.car.CarAppFocusManager;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+import com.android.car.test.MockedCarTestBase;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+public class AppFocusTest extends MockedCarTestBase {
+ private static final String TAG = AppFocusTest.class.getSimpleName();
+ private static final long DEFAULT_WAIT_TIMEOUT_MS = 1000;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ getVehicleHalEmulator().start();
+ }
+
+ public void testFocusChange() throws Exception {
+ CarAppFocusManager manager = (CarAppFocusManager) getSupportCar().getCarManager(
+ Car.APP_FOCUS_SERVICE);
+ FocusChangeListener listener = new FocusChangeListener();
+ FocusOwnershipChangeListerner ownershipListener = new FocusOwnershipChangeListerner();
+ manager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ manager.registerFocusListener(listener, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ manager.requestAppFocus(ownershipListener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, true);
+ manager.requestAppFocus(ownershipListener, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, true);
+ manager.abandonAppFocus(ownershipListener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);
+ listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, false);
+ manager.abandonAppFocus(ownershipListener, CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND);
+ listener.waitForFocusChangeAndAssert(DEFAULT_WAIT_TIMEOUT_MS,
+ CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND, false);
+ manager.unregisterFocusListener(listener);
+ }
+
+ private class FocusChangeListener implements CarAppFocusManager.AppFocusChangeListener {
+ private int mLastChangeAppType;
+ private boolean mLastChangeAppActive;
+ private final Semaphore mChangeWait = new Semaphore(0);
+
+ public boolean waitForFocusChangeAndAssert(long timeoutMs, int expectedAppType,
+ boolean expectedAppActive) throws Exception {
+ if (!mChangeWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ assertEquals(expectedAppType, mLastChangeAppType);
+ assertEquals(expectedAppActive, mLastChangeAppActive);
+ return true;
+ }
+
+ @Override
+ public void onAppFocusChange(int appType, boolean active) {
+ Log.i(TAG, "onAppFocusChange appType=" + appType + " active=" + active);
+ mLastChangeAppType = appType;
+ mLastChangeAppActive = active;
+ mChangeWait.release();
+ }
+ }
+
+ private class FocusOwnershipChangeListerner
+ implements CarAppFocusManager.AppFocusOwnershipChangeListener {
+ private int mLastLossEvent;
+ private final Semaphore mLossEventWait = new Semaphore(0);
+
+ public boolean waitForOwnershipLossAndAssert(long timeoutMs, int expectedLossAppType)
+ throws Exception {
+ if (!mLossEventWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ return false;
+ }
+ assertEquals(expectedLossAppType, mLastLossEvent);
+ return true;
+ }
+
+ @Override
+ public void onAppFocusOwnershipLoss(int appType) {
+ Log.i(TAG, "onAppFocusOwnershipLoss " + appType);
+ mLastLossEvent = appType;
+ mLossEventWait.release();
+ }
+ }
+}
diff --git a/vehicle_network_service/VehicleHalPropertyUtil.h b/vehicle_network_service/VehicleHalPropertyUtil.h
index 0a9eb31744..17319696b4 100644
--- a/vehicle_network_service/VehicleHalPropertyUtil.h
+++ b/vehicle_network_service/VehicleHalPropertyUtil.h
@@ -30,13 +30,25 @@
namespace android {
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
class VechilePropertyUtil {
public:
static void dumpProperty(String8& msg, const vehicle_prop_config_t& config) {
msg.appendFormat("property 0x%x, access:0x%x, change_mode:0x%x, value_type:0x%x",
config.prop, config.access, config.change_mode, config.value_type);
- msg.appendFormat(",permission:0x%x, zones:0x%x, conflg_flag:0x%x, fsmin:%f, fsmax:%f",
+ char configString[100];
+ if (config.config_string.len > 0 && config.config_string.data != NULL) {
+ int stringLen = MIN(config.config_string.len, (int32_t) sizeof(configString) - 1);
+ memcpy(configString, config.config_string.data, stringLen);
+ configString[stringLen] = 0;
+ }
+ msg.appendFormat(",permission:0x%x, zones:0x%x, conflg_flag:0x%x, config_string:%s, " \
+ "fsmin:%f, fsmax:%f",
config.permission_model, config.vehicle_zone_flags, config.config_flags,
+ (config.config_string.len > 0) ? configString : "N/A",
config.min_sample_rate, config.max_sample_rate);
switch (config.value_type) {
case VEHICLE_VALUE_TYPE_FLOAT:
diff --git a/vehicle_network_service/VehicleNetworkService.cpp b/vehicle_network_service/VehicleNetworkService.cpp
index dee0c25b90..c46a96730a 100644
--- a/vehicle_network_service/VehicleNetworkService.cpp
+++ b/vehicle_network_service/VehicleNetworkService.cpp
@@ -382,6 +382,11 @@ void VehicleNetworkService::onFirstRef() {
*this));
ASSERT_ALWAYS_ON_NO_MEMORY(handler.get());
mHandler = handler;
+
+ // populate empty list before hal init.
+ mProperties = new VehiclePropertiesHolder(false /* deleteConfigsInDestructor */);
+ ASSERT_ALWAYS_ON_NO_MEMORY(mProperties);
+
r = mDevice->init(mDevice, eventCallback, errorCallback);
if (r != NO_ERROR) {
ALOGE("HAL init failed:%d", r);
@@ -389,8 +394,6 @@ void VehicleNetworkService::onFirstRef() {
}
int numConfigs = 0;
vehicle_prop_config_t const* configs = mDevice->list_properties(mDevice, &numConfigs);
- mProperties = new VehiclePropertiesHolder(false /* deleteConfigsInDestructor */);
- ASSERT_ALWAYS_ON_NO_MEMORY(mProperties);
for (int i = 0; i < numConfigs; i++) {
mProperties->getList().push_back(&configs[i]);
}
@@ -648,6 +651,7 @@ status_t VehicleNetworkService::subscribe(const sp<IVehicleNetworkListener> &lis
int32_t newZones = zones;
vehicle_prop_config_t const * config = NULL;
sp<HalClient> client;
+ bool autoGetEnabled = false;
do {
Mutex::Autolock autoLock(mLock);
if (!isSubscribableLocked(prop)) {
@@ -709,6 +713,7 @@ status_t VehicleNetworkService::subscribe(const sp<IVehicleNetworkListener> &lis
}
client->setSubscriptionInfo(prop, sampleRate, zones);
if (shouldSubscribe) {
+ autoGetEnabled = mVehiclePropertyAccessControl.isAutoGetEnabled(prop);
inMock = mMockingEnabled;
SubscriptionInfo info(sampleRate, newZones);
mSubscriptionInfos.add(prop, info);
@@ -736,7 +741,7 @@ status_t VehicleNetworkService::subscribe(const sp<IVehicleNetworkListener> &lis
}
}
}
- if (isSampleRateFixed(config->change_mode)) {
+ if (autoGetEnabled && isSampleRateFixed(config->change_mode)) {
status_t r = notifyClientWithCurrentValue(inMock, config, zones);
if (r != NO_ERROR) {
return r;
@@ -772,6 +777,7 @@ status_t VehicleNetworkService::notifyClientWithCurrentValue(bool isMocking,
status_t VehicleNetworkService::notifyClientWithCurrentValue(bool isMocking,
int32_t prop, int32_t valueType, int32_t zone) {
vehicle_prop_value_t value;
+ memset(&value, 0, sizeof(value));
value.prop = prop;
value.value_type = valueType;
value.zone = zone;
diff --git a/vehicle_network_service/VehiclePropertyAccessControl.cpp b/vehicle_network_service/VehiclePropertyAccessControl.cpp
index 3fe5f157c9..4adb55f5f0 100644
--- a/vehicle_network_service/VehiclePropertyAccessControl.cpp
+++ b/vehicle_network_service/VehiclePropertyAccessControl.cpp
@@ -171,6 +171,14 @@ bool VehiclePropertyAccessControl::populate(xmlNode * a_node) {
property_value = std::stoul(tmp_str, nullptr, 10);
}
+ // property with this set to true will not call get when it is subscribed.
+ property_value_str = xmlGetProp(cur_node, (const xmlChar*)"no_auto_get");
+ if (property_value_str) {
+ if (xmlStrcmp(property_value_str, (const xmlChar*)"true")==0) {
+ mPropertiesWithNoAutoGet.insert(property_value);
+ }
+ }
+
// Loop over all UID tags
for (child = cur_node->children; child; child = child->next) {
if ((xmlStrcmp(child->name, (const xmlChar*)"UID")==0) &&
@@ -354,4 +362,8 @@ bool VehiclePropertyAccessControl::testAccess(int32_t property, int32_t uid,
}
}
+bool VehiclePropertyAccessControl::isAutoGetEnabled(int32_t property) {
+ return mPropertiesWithNoAutoGet.count(property) == 0;
+}
+
};
diff --git a/vehicle_network_service/VehiclePropertyAccessControl.h b/vehicle_network_service/VehiclePropertyAccessControl.h
index c32209b16d..c079c935d1 100644
--- a/vehicle_network_service/VehiclePropertyAccessControl.h
+++ b/vehicle_network_service/VehiclePropertyAccessControl.h
@@ -21,6 +21,7 @@
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <map>
+#include <set>
#include <string>
#include <private/android_filesystem_config.h>
#include <vehicle-internal.h>
@@ -38,6 +39,7 @@ public:
~VehiclePropertyAccessControl();
bool init();
bool testAccess(int32_t property, int32_t uid, bool isWrite);
+ bool isAutoGetEnabled(int32_t property);
void dump(String8& msg);
// protected for testing
protected:
@@ -56,6 +58,7 @@ protected:
//
// So "property" is used to find "uid" and "uid" is used to find "access".
std::map<int32_t, std::map<int32_t, int32_t>*> mVehicleAccessControlMap;
+ std::set<int32_t> mPropertiesWithNoAutoGet;
};
};
diff --git a/vns_policy/vns_policy.xml b/vns_policy/vns_policy.xml
index 75f9bb5607..c20ac664fe 100644
--- a/vns_policy/vns_policy.xml
+++ b/vns_policy/vns_policy.xml
@@ -130,11 +130,11 @@
<UID name="AID_AUDIOSERVER" access="r" value="1041"/>
</PROPERTY>
- <PROPERTY name="VEHICLE_PROPERTY_AUDIO_VOLUME" value = "0x00000901">
+ <PROPERTY name="VEHICLE_PROPERTY_AUDIO_VOLUME" value = "0x00000901" no_auto_get="true">
<UID name="AID_SYSTEM" access="rw" value="1000"/>
</PROPERTY>
- <PROPERTY name="VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT" value = "0x00000902">
+ <PROPERTY name="VEHICLE_PROPERTY_AUDIO_VOLUME_LIMIT" value = "0x00000902" no_auto_get="true">
<UID name="AID_SYSTEM" access="rw" value="1000"/>
</PROPERTY>
@@ -146,6 +146,10 @@
<UID name="AID_SYSTEM" access="r" value="1000"/>
</PROPERTY>
+ <PROPERTY name="VEHICLE_PROPERTY_AUDIO_EXT_ROUTING_HINT" value = "0x00000905">
+ <UID name="AID_SYSTEM" access="rw" value="1000"/>
+ </PROPERTY>
+
<PROPERTY name="VEHICLE_PROPERTY_AP_POWER_STATE" value = "0x00000A00">
<UID name="AID_SYSTEM" access="rw" value="1000"/>
</PROPERTY>