aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeun-young Park <keunyoung@google.com>2016-07-11 10:33:18 -0700
committerKeun-young Park <keunyoung@google.com>2016-07-11 10:36:12 -0700
commita89dc821cb8aabec4045487c6c8b5603b8fcb89a (patch)
tree7ff6839cc9afa4a13a82d51f122485a191c12f04
parent2fdfa459543bf558d435b48730473de37dbbefcf (diff)
parentc837829409a384cf4e081987b84d4a2ac9a2808c (diff)
downloadCar-a89dc821cb8aabec4045487c6c8b5603b8fcb89a.tar.gz
Merge nyc-car-dev into master
809b5475 handle init failure and service launch delay d808cac0 showNaviationBar should default to true cd89ffb1 updating CarNavigationManager api to have new signature that maps requested and renamed to CarNavigationStatusListener. 0ad4c14f Merge "updating CarNavigationManager api to have new signature that maps requested and renamed to CarNavigationStatusListener." into nyc-car-dev 46371473 Car api review: CarAppContextManager renaming to CarAppFocusManager. d36a995a more volume support ac71076a Merge "more volume support" into nyc-car-dev c1098ce2 Merge "Car api review: CarAppContextManager renaming to CarAppFocusManager." into nyc-car-dev 0477e29b Refactor Instrument Cluster API 3c37689b Fix build: Remove duplicate types eaad6efb Expose the ability to mute audio in CarAudioManager. c5005504 Update current.txt with the changes in ag/1146522. a19f6784 fix build: update api current.txt 5d356ccd Merge "fix build: update api current.txt" into nyc-car-dev c5dc1964 added some exceptionss to the interface. b2f3cd68 Shift HVAC controls in Kitchen Sink below menu bar 401757c6 Remove BluetoothMidiService and DeskClock 4290134b Merge "added some exceptionss to the interface." into nyc-car-dev b56bd60f Merge "Remove BluetoothMidiService and DeskClock" into nyc-car-dev 35526712 Cleanup/Fix SElinux policy for car_product common a757f4b1 Merge "Cleanup/Fix SElinux policy for car_product common" into nyc-car-dev 4727da37 implement activity blocking 03cf60ce Handle key events in instrument cluster d79c47ad Merge "Handle key events in instrument cluster" into nyc-car-dev e5314732 Prevent warnings in Car native code ffb3fb50 Handle abandoning of nav app focus ownership 09ca973f Merge "Handle abandoning of nav app focus ownership" into nyc-car-dev 461ecc6c Add CarGenericManager and refactor HVAC to use it 845e54a0 Generate VehicleNetworkConsts.java with Car Cabin Manager additions 1b12ef6e Merge "Generate VehicleNetworkConsts.java with Car Cabin Manager additions" into nyc-car-dev 3ebef84a Merge "Add CarGenericManager and refactor HVAC to use it" into nyc-car-dev 87397155 Fix build 4c6834a2 add external audio routing support a4f44e1c add config_string to vns's dump f521fd7b add VERSION to car API c8378294 add dump to volume service Change-Id: If5d4c4e60c65979881f76e2ce7454361611585e1
-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>