aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-01-27 20:59:05 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-01-27 20:59:05 +0000
commite486d95b5a3af9a452f7d51fe18582a95f9c3b2d (patch)
tree18d7ad26d8cc41d78fec4fa01a9f4d6f1b480dbf
parentf9e2ee5b70ab7288ea973dfac11f779cc8b20e21 (diff)
parent814e7b36d26b26af07c00173e89026e3df68171c (diff)
downloadcuttlefish-emu-31-stable-release.tar.gz
Snap for 8074862 from 814e7b36d26b26af07c00173e89026e3df68171c to emu-31-stable-releaseemu-31-stable-release
Change-Id: Id6da07d27a0cd04c7ccf18c089a60ad86a2b6a24
-rw-r--r--CleanSpec.mk5
-rw-r--r--README.md2
-rw-r--r--apex/com.android.hardware.core_permissions/Android.bp2
-rw-r--r--apex/com.google.cf.wifi/Android.bp20
-rw-r--r--apex/com.google.cf.wifi_hwsim/Android.bp79
-rw-r--r--apex/com.google.cf.wifi_hwsim/apex_manifest.json4
-rw-r--r--apex/com.google.cf.wifi_hwsim/com.google.cf.wifi_hwsim.rc45
-rw-r--r--apex/com.google.cf.wifi_hwsim/com.google.cf.wifi_hwsim.xml29
-rw-r--r--apex/com.google.cf.wifi_hwsim/file_contexts8
-rw-r--r--build/Android.bp12
-rw-r--r--common/libs/utils/files.cpp8
-rw-r--r--common/libs/utils/files.h4
-rw-r--r--guest/hals/health/Android.bp54
-rw-r--r--guest/hals/health/android.hardware.health-service.cuttlefish.rc8
-rw-r--r--guest/hals/health/android.hardware.health-service.cuttlefish.xml7
-rw-r--r--guest/hals/health/android.hardware.health-service.cuttlefish_recovery.rc7
-rw-r--r--guest/hals/health/health-aidl.cpp119
-rw-r--r--guest/hals/health/health-hidl.cpp (renamed from guest/hals/health/health.cpp)0
-rw-r--r--guest/hals/hostapd/Android.bp27
-rw-r--r--guest/hals/identity/Android.bp57
-rw-r--r--guest/hals/identity/OWNERS1
-rw-r--r--guest/hals/identity/RemoteSecureHardwareProxy.cpp412
-rw-r--r--guest/hals/identity/RemoteSecureHardwareProxy.h169
-rw-r--r--guest/hals/identity/android.hardware.identity-service.remote.rc3
-rw-r--r--guest/hals/identity/android.hardware.identity-service.remote.xml10
-rw-r--r--guest/hals/identity/android.hardware.identity_credential.remote.xml18
-rw-r--r--guest/hals/identity/common/IdentityCredential.cpp945
-rw-r--r--guest/hals/identity/common/IdentityCredential.h153
-rw-r--r--guest/hals/identity/common/IdentityCredentialStore.cpp86
-rw-r--r--guest/hals/identity/common/IdentityCredentialStore.h59
-rw-r--r--guest/hals/identity/common/SecureHardwareProxy.h191
-rw-r--r--guest/hals/identity/common/WritableIdentityCredential.cpp424
-rw-r--r--guest/hals/identity/common/WritableIdentityCredential.h121
-rw-r--r--guest/hals/identity/libeic/EicCbor.c256
-rw-r--r--guest/hals/identity/libeic/EicCbor.h160
-rw-r--r--guest/hals/identity/libeic/EicCommon.h40
-rw-r--r--guest/hals/identity/libeic/EicOps.h316
-rw-r--r--guest/hals/identity/libeic/EicOpsImpl.cc546
-rw-r--r--guest/hals/identity/libeic/EicOpsImpl.h46
-rw-r--r--guest/hals/identity/libeic/EicPresentation.c916
-rw-r--r--guest/hals/identity/libeic/EicPresentation.h264
-rw-r--r--guest/hals/identity/libeic/EicProvisioning.c403
-rw-r--r--guest/hals/identity/libeic/EicProvisioning.h144
-rw-r--r--guest/hals/identity/libeic/libeic.h40
-rw-r--r--guest/hals/identity/service.cpp53
-rw-r--r--guest/hals/keymint/remote/Android.bp2
-rw-r--r--guest/hals/keymint/remote/android.hardware.security.keymint-service.remote.xml2
-rw-r--r--guest/hals/keymint/remote/remote_keymaster.cpp11
-rw-r--r--guest/hals/keymint/remote/service.cpp2
-rw-r--r--guest/hals/ril/reference-libril/ril.h8
-rw-r--r--guest/hals/ril/reference-libril/ril_commands.h363
-rw-r--r--guest/hals/ril/reference-libril/ril_service.cpp1180
-rw-r--r--guest/hals/ril/reference-ril/reference-ril.c54
-rw-r--r--guest/services/wifi/Android.bp44
-rw-r--r--guest/services/wifi/init.wifi.sh.rc8
-rw-r--r--host/commands/append_squashfs_overlay/Android.bp1
-rw-r--r--host/commands/append_squashfs_overlay/Cargo.toml2
-rw-r--r--host/commands/assemble_cvd/assemble_cvd.cc109
-rw-r--r--host/commands/assemble_cvd/disk_flags.cc141
-rw-r--r--host/commands/assemble_cvd/flags.cc81
-rw-r--r--host/commands/assemble_cvd/flags.h7
-rw-r--r--host/commands/assemble_cvd/super_image_mixer.cc11
-rw-r--r--host/commands/cvd/Android.bp5
-rw-r--r--host/commands/cvd/main.cc35
-rw-r--r--host/commands/cvd/proto/cvd_server.proto1
-rw-r--r--host/commands/cvd/server.cc8
-rw-r--r--host/commands/cvd/server.h2
-rw-r--r--host/commands/fetcher/build_api.cc24
-rw-r--r--host/commands/fetcher/curl_wrapper.cc11
-rw-r--r--host/commands/fetcher/curl_wrapper.h2
-rw-r--r--host/commands/kernel_log_monitor/main.cc2
-rw-r--r--host/commands/log_tee/log_tee.cpp2
-rw-r--r--host/commands/metrics/metrics.cc2
-rw-r--r--host/commands/modem_simulator/call_service.cpp17
-rw-r--r--host/commands/modem_simulator/data_service.cpp6
-rw-r--r--host/commands/modem_simulator/main.cpp2
-rw-r--r--host/commands/modem_simulator/network_service.cpp323
-rw-r--r--host/commands/modem_simulator/network_service.h87
-rw-r--r--host/commands/modem_simulator/network_service_constants.h34
-rw-r--r--host/commands/modem_simulator/sms_service.cpp30
-rw-r--r--host/commands/modem_simulator/thread_looper.cpp4
-rw-r--r--host/commands/modem_simulator/thread_looper.h2
-rw-r--r--host/commands/modem_simulator/unittest/service_test.cpp20
-rw-r--r--host/commands/run_cvd/launch.cc73
-rw-r--r--host/commands/run_cvd/launch_streamer.cpp11
-rw-r--r--host/commands/secure_env/secure_env.cpp54
-rw-r--r--host/commands/secure_env/tpm_attestation_record.cpp2
-rw-r--r--host/commands/stop/main.cc46
-rw-r--r--host/commands/wmediumd_control/main.cpp29
-rw-r--r--host/frontend/operator_proxy/Android.bp43
-rw-r--r--host/frontend/operator_proxy/main.cpp87
-rw-r--r--host/frontend/webrtc/client/client.html95
-rw-r--r--host/frontend/webrtc/client/js/app.js123
-rw-r--r--host/frontend/webrtc/client/js/controls.js163
-rw-r--r--host/frontend/webrtc/client/style.css71
-rw-r--r--host/frontend/webrtc/lib/server_connection.cpp346
-rw-r--r--host/frontend/webrtc/lib/server_connection.h72
-rw-r--r--host/frontend/webrtc/lib/streamer.cpp70
-rw-r--r--host/frontend/webrtc/lib/streamer.h13
-rw-r--r--host/frontend/webrtc/main.cpp7
-rw-r--r--host/libs/config/bootconfig_args.cpp3
-rw-r--r--host/libs/config/cuttlefish_config.cpp50
-rw-r--r--host/libs/config/cuttlefish_config.h32
-rw-r--r--host/libs/config/cuttlefish_config_instance.cpp44
-rw-r--r--host/libs/config/data_image.cpp186
-rw-r--r--host/libs/config/data_image.h9
-rw-r--r--host/libs/config/known_paths.cpp8
-rw-r--r--host/libs/config/known_paths.h1
-rw-r--r--host/libs/confui/host_renderer.cc18
-rw-r--r--host/libs/image_aggregator/image_aggregator.cc6
-rw-r--r--host/libs/vm_manager/crosvm_manager.cpp18
-rw-r--r--host/libs/vm_manager/crosvm_manager.h3
-rw-r--r--host/libs/vm_manager/qemu_manager.cpp10
-rw-r--r--host/libs/vm_manager/qemu_manager.h3
-rw-r--r--host/libs/vm_manager/vm_manager.h3
-rw-r--r--host/libs/websocket/websocket_server.cpp7
-rw-r--r--host/libs/wmediumd_controller/wmediumd_api_protocol.cpp6
-rw-r--r--host/libs/wmediumd_controller/wmediumd_api_protocol.h26
-rw-r--r--host/libs/wmediumd_controller/wmediumd_controller.cpp8
-rw-r--r--host/libs/wmediumd_controller/wmediumd_controller.h2
-rw-r--r--host_package.mk4
-rw-r--r--required_images1
-rw-r--r--shared/BoardConfig.mk21
-rw-r--r--shared/auto/preinstalled-packages-product-car-cuttlefish.xml8
-rw-r--r--shared/config/fstab-erofs.ext45
-rw-r--r--shared/config/fstab-erofs.f2fs5
-rw-r--r--shared/config/fstab.ext41
-rw-r--r--shared/config/fstab.f2fs1
-rw-r--r--shared/config/init.vendor.rc12
-rw-r--r--shared/device.mk80
-rw-r--r--shared/permissions/cuttlefish_excluded_hardware.xml3
-rw-r--r--shared/sepolicy/OWNERS14
-rw-r--r--shared/sepolicy/system_ext/private/platform_app.te3
-rw-r--r--shared/sepolicy/system_ext/private/property_contexts6
-rw-r--r--shared/sepolicy/vendor/bug_map1
-rw-r--r--shared/sepolicy/vendor/file_contexts5
-rw-r--r--shared/sepolicy/vendor/genfs_contexts2
-rw-r--r--shared/sepolicy/vendor/hal_identity_remote.te5
-rw-r--r--shared/sepolicy/vendor/hal_keymint_remote.te1
-rw-r--r--shared/sepolicy/vendor/mediatranscoding.te (renamed from shared/sepolicy/system_ext/private/mediatranscoding.te)0
-rw-r--r--shared/sepolicy/vendor/property.te1
-rw-r--r--shared/sepolicy/vendor/property_contexts1
-rw-r--r--shared/sepolicy/vendor/vendor_init.te2
-rw-r--r--shared/tv/OWNERS2
-rw-r--r--shared/tv/device.mk3
-rw-r--r--tests/hal/hal_implementation_test.cpp26
-rwxr-xr-xtools/create_base_image_gce.sh4
-rw-r--r--vsoc_arm64/auto/aosp_cf.mk3
-rw-r--r--vsoc_arm64/phone/aosp_cf.mk2
-rw-r--r--vsoc_arm64_only/phone/aosp_cf.mk3
-rw-r--r--vsoc_arm_only/phone/aosp_cf.mk3
-rw-r--r--vsoc_x86/go_phone/OWNERS2
-rw-r--r--vsoc_x86/go_phone/device.mk2
-rw-r--r--vsoc_x86/go_phone/overlay/frameworks/base/core/res/res/values/config.xml27
-rw-r--r--vsoc_x86_64/tv/OWNERS2
-rw-r--r--vsoc_x86_64/tv/device.mk2
156 files changed, 9642 insertions, 1287 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk
index db90f0c08..6d10d1271 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -71,3 +71,8 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/init/android.hardware.he
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/etc/vintf/manifest/manifest_android.hardware.health.storage@1.0.cuttlefish.xml)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/apex/com.android.gki.*)
+
+$(call add-clean-step, find $(PRODUCT_OUT)/system -type f -name "*charger*" -print0 | xargs -0 rm -f)
+$(call add-clean-step, find $(PRODUCT_OUT)/vendor -type f -name "*health@*" -print0 | xargs -0 rm -f)
+$(call add-clean-step, find $(PRODUCT_OUT)/recovery/root -type f -name "*charger*" -print0 | xargs -0 rm -f)
+$(call add-clean-step, find $(PRODUCT_OUT)/recovery/root -type f -name "*health@*" -print0 | xargs -0 rm -f)
diff --git a/README.md b/README.md
index 06db0c438..25dd51b7f 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@
2. Download, build, and install the host debian package:
```bash
- sudo apt install -y git devscripts config-package-dev debhelper-compat
+ sudo apt install -y git devscripts config-package-dev debhelper-compat golang
git clone https://github.com/google/android-cuttlefish
cd android-cuttlefish
debuild -i -us -uc -b
diff --git a/apex/com.android.hardware.core_permissions/Android.bp b/apex/com.android.hardware.core_permissions/Android.bp
index 5ab7dc4e3..11e28d754 100644
--- a/apex/com.android.hardware.core_permissions/Android.bp
+++ b/apex/com.android.hardware.core_permissions/Android.bp
@@ -32,8 +32,6 @@ override_apex {
"android.hardware.fingerprint.prebuilt.xml",
"android.hardware.location.gps.prebuilt.xml",
"android.hardware.reboot_escrow.prebuilt.xml",
- "android.hardware.usb.accessory.prebuilt.xml",
- "android.hardware.usb.host.prebuilt.xml",
"android.hardware.vulkan.level-0.prebuilt.xml",
"android.hardware.vulkan.version-1_0_3.prebuilt.xml",
"android.software.device_id_attestation.prebuilt.xml",
diff --git a/apex/com.google.cf.wifi/Android.bp b/apex/com.google.cf.wifi/Android.bp
index e6ab72860..3a57dd628 100644
--- a/apex/com.google.cf.wifi/Android.bp
+++ b/apex/com.google.cf.wifi/Android.bp
@@ -30,8 +30,8 @@ prebuilt_etc {
installable: false,
}
-apex {
- name: "com.google.cf.wifi",
+apex_defaults {
+ name: "com.google.cf.wifi.defaults",
// Name expected by wpa_supplicant when it looks for config files.
apex_name: "com.android.wifi.hal",
manifest: "apex_manifest.json",
@@ -48,7 +48,6 @@ apex {
"wpa_supplicant_cf",
],
prebuilts: [
- "android.hardware.wifi.passpoint.prebuilt.xml",
"android.hardware.wifi.prebuilt.xml",
"com.google.cf.wifi.rc",
"wpa_supplicant.conf.cf",
@@ -57,3 +56,18 @@ apex {
// TODO(b/202992812): Use the vintf_fragment from the wpa_supplicant project.
vintf_fragments: ["com.google.cf.wifi.xml"],
}
+
+apex {
+ name: "com.google.cf.wifi",
+ defaults: ["com.google.cf.wifi.defaults"],
+ prebuilts: [
+ "android.hardware.wifi.passpoint.prebuilt.xml",
+ ],
+ multi_install_skip_symbol_files: true,
+}
+
+apex {
+ name: "com.google.cf.wifi.no-passpoint",
+ defaults: ["com.google.cf.wifi.defaults"],
+ multi_install_skip_symbol_files: true,
+}
diff --git a/apex/com.google.cf.wifi_hwsim/Android.bp b/apex/com.google.cf.wifi_hwsim/Android.bp
new file mode 100644
index 000000000..49eec46fd
--- /dev/null
+++ b/apex/com.google.cf.wifi_hwsim/Android.bp
@@ -0,0 +1,79 @@
+// Copyright (C) 2021 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.
+
+soong_namespace {
+ imports: [
+ "device/generic/goldfish",
+ ],
+}
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+prebuilt_etc {
+ name: "com.google.cf.wifi_hwsim.rc",
+ src: "com.google.cf.wifi_hwsim.rc",
+ installable: false,
+}
+
+cc_binary {
+ name: "android.hardware.wifi@1.0-service_cf",
+ defaults: ["android.hardware.wifi@1.0-service_default"],
+ shared_libs: ["libwifi-hal_cf"],
+ static_libs: ["android.hardware.wifi@1.0-service-lib_cf"],
+}
+
+cc_library_static {
+ name: "android.hardware.wifi@1.0-service-lib_cf",
+ defaults: ["android.hardware.wifi@1.0-service-lib_defaults"],
+ shared_libs: ["libwifi-hal_cf"],
+}
+
+cc_library_shared {
+ name: "libwifi-hal_cf",
+ defaults: ["libwifi-hal_defaults"],
+ whole_static_libs: ["libwifi-hal-emu"],
+}
+
+apex {
+ name: "com.google.cf.wifi_hwsim",
+ // Name expected by wpa_supplicant when it looks for config files.
+ apex_name: "com.android.wifi.hal",
+ manifest: "apex_manifest.json",
+ key: "com.google.cf.apex.key",
+ certificate: ":com.google.cf.apex.certificate",
+ file_contexts: "file_contexts",
+ use_vndk_as_stable: true,
+ updatable: false,
+ // Install the apex in /vendor/apex
+ soc_specific: true,
+ binaries: [
+ "mac80211_create_radios",
+ "rename_netiface",
+ "wpa_supplicant_cf",
+ "hostapd_cf",
+ "android.hardware.wifi@1.0-service_cf",
+ ],
+ sh_binaries: ["init.wifi.sh_apex"],
+ prebuilts: [
+ "android.hardware.wifi.passpoint.prebuilt.xml",
+ "android.hardware.wifi.prebuilt.xml",
+ "com.google.cf.wifi_hwsim.rc",
+ "wpa_supplicant.conf.cf",
+ "wpa_supplicant_overlay.conf.cf",
+ ],
+ // TODO(b/202992812): Use the vintf_fragment from the wpa_supplicant project.
+ vintf_fragments: ["com.google.cf.wifi_hwsim.xml"],
+}
diff --git a/apex/com.google.cf.wifi_hwsim/apex_manifest.json b/apex/com.google.cf.wifi_hwsim/apex_manifest.json
new file mode 100644
index 000000000..ffd1a2c34
--- /dev/null
+++ b/apex/com.google.cf.wifi_hwsim/apex_manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.wifi.hal",
+ "version": 1
+}
diff --git a/apex/com.google.cf.wifi_hwsim/com.google.cf.wifi_hwsim.rc b/apex/com.google.cf.wifi_hwsim/com.google.cf.wifi_hwsim.rc
new file mode 100644
index 000000000..1926ceb4e
--- /dev/null
+++ b/apex/com.google.cf.wifi_hwsim/com.google.cf.wifi_hwsim.rc
@@ -0,0 +1,45 @@
+
+service rename_eth0 /apex/com.android.wifi.hal/bin/rename_netiface eth0 rmnet0
+ oneshot
+
+service init_wifi_sh /apex/com.android.wifi.hal/bin/init.wifi.sh
+ class late_start
+ user root
+ group root wakelock wifi
+ oneshot
+ disabled # Started on post-fs-data
+
+service wpa_supplicant /apex/com.android.wifi.hal/bin/hw/wpa_supplicant_cf -g@android:wpa_wlan0
+ interface android.hardware.wifi.supplicant@1.0::ISupplicant default
+ interface android.hardware.wifi.supplicant@1.1::ISupplicant default
+ interface android.hardware.wifi.supplicant@1.2::ISupplicant default
+ interface android.hardware.wifi.supplicant@1.3::ISupplicant default
+ interface android.hardware.wifi.supplicant@1.4::ISupplicant default
+ socket wpa_wlan0 dgram 660 wifi wifi
+ group system wifi inet
+ disabled
+ oneshot
+
+service hostapd /apex/com.android.wifi.hal/bin/hw/hostapd_cf
+ interface android.hardware.wifi.hostapd@1.0::IHostapd default
+ interface android.hardware.wifi.hostapd@1.1::IHostapd default
+ interface android.hardware.wifi.hostapd@1.2::IHostapd default
+ interface android.hardware.wifi.hostapd@1.3::IHostapd default
+ class main
+ capabilities NET_ADMIN NET_RAW
+ user wifi
+ group wifi net_raw net_admin
+ disabled
+ oneshot
+
+service vendor.wifi_hal_legacy /apex/com.android.wifi.hal/bin/hw/android.hardware.wifi@1.0-service_cf
+ interface android.hardware.wifi@1.0::IWifi default
+ interface android.hardware.wifi@1.1::IWifi default
+ interface android.hardware.wifi@1.2::IWifi default
+ interface android.hardware.wifi@1.3::IWifi default
+ interface android.hardware.wifi@1.4::IWifi default
+ interface android.hardware.wifi@1.5::IWifi default
+ class hal
+ capabilities NET_ADMIN NET_RAW SYS_MODULE
+ user wifi
+ group wifi gps
diff --git a/apex/com.google.cf.wifi_hwsim/com.google.cf.wifi_hwsim.xml b/apex/com.google.cf.wifi_hwsim/com.google.cf.wifi_hwsim.xml
new file mode 100644
index 000000000..19ef2eb51
--- /dev/null
+++ b/apex/com.google.cf.wifi_hwsim/com.google.cf.wifi_hwsim.xml
@@ -0,0 +1,29 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.wifi.supplicant</name>
+ <transport>hwbinder</transport>
+ <version>1.4</version>
+ <interface>
+ <name>ISupplicant</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl">
+ <name>android.hardware.wifi.hostapd</name>
+ <transport>hwbinder</transport>
+ <version>1.3</version>
+ <interface>
+ <name>IHostapd</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl">
+ <name>android.hardware.wifi</name>
+ <transport>hwbinder</transport>
+ <version>1.5</version>
+ <interface>
+ <name>IWifi</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/apex/com.google.cf.wifi_hwsim/file_contexts b/apex/com.google.cf.wifi_hwsim/file_contexts
new file mode 100644
index 000000000..083cfeda8
--- /dev/null
+++ b/apex/com.google.cf.wifi_hwsim/file_contexts
@@ -0,0 +1,8 @@
+(/.*)? u:object_r:vendor_file:s0
+/bin/rename_netiface u:object_r:rename_netiface_exec:s0
+/bin/init\.wifi\.sh u:object_r:init_wifi_sh_exec:s0
+/bin/hw/wpa_supplicant_cf u:object_r:hal_wifi_supplicant_default_exec:s0
+/bin/hw/hostapd_cf u:object_r:hal_wifi_hostapd_default_exec:s0
+/bin/mac80211_create_radios u:object_r:mac80211_create_radios_exec:s0
+/etc/permissions(/.*)? u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.wifi@1\.0-service_cf u:object_r:hal_wifi_default_exec:s0
diff --git a/build/Android.bp b/build/Android.bp
index edd9c702e..b70b050f8 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -88,6 +88,7 @@ cvd_host_tools = [
"unpack_bootimg",
"webRTC",
"webrtc_operator",
+ "operator_proxy",
"wmediumd",
"wmediumd_control",
"wmediumd_gen_config",
@@ -136,11 +137,15 @@ cvd_host_model_simulator_files = [
cvd_host_seccomp_policy_x86_64 = [
"9p_device.policy_x86_64",
"balloon_device.policy_x86_64",
+ "battery.policy_x86_64",
"block_device.policy_x86_64",
"cras_audio_device.policy_x86_64",
+ "cras_snd_device.policy_x86_64",
"fs_device.policy_x86_64",
"gpu_device.policy_x86_64",
+ "gpu_render_server.policy_x86_64",
"input_device.policy_x86_64",
+ "iommu_device.policy_x86_64",
"net_device.policy_x86_64",
"null_audio_device.policy_x86_64",
"pmem_device.policy_x86_64",
@@ -156,13 +161,16 @@ cvd_host_seccomp_policy_x86_64 = [
"xhci.policy_x86_64",
]
-cvd_host_seccomp_policy_arm64 = [
+cvd_host_seccomp_policy_aarch64 = [
"9p_device.policy_aarch64",
"balloon_device.policy_aarch64",
+ "battery.policy_aarch64",
"block_device.policy_aarch64",
"cras_audio_device.policy_aarch64",
+ "cras_snd_device.policy_aarch64",
"fs_device.policy_aarch64",
"gpu_device.policy_aarch64",
+ "gpu_render_server.policy_aarch64",
"input_device.policy_aarch64",
"net_device.policy_aarch64",
"null_audio_device.policy_aarch64",
@@ -208,7 +216,7 @@ cvd_host_package_customization {
arm64: {
multilib: {
common: {
- deps: cvd_host_seccomp_policy_arm64,
+ deps: cvd_host_seccomp_policy_aarch64,
},
},
},
diff --git a/common/libs/utils/files.cpp b/common/libs/utils/files.cpp
index c24bd5fbb..3a3376305 100644
--- a/common/libs/utils/files.cpp
+++ b/common/libs/utils/files.cpp
@@ -35,9 +35,9 @@
namespace cuttlefish {
-bool FileExists(const std::string& path) {
+bool FileExists(const std::string& path, bool follow_symlinks) {
struct stat st;
- return stat(path.c_str(), &st) == 0;
+ return (follow_symlinks ? stat : lstat)(path.c_str(), &st) == 0;
}
bool FileHasContent(const std::string& path) {
@@ -57,9 +57,9 @@ std::vector<std::string> DirectoryContents(const std::string& path) {
return ret;
}
-bool DirectoryExists(const std::string& path) {
+bool DirectoryExists(const std::string& path, bool follow_symlinks) {
struct stat st;
- if (stat(path.c_str(), &st) == -1) {
+ if ((follow_symlinks ? stat : lstat)(path.c_str(), &st) == -1) {
return false;
}
if ((st.st_mode & S_IFMT) != S_IFDIR) {
diff --git a/common/libs/utils/files.h b/common/libs/utils/files.h
index 88d1762a2..cc784ab81 100644
--- a/common/libs/utils/files.h
+++ b/common/libs/utils/files.h
@@ -21,10 +21,10 @@
#include <string>
namespace cuttlefish {
-bool FileExists(const std::string& path);
+bool FileExists(const std::string& path, bool follow_symlinks = true);
bool FileHasContent(const std::string& path);
std::vector<std::string> DirectoryContents(const std::string& path);
-bool DirectoryExists(const std::string& path);
+bool DirectoryExists(const std::string& path, bool follow_symlinks = true);
bool IsDirectoryEmpty(const std::string& path);
bool RecursivelyRemoveDirectory(const std::string& path);
off_t FileSize(const std::string& path);
diff --git a/guest/hals/health/Android.bp b/guest/hals/health/Android.bp
index 81d19a3a5..cb9d866fb 100644
--- a/guest/hals/health/Android.bp
+++ b/guest/hals/health/Android.bp
@@ -17,6 +17,58 @@ package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
+cc_defaults {
+ name: "android.hardware.health-service.cuttlefish-defaults",
+ relative_install_path: "hw",
+ vintf_fragments: ["android.hardware.health-service.cuttlefish.xml"],
+
+ srcs: [
+ "health-aidl.cpp",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ static_libs: [
+ "android.hardware.health-translate-ndk",
+ "libbatterymonitor",
+ "libhealthloop",
+ "libhealth_aidl_impl",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcutils",
+ "libhidlbase",
+ "liblog",
+ "libutils",
+ "android.hardware.health-V1-ndk",
+ ],
+
+ defaults: ["enabled_on_p_and_later"],
+}
+
+cc_binary {
+ name: "android.hardware.health-service.cuttlefish",
+ defaults: ["android.hardware.health-service.cuttlefish-defaults"],
+ proprietary: true,
+ init_rc: ["android.hardware.health-service.cuttlefish.rc"],
+ overrides: ["charger"],
+}
+
+cc_binary {
+ name: "android.hardware.health-service.cuttlefish_recovery",
+ defaults: ["android.hardware.health-service.cuttlefish-defaults"],
+ recovery: true,
+ init_rc: ["android.hardware.health-service.cuttlefish_recovery.rc"],
+ overrides: ["charger.recovery"],
+}
+
+// Deprecated. Retained to be used on other devices. It is not installed on cuttlefish.
+// TODO(b/210183170): Delete once other devices transition to the AIDL HAL.
cc_library_shared {
name: "android.hardware.health@2.1-impl-cuttlefish",
stem: "android.hardware.health@2.0-impl-2.1-cuttlefish",
@@ -26,7 +78,7 @@ cc_library_shared {
relative_install_path: "hw",
srcs: [
- "health.cpp",
+ "health-hidl.cpp",
],
cflags: [
diff --git a/guest/hals/health/android.hardware.health-service.cuttlefish.rc b/guest/hals/health/android.hardware.health-service.cuttlefish.rc
new file mode 100644
index 000000000..8c2f15388
--- /dev/null
+++ b/guest/hals/health/android.hardware.health-service.cuttlefish.rc
@@ -0,0 +1,8 @@
+service vendor.health-cuttlefish /vendor/bin/hw/android.hardware.health-service.cuttlefish
+ class hal
+ user system
+ group system
+ capabilities WAKE_ALARM BLOCK_SUSPEND
+ file /dev/kmsg w
+
+# cuttlefish has no charger mode.
diff --git a/guest/hals/health/android.hardware.health-service.cuttlefish.xml b/guest/hals/health/android.hardware.health-service.cuttlefish.xml
new file mode 100644
index 000000000..98026cbdc
--- /dev/null
+++ b/guest/hals/health/android.hardware.health-service.cuttlefish.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.health</name>
+ <version>1</version>
+ <fqname>IHealth/default</fqname>
+ </hal>
+</manifest>
diff --git a/guest/hals/health/android.hardware.health-service.cuttlefish_recovery.rc b/guest/hals/health/android.hardware.health-service.cuttlefish_recovery.rc
new file mode 100644
index 000000000..58e440519
--- /dev/null
+++ b/guest/hals/health/android.hardware.health-service.cuttlefish_recovery.rc
@@ -0,0 +1,7 @@
+service vendor.health-cuttlefish /system/bin/hw/android.hardware.health-service.cuttlefish_recovery
+ class hal
+ seclabel u:r:hal_health_default:s0
+ user system
+ group system
+ capabilities WAKE_ALARM BLOCK_SUSPEND
+ file /dev/kmsg w
diff --git a/guest/hals/health/health-aidl.cpp b/guest/hals/health/health-aidl.cpp
new file mode 100644
index 000000000..595971f76
--- /dev/null
+++ b/guest/hals/health/health-aidl.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+#define LOG_TAG "android.hardware.health-service.cuttlefish"
+
+#include <memory>
+#include <string_view>
+
+#include <android-base/logging.h>
+#include <android/binder_interface_utils.h>
+#include <health-impl/Health.h>
+#include <health/utils.h>
+
+using ::aidl::android::hardware::health::BatteryHealth;
+using ::aidl::android::hardware::health::BatteryStatus;
+using ::aidl::android::hardware::health::HalHealthLoop;
+using ::aidl::android::hardware::health::Health;
+using ::aidl::android::hardware::health::HealthInfo;
+using ::aidl::android::hardware::health::IHealth;
+using ::android::hardware::health::InitHealthdConfig;
+using ::ndk::ScopedAStatus;
+using ::ndk::SharedRefBase;
+using namespace std::literals;
+
+namespace aidl::android::hardware::health {
+
+// Health HAL implementation for cuttlefish. Note that in this implementation,
+// cuttlefish pretends to be a device with a battery being charged.
+// Implementations on real devices should not insert these fake values. For
+// example, a battery-less device should report batteryPresent = false and
+// batteryStatus = UNKNOWN.
+
+class HealthImpl : public Health {
+ public:
+ // Inherit constructor.
+ using Health::Health;
+ virtual ~HealthImpl() {}
+
+ ScopedAStatus getChargeCounterUah(int32_t* out) override;
+ ScopedAStatus getCurrentNowMicroamps(int32_t* out) override;
+ ScopedAStatus getCurrentAverageMicroamps(int32_t* out) override;
+ ScopedAStatus getCapacity(int32_t* out) override;
+ ScopedAStatus getChargeStatus(BatteryStatus* out) override;
+
+ protected:
+ void UpdateHealthInfo(HealthInfo* health_info) override;
+};
+
+void HealthImpl::UpdateHealthInfo(HealthInfo* health_info) {
+ health_info->chargerAcOnline = true;
+ health_info->chargerUsbOnline = true;
+ health_info->chargerWirelessOnline = false;
+ health_info->maxChargingCurrentMicroamps = 500000;
+ health_info->maxChargingVoltageMicrovolts = 5000000;
+ health_info->batteryStatus = BatteryStatus::CHARGING;
+ health_info->batteryHealth = BatteryHealth::GOOD;
+ health_info->batteryPresent = true;
+ health_info->batteryLevel = 85;
+ health_info->batteryVoltageMillivolts = 3600;
+ health_info->batteryTemperatureTenthsCelsius = 350;
+ health_info->batteryCurrentMicroamps = 400000;
+ health_info->batteryCycleCount = 32;
+ health_info->batteryFullChargeUah = 4000000;
+ health_info->batteryChargeCounterUah = 1900000;
+ health_info->batteryTechnology = "Li-ion";
+}
+
+ScopedAStatus HealthImpl::getChargeCounterUah(int32_t* out) {
+ *out = 1900000;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HealthImpl::getCurrentNowMicroamps(int32_t* out) {
+ *out = 400000;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HealthImpl::getCurrentAverageMicroamps(int32_t*) {
+ return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+}
+
+ScopedAStatus HealthImpl::getCapacity(int32_t* out) {
+ *out = 85;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus HealthImpl::getChargeStatus(BatteryStatus* out) {
+ *out = BatteryStatus::CHARGING;
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::health
+
+int main(int, [[maybe_unused]] char** argv) {
+#ifdef __ANDROID_RECOVERY__
+ android::base::InitLogging(argv, android::base::KernelLogger);
+#endif
+ // Cuttlefish does not support offline-charging mode, hence do not handle
+ // --charger option.
+ using aidl::android::hardware::health::HealthImpl;
+ LOG(INFO) << "Starting health HAL.";
+ auto config = std::make_unique<healthd_config>();
+ InitHealthdConfig(config.get());
+ auto binder = SharedRefBase::make<HealthImpl>("default", std::move(config));
+ auto hal_health_loop = std::make_shared<HalHealthLoop>(binder, binder);
+ return hal_health_loop->StartLoop();
+}
diff --git a/guest/hals/health/health.cpp b/guest/hals/health/health-hidl.cpp
index ac03fe090..ac03fe090 100644
--- a/guest/hals/health/health.cpp
+++ b/guest/hals/health/health-hidl.cpp
diff --git a/guest/hals/hostapd/Android.bp b/guest/hals/hostapd/Android.bp
new file mode 100644
index 000000000..f12d0dc6b
--- /dev/null
+++ b/guest/hals/hostapd/Android.bp
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 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 {
+ default_applicable_licenses: [
+ "external_wpa_supplicant_8_wpa_supplicant_license",
+ ],
+}
+
+cc_binary {
+ name: "hostapd_cf",
+ defaults: ["hostapd_defaults"],
+ static_libs: [
+ "lib_driver_cmd_simulated_cf_bp",
+ ],
+}
diff --git a/guest/hals/identity/Android.bp b/guest/hals/identity/Android.bp
new file mode 100644
index 000000000..bb321b825
--- /dev/null
+++ b/guest/hals/identity/Android.bp
@@ -0,0 +1,57 @@
+cc_binary {
+ name: "android.hardware.identity-service.remote",
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.identity-service.remote.rc"],
+ vintf_fragments: ["android.hardware.identity-service.remote.xml"],
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-g",
+ ],
+ shared_libs: [
+ "liblog",
+ "libcrypto",
+ "libbinder_ndk",
+ "libkeymaster_messages",
+ ],
+ static_libs: [
+ "libbase",
+ "libcppbor_external",
+ "libcppcose_rkp",
+ "libutils",
+ "libsoft_attestation_cert",
+ "libkeymaster_portable",
+ "libsoft_attestation_cert",
+ "libpuresoftkeymasterdevice",
+ "android.hardware.identity-support-lib",
+ "android.hardware.identity-V3-ndk",
+ "android.hardware.keymaster-V3-ndk",
+ "android.hardware.security.keymint-V1-ndk",
+ ],
+ local_include_dirs: [
+ "common",
+ "libeic",
+ ],
+ srcs: [
+ "service.cpp",
+ "RemoteSecureHardwareProxy.cpp",
+ "common/IdentityCredential.cpp",
+ "common/IdentityCredentialStore.cpp",
+ "common/WritableIdentityCredential.cpp",
+ "libeic/EicCbor.c",
+ "libeic/EicPresentation.c",
+ "libeic/EicProvisioning.c",
+ "libeic/EicOpsImpl.cc",
+ ],
+ required: [
+ "android.hardware.identity_credential.remote.xml",
+ ],
+}
+
+prebuilt_etc {
+ name: "android.hardware.identity_credential.remote.xml",
+ sub_dir: "permissions",
+ vendor: true,
+ src: "android.hardware.identity_credential.remote.xml",
+}
diff --git a/guest/hals/identity/OWNERS b/guest/hals/identity/OWNERS
new file mode 100644
index 000000000..190f95ca2
--- /dev/null
+++ b/guest/hals/identity/OWNERS
@@ -0,0 +1 @@
+include /platform/hardware/interfaces:/identity/OWNERS
diff --git a/guest/hals/identity/RemoteSecureHardwareProxy.cpp b/guest/hals/identity/RemoteSecureHardwareProxy.cpp
new file mode 100644
index 000000000..3ec8aaa11
--- /dev/null
+++ b/guest/hals/identity/RemoteSecureHardwareProxy.cpp
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2021, 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.
+ */
+
+#define LOG_TAG "RemoteSecureHardwareProxy"
+
+#include "RemoteSecureHardwareProxy.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <string.h>
+
+#include <openssl/sha.h>
+
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hkdf.h>
+#include <openssl/hmac.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+
+#include <libeic.h>
+
+using ::std::optional;
+using ::std::string;
+using ::std::tuple;
+using ::std::vector;
+
+namespace android::hardware::identity {
+
+// ----------------------------------------------------------------------
+
+RemoteSecureHardwareProvisioningProxy::RemoteSecureHardwareProvisioningProxy() {
+}
+
+RemoteSecureHardwareProvisioningProxy::
+ ~RemoteSecureHardwareProvisioningProxy() {}
+
+bool RemoteSecureHardwareProvisioningProxy::shutdown() {
+ LOG(INFO) << "RemoteSecureHardwarePresentationProxy shutdown";
+ return true;
+}
+
+bool RemoteSecureHardwareProvisioningProxy::initialize(bool testCredential) {
+ LOG(INFO) << "RemoteSecureHardwareProvisioningProxy created, "
+ "sizeof(EicProvisioning): "
+ << sizeof(EicProvisioning);
+ return eicProvisioningInit(&ctx_, testCredential);
+}
+
+bool RemoteSecureHardwareProvisioningProxy::initializeForUpdate(
+ bool testCredential, string docType,
+ vector<uint8_t> encryptedCredentialKeys) {
+ return eicProvisioningInitForUpdate(
+ &ctx_, testCredential, docType.c_str(), docType.size(),
+ encryptedCredentialKeys.data(), encryptedCredentialKeys.size());
+}
+
+// Returns public key certificate.
+optional<vector<uint8_t>>
+RemoteSecureHardwareProvisioningProxy::createCredentialKey(
+ const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
+ uint8_t publicKeyCert[4096];
+ size_t publicKeyCertSize = sizeof publicKeyCert;
+ if (!eicProvisioningCreateCredentialKey(
+ &ctx_, challenge.data(), challenge.size(), applicationId.data(),
+ applicationId.size(), publicKeyCert, &publicKeyCertSize)) {
+ return {};
+ }
+ vector<uint8_t> pubKeyCert(publicKeyCertSize);
+ memcpy(pubKeyCert.data(), publicKeyCert, publicKeyCertSize);
+ return pubKeyCert;
+}
+
+bool RemoteSecureHardwareProvisioningProxy::startPersonalization(
+ int accessControlProfileCount, vector<int> entryCounts,
+ const string& docType, size_t expectedProofOfProvisioningSize) {
+ if (!eicProvisioningStartPersonalization(
+ &ctx_, accessControlProfileCount, entryCounts.data(),
+ entryCounts.size(), docType.c_str(), docType.size(),
+ expectedProofOfProvisioningSize)) {
+ return false;
+ }
+ return true;
+}
+
+// Returns MAC (28 bytes).
+optional<vector<uint8_t>>
+RemoteSecureHardwareProvisioningProxy::addAccessControlProfile(
+ int id, const vector<uint8_t>& readerCertificate,
+ bool userAuthenticationRequired, uint64_t timeoutMillis,
+ uint64_t secureUserId) {
+ vector<uint8_t> mac(28);
+ uint8_t scratchSpace[512];
+ if (!eicProvisioningAddAccessControlProfile(
+ &ctx_, id, readerCertificate.data(), readerCertificate.size(),
+ userAuthenticationRequired, timeoutMillis, secureUserId, mac.data(),
+ scratchSpace, sizeof(scratchSpace))) {
+ return {};
+ }
+ return mac;
+}
+
+bool RemoteSecureHardwareProvisioningProxy::beginAddEntry(
+ const vector<int>& accessControlProfileIds, const string& nameSpace,
+ const string& name, uint64_t entrySize) {
+ uint8_t scratchSpace[512];
+ vector<uint8_t> uint8AccessControlProfileIds;
+ for (size_t i = 0; i < accessControlProfileIds.size(); i++) {
+ uint8AccessControlProfileIds.push_back(accessControlProfileIds[i] & 0xFF);
+ }
+
+ return eicProvisioningBeginAddEntry(
+ &ctx_, uint8AccessControlProfileIds.data(),
+ uint8AccessControlProfileIds.size(), nameSpace.c_str(), nameSpace.size(),
+ name.c_str(), name.size(), entrySize, scratchSpace, sizeof(scratchSpace));
+}
+
+// Returns encryptedContent.
+optional<vector<uint8_t>> RemoteSecureHardwareProvisioningProxy::addEntryValue(
+ const vector<int>& accessControlProfileIds, const string& nameSpace,
+ const string& name, const vector<uint8_t>& content) {
+ vector<uint8_t> eicEncryptedContent;
+ uint8_t scratchSpace[512];
+ vector<uint8_t> uint8AccessControlProfileIds;
+ for (size_t i = 0; i < accessControlProfileIds.size(); i++) {
+ uint8AccessControlProfileIds.push_back(accessControlProfileIds[i] & 0xFF);
+ }
+
+ eicEncryptedContent.resize(content.size() + 28);
+ if (!eicProvisioningAddEntryValue(&ctx_, uint8AccessControlProfileIds.data(),
+ uint8AccessControlProfileIds.size(),
+ nameSpace.c_str(), nameSpace.size(),
+ name.c_str(), name.size(), content.data(),
+ content.size(), eicEncryptedContent.data(),
+ scratchSpace, sizeof(scratchSpace))) {
+ return {};
+ }
+ return eicEncryptedContent;
+}
+
+// Returns signatureOfToBeSigned (EIC_ECDSA_P256_SIGNATURE_SIZE bytes).
+optional<vector<uint8_t>>
+RemoteSecureHardwareProvisioningProxy::finishAddingEntries() {
+ vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
+ if (!eicProvisioningFinishAddingEntries(&ctx_,
+ signatureOfToBeSigned.data())) {
+ return {};
+ }
+ return signatureOfToBeSigned;
+}
+
+// Returns encryptedCredentialKeys.
+optional<vector<uint8_t>>
+RemoteSecureHardwareProvisioningProxy::finishGetCredentialData(
+ const string& docType) {
+ vector<uint8_t> encryptedCredentialKeys(116);
+ size_t size = encryptedCredentialKeys.size();
+ if (!eicProvisioningFinishGetCredentialData(
+ &ctx_, docType.c_str(), docType.size(),
+ encryptedCredentialKeys.data(), &size)) {
+ return {};
+ }
+ encryptedCredentialKeys.resize(size);
+ return encryptedCredentialKeys;
+}
+
+// ----------------------------------------------------------------------
+
+RemoteSecureHardwarePresentationProxy::RemoteSecureHardwarePresentationProxy() {
+}
+
+RemoteSecureHardwarePresentationProxy::
+ ~RemoteSecureHardwarePresentationProxy() {}
+
+bool RemoteSecureHardwarePresentationProxy::initialize(
+ bool testCredential, string docType,
+ vector<uint8_t> encryptedCredentialKeys) {
+ LOG(INFO) << "RemoteSecureHardwarePresentationProxy created, "
+ "sizeof(EicPresentation): "
+ << sizeof(EicPresentation);
+ return eicPresentationInit(&ctx_, testCredential, docType.c_str(),
+ docType.size(), encryptedCredentialKeys.data(),
+ encryptedCredentialKeys.size());
+}
+
+// Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
+optional<pair<vector<uint8_t>, vector<uint8_t>>>
+RemoteSecureHardwarePresentationProxy::generateSigningKeyPair(string docType,
+ time_t now) {
+ uint8_t publicKeyCert[512];
+ size_t publicKeyCertSize = sizeof(publicKeyCert);
+ vector<uint8_t> signingKeyBlob(60);
+
+ if (!eicPresentationGenerateSigningKeyPair(
+ &ctx_, docType.c_str(), docType.size(), now, publicKeyCert,
+ &publicKeyCertSize, signingKeyBlob.data())) {
+ return {};
+ }
+
+ vector<uint8_t> cert;
+ cert.resize(publicKeyCertSize);
+ memcpy(cert.data(), publicKeyCert, publicKeyCertSize);
+
+ return std::make_pair(cert, signingKeyBlob);
+}
+
+// Returns private key
+optional<vector<uint8_t>>
+RemoteSecureHardwarePresentationProxy::createEphemeralKeyPair() {
+ vector<uint8_t> priv(EIC_P256_PRIV_KEY_SIZE);
+ if (!eicPresentationCreateEphemeralKeyPair(&ctx_, priv.data())) {
+ return {};
+ }
+ return priv;
+}
+
+optional<uint64_t>
+RemoteSecureHardwarePresentationProxy::createAuthChallenge() {
+ uint64_t challenge;
+ if (!eicPresentationCreateAuthChallenge(&ctx_, &challenge)) {
+ return {};
+ }
+ return challenge;
+}
+
+bool RemoteSecureHardwarePresentationProxy::shutdown() {
+ LOG(INFO) << "RemoteSecureHardwarePresentationProxy shutdown";
+ return true;
+}
+
+bool RemoteSecureHardwarePresentationProxy::pushReaderCert(
+ const vector<uint8_t>& certX509) {
+ return eicPresentationPushReaderCert(&ctx_, certX509.data(), certX509.size());
+}
+
+bool RemoteSecureHardwarePresentationProxy::validateRequestMessage(
+ const vector<uint8_t>& sessionTranscript,
+ const vector<uint8_t>& requestMessage, int coseSignAlg,
+ const vector<uint8_t>& readerSignatureOfToBeSigned) {
+ return eicPresentationValidateRequestMessage(
+ &ctx_, sessionTranscript.data(), sessionTranscript.size(),
+ requestMessage.data(), requestMessage.size(), coseSignAlg,
+ readerSignatureOfToBeSigned.data(), readerSignatureOfToBeSigned.size());
+}
+
+bool RemoteSecureHardwarePresentationProxy::setAuthToken(
+ uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+ int hardwareAuthenticatorType, uint64_t timeStamp,
+ const vector<uint8_t>& mac, uint64_t verificationTokenChallenge,
+ uint64_t verificationTokenTimestamp, int verificationTokenSecurityLevel,
+ const vector<uint8_t>& verificationTokenMac) {
+ return eicPresentationSetAuthToken(
+ &ctx_, challenge, secureUserId, authenticatorId,
+ hardwareAuthenticatorType, timeStamp, mac.data(), mac.size(),
+ verificationTokenChallenge, verificationTokenTimestamp,
+ verificationTokenSecurityLevel, verificationTokenMac.data(),
+ verificationTokenMac.size());
+}
+
+optional<bool>
+RemoteSecureHardwarePresentationProxy::validateAccessControlProfile(
+ int id, const vector<uint8_t>& readerCertificate,
+ bool userAuthenticationRequired, int timeoutMillis, uint64_t secureUserId,
+ const vector<uint8_t>& mac) {
+ bool accessGranted = false;
+ uint8_t scratchSpace[512];
+ if (!eicPresentationValidateAccessControlProfile(
+ &ctx_, id, readerCertificate.data(), readerCertificate.size(),
+ userAuthenticationRequired, timeoutMillis, secureUserId, mac.data(),
+ &accessGranted, scratchSpace, sizeof(scratchSpace))) {
+ return {};
+ }
+ return accessGranted;
+}
+
+bool RemoteSecureHardwarePresentationProxy::startRetrieveEntries() {
+ return eicPresentationStartRetrieveEntries(&ctx_);
+}
+
+bool RemoteSecureHardwarePresentationProxy::calcMacKey(
+ const vector<uint8_t>& sessionTranscript,
+ const vector<uint8_t>& readerEphemeralPublicKey,
+ const vector<uint8_t>& signingKeyBlob, const string& docType,
+ unsigned int numNamespacesWithValues,
+ size_t expectedProofOfProvisioningSize) {
+ if (signingKeyBlob.size() != 60) {
+ eicDebug("Unexpected size %zd of signingKeyBlob, expected 60",
+ signingKeyBlob.size());
+ return false;
+ }
+ return eicPresentationCalcMacKey(
+ &ctx_, sessionTranscript.data(), sessionTranscript.size(),
+ readerEphemeralPublicKey.data(), signingKeyBlob.data(), docType.c_str(),
+ docType.size(), numNamespacesWithValues, expectedProofOfProvisioningSize);
+}
+
+AccessCheckResult
+RemoteSecureHardwarePresentationProxy::startRetrieveEntryValue(
+ const string& nameSpace, const string& name,
+ unsigned int newNamespaceNumEntries, int32_t entrySize,
+ const vector<int32_t>& accessControlProfileIds) {
+ uint8_t scratchSpace[512];
+ vector<uint8_t> uint8AccessControlProfileIds;
+ for (size_t i = 0; i < accessControlProfileIds.size(); i++) {
+ uint8AccessControlProfileIds.push_back(accessControlProfileIds[i] & 0xFF);
+ }
+
+ EicAccessCheckResult result = eicPresentationStartRetrieveEntryValue(
+ &ctx_, nameSpace.c_str(), nameSpace.size(), name.c_str(), name.size(),
+ newNamespaceNumEntries, entrySize, uint8AccessControlProfileIds.data(),
+ uint8AccessControlProfileIds.size(), scratchSpace, sizeof(scratchSpace));
+ switch (result) {
+ case EIC_ACCESS_CHECK_RESULT_OK:
+ return AccessCheckResult::kOk;
+ case EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES:
+ return AccessCheckResult::kNoAccessControlProfiles;
+ case EIC_ACCESS_CHECK_RESULT_FAILED:
+ return AccessCheckResult::kFailed;
+ case EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED:
+ return AccessCheckResult::kUserAuthenticationFailed;
+ case EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED:
+ return AccessCheckResult::kReaderAuthenticationFailed;
+ }
+ eicDebug("Unknown result with code %d, returning kFailed", (int)result);
+ return AccessCheckResult::kFailed;
+}
+
+optional<vector<uint8_t>>
+RemoteSecureHardwarePresentationProxy::retrieveEntryValue(
+ const vector<uint8_t>& encryptedContent, const string& nameSpace,
+ const string& name, const vector<int32_t>& accessControlProfileIds) {
+ uint8_t scratchSpace[512];
+ vector<uint8_t> uint8AccessControlProfileIds;
+ for (size_t i = 0; i < accessControlProfileIds.size(); i++) {
+ uint8AccessControlProfileIds.push_back(accessControlProfileIds[i] & 0xFF);
+ }
+
+ vector<uint8_t> content;
+ content.resize(encryptedContent.size() - 28);
+ if (!eicPresentationRetrieveEntryValue(
+ &ctx_, encryptedContent.data(), encryptedContent.size(),
+ content.data(), nameSpace.c_str(), nameSpace.size(), name.c_str(),
+ name.size(), uint8AccessControlProfileIds.data(),
+ uint8AccessControlProfileIds.size(), scratchSpace,
+ sizeof(scratchSpace))) {
+ return {};
+ }
+ return content;
+}
+
+optional<vector<uint8_t>>
+RemoteSecureHardwarePresentationProxy::finishRetrieval() {
+ vector<uint8_t> mac(32);
+ size_t macSize = 32;
+ if (!eicPresentationFinishRetrieval(&ctx_, mac.data(), &macSize)) {
+ return {};
+ }
+ mac.resize(macSize);
+ return mac;
+}
+
+optional<vector<uint8_t>>
+RemoteSecureHardwarePresentationProxy::deleteCredential(
+ const string& docType, const vector<uint8_t>& challenge,
+ bool includeChallenge, size_t proofOfDeletionCborSize) {
+ vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
+ if (!eicPresentationDeleteCredential(
+ &ctx_, docType.c_str(), docType.size(), challenge.data(),
+ challenge.size(), includeChallenge, proofOfDeletionCborSize,
+ signatureOfToBeSigned.data())) {
+ return {};
+ }
+ return signatureOfToBeSigned;
+}
+
+optional<vector<uint8_t>> RemoteSecureHardwarePresentationProxy::proveOwnership(
+ const string& docType, bool testCredential,
+ const vector<uint8_t>& challenge, size_t proofOfOwnershipCborSize) {
+ vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
+ if (!eicPresentationProveOwnership(&ctx_, docType.c_str(), docType.size(),
+ testCredential, challenge.data(),
+ challenge.size(), proofOfOwnershipCborSize,
+ signatureOfToBeSigned.data())) {
+ return {};
+ }
+ return signatureOfToBeSigned;
+}
+
+} // namespace android::hardware::identity
diff --git a/guest/hals/identity/RemoteSecureHardwareProxy.h b/guest/hals/identity/RemoteSecureHardwareProxy.h
new file mode 100644
index 000000000..39cb42269
--- /dev/null
+++ b/guest/hals/identity/RemoteSecureHardwareProxy.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2021, 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_FAKESECUREHARDWAREPROXY_H
+#define ANDROID_HARDWARE_IDENTITY_FAKESECUREHARDWAREPROXY_H
+
+#include <libeic.h>
+
+#include "SecureHardwareProxy.h"
+
+namespace android::hardware::identity {
+
+// This implementation uses libEmbeddedIC in-process.
+//
+class RemoteSecureHardwareProvisioningProxy
+ : public SecureHardwareProvisioningProxy {
+ public:
+ RemoteSecureHardwareProvisioningProxy();
+ virtual ~RemoteSecureHardwareProvisioningProxy();
+
+ bool initialize(bool testCredential) override;
+
+ bool initializeForUpdate(bool testCredential, string docType,
+ vector<uint8_t> encryptedCredentialKeys) override;
+
+ bool shutdown() override;
+
+ // Returns public key certificate.
+ optional<vector<uint8_t>> createCredentialKey(
+ const vector<uint8_t>& challenge,
+ const vector<uint8_t>& applicationId) override;
+
+ bool startPersonalization(int accessControlProfileCount,
+ vector<int> entryCounts, const string& docType,
+ size_t expectedProofOfProvisioningSize) override;
+
+ // Returns MAC (28 bytes).
+ optional<vector<uint8_t>> addAccessControlProfile(
+ int id, const vector<uint8_t>& readerCertificate,
+ bool userAuthenticationRequired, uint64_t timeoutMillis,
+ uint64_t secureUserId) override;
+
+ bool beginAddEntry(const vector<int>& accessControlProfileIds,
+ const string& nameSpace, const string& name,
+ uint64_t entrySize) override;
+
+ // Returns encryptedContent.
+ optional<vector<uint8_t>> addEntryValue(
+ const vector<int>& accessControlProfileIds, const string& nameSpace,
+ const string& name, const vector<uint8_t>& content) override;
+
+ // Returns signatureOfToBeSigned (EIC_ECDSA_P256_SIGNATURE_SIZE bytes).
+ optional<vector<uint8_t>> finishAddingEntries() override;
+
+ // Returns encryptedCredentialKeys (80 bytes).
+ optional<vector<uint8_t>> finishGetCredentialData(
+ const string& docType) override;
+
+ protected:
+ EicProvisioning ctx_;
+};
+
+// This implementation uses libEmbeddedIC in-process.
+//
+class RemoteSecureHardwarePresentationProxy
+ : public SecureHardwarePresentationProxy {
+ public:
+ RemoteSecureHardwarePresentationProxy();
+ virtual ~RemoteSecureHardwarePresentationProxy();
+
+ bool initialize(bool testCredential, string docType,
+ vector<uint8_t> encryptedCredentialKeys) override;
+
+ // Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
+ optional<pair<vector<uint8_t>, vector<uint8_t>>> generateSigningKeyPair(
+ string docType, time_t now) override;
+
+ // Returns private key
+ optional<vector<uint8_t>> createEphemeralKeyPair() override;
+
+ optional<uint64_t> createAuthChallenge() override;
+
+ bool startRetrieveEntries() override;
+
+ bool setAuthToken(uint64_t challenge, uint64_t secureUserId,
+ uint64_t authenticatorId, int hardwareAuthenticatorType,
+ uint64_t timeStamp, const vector<uint8_t>& mac,
+ uint64_t verificationTokenChallenge,
+ uint64_t verificationTokenTimestamp,
+ int verificationTokenSecurityLevel,
+ const vector<uint8_t>& verificationTokenMac) override;
+
+ bool pushReaderCert(const vector<uint8_t>& certX509) override;
+
+ optional<bool> validateAccessControlProfile(
+ int id, const vector<uint8_t>& readerCertificate,
+ bool userAuthenticationRequired, int timeoutMillis, uint64_t secureUserId,
+ const vector<uint8_t>& mac) override;
+
+ bool validateRequestMessage(
+ const vector<uint8_t>& sessionTranscript,
+ const vector<uint8_t>& requestMessage, int coseSignAlg,
+ const vector<uint8_t>& readerSignatureOfToBeSigned) override;
+
+ bool calcMacKey(const vector<uint8_t>& sessionTranscript,
+ const vector<uint8_t>& readerEphemeralPublicKey,
+ const vector<uint8_t>& signingKeyBlob, const string& docType,
+ unsigned int numNamespacesWithValues,
+ size_t expectedProofOfProvisioningSize) override;
+
+ AccessCheckResult startRetrieveEntryValue(
+ const string& nameSpace, const string& name,
+ unsigned int newNamespaceNumEntries, int32_t entrySize,
+ const vector<int32_t>& accessControlProfileIds) override;
+
+ optional<vector<uint8_t>> retrieveEntryValue(
+ const vector<uint8_t>& encryptedContent, const string& nameSpace,
+ const string& name,
+ const vector<int32_t>& accessControlProfileIds) override;
+
+ optional<vector<uint8_t>> finishRetrieval() override;
+
+ optional<vector<uint8_t>> deleteCredential(
+ const string& docType, const vector<uint8_t>& challenge,
+ bool includeChallenge, size_t proofOfDeletionCborSize) override;
+
+ optional<vector<uint8_t>> proveOwnership(
+ const string& docType, bool testCredential,
+ const vector<uint8_t>& challenge,
+ size_t proofOfOwnershipCborSize) override;
+
+ bool shutdown() override;
+
+ protected:
+ EicPresentation ctx_;
+};
+
+// Factory implementation.
+//
+class RemoteSecureHardwareProxyFactory : public SecureHardwareProxyFactory {
+ public:
+ RemoteSecureHardwareProxyFactory() {}
+ virtual ~RemoteSecureHardwareProxyFactory() {}
+
+ sp<SecureHardwareProvisioningProxy> createProvisioningProxy() override {
+ return new RemoteSecureHardwareProvisioningProxy();
+ }
+
+ sp<SecureHardwarePresentationProxy> createPresentationProxy() override {
+ return new RemoteSecureHardwarePresentationProxy();
+ }
+};
+
+} // namespace android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_FAKESECUREHARDWAREPROXY_H
diff --git a/guest/hals/identity/android.hardware.identity-service.remote.rc b/guest/hals/identity/android.hardware.identity-service.remote.rc
new file mode 100644
index 000000000..e1dc7a97f
--- /dev/null
+++ b/guest/hals/identity/android.hardware.identity-service.remote.rc
@@ -0,0 +1,3 @@
+service vendor.identity-remote /vendor/bin/hw/android.hardware.identity-service.remote
+ class hal
+ user nobody
diff --git a/guest/hals/identity/android.hardware.identity-service.remote.xml b/guest/hals/identity/android.hardware.identity-service.remote.xml
new file mode 100644
index 000000000..a07425090
--- /dev/null
+++ b/guest/hals/identity/android.hardware.identity-service.remote.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.identity</name>
+ <version>3</version>
+ <interface>
+ <name>IIdentityCredentialStore</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/guest/hals/identity/android.hardware.identity_credential.remote.xml b/guest/hals/identity/android.hardware.identity_credential.remote.xml
new file mode 100644
index 000000000..5149792b7
--- /dev/null
+++ b/guest/hals/identity/android.hardware.identity_credential.remote.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 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.
+-->
+<permissions>
+ <feature name="android.hardware.identity_credential" version="202101" />
+</permissions>
diff --git a/guest/hals/identity/common/IdentityCredential.cpp b/guest/hals/identity/common/IdentityCredential.cpp
new file mode 100644
index 000000000..3555c53da
--- /dev/null
+++ b/guest/hals/identity/common/IdentityCredential.cpp
@@ -0,0 +1,945 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IdentityCredential"
+
+#include "IdentityCredential.h"
+#include "IdentityCredentialStore.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <string.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+#include "SecureHardwareProxy.h"
+#include "WritableIdentityCredential.h"
+
+namespace aidl::android::hardware::identity {
+
+using ::aidl::android::hardware::keymaster::Timestamp;
+using ::android::base::StringPrintf;
+using ::std::optional;
+
+using namespace ::android::hardware::identity;
+
+int IdentityCredential::initialize() {
+ if (credentialData_.size() == 0) {
+ LOG(ERROR) << "CredentialData is empty";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+ auto [item, _, message] = cppbor::parse(credentialData_);
+ if (item == nullptr) {
+ LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ const cppbor::Array* arrayItem = item->asArray();
+ if (arrayItem == nullptr || arrayItem->size() != 3) {
+ LOG(ERROR) << "CredentialData is not an array with three elements";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
+ const cppbor::Bool* testCredentialItem =
+ ((*arrayItem)[1]->asSimple() != nullptr
+ ? ((*arrayItem)[1]->asSimple()->asBool())
+ : nullptr);
+ const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
+ if (docTypeItem == nullptr || testCredentialItem == nullptr ||
+ encryptedCredentialKeysItem == nullptr) {
+ LOG(ERROR) << "CredentialData unexpected item types";
+ return IIdentityCredentialStore::STATUS_INVALID_DATA;
+ }
+
+ docType_ = docTypeItem->value();
+ testCredential_ = testCredentialItem->value();
+
+ encryptedCredentialKeys_ = encryptedCredentialKeysItem->value();
+ if (!hwProxy_->initialize(testCredential_, docType_,
+ encryptedCredentialKeys_)) {
+ LOG(ERROR) << "hwProxy->initialize failed";
+ return false;
+ }
+
+ return IIdentityCredentialStore::STATUS_OK;
+}
+
+ndk::ScopedAStatus IdentityCredential::deleteCredential(
+ vector<uint8_t>* outProofOfDeletionSignature) {
+ return deleteCredentialCommon({}, false, outProofOfDeletionSignature);
+}
+
+ndk::ScopedAStatus IdentityCredential::deleteCredentialWithChallenge(
+ const vector<uint8_t>& challenge,
+ vector<uint8_t>* outProofOfDeletionSignature) {
+ return deleteCredentialCommon(challenge, true, outProofOfDeletionSignature);
+}
+
+ndk::ScopedAStatus IdentityCredential::deleteCredentialCommon(
+ const vector<uint8_t>& challenge, bool includeChallenge,
+ vector<uint8_t>* outProofOfDeletionSignature) {
+ if (challenge.size() > 32) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
+ }
+
+ cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
+ if (includeChallenge) {
+ array = {"ProofOfDeletion", docType_, challenge, testCredential_};
+ }
+
+ vector<uint8_t> proofOfDeletionCbor = array.encode();
+ vector<uint8_t> podDigest = support::sha256(proofOfDeletionCbor);
+
+ optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->deleteCredential(
+ docType_, challenge, includeChallenge, proofOfDeletionCbor.size());
+ if (!signatureOfToBeSigned) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error signing ProofOfDeletion"));
+ }
+
+ optional<vector<uint8_t>> signature =
+ support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
+ proofOfDeletionCbor, // data
+ {}); // certificateChain
+ if (!signature) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
+ }
+
+ *outProofOfDeletionSignature = signature.value();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::proveOwnership(
+ const vector<uint8_t>& challenge,
+ vector<uint8_t>* outProofOfOwnershipSignature) {
+ if (challenge.size() > 32) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
+ }
+
+ cppbor::Array array;
+ array = {"ProofOfOwnership", docType_, challenge, testCredential_};
+ vector<uint8_t> proofOfOwnershipCbor = array.encode();
+ vector<uint8_t> podDigest = support::sha256(proofOfOwnershipCbor);
+
+ optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->proveOwnership(
+ docType_, testCredential_, challenge, proofOfOwnershipCbor.size());
+ if (!signatureOfToBeSigned) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error signing ProofOfOwnership"));
+ }
+
+ optional<vector<uint8_t>> signature =
+ support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
+ proofOfOwnershipCbor, // data
+ {}); // certificateChain
+ if (!signature) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
+ }
+
+ *outProofOfOwnershipSignature = signature.value();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(
+ vector<uint8_t>* outKeyPair) {
+ optional<vector<uint8_t>> ephemeralPriv = hwProxy_->createEphemeralKeyPair();
+ if (!ephemeralPriv) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error creating ephemeral key"));
+ }
+ optional<vector<uint8_t>> keyPair =
+ support::ecPrivateKeyToKeyPair(ephemeralPriv.value());
+ if (!keyPair) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error creating ephemeral key-pair"));
+ }
+
+ // Stash public key of this key-pair for later check in startRetrieval().
+ optional<vector<uint8_t>> publicKey =
+ support::ecKeyPairGetPublicKey(keyPair.value());
+ if (!publicKey) {
+ LOG(ERROR) << "Error getting public part of ephemeral key pair";
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting public part of ephemeral key pair"));
+ }
+ ephemeralPublicKey_ = publicKey.value();
+
+ *outKeyPair = keyPair.value();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey(
+ const vector<uint8_t>& publicKey) {
+ readerPublicKey_ = publicKey;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::createAuthChallenge(
+ int64_t* outChallenge) {
+ optional<uint64_t> challenge = hwProxy_->createAuthChallenge();
+ if (!challenge) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error generating challenge"));
+ }
+ *outChallenge = challenge.value();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::setRequestedNamespaces(
+ const vector<RequestNamespace>& requestNamespaces) {
+ requestNamespaces_ = requestNamespaces;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::setVerificationToken(
+ const VerificationToken& verificationToken) {
+ verificationToken_ = verificationToken;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::startRetrieval(
+ const vector<SecureAccessControlProfile>& accessControlProfiles,
+ const HardwareAuthToken& authToken, const vector<uint8_t>& itemsRequest,
+ const vector<uint8_t>& signingKeyBlob,
+ const vector<uint8_t>& sessionTranscript,
+ const vector<uint8_t>& readerSignature,
+ const vector<int32_t>& requestCounts) {
+ std::unique_ptr<cppbor::Item> sessionTranscriptItem;
+ if (sessionTranscript.size() > 0) {
+ auto [item, _, message] = cppbor::parse(sessionTranscript);
+ if (item == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "SessionTranscript contains invalid CBOR"));
+ }
+ sessionTranscriptItem = std::move(item);
+ }
+ if (numStartRetrievalCalls_ > 0) {
+ if (sessionTranscript_ != sessionTranscript) {
+ LOG(ERROR) << "Session Transcript changed";
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
+ "Passed-in SessionTranscript doesn't match previously used "
+ "SessionTranscript"));
+ }
+ }
+ sessionTranscript_ = sessionTranscript;
+
+ // This resets various state in the TA...
+ if (!hwProxy_->startRetrieveEntries()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error starting retrieving entries"));
+ }
+
+ optional<vector<uint8_t>> signatureOfToBeSigned;
+ if (readerSignature.size() > 0) {
+ signatureOfToBeSigned = support::coseSignGetSignature(readerSignature);
+ if (!signatureOfToBeSigned) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Error extracting signatureOfToBeSigned from COSE_Sign1"));
+ }
+ }
+
+ // Feed the auth token to secure hardware only if they're valid.
+ if (authToken.timestamp.milliSeconds != 0) {
+ if (!hwProxy_->setAuthToken(
+ authToken.challenge, authToken.userId, authToken.authenticatorId,
+ int(authToken.authenticatorType), authToken.timestamp.milliSeconds,
+ authToken.mac, verificationToken_.challenge,
+ verificationToken_.timestamp.milliSeconds,
+ int(verificationToken_.securityLevel), verificationToken_.mac)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Invalid Auth Token"));
+ }
+ }
+
+ // We'll be feeding ACPs interleaved with certificates from the reader
+ // certificate chain...
+ vector<SecureAccessControlProfile> remainingAcps = accessControlProfiles;
+
+ // ... and we'll use those ACPs to build up a 32-bit mask indicating which
+ // of the possible 32 ACPs grants access.
+ uint32_t accessControlProfileMask = 0;
+
+ // If there is a signature, validate that it was made with the top-most key in
+ // the certificate chain embedded in the COSE_Sign1 structure.
+ optional<vector<uint8_t>> readerCertificateChain;
+ if (readerSignature.size() > 0) {
+ readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
+ if (!readerCertificateChain) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Unable to get reader certificate chain from COSE_Sign1"));
+ }
+
+ // First, feed all the reader certificates to the secure hardware. We start
+ // at the end..
+ optional<vector<vector<uint8_t>>> splitCerts =
+ support::certificateChainSplit(readerCertificateChain.value());
+ if (!splitCerts || splitCerts.value().size() == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Error splitting certificate chain from COSE_Sign1"));
+ }
+ for (ssize_t n = splitCerts.value().size() - 1; n >= 0; --n) {
+ const vector<uint8_t>& x509Cert = splitCerts.value()[n];
+ if (!hwProxy_->pushReaderCert(x509Cert)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ StringPrintf("Error validating reader certificate %zd", n)
+ .c_str()));
+ }
+
+ // If we have ACPs for that particular certificate, send them to the
+ // TA right now...
+ //
+ // Remember in this case certificate equality is done by comparing public
+ // keys, not bitwise comparison of the certificates.
+ //
+ optional<vector<uint8_t>> x509CertPubKey =
+ support::certificateChainGetTopMostKey(x509Cert);
+ if (!x509CertPubKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ StringPrintf("Error getting public key from reader certificate %zd",
+ n)
+ .c_str()));
+ }
+ vector<SecureAccessControlProfile>::iterator it = remainingAcps.begin();
+ while (it != remainingAcps.end()) {
+ const SecureAccessControlProfile& profile = *it;
+ if (profile.readerCertificate.encodedCertificate.size() == 0) {
+ ++it;
+ continue;
+ }
+ optional<vector<uint8_t>> profilePubKey =
+ support::certificateChainGetTopMostKey(
+ profile.readerCertificate.encodedCertificate);
+ if (!profilePubKey) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error getting public key from profile"));
+ }
+ if (profilePubKey.value() == x509CertPubKey.value()) {
+ optional<bool> res = hwProxy_->validateAccessControlProfile(
+ profile.id, profile.readerCertificate.encodedCertificate,
+ profile.userAuthenticationRequired, profile.timeoutMillis,
+ profile.secureUserId, profile.mac);
+ if (!res) {
+ return ndk::ScopedAStatus(
+ AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Error validating access control profile"));
+ }
+ if (res.value()) {
+ accessControlProfileMask |= (1 << profile.id);
+ }
+ it = remainingAcps.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ // ... then pass the request message and have the TA check it's signed by
+ // the key in last certificate we pushed.
+ if (sessionTranscript.size() > 0 && itemsRequest.size() > 0 &&
+ readerSignature.size() > 0) {
+ optional<vector<uint8_t>> tbsSignature =
+ support::coseSignGetSignature(readerSignature);
+ if (!tbsSignature) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Error extracting toBeSigned from COSE_Sign1"));
+ }
+ optional<int> coseSignAlg = support::coseSignGetAlg(readerSignature);
+ if (!coseSignAlg) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "Error extracting signature algorithm from COSE_Sign1"));
+ }
+ if (!hwProxy_->validateRequestMessage(sessionTranscript, itemsRequest,
+ coseSignAlg.value(),
+ tbsSignature.value())) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+ "readerMessage is not signed by top-level certificate"));
+ }
+ }
+ }
+
+ // Feed remaining access control profiles...
+ for (const SecureAccessControlProfile& profile : remainingAcps) {
+ optional<bool> res = hwProxy_->validateAccessControlProfile(
+ profile.id, profile.readerCertificate.encodedCertificate,
+ profile.userAuthenticationRequired, profile.timeoutMillis,
+ profile.secureUserId, profile.mac);
+ if (!res) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Error validating access control profile"));
+ }
+ if (res.value()) {
+ accessControlProfileMask |= (1 << profile.id);
+ }
+ }
+
+ // TODO: move this check to the TA
+#if 1
+ // To prevent replay-attacks, we check that the public part of the ephemeral
+ // key we previously created, is present in the DeviceEngagement part of
+ // SessionTranscript as a COSE_Key, in uncompressed form.
+ //
+ // We do this by just searching for the X and Y coordinates.
+ if (sessionTranscript.size() > 0) {
+ auto [getXYSuccess, ePubX, ePubY] =
+ support::ecPublicKeyGetXandY(ephemeralPublicKey_);
+ if (!getXYSuccess) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "Error extracting X and Y from ePub"));
+ }
+ if (sessionTranscript.size() > 0 &&
+ !(memmem(sessionTranscript.data(), sessionTranscript.size(),
+ ePubX.data(), ePubX.size()) != nullptr &&
+ memmem(sessionTranscript.data(), sessionTranscript.size(),
+ ePubY.data(), ePubY.size()) != nullptr)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+ "Did not find ephemeral public key's X and Y coordinates in "
+ "SessionTranscript (make sure leading zeroes are not used)"));
+ }
+ }
+#endif
+
+ // itemsRequest: If non-empty, contains request data that may be signed by the
+ // reader. The content can be defined in the way appropriate for the
+ // credential, but there are three requirements that must be met to work with
+ // this HAL:
+ if (itemsRequest.size() > 0) {
+ // 1. The content must be a CBOR-encoded structure.
+ auto [item, _, message] = cppbor::parse(itemsRequest);
+ if (item == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "Error decoding CBOR in itemsRequest"));
+ }
+
+ // 2. The CBOR structure must be a map.
+ const cppbor::Map* map = item->asMap();
+ if (map == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "itemsRequest is not a CBOR map"));
+ }
+
+ // 3. The map must contain a key "nameSpaces" whose value contains a map, as
+ // described in
+ // the example below.
+ //
+ // NameSpaces = {
+ // + NameSpace => DataElements ; Requested data elements for each
+ // NameSpace
+ // }
+ //
+ // NameSpace = tstr
+ //
+ // DataElements = {
+ // + DataElement => IntentToRetain
+ // }
+ //
+ // DataElement = tstr
+ // IntentToRetain = bool
+ //
+ // Here's an example of an |itemsRequest| CBOR value satisfying above
+ // requirements 1. through 3.:
+ //
+ // {
+ // 'docType' : 'org.iso.18013-5.2019',
+ // 'nameSpaces' : {
+ // 'org.iso.18013-5.2019' : {
+ // 'Last name' : false,
+ // 'Birth date' : false,
+ // 'First name' : false,
+ // 'Home address' : true
+ // },
+ // 'org.aamva.iso.18013-5.2019' : {
+ // 'Real Id' : false
+ // }
+ // }
+ // }
+ //
+ const cppbor::Map* nsMap = nullptr;
+ for (size_t n = 0; n < map->size(); n++) {
+ const auto& [keyItem, valueItem] = (*map)[n];
+ if (keyItem->type() == cppbor::TSTR &&
+ keyItem->asTstr()->value() == "nameSpaces" &&
+ valueItem->type() == cppbor::MAP) {
+ nsMap = valueItem->asMap();
+ break;
+ }
+ }
+ if (nsMap == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "No nameSpaces map in top-most map"));
+ }
+
+ for (size_t n = 0; n < nsMap->size(); n++) {
+ auto& [nsKeyItem, nsValueItem] = (*nsMap)[n];
+ const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
+ const cppbor::Map* nsInnerMap = nsValueItem->asMap();
+ if (nsKey == nullptr || nsInnerMap == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "Type mismatch in nameSpaces map"));
+ }
+ string requestedNamespace = nsKey->value();
+ set<string> requestedKeys;
+ for (size_t m = 0; m < nsInnerMap->size(); m++) {
+ const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
+ const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
+ const cppbor::Simple* simple = innerMapValueItem->asSimple();
+ const cppbor::Bool* intentToRetainItem =
+ (simple != nullptr) ? simple->asBool() : nullptr;
+ if (nameItem == nullptr || intentToRetainItem == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+ "Type mismatch in value in nameSpaces map"));
+ }
+ requestedKeys.insert(nameItem->value());
+ }
+ requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
+ }
+ }
+
+ deviceNameSpacesMap_ = cppbor::Map();
+ currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
+
+ requestCountsRemaining_ = requestCounts;
+ currentNameSpace_ = "";
+
+ itemsRequest_ = itemsRequest;
+ signingKeyBlob_ = signingKeyBlob;
+
+ // calculate the size of DeviceNameSpaces. We need to know it ahead of time.
+ calcDeviceNameSpacesSize(accessControlProfileMask);
+
+ // Count the number of non-empty namespaces
+ size_t numNamespacesWithValues = 0;
+ for (size_t n = 0; n < expectedNumEntriesPerNamespace_.size(); n++) {
+ if (expectedNumEntriesPerNamespace_[n] > 0) {
+ numNamespacesWithValues += 1;
+ }
+ }
+
+ // Finally, pass info so the HMAC key can be derived and the TA can start
+ // creating the DeviceNameSpaces CBOR...
+ if (sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0 &&
+ signingKeyBlob.size() > 0) {
+ // We expect the reader ephemeral public key to be same size and curve
+ // as the ephemeral key we generated (e.g. P-256 key), otherwise ECDH
+ // won't work. So its length should be 65 bytes and it should be
+ // starting with 0x04.
+ if (readerPublicKey_.size() != 65 || readerPublicKey_[0] != 0x04) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Reader public key is not in expected format"));
+ }
+ vector<uint8_t> pubKeyP256(readerPublicKey_.begin() + 1,
+ readerPublicKey_.end());
+ if (!hwProxy_->calcMacKey(sessionTranscript_, pubKeyP256, signingKeyBlob,
+ docType_, numNamespacesWithValues,
+ expectedDeviceNameSpacesSize_)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error starting retrieving entries"));
+ }
+ }
+
+ numStartRetrievalCalls_ += 1;
+ return ndk::ScopedAStatus::ok();
+}
+
+size_t cborNumBytesForLength(size_t length) {
+ if (length < 24) {
+ return 0;
+ } else if (length <= 0xff) {
+ return 1;
+ } else if (length <= 0xffff) {
+ return 2;
+ } else if (length <= 0xffffffff) {
+ return 4;
+ }
+ return 8;
+}
+
+size_t cborNumBytesForTstr(const string& value) {
+ return 1 + cborNumBytesForLength(value.size()) + value.size();
+}
+
+void IdentityCredential::calcDeviceNameSpacesSize(
+ uint32_t accessControlProfileMask) {
+ /*
+ * This is how DeviceNameSpaces is defined:
+ *
+ * DeviceNameSpaces = {
+ * * NameSpace => DeviceSignedItems
+ * }
+ * DeviceSignedItems = {
+ * + DataItemName => DataItemValue
+ * }
+ *
+ * Namespace = tstr
+ * DataItemName = tstr
+ * DataItemValue = any
+ *
+ * This function will calculate its length using knowledge of how CBOR is
+ * encoded.
+ */
+ size_t ret = 0;
+ vector<unsigned int> numEntriesPerNamespace;
+ for (const RequestNamespace& rns : requestNamespaces_) {
+ vector<RequestDataItem> itemsToInclude;
+
+ for (const RequestDataItem& rdi : rns.items) {
+ // If we have a CBOR request message, skip if item isn't in it
+ if (itemsRequest_.size() > 0) {
+ const auto& it = requestedNameSpacesAndNames_.find(rns.namespaceName);
+ if (it == requestedNameSpacesAndNames_.end()) {
+ continue;
+ }
+ const set<string>& dataItemNames = it->second;
+ if (dataItemNames.find(rdi.name) == dataItemNames.end()) {
+ continue;
+ }
+ }
+
+ // Access is granted if at least one of the profiles grants access.
+ //
+ // If an item is configured without any profiles, access is denied.
+ //
+ bool authorized = false;
+ for (auto id : rdi.accessControlProfileIds) {
+ if (accessControlProfileMask & (1 << id)) {
+ authorized = true;
+ break;
+ }
+ }
+ if (!authorized) {
+ continue;
+ }
+
+ itemsToInclude.push_back(rdi);
+ }
+
+ numEntriesPerNamespace.push_back(itemsToInclude.size());
+
+ // If no entries are to be in the namespace, we don't include it in
+ // the CBOR...
+ if (itemsToInclude.size() == 0) {
+ continue;
+ }
+
+ // Key: NameSpace
+ ret += cborNumBytesForTstr(rns.namespaceName);
+
+ // Value: Open the DeviceSignedItems map
+ ret += 1 + cborNumBytesForLength(itemsToInclude.size());
+
+ for (const RequestDataItem& item : itemsToInclude) {
+ // Key: DataItemName
+ ret += cborNumBytesForTstr(item.name);
+
+ // Value: DataItemValue - entryData.size is the length of serialized CBOR
+ // so we use that.
+ ret += item.size;
+ }
+ }
+
+ // Now that we know the number of namespaces with values, we know how many
+ // bytes the DeviceNamespaces map in the beginning is going to take up.
+ ret += 1 + cborNumBytesForLength(numEntriesPerNamespace.size());
+
+ expectedDeviceNameSpacesSize_ = ret;
+ expectedNumEntriesPerNamespace_ = numEntriesPerNamespace;
+}
+
+ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
+ const string& nameSpace, const string& name, int32_t entrySize,
+ const vector<int32_t>& accessControlProfileIds) {
+ if (name.empty()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty"));
+ }
+ if (nameSpace.empty()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Name space cannot be empty"));
+ }
+
+ if (requestCountsRemaining_.size() == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "No more name spaces left to go through"));
+ }
+
+ bool newNamespace;
+ if (currentNameSpace_ == "") {
+ // First call.
+ currentNameSpace_ = nameSpace;
+ newNamespace = true;
+ }
+
+ if (nameSpace == currentNameSpace_) {
+ // Same namespace.
+ if (requestCountsRemaining_[0] == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "No more entries to be retrieved in current name space"));
+ }
+ requestCountsRemaining_[0] -= 1;
+ } else {
+ // New namespace.
+ if (requestCountsRemaining_[0] != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Moved to new name space but one or more entries need to be "
+ "retrieved "
+ "in current name space"));
+ }
+ if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
+ deviceNameSpacesMap_.add(currentNameSpace_,
+ std::move(currentNameSpaceDeviceNameSpacesMap_));
+ }
+ currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
+
+ requestCountsRemaining_.erase(requestCountsRemaining_.begin());
+ currentNameSpace_ = nameSpace;
+ newNamespace = true;
+ }
+
+ // It's permissible to have an empty itemsRequest... but if non-empty you can
+ // only request what was specified in said itemsRequest. Enforce that.
+ if (itemsRequest_.size() > 0) {
+ const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
+ if (it == requestedNameSpacesAndNames_.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
+ "Name space was not requested in startRetrieval"));
+ }
+ const set<string>& dataItemNames = it->second;
+ if (dataItemNames.find(name) == dataItemNames.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
+ "Data item name in name space was not requested in startRetrieval"));
+ }
+ }
+
+ unsigned int newNamespaceNumEntries = 0;
+ if (newNamespace) {
+ if (expectedNumEntriesPerNamespace_.size() == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "No more populated name spaces left to go through"));
+ }
+ newNamespaceNumEntries = expectedNumEntriesPerNamespace_[0];
+ expectedNumEntriesPerNamespace_.erase(
+ expectedNumEntriesPerNamespace_.begin());
+ }
+
+ // Access control is enforced in the secure hardware.
+ //
+ // ... except for STATUS_NOT_IN_REQUEST_MESSAGE, that's handled above (TODO:
+ // consolidate).
+ //
+ AccessCheckResult res =
+ hwProxy_->startRetrieveEntryValue(nameSpace, name, newNamespaceNumEntries,
+ entrySize, accessControlProfileIds);
+ switch (res) {
+ case AccessCheckResult::kOk:
+ /* Do nothing. */
+ break;
+ case AccessCheckResult::kFailed:
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Access control check failed (failed)"));
+ break;
+ case AccessCheckResult::kNoAccessControlProfiles:
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES,
+ "Access control check failed (no access control profiles)"));
+ break;
+ case AccessCheckResult::kUserAuthenticationFailed:
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED,
+ "Access control check failed (user auth)"));
+ break;
+ case AccessCheckResult::kReaderAuthenticationFailed:
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED,
+ "Access control check failed (reader auth)"));
+ break;
+ }
+
+ currentName_ = name;
+ currentAccessControlProfileIds_ = accessControlProfileIds;
+ entryRemainingBytes_ = entrySize;
+ entryValue_.resize(0);
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(
+ const vector<uint8_t>& encryptedContent, vector<uint8_t>* outContent) {
+ optional<vector<uint8_t>> content = hwProxy_->retrieveEntryValue(
+ encryptedContent, currentNameSpace_, currentName_,
+ currentAccessControlProfileIds_);
+ if (!content) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Error decrypting data"));
+ }
+
+ size_t chunkSize = content.value().size();
+
+ if (chunkSize > entryRemainingBytes_) {
+ LOG(ERROR) << "Retrieved chunk of size " << chunkSize
+ << " is bigger than remaining space of size "
+ << entryRemainingBytes_;
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved chunk is bigger than remaining space"));
+ }
+
+ entryRemainingBytes_ -= chunkSize;
+ if (entryRemainingBytes_ > 0) {
+ if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved non-final chunk of size which isn't kGcmChunkSize"));
+ }
+ }
+
+ entryValue_.insert(entryValue_.end(), content.value().begin(),
+ content.value().end());
+
+ if (entryRemainingBytes_ == 0) {
+ auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
+ if (entryValueItem == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved data which is invalid CBOR"));
+ }
+ currentNameSpaceDeviceNameSpacesMap_.add(currentName_,
+ std::move(entryValueItem));
+ }
+
+ *outContent = content.value();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::finishRetrieval(
+ vector<uint8_t>* outMac, vector<uint8_t>* outDeviceNameSpaces) {
+ if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
+ deviceNameSpacesMap_.add(currentNameSpace_,
+ std::move(currentNameSpaceDeviceNameSpacesMap_));
+ }
+ vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
+
+ if (encodedDeviceNameSpaces.size() != expectedDeviceNameSpacesSize_) {
+ LOG(ERROR) << "encodedDeviceNameSpaces is "
+ << encodedDeviceNameSpaces.size() << " bytes, "
+ << "was expecting " << expectedDeviceNameSpacesSize_;
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ StringPrintf("Unexpected CBOR size %zd for encodedDeviceNameSpaces, "
+ "was expecting %zd",
+ encodedDeviceNameSpaces.size(),
+ expectedDeviceNameSpacesSize_)
+ .c_str()));
+ }
+
+ // If there's no signing key or no sessionTranscript or no reader ephemeral
+ // public key, we return the empty MAC.
+ optional<vector<uint8_t>> mac;
+ if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
+ readerPublicKey_.size() > 0) {
+ optional<vector<uint8_t>> digestToBeMaced = hwProxy_->finishRetrieval();
+ if (!digestToBeMaced || digestToBeMaced.value().size() != 32) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Error generating digestToBeMaced"));
+ }
+ // Now construct COSE_Mac0 from the returned MAC...
+ mac = support::coseMacWithDigest(digestToBeMaced.value(), {} /* data */);
+ }
+
+ *outMac = mac.value_or(vector<uint8_t>({}));
+ *outDeviceNameSpaces = encodedDeviceNameSpaces;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
+ vector<uint8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
+ time_t now = time(NULL);
+ optional<pair<vector<uint8_t>, vector<uint8_t>>> pair =
+ hwProxy_->generateSigningKeyPair(docType_, now);
+ if (!pair) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
+ }
+
+ *outSigningKeyCertificate = Certificate();
+ outSigningKeyCertificate->encodedCertificate = pair->first;
+
+ *outSigningKeyBlob = pair->second;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::updateCredential(
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
+ sp<SecureHardwareProvisioningProxy> hwProxy =
+ hwProxyFactory_->createProvisioningProxy();
+ shared_ptr<WritableIdentityCredential> wc =
+ ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType_,
+ testCredential_);
+ if (!wc->initializeForUpdate(encryptedCredentialKeys_)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error initializing WritableIdentityCredential for update"));
+ }
+ *outWritableCredential = wc;
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::identity
diff --git a/guest/hals/identity/common/IdentityCredential.h b/guest/hals/identity/common/IdentityCredential.h
new file mode 100644
index 000000000..aaf772a0a
--- /dev/null
+++ b/guest/hals/identity/common/IdentityCredential.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
+#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
+
+#include <aidl/android/hardware/identity/BnIdentityCredential.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <cppbor.h>
+
+#include "IdentityCredentialStore.h"
+#include "SecureHardwareProxy.h"
+
+namespace aidl::android::hardware::identity {
+
+using ::aidl::android::hardware::keymaster::HardwareAuthToken;
+using ::aidl::android::hardware::keymaster::VerificationToken;
+using ::android::sp;
+using ::android::hardware::identity::SecureHardwarePresentationProxy;
+using ::std::map;
+using ::std::set;
+using ::std::string;
+using ::std::vector;
+
+class IdentityCredential : public BnIdentityCredential {
+ public:
+ IdentityCredential(sp<SecureHardwareProxyFactory> hwProxyFactory,
+ sp<SecureHardwarePresentationProxy> hwProxy,
+ const vector<uint8_t>& credentialData)
+ : hwProxyFactory_(hwProxyFactory),
+ hwProxy_(hwProxy),
+ credentialData_(credentialData),
+ numStartRetrievalCalls_(0),
+ expectedDeviceNameSpacesSize_(0) {}
+
+ // Parses and decrypts credentialData_, return a status code from
+ // IIdentityCredentialStore. Must be called right after construction.
+ int initialize();
+
+ // Methods from IIdentityCredential follow.
+ ndk::ScopedAStatus deleteCredential(
+ vector<uint8_t>* outProofOfDeletionSignature) override;
+ ndk::ScopedAStatus deleteCredentialWithChallenge(
+ const vector<uint8_t>& challenge,
+ vector<uint8_t>* outProofOfDeletionSignature) override;
+ ndk::ScopedAStatus proveOwnership(
+ const vector<uint8_t>& challenge,
+ vector<uint8_t>* outProofOfOwnershipSignature) override;
+ ndk::ScopedAStatus createEphemeralKeyPair(
+ vector<uint8_t>* outKeyPair) override;
+ ndk::ScopedAStatus setReaderEphemeralPublicKey(
+ const vector<uint8_t>& publicKey) override;
+ ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override;
+ ndk::ScopedAStatus setRequestedNamespaces(
+ const vector<RequestNamespace>& requestNamespaces) override;
+ ndk::ScopedAStatus setVerificationToken(
+ const VerificationToken& verificationToken) override;
+ ndk::ScopedAStatus startRetrieval(
+ const vector<SecureAccessControlProfile>& accessControlProfiles,
+ const HardwareAuthToken& authToken, const vector<uint8_t>& itemsRequest,
+ const vector<uint8_t>& signingKeyBlob,
+ const vector<uint8_t>& sessionTranscript,
+ const vector<uint8_t>& readerSignature,
+ const vector<int32_t>& requestCounts) override;
+ ndk::ScopedAStatus startRetrieveEntryValue(
+ const string& nameSpace, const string& name, int32_t entrySize,
+ const vector<int32_t>& accessControlProfileIds) override;
+ ndk::ScopedAStatus retrieveEntryValue(const vector<uint8_t>& encryptedContent,
+ vector<uint8_t>* outContent) override;
+ ndk::ScopedAStatus finishRetrieval(
+ vector<uint8_t>* outMac, vector<uint8_t>* outDeviceNameSpaces) override;
+ ndk::ScopedAStatus generateSigningKeyPair(
+ vector<uint8_t>* outSigningKeyBlob,
+ Certificate* outSigningKeyCertificate) override;
+
+ ndk::ScopedAStatus updateCredential(
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) override;
+
+ private:
+ ndk::ScopedAStatus deleteCredentialCommon(
+ const vector<uint8_t>& challenge, bool includeChallenge,
+ vector<uint8_t>* outProofOfDeletionSignature);
+
+ // Set by constructor
+ sp<SecureHardwareProxyFactory> hwProxyFactory_;
+ sp<SecureHardwarePresentationProxy> hwProxy_;
+ vector<uint8_t> credentialData_;
+ int numStartRetrievalCalls_;
+
+ // Set by initialize()
+ string docType_;
+ bool testCredential_;
+ vector<uint8_t> encryptedCredentialKeys_;
+
+ // Set by createEphemeralKeyPair()
+ vector<uint8_t> ephemeralPublicKey_;
+
+ // Set by setReaderEphemeralPublicKey()
+ vector<uint8_t> readerPublicKey_;
+
+ // Set by setRequestedNamespaces()
+ vector<RequestNamespace> requestNamespaces_;
+
+ // Set by setVerificationToken().
+ VerificationToken verificationToken_;
+
+ // Set at startRetrieval() time.
+ vector<uint8_t> signingKeyBlob_;
+ vector<uint8_t> sessionTranscript_;
+ vector<uint8_t> itemsRequest_;
+ vector<int32_t> requestCountsRemaining_;
+ map<string, set<string>> requestedNameSpacesAndNames_;
+ cppbor::Map deviceNameSpacesMap_;
+ cppbor::Map currentNameSpaceDeviceNameSpacesMap_;
+
+ // Calculated at startRetrieval() time.
+ size_t expectedDeviceNameSpacesSize_;
+ vector<unsigned int> expectedNumEntriesPerNamespace_;
+
+ // Set at startRetrieveEntryValue() time.
+ string currentNameSpace_;
+ string currentName_;
+ vector<int32_t> currentAccessControlProfileIds_;
+ size_t entryRemainingBytes_;
+ vector<uint8_t> entryValue_;
+
+ void calcDeviceNameSpacesSize(uint32_t accessControlProfileMask);
+};
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
diff --git a/guest/hals/identity/common/IdentityCredentialStore.cpp b/guest/hals/identity/common/IdentityCredentialStore.cpp
new file mode 100644
index 000000000..0847c0cee
--- /dev/null
+++ b/guest/hals/identity/common/IdentityCredentialStore.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IdentityCredentialStore"
+
+#include <android-base/logging.h>
+
+#include "IdentityCredential.h"
+#include "IdentityCredentialStore.h"
+#include "WritableIdentityCredential.h"
+
+namespace aidl::android::hardware::identity {
+
+using ::aidl::android::hardware::security::keymint::
+ IRemotelyProvisionedComponent;
+
+ndk::ScopedAStatus IdentityCredentialStore::getHardwareInformation(
+ HardwareInformation* hardwareInformation) {
+ HardwareInformation hw;
+ hw.credentialStoreName =
+ "Identity Credential Cuttlefish Remote Implementation";
+ hw.credentialStoreAuthorName = "Google";
+ hw.dataChunkSize = kGcmChunkSize;
+ hw.isDirectAccess = false;
+ hw.supportedDocTypes = {};
+ *hardwareInformation = hw;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredentialStore::createCredential(
+ const string& docType, bool testCredential,
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
+ sp<SecureHardwareProvisioningProxy> hwProxy =
+ hwProxyFactory_->createProvisioningProxy();
+ shared_ptr<WritableIdentityCredential> wc =
+ ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType,
+ testCredential);
+ if (!wc->initialize()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error initializing WritableIdentityCredential"));
+ }
+ *outWritableCredential = wc;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredentialStore::getCredential(
+ CipherSuite cipherSuite, const vector<uint8_t>& credentialData,
+ shared_ptr<IIdentityCredential>* outCredential) {
+ // We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right
+ // now.
+ if (cipherSuite !=
+ CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED,
+ "Unsupported cipher suite"));
+ }
+
+ sp<SecureHardwarePresentationProxy> hwProxy =
+ hwProxyFactory_->createPresentationProxy();
+ shared_ptr<IdentityCredential> credential =
+ ndk::SharedRefBase::make<IdentityCredential>(hwProxyFactory_, hwProxy,
+ credentialData);
+ auto ret = credential->initialize();
+ if (ret != IIdentityCredentialStore::STATUS_OK) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ int(ret), "Error initializing IdentityCredential"));
+ }
+ *outCredential = credential;
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::identity
diff --git a/guest/hals/identity/common/IdentityCredentialStore.h b/guest/hals/identity/common/IdentityCredentialStore.h
new file mode 100644
index 000000000..1d65b2c66
--- /dev/null
+++ b/guest/hals/identity/common/IdentityCredentialStore.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
+#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
+
+#include <aidl/android/hardware/identity/BnIdentityCredentialStore.h>
+#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+
+#include "SecureHardwareProxy.h"
+
+namespace aidl::android::hardware::identity {
+
+using ::android::sp;
+using ::android::hardware::identity::SecureHardwareProxyFactory;
+using ::std::shared_ptr;
+using ::std::string;
+using ::std::vector;
+
+class IdentityCredentialStore : public BnIdentityCredentialStore {
+ public:
+ IdentityCredentialStore(sp<SecureHardwareProxyFactory> hwProxyFactory)
+ : hwProxyFactory_(hwProxyFactory) {}
+
+ // The GCM chunk size used by this implementation is 64 KiB.
+ static constexpr size_t kGcmChunkSize = 64 * 1024;
+
+ // Methods from IIdentityCredentialStore follow.
+ ndk::ScopedAStatus getHardwareInformation(
+ HardwareInformation* hardwareInformation) override;
+
+ ndk::ScopedAStatus createCredential(
+ const string& docType, bool testCredential,
+ shared_ptr<IWritableIdentityCredential>* outWritableCredential) override;
+
+ ndk::ScopedAStatus getCredential(
+ CipherSuite cipherSuite, const vector<uint8_t>& credentialData,
+ shared_ptr<IIdentityCredential>* outCredential) override;
+
+ private:
+ sp<SecureHardwareProxyFactory> hwProxyFactory_;
+};
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
diff --git a/guest/hals/identity/common/SecureHardwareProxy.h b/guest/hals/identity/common/SecureHardwareProxy.h
new file mode 100644
index 000000000..bd252a706
--- /dev/null
+++ b/guest/hals/identity/common/SecureHardwareProxy.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_SECUREHARDWAREPROXY_H
+#define ANDROID_HARDWARE_IDENTITY_SECUREHARDWAREPROXY_H
+
+#include <utils/RefBase.h>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::identity {
+
+using ::android::RefBase;
+using ::std::optional;
+using ::std::pair;
+using ::std::string;
+using ::std::vector;
+
+// These classes are used to communicate with Secure Hardware. They mimic the
+// API in libEmbeddedIC 1:1 (except for using C++ types) as each call is
+// intended to be forwarded to the Secure Hardware.
+//
+// Instances are instantiated when a provisioning or presentation session
+// starts. When the session is complete, the shutdown() method is called.
+//
+
+// Forward declare.
+//
+class SecureHardwareProvisioningProxy;
+class SecureHardwarePresentationProxy;
+
+// This is a class used to create proxies.
+//
+class SecureHardwareProxyFactory : public RefBase {
+ public:
+ SecureHardwareProxyFactory() {}
+ virtual ~SecureHardwareProxyFactory() {}
+
+ virtual sp<SecureHardwareProvisioningProxy> createProvisioningProxy() = 0;
+ virtual sp<SecureHardwarePresentationProxy> createPresentationProxy() = 0;
+};
+
+// The proxy used for provisioning.
+//
+class SecureHardwareProvisioningProxy : public RefBase {
+ public:
+ SecureHardwareProvisioningProxy() {}
+ virtual ~SecureHardwareProvisioningProxy() {}
+
+ virtual bool initialize(bool testCredential) = 0;
+
+ virtual bool initializeForUpdate(bool testCredential, string docType,
+ vector<uint8_t> encryptedCredentialKeys) = 0;
+
+ // Returns public key certificate chain with attestation.
+ //
+ // This must return an entire certificate chain and its implementation must
+ // be coordinated with the implementation of eicOpsCreateCredentialKey() on
+ // the TA side (which may return just a single certificate or the entire
+ // chain).
+ virtual optional<vector<uint8_t>> createCredentialKey(
+ const vector<uint8_t>& challenge,
+ const vector<uint8_t>& applicationId) = 0;
+
+ virtual bool startPersonalization(int accessControlProfileCount,
+ vector<int> entryCounts,
+ const string& docType,
+ size_t expectedProofOfProvisioningSize) = 0;
+
+ // Returns MAC (28 bytes).
+ virtual optional<vector<uint8_t>> addAccessControlProfile(
+ int id, const vector<uint8_t>& readerCertificate,
+ bool userAuthenticationRequired, uint64_t timeoutMillis,
+ uint64_t secureUserId) = 0;
+
+ virtual bool beginAddEntry(const vector<int>& accessControlProfileIds,
+ const string& nameSpace, const string& name,
+ uint64_t entrySize) = 0;
+
+ // Returns encryptedContent.
+ virtual optional<vector<uint8_t>> addEntryValue(
+ const vector<int>& accessControlProfileIds, const string& nameSpace,
+ const string& name, const vector<uint8_t>& content) = 0;
+
+ // Returns signatureOfToBeSigned (EIC_ECDSA_P256_SIGNATURE_SIZE bytes).
+ virtual optional<vector<uint8_t>> finishAddingEntries() = 0;
+
+ // Returns encryptedCredentialKeys (80 bytes).
+ virtual optional<vector<uint8_t>> finishGetCredentialData(
+ const string& docType) = 0;
+
+ virtual bool shutdown() = 0;
+};
+
+enum AccessCheckResult {
+ kOk,
+ kFailed,
+ kNoAccessControlProfiles,
+ kUserAuthenticationFailed,
+ kReaderAuthenticationFailed,
+};
+
+// The proxy used for presentation.
+//
+class SecureHardwarePresentationProxy : public RefBase {
+ public:
+ SecureHardwarePresentationProxy() {}
+ virtual ~SecureHardwarePresentationProxy() {}
+
+ virtual bool initialize(bool testCredential, string docType,
+ vector<uint8_t> encryptedCredentialKeys) = 0;
+
+ // Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
+ virtual optional<pair<vector<uint8_t>, vector<uint8_t>>>
+ generateSigningKeyPair(string docType, time_t now) = 0;
+
+ // Returns private key
+ virtual optional<vector<uint8_t>> createEphemeralKeyPair() = 0;
+
+ virtual optional<uint64_t> createAuthChallenge() = 0;
+
+ virtual bool startRetrieveEntries() = 0;
+
+ virtual bool setAuthToken(uint64_t challenge, uint64_t secureUserId,
+ uint64_t authenticatorId,
+ int hardwareAuthenticatorType, uint64_t timeStamp,
+ const vector<uint8_t>& mac,
+ uint64_t verificationTokenChallenge,
+ uint64_t verificationTokenTimestamp,
+ int verificationTokenSecurityLevel,
+ const vector<uint8_t>& verificationTokenMac) = 0;
+
+ virtual bool pushReaderCert(const vector<uint8_t>& certX509) = 0;
+
+ virtual optional<bool> validateAccessControlProfile(
+ int id, const vector<uint8_t>& readerCertificate,
+ bool userAuthenticationRequired, int timeoutMillis, uint64_t secureUserId,
+ const vector<uint8_t>& mac) = 0;
+
+ virtual bool validateRequestMessage(
+ const vector<uint8_t>& sessionTranscript,
+ const vector<uint8_t>& requestMessage, int coseSignAlg,
+ const vector<uint8_t>& readerSignatureOfToBeSigned) = 0;
+
+ virtual bool calcMacKey(const vector<uint8_t>& sessionTranscript,
+ const vector<uint8_t>& readerEphemeralPublicKey,
+ const vector<uint8_t>& signingKeyBlob,
+ const string& docType,
+ unsigned int numNamespacesWithValues,
+ size_t expectedProofOfProvisioningSize) = 0;
+
+ virtual AccessCheckResult startRetrieveEntryValue(
+ const string& nameSpace, const string& name,
+ unsigned int newNamespaceNumEntries, int32_t entrySize,
+ const vector<int32_t>& accessControlProfileIds) = 0;
+
+ virtual optional<vector<uint8_t>> retrieveEntryValue(
+ const vector<uint8_t>& encryptedContent, const string& nameSpace,
+ const string& name, const vector<int32_t>& accessControlProfileIds) = 0;
+
+ virtual optional<vector<uint8_t>> finishRetrieval();
+
+ virtual optional<vector<uint8_t>> deleteCredential(
+ const string& docType, const vector<uint8_t>& challenge,
+ bool includeChallenge, size_t proofOfDeletionCborSize) = 0;
+
+ virtual optional<vector<uint8_t>> proveOwnership(
+ const string& docType, bool testCredential,
+ const vector<uint8_t>& challenge, size_t proofOfOwnershipCborSize) = 0;
+
+ virtual bool shutdown() = 0;
+};
+
+} // namespace android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_SECUREHARDWAREPROXY_H
diff --git a/guest/hals/identity/common/WritableIdentityCredential.cpp b/guest/hals/identity/common/WritableIdentityCredential.cpp
new file mode 100644
index 000000000..13ee24444
--- /dev/null
+++ b/guest/hals/identity/common/WritableIdentityCredential.cpp
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "WritableIdentityCredential"
+
+#include "WritableIdentityCredential.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+#include <utility>
+
+#include "IdentityCredentialStore.h"
+
+#include "SecureHardwareProxy.h"
+
+namespace aidl::android::hardware::identity {
+
+using ::android::base::StringPrintf;
+using ::std::optional;
+using namespace ::android::hardware::identity;
+
+bool WritableIdentityCredential::initialize() {
+ if (!hwProxy_->initialize(testCredential_)) {
+ LOG(ERROR) << "hwProxy->initialize() failed";
+ return false;
+ }
+ startPersonalizationCalled_ = false;
+ firstEntry_ = true;
+
+ return true;
+}
+
+// Used when updating a credential. Returns false on failure.
+bool WritableIdentityCredential::initializeForUpdate(
+ const vector<uint8_t>& encryptedCredentialKeys) {
+ if (!hwProxy_->initializeForUpdate(testCredential_, docType_,
+ encryptedCredentialKeys)) {
+ LOG(ERROR) << "hwProxy->initializeForUpdate() failed";
+ return false;
+ }
+ startPersonalizationCalled_ = false;
+ firstEntry_ = true;
+
+ return true;
+}
+
+WritableIdentityCredential::~WritableIdentityCredential() {}
+
+ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
+ const vector<uint8_t>& attestationApplicationId,
+ const vector<uint8_t>& attestationChallenge,
+ vector<Certificate>* outCertificateChain) {
+ if (getAttestationCertificateAlreadyCalled_) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error attestation certificate previously generated"));
+ }
+ getAttestationCertificateAlreadyCalled_ = true;
+
+ if (attestationChallenge.empty()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Challenge can not be empty"));
+ }
+
+ optional<vector<uint8_t>> certChain = hwProxy_->createCredentialKey(
+ attestationChallenge, attestationApplicationId);
+ if (!certChain) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error generating attestation certificate chain"));
+ }
+
+ optional<vector<vector<uint8_t>>> certs =
+ support::certificateChainSplit(certChain.value());
+ if (!certs) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error splitting chain into separate certificates"));
+ }
+
+ *outCertificateChain = vector<Certificate>();
+ for (const vector<uint8_t>& cert : certs.value()) {
+ Certificate c = Certificate();
+ c.encodedCertificate = cert;
+ outCertificateChain->push_back(std::move(c));
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus
+WritableIdentityCredential::setExpectedProofOfProvisioningSize(
+ int32_t expectedProofOfProvisioningSize) {
+ expectedProofOfProvisioningSize_ = expectedProofOfProvisioningSize;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
+ int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
+ if (startPersonalizationCalled_) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "startPersonalization called already"));
+ }
+ startPersonalizationCalled_ = true;
+
+ numAccessControlProfileRemaining_ = accessControlProfileCount;
+ remainingEntryCounts_ = entryCounts;
+ entryNameSpace_ = "";
+
+ signedDataAccessControlProfiles_ = cppbor::Array();
+ signedDataNamespaces_ = cppbor::Map();
+ signedDataCurrentNamespace_ = cppbor::Array();
+
+ if (!hwProxy_->startPersonalization(accessControlProfileCount, entryCounts,
+ docType_,
+ expectedProofOfProvisioningSize_)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "eicStartPersonalization"));
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
+ int32_t id, const Certificate& readerCertificate,
+ bool userAuthenticationRequired, int64_t timeoutMillis,
+ int64_t secureUserId,
+ SecureAccessControlProfile* outSecureAccessControlProfile) {
+ if (numAccessControlProfileRemaining_ == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
+ }
+
+ if (accessControlProfileIds_.find(id) != accessControlProfileIds_.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Access Control Profile id must be unique"));
+ }
+ accessControlProfileIds_.insert(id);
+
+ if (id < 0 || id >= 32) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Access Control Profile id must be non-negative and less than 32"));
+ }
+
+ // Spec requires if |userAuthenticationRequired| is false, then
+ // |timeoutMillis| must also be zero.
+ if (!userAuthenticationRequired && timeoutMillis != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "userAuthenticationRequired is false but timeout is non-zero"));
+ }
+
+ optional<vector<uint8_t>> mac = hwProxy_->addAccessControlProfile(
+ id, readerCertificate.encodedCertificate, userAuthenticationRequired,
+ timeoutMillis, secureUserId);
+ if (!mac) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "eicAddAccessControlProfile"));
+ }
+
+ SecureAccessControlProfile profile;
+ profile.id = id;
+ profile.readerCertificate = readerCertificate;
+ profile.userAuthenticationRequired = userAuthenticationRequired;
+ profile.timeoutMillis = timeoutMillis;
+ profile.secureUserId = secureUserId;
+ profile.mac = mac.value();
+ cppbor::Map profileMap;
+ profileMap.add("id", profile.id);
+ if (profile.readerCertificate.encodedCertificate.size() > 0) {
+ profileMap.add("readerCertificate",
+ cppbor::Bstr(profile.readerCertificate.encodedCertificate));
+ }
+ if (profile.userAuthenticationRequired) {
+ profileMap.add("userAuthenticationRequired",
+ profile.userAuthenticationRequired);
+ profileMap.add("timeoutMillis", profile.timeoutMillis);
+ }
+ signedDataAccessControlProfiles_.add(std::move(profileMap));
+
+ numAccessControlProfileRemaining_--;
+
+ *outSecureAccessControlProfile = profile;
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
+ const vector<int32_t>& accessControlProfileIds, const string& nameSpace,
+ const string& name, int32_t entrySize) {
+ if (numAccessControlProfileRemaining_ != 0) {
+ LOG(ERROR) << "numAccessControlProfileRemaining_ is "
+ << numAccessControlProfileRemaining_ << " and expected zero";
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "numAccessControlProfileRemaining_ is not zero"));
+ }
+
+ // Ensure passed-in profile ids reference valid access control profiles
+ for (const int32_t id : accessControlProfileIds) {
+ if (accessControlProfileIds_.find(id) == accessControlProfileIds_.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "An id in accessControlProfileIds references non-existing ACP"));
+ }
+ }
+
+ if (remainingEntryCounts_.size() == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "No more namespaces to add to"));
+ }
+
+ // Handle initial beginEntry() call.
+ if (firstEntry_) {
+ firstEntry_ = false;
+ entryNameSpace_ = nameSpace;
+ allNameSpaces_.insert(nameSpace);
+ }
+
+ // If the namespace changed...
+ if (nameSpace != entryNameSpace_) {
+ if (allNameSpaces_.find(nameSpace) != allNameSpaces_.end()) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Name space cannot be added in interleaving fashion"));
+ }
+
+ // Then check that all entries in the previous namespace have been added..
+ if (remainingEntryCounts_[0] != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "New namespace but a non-zero number of entries remain to be added"));
+ }
+ remainingEntryCounts_.erase(remainingEntryCounts_.begin());
+ remainingEntryCounts_[0] -= 1;
+ allNameSpaces_.insert(nameSpace);
+
+ if (signedDataCurrentNamespace_.size() > 0) {
+ signedDataNamespaces_.add(entryNameSpace_,
+ std::move(signedDataCurrentNamespace_));
+ signedDataCurrentNamespace_ = cppbor::Array();
+ }
+ } else {
+ // Same namespace...
+ if (remainingEntryCounts_[0] == 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Same namespace but no entries remain to be added"));
+ }
+ remainingEntryCounts_[0] -= 1;
+ }
+
+ entryRemainingBytes_ = entrySize;
+ entryNameSpace_ = nameSpace;
+ entryName_ = name;
+ entryAccessControlProfileIds_ = accessControlProfileIds;
+ entryBytes_.resize(0);
+ // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
+
+ if (!hwProxy_->beginAddEntry(accessControlProfileIds, nameSpace, name,
+ entrySize)) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "eicBeginAddEntry"));
+ }
+
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(
+ const vector<uint8_t>& content, vector<uint8_t>* outEncryptedContent) {
+ size_t contentSize = content.size();
+
+ if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Passed in chunk of is bigger than kGcmChunkSize"));
+ }
+ if (contentSize > entryRemainingBytes_) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Passed in chunk is bigger than remaining space"));
+ }
+
+ entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
+ entryRemainingBytes_ -= contentSize;
+ if (entryRemainingBytes_ > 0) {
+ if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Retrieved non-final chunk which isn't kGcmChunkSize"));
+ }
+ }
+
+ optional<vector<uint8_t>> encryptedContent = hwProxy_->addEntryValue(
+ entryAccessControlProfileIds_, entryNameSpace_, entryName_, content);
+ if (!encryptedContent) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "eicAddEntryValue"));
+ }
+
+ if (entryRemainingBytes_ == 0) {
+ // TODO: ideally do do this without parsing the data (but still validate
+ // data is valid CBOR).
+ auto [item, _, message] = cppbor::parse(entryBytes_);
+ if (item == nullptr) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "Data is not valid CBOR"));
+ }
+ cppbor::Map entryMap;
+ entryMap.add("name", entryName_);
+ entryMap.add("value", std::move(item));
+ cppbor::Array profileIdArray;
+ for (auto id : entryAccessControlProfileIds_) {
+ profileIdArray.add(id);
+ }
+ entryMap.add("accessControlProfiles", std::move(profileIdArray));
+ signedDataCurrentNamespace_.add(std::move(entryMap));
+ }
+
+ *outEncryptedContent = encryptedContent.value();
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
+ vector<uint8_t>* outCredentialData,
+ vector<uint8_t>* outProofOfProvisioningSignature) {
+ if (numAccessControlProfileRemaining_ != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "numAccessControlProfileRemaining_ is not 0 and expected zero"));
+ }
+
+ if (remainingEntryCounts_.size() > 1 || remainingEntryCounts_[0] != 0) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ "More entry spaces remain than startPersonalization configured"));
+ }
+
+ if (signedDataCurrentNamespace_.size() > 0) {
+ signedDataNamespaces_.add(entryNameSpace_,
+ std::move(signedDataCurrentNamespace_));
+ }
+ cppbor::Array popArray;
+ popArray.add("ProofOfProvisioning")
+ .add(docType_)
+ .add(std::move(signedDataAccessControlProfiles_))
+ .add(std::move(signedDataNamespaces_))
+ .add(testCredential_);
+ vector<uint8_t> encodedCbor = popArray.encode();
+
+ if (encodedCbor.size() != expectedProofOfProvisioningSize_) {
+ LOG(ERROR) << "CBOR for proofOfProvisioning is " << encodedCbor.size()
+ << " bytes, "
+ << "was expecting " << expectedProofOfProvisioningSize_;
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_INVALID_DATA,
+ StringPrintf("Unexpected CBOR size %zd for proofOfProvisioning, was "
+ "expecting %zd",
+ encodedCbor.size(), expectedProofOfProvisioningSize_)
+ .c_str()));
+ }
+
+ optional<vector<uint8_t>> signatureOfToBeSigned =
+ hwProxy_->finishAddingEntries();
+ if (!signatureOfToBeSigned) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "eicFinishAddingEntries"));
+ }
+
+ optional<vector<uint8_t>> signature =
+ support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
+ encodedCbor, // data
+ {}); // certificateChain
+ if (!signature) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
+ }
+
+ optional<vector<uint8_t>> encryptedCredentialKeys =
+ hwProxy_->finishGetCredentialData(docType_);
+ if (!encryptedCredentialKeys) {
+ return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+ IIdentityCredentialStore::STATUS_FAILED,
+ "Error generating encrypted CredentialKeys"));
+ }
+ cppbor::Array array;
+ array.add(docType_);
+ array.add(testCredential_);
+ array.add(encryptedCredentialKeys.value());
+ vector<uint8_t> credentialData = array.encode();
+
+ *outCredentialData = credentialData;
+ *outProofOfProvisioningSignature = signature.value();
+ hwProxy_->shutdown();
+
+ return ndk::ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::identity
diff --git a/guest/hals/identity/common/WritableIdentityCredential.h b/guest/hals/identity/common/WritableIdentityCredential.h
new file mode 100644
index 000000000..47a22adbe
--- /dev/null
+++ b/guest/hals/identity/common/WritableIdentityCredential.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
+#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
+
+#include <aidl/android/hardware/identity/BnWritableIdentityCredential.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <cppbor.h>
+#include <set>
+
+#include "IdentityCredentialStore.h"
+#include "SecureHardwareProxy.h"
+
+namespace aidl::android::hardware::identity {
+
+using ::android::sp;
+using ::android::hardware::identity::SecureHardwareProvisioningProxy;
+using ::std::set;
+using ::std::string;
+using ::std::vector;
+
+class WritableIdentityCredential : public BnWritableIdentityCredential {
+ public:
+ // For a new credential, call initialize() right after construction.
+ //
+ // For an updated credential, call initializeForUpdate() right after
+ // construction.
+ //
+ WritableIdentityCredential(sp<SecureHardwareProvisioningProxy> hwProxy,
+ const string& docType, bool testCredential)
+ : hwProxy_(hwProxy), docType_(docType), testCredential_(testCredential) {}
+
+ ~WritableIdentityCredential();
+
+ // Creates the Credential Key. Returns false on failure.
+ bool initialize();
+
+ // Used when updating a credential. Returns false on failure.
+ bool initializeForUpdate(const vector<uint8_t>& encryptedCredentialKeys);
+
+ // Methods from IWritableIdentityCredential follow.
+ ndk::ScopedAStatus getAttestationCertificate(
+ const vector<uint8_t>& attestationApplicationId,
+ const vector<uint8_t>& attestationChallenge,
+ vector<Certificate>* outCertificateChain) override;
+
+ ndk::ScopedAStatus setExpectedProofOfProvisioningSize(
+ int32_t expectedProofOfProvisioningSize) override;
+
+ ndk::ScopedAStatus startPersonalization(
+ int32_t accessControlProfileCount,
+ const vector<int32_t>& entryCounts) override;
+
+ ndk::ScopedAStatus addAccessControlProfile(
+ int32_t id, const Certificate& readerCertificate,
+ bool userAuthenticationRequired, int64_t timeoutMillis,
+ int64_t secureUserId,
+ SecureAccessControlProfile* outSecureAccessControlProfile) override;
+
+ ndk::ScopedAStatus beginAddEntry(
+ const vector<int32_t>& accessControlProfileIds, const string& nameSpace,
+ const string& name, int32_t entrySize) override;
+ ndk::ScopedAStatus addEntryValue(
+ const vector<uint8_t>& content,
+ vector<uint8_t>* outEncryptedContent) override;
+
+ ndk::ScopedAStatus finishAddingEntries(
+ vector<uint8_t>* outCredentialData,
+ vector<uint8_t>* outProofOfProvisioningSignature) override;
+
+ private:
+ // Set by constructor.
+ sp<SecureHardwareProvisioningProxy> hwProxy_;
+ string docType_;
+ bool testCredential_;
+
+ // This is set in initialize().
+ bool startPersonalizationCalled_;
+ bool firstEntry_;
+
+ // This is set in getAttestationCertificate().
+ bool getAttestationCertificateAlreadyCalled_ = false;
+
+ // These fields are initialized during startPersonalization()
+ size_t numAccessControlProfileRemaining_;
+ vector<int32_t> remainingEntryCounts_;
+ cppbor::Array signedDataAccessControlProfiles_;
+ cppbor::Map signedDataNamespaces_;
+ cppbor::Array signedDataCurrentNamespace_;
+ size_t expectedProofOfProvisioningSize_;
+
+ // This field is initialized in addAccessControlProfile
+ set<int32_t> accessControlProfileIds_;
+
+ // These fields are initialized during beginAddEntry()
+ size_t entryRemainingBytes_;
+ string entryNameSpace_;
+ string entryName_;
+ vector<int32_t> entryAccessControlProfileIds_;
+ vector<uint8_t> entryBytes_;
+ set<string> allNameSpaces_;
+};
+
+} // namespace aidl::android::hardware::identity
+
+#endif // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
diff --git a/guest/hals/identity/libeic/EicCbor.c b/guest/hals/identity/libeic/EicCbor.c
new file mode 100644
index 000000000..b49634258
--- /dev/null
+++ b/guest/hals/identity/libeic/EicCbor.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#include "EicCbor.h"
+
+void eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize) {
+ eicMemSet(cbor, '\0', sizeof(EicCbor));
+ cbor->size = 0;
+ cbor->bufferSize = bufferSize;
+ cbor->buffer = buffer;
+ cbor->digestType = EIC_CBOR_DIGEST_TYPE_SHA256;
+ eicOpsSha256Init(&cbor->digester.sha256);
+}
+
+void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
+ const uint8_t* hmacKey, size_t hmacKeySize) {
+ eicMemSet(cbor, '\0', sizeof(EicCbor));
+ cbor->size = 0;
+ cbor->bufferSize = bufferSize;
+ cbor->buffer = buffer;
+ cbor->digestType = EIC_CBOR_DIGEST_TYPE_HMAC_SHA256;
+ eicOpsHmacSha256Init(&cbor->digester.hmacSha256, hmacKey, hmacKeySize);
+}
+
+void eicCborEnableSecondaryDigesterSha256(EicCbor* cbor, EicSha256Ctx* sha256) {
+ cbor->secondaryDigesterSha256 = sha256;
+}
+
+void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
+ switch (cbor->digestType) {
+ case EIC_CBOR_DIGEST_TYPE_SHA256:
+ eicOpsSha256Final(&cbor->digester.sha256, digest);
+ break;
+ case EIC_CBOR_DIGEST_TYPE_HMAC_SHA256:
+ eicOpsHmacSha256Final(&cbor->digester.hmacSha256, digest);
+ break;
+ }
+}
+
+void eicCborAppend(EicCbor* cbor, const uint8_t* data, size_t size) {
+ switch (cbor->digestType) {
+ case EIC_CBOR_DIGEST_TYPE_SHA256:
+ eicOpsSha256Update(&cbor->digester.sha256, data, size);
+ break;
+ case EIC_CBOR_DIGEST_TYPE_HMAC_SHA256:
+ eicOpsHmacSha256Update(&cbor->digester.hmacSha256, data, size);
+ break;
+ }
+ if (cbor->secondaryDigesterSha256 != NULL) {
+ eicOpsSha256Update(cbor->secondaryDigesterSha256, data, size);
+ }
+
+ if (cbor->size >= cbor->bufferSize) {
+ cbor->size += size;
+ return;
+ }
+
+ size_t numBytesLeft = cbor->bufferSize - cbor->size;
+ size_t numBytesToCopy = size;
+ if (numBytesToCopy > numBytesLeft) {
+ numBytesToCopy = numBytesLeft;
+ }
+ eicMemCpy(cbor->buffer + cbor->size, data, numBytesToCopy);
+
+ cbor->size += size;
+}
+
+size_t eicCborAdditionalLengthBytesFor(size_t size) {
+ if (size < 24) {
+ return 0;
+ } else if (size <= 0xff) {
+ return 1;
+ } else if (size <= 0xffff) {
+ return 2;
+ } else if (size <= 0xffffffff) {
+ return 4;
+ }
+ return 8;
+}
+
+void eicCborBegin(EicCbor* cbor, int majorType, uint64_t size) {
+ uint8_t data[9];
+
+ if (size < 24) {
+ data[0] = (majorType << 5) | size;
+ eicCborAppend(cbor, data, 1);
+ } else if (size <= 0xff) {
+ data[0] = (majorType << 5) | 24;
+ data[1] = size;
+ eicCborAppend(cbor, data, 2);
+ } else if (size <= 0xffff) {
+ data[0] = (majorType << 5) | 25;
+ data[1] = size >> 8;
+ data[2] = size & 0xff;
+ eicCborAppend(cbor, data, 3);
+ } else if (size <= 0xffffffff) {
+ data[0] = (majorType << 5) | 26;
+ data[1] = (size >> 24) & 0xff;
+ data[2] = (size >> 16) & 0xff;
+ data[3] = (size >> 8) & 0xff;
+ data[4] = size & 0xff;
+ eicCborAppend(cbor, data, 5);
+ } else {
+ data[0] = (majorType << 5) | 27;
+ data[1] = (((uint64_t)size) >> 56) & 0xff;
+ data[2] = (((uint64_t)size) >> 48) & 0xff;
+ data[3] = (((uint64_t)size) >> 40) & 0xff;
+ data[4] = (((uint64_t)size) >> 32) & 0xff;
+ data[5] = (((uint64_t)size) >> 24) & 0xff;
+ data[6] = (((uint64_t)size) >> 16) & 0xff;
+ data[7] = (((uint64_t)size) >> 8) & 0xff;
+ data[8] = ((uint64_t)size) & 0xff;
+ eicCborAppend(cbor, data, 9);
+ }
+}
+
+void eicCborAppendByteString(EicCbor* cbor, const uint8_t* data,
+ size_t dataSize) {
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, dataSize);
+ eicCborAppend(cbor, data, dataSize);
+}
+
+void eicCborAppendString(EicCbor* cbor, const char* str, size_t strLength) {
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_STRING, strLength);
+ eicCborAppend(cbor, (const uint8_t*)str, strLength);
+}
+
+void eicCborAppendStringZ(EicCbor* cbor, const char* str) {
+ eicCborAppendString(cbor, str, eicStrLen(str));
+}
+
+void eicCborAppendSimple(EicCbor* cbor, uint8_t simpleValue) {
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_SIMPLE, simpleValue);
+}
+
+void eicCborAppendBool(EicCbor* cbor, bool value) {
+ uint8_t simpleValue =
+ value ? EIC_CBOR_SIMPLE_VALUE_TRUE : EIC_CBOR_SIMPLE_VALUE_FALSE;
+ eicCborAppendSimple(cbor, simpleValue);
+}
+
+void eicCborAppendSemantic(EicCbor* cbor, uint64_t value) {
+ size_t encoded = value;
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_SEMANTIC, encoded);
+}
+
+void eicCborAppendUnsigned(EicCbor* cbor, uint64_t value) {
+ uint64_t encoded = value;
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_UNSIGNED, encoded);
+}
+
+void eicCborAppendNumber(EicCbor* cbor, int64_t value) {
+ if (value < 0) {
+ uint64_t encoded = -1 - value;
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_NEGATIVE, encoded);
+ } else {
+ eicCborAppendUnsigned(cbor, value);
+ }
+}
+
+void eicCborAppendArray(EicCbor* cbor, size_t numElements) {
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_ARRAY, numElements);
+}
+
+void eicCborAppendMap(EicCbor* cbor, size_t numPairs) {
+ eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_MAP, numPairs);
+}
+
+bool eicCborCalcAccessControl(EicCbor* cborBuilder, int id,
+ const uint8_t* readerCertificate,
+ size_t readerCertificateSize,
+ bool userAuthenticationRequired,
+ uint64_t timeoutMillis, uint64_t secureUserId) {
+ size_t numPairs = 1;
+ if (readerCertificateSize > 0) {
+ numPairs += 1;
+ }
+ if (userAuthenticationRequired) {
+ numPairs += 2;
+ if (secureUserId > 0) {
+ numPairs += 1;
+ }
+ }
+ eicCborAppendMap(cborBuilder, numPairs);
+ eicCborAppendStringZ(cborBuilder, "id");
+ eicCborAppendUnsigned(cborBuilder, id);
+ if (readerCertificateSize > 0) {
+ eicCborAppendStringZ(cborBuilder, "readerCertificate");
+ eicCborAppendByteString(cborBuilder, readerCertificate,
+ readerCertificateSize);
+ }
+ if (userAuthenticationRequired) {
+ eicCborAppendStringZ(cborBuilder, "userAuthenticationRequired");
+ eicCborAppendBool(cborBuilder, userAuthenticationRequired);
+ eicCborAppendStringZ(cborBuilder, "timeoutMillis");
+ eicCborAppendUnsigned(cborBuilder, timeoutMillis);
+ if (secureUserId > 0) {
+ eicCborAppendStringZ(cborBuilder, "secureUserId");
+ eicCborAppendUnsigned(cborBuilder, secureUserId);
+ }
+ }
+
+ if (cborBuilder->size > cborBuilder->bufferSize) {
+ eicDebug("Buffer for ACP CBOR is too small (%zd) - need %zd bytes",
+ cborBuilder->bufferSize, cborBuilder->size);
+ return false;
+ }
+
+ return true;
+}
+
+bool eicCborCalcEntryAdditionalData(
+ const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds,
+ const char* nameSpace, size_t nameSpaceLength, const char* name,
+ size_t nameLength, uint8_t* cborBuffer, size_t cborBufferSize,
+ size_t* outAdditionalDataCborSize,
+ uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE]) {
+ EicCbor cborBuilder;
+
+ eicCborInit(&cborBuilder, cborBuffer, cborBufferSize);
+ eicCborAppendMap(&cborBuilder, 3);
+ eicCborAppendStringZ(&cborBuilder, "Namespace");
+ eicCborAppendString(&cborBuilder, nameSpace, nameSpaceLength);
+ eicCborAppendStringZ(&cborBuilder, "Name");
+ eicCborAppendString(&cborBuilder, name, nameLength);
+ eicCborAppendStringZ(&cborBuilder, "AccessControlProfileIds");
+ eicCborAppendArray(&cborBuilder, numAccessControlProfileIds);
+ for (size_t n = 0; n < numAccessControlProfileIds; n++) {
+ eicCborAppendNumber(&cborBuilder, accessControlProfileIds[n]);
+ }
+ if (cborBuilder.size > cborBufferSize) {
+ eicDebug(
+ "Not enough space for additionalData - buffer is only %zd bytes, "
+ "content is %zd",
+ cborBufferSize, cborBuilder.size);
+ return false;
+ }
+ if (outAdditionalDataCborSize != NULL) {
+ *outAdditionalDataCborSize = cborBuilder.size;
+ }
+ eicCborFinal(&cborBuilder, additionalDataSha256);
+ return true;
+}
diff --git a/guest/hals/identity/libeic/EicCbor.h b/guest/hals/identity/libeic/EicCbor.h
new file mode 100644
index 000000000..384766f6f
--- /dev/null
+++ b/guest/hals/identity/libeic/EicCbor.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_CBOR_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_CBOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EicOps.h"
+
+typedef enum {
+ EIC_CBOR_DIGEST_TYPE_SHA256,
+ EIC_CBOR_DIGEST_TYPE_HMAC_SHA256,
+} EicCborDigestType;
+
+/* EicCbor is a utility class to build CBOR data structures and calculate
+ * digests on the fly.
+ */
+typedef struct {
+ // Contains the size of the built CBOR, even if it exceeds bufferSize (will
+ // never write to buffer beyond bufferSize though)
+ size_t size;
+
+ // The size of the buffer. Is zero if no data is recorded in which case
+ // only digesting is performed.
+ size_t bufferSize;
+
+ // Whether we're producing a SHA-256 or HMAC-SHA256 digest.
+ EicCborDigestType digestType;
+
+ // The SHA-256 digester object.
+ union {
+ EicSha256Ctx sha256;
+ EicHmacSha256Ctx hmacSha256;
+ } digester;
+
+ // The secondary digester, may be unset.
+ EicSha256Ctx* secondaryDigesterSha256;
+
+ // The buffer used for building up CBOR or NULL if bufferSize is 0.
+ uint8_t* buffer;
+} EicCbor;
+
+/* Initializes an EicCbor.
+ *
+ * The given buffer will be used, up to bufferSize.
+ *
+ * If bufferSize is 0, buffer may be NULL.
+ */
+void eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize);
+
+/* Like eicCborInit() but uses HMAC-SHA256 instead of SHA-256.
+ */
+void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
+ const uint8_t* hmacKey, size_t hmacKeySize);
+
+/* Enables a secondary digester.
+ *
+ * May be enabled midway through processing, this can be used to e.g. calculate
+ * a digest of Sig_structure (for COSE_Sign1) and a separate digest of its
+ * payload.
+ */
+void eicCborEnableSecondaryDigesterSha256(EicCbor* cbor, EicSha256Ctx* sha256);
+
+/* Finishes building CBOR and returns the digest. */
+void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
+
+/* Appends CBOR data to the EicCbor. */
+void eicCborAppend(EicCbor* cbor, const uint8_t* data, size_t size);
+
+#define EIC_CBOR_MAJOR_TYPE_UNSIGNED 0
+#define EIC_CBOR_MAJOR_TYPE_NEGATIVE 1
+#define EIC_CBOR_MAJOR_TYPE_BYTE_STRING 2
+#define EIC_CBOR_MAJOR_TYPE_STRING 3
+#define EIC_CBOR_MAJOR_TYPE_ARRAY 4
+#define EIC_CBOR_MAJOR_TYPE_MAP 5
+#define EIC_CBOR_MAJOR_TYPE_SEMANTIC 6
+#define EIC_CBOR_MAJOR_TYPE_SIMPLE 7
+
+#define EIC_CBOR_SIMPLE_VALUE_FALSE 20
+#define EIC_CBOR_SIMPLE_VALUE_TRUE 21
+
+#define EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR 24
+
+/* Begins a new CBOR value. */
+void eicCborBegin(EicCbor* cbor, int majorType, uint64_t size);
+
+/* Appends a bytestring. */
+void eicCborAppendByteString(EicCbor* cbor, const uint8_t* data,
+ size_t dataSize);
+
+/* Appends a UTF-8 string. */
+void eicCborAppendString(EicCbor* cbor, const char* str, size_t strLength);
+
+/* Appends a NUL-terminated UTF-8 string. */
+void eicCborAppendStringZ(EicCbor* cbor, const char* str);
+
+/* Appends a simple value. */
+void eicCborAppendSimple(EicCbor* cbor, uint8_t simpleValue);
+
+/* Appends a boolean. */
+void eicCborAppendBool(EicCbor* cbor, bool value);
+
+/* Appends a semantic */
+void eicCborAppendSemantic(EicCbor* cbor, uint64_t value);
+
+/* Appends an unsigned number. */
+void eicCborAppendUnsigned(EicCbor* cbor, uint64_t value);
+
+/* Appends a number. */
+void eicCborAppendNumber(EicCbor* cbor, int64_t value);
+
+/* Starts appending an array.
+ *
+ * After this numElements CBOR elements must follow.
+ */
+void eicCborAppendArray(EicCbor* cbor, size_t numElements);
+
+/* Starts appending a map.
+ *
+ * After this numPairs pairs of CBOR elements must follow.
+ */
+void eicCborAppendMap(EicCbor* cbor, size_t numPairs);
+
+/* Calculates how many bytes are needed to store a size. */
+size_t eicCborAdditionalLengthBytesFor(size_t size);
+
+bool eicCborCalcAccessControl(EicCbor* cborBuilder, int id,
+ const uint8_t* readerCertificate,
+ size_t readerCertificateSize,
+ bool userAuthenticationRequired,
+ uint64_t timeoutMillis, uint64_t secureUserId);
+
+bool eicCborCalcEntryAdditionalData(
+ const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds,
+ const char* nameSpace, size_t nameSpaceLength, const char* name,
+ size_t nameLength, uint8_t* cborBuffer, size_t cborBufferSize,
+ size_t* outAdditionalDataCborSize,
+ uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_HARDWARE_IDENTITY_EIC_CBOR_H
diff --git a/guest/hals/identity/libeic/EicCommon.h b/guest/hals/identity/libeic/EicCommon.h
new file mode 100644
index 000000000..1fab26a91
--- /dev/null
+++ b/guest/hals/identity/libeic/EicCommon.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_COMMON_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_COMMON_H
+
+// Feature version 202009:
+//
+// CredentialKeys = [
+// bstr, ; storageKey, a 128-bit AES key
+// bstr, ; credentialPrivKey, the private key for credentialKey
+// ]
+//
+// Feature version 202101:
+//
+// CredentialKeys = [
+// bstr, ; storageKey, a 128-bit AES key
+// bstr, ; credentialPrivKey, the private key for credentialKey
+// bstr ; proofOfProvisioning SHA-256
+// ]
+//
+// where storageKey is 16 bytes, credentialPrivateKey is 32 bytes, and
+// proofOfProvisioning SHA-256 is 32 bytes.
+#define EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202009 52
+#define EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101 86
+
+#endif // ANDROID_HARDWARE_IDENTITY_EIC_COMMON_H
diff --git a/guest/hals/identity/libeic/EicOps.h b/guest/hals/identity/libeic/EicOps.h
new file mode 100644
index 000000000..849d6fc28
--- /dev/null
+++ b/guest/hals/identity/libeic/EicOps.h
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_OPS_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_OPS_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+// Uncomment or define if debug messages are needed.
+//
+//#define EIC_DEBUG
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// The following defines must be set to something appropriate
+//
+// EIC_SHA256_CONTEXT_SIZE - the size of EicSha256Ctx
+// EIC_HMAC_SHA256_CONTEXT_SIZE - the size of EicHmacSha256Ctx
+//
+// For example, if EicSha256Ctx is implemented using BoringSSL this would be
+// defined as sizeof(SHA256_CTX).
+//
+// We expect the implementation to provide a header file with the name
+// EicOpsImpl.h to do all this.
+//
+#include "EicOpsImpl.h"
+
+#define EIC_SHA256_DIGEST_SIZE 32
+
+// The size of a P-256 private key.
+//
+#define EIC_P256_PRIV_KEY_SIZE 32
+
+// The size of a P-256 public key in uncompressed form.
+//
+// The public key is stored in uncompressed form, first the X coordinate, then
+// the Y coordinate.
+//
+#define EIC_P256_PUB_KEY_SIZE 64
+
+// Size of one of the coordinates in a curve-point.
+//
+#define EIC_P256_COORDINATE_SIZE 32
+
+// The size of an ECSDA signature using P-256.
+//
+// The R and S values are stored here, first R then S.
+//
+#define EIC_ECDSA_P256_SIGNATURE_SIZE 64
+
+#define EIC_AES_128_KEY_SIZE 16
+
+// The following are definitions of implementation functions the
+// underlying platform must provide.
+//
+
+struct EicSha256Ctx {
+ uint8_t reserved[EIC_SHA256_CONTEXT_SIZE];
+};
+typedef struct EicSha256Ctx EicSha256Ctx;
+
+struct EicHmacSha256Ctx {
+ uint8_t reserved[EIC_HMAC_SHA256_CONTEXT_SIZE];
+};
+typedef struct EicHmacSha256Ctx EicHmacSha256Ctx;
+
+#ifdef EIC_DEBUG
+// Debug macro. Don't include a new-line in message.
+//
+#define eicDebug(...) \
+ do { \
+ eicPrint("%s:%d: ", __FILE__, __LINE__); \
+ eicPrint(__VA_ARGS__); \
+ eicPrint("\n"); \
+ } while (0)
+#else
+#define eicDebug(...) \
+ do { \
+ } while (0)
+#endif
+
+// Prints message which should include new-line character. Can be no-op.
+//
+// Don't use this from code, use eicDebug() instead.
+//
+#ifdef EIC_DEBUG
+void eicPrint(const char* format, ...);
+#else
+inline void eicPrint(const char*, ...) {}
+#endif
+
+// Dumps data as pretty-printed hex. Can be no-op.
+//
+#ifdef EIC_DEBUG
+void eicHexdump(const char* message, const uint8_t* data, size_t dataSize);
+#else
+inline void eicHexdump(const char*, const uint8_t*, size_t) {}
+#endif
+
+// Pretty-prints encoded CBOR. Can be no-op.
+//
+// If a byte-string is larger than |maxBStrSize| its contents will not be
+// printed, instead the value of the form "<bstr size=1099016
+// sha1=ef549cca331f73dfae2090e6a37c04c23f84b07b>" will be printed. Pass zero
+// for |maxBStrSize| to disable this.
+//
+#ifdef EIC_DEBUG
+void eicCborPrettyPrint(const uint8_t* cborData, size_t cborDataSize,
+ size_t maxBStrSize);
+#else
+inline void eicCborPrettyPrint(const uint8_t*, size_t, size_t) {}
+#endif
+
+// Memory setting, see memset(3).
+void* eicMemSet(void* s, int c, size_t n);
+
+// Memory copying, see memcpy(3).
+void* eicMemCpy(void* dest, const void* src, size_t n);
+
+// String length, see strlen(3).
+size_t eicStrLen(const char* s);
+
+// Memory compare, see CRYPTO_memcmp(3SSL)
+//
+// It takes an amount of time dependent on len, but independent of the contents
+// of the memory regions pointed to by s1 and s2.
+//
+int eicCryptoMemCmp(const void* s1, const void* s2, size_t n);
+
+// Random number generation.
+bool eicOpsRandom(uint8_t* buf, size_t numBytes);
+
+// If |testCredential| is true, returns the 128-bit AES Hardware-Bound Key (16
+// bytes).
+//
+// Otherwise returns all zeroes (16 bytes).
+//
+const uint8_t* eicOpsGetHardwareBoundKey(bool testCredential);
+
+// Encrypts |data| with |key| and |additionalAuthenticatedData| using |nonce|,
+// returns the resulting (nonce || ciphertext || tag) in |encryptedData| which
+// must be of size |dataSize| + 28.
+bool eicOpsEncryptAes128Gcm(
+ const uint8_t* key, // Must be 16 bytes
+ const uint8_t* nonce, // Must be 12 bytes
+ const uint8_t* data, // May be NULL if size is 0
+ size_t dataSize,
+ const uint8_t* additionalAuthenticationData, // May be NULL if size is 0
+ size_t additionalAuthenticationDataSize, uint8_t* encryptedData);
+
+// Decrypts |encryptedData| using |key| and |additionalAuthenticatedData|,
+// returns resulting plaintext in |data| must be of size |encryptedDataSize|
+// - 28.
+//
+// The format of |encryptedData| must be as specified in the
+// encryptAes128Gcm() function.
+bool eicOpsDecryptAes128Gcm(const uint8_t* key, // Must be 16 bytes
+ const uint8_t* encryptedData,
+ size_t encryptedDataSize,
+ const uint8_t* additionalAuthenticationData,
+ size_t additionalAuthenticationDataSize,
+ uint8_t* data);
+
+// Creates an EC key using the P-256 curve. The private key is written to
+// |privateKey|. The public key is written to |publicKey|.
+//
+bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+ uint8_t publicKey[EIC_P256_PUB_KEY_SIZE]);
+
+// Generates CredentialKey plus an attestation certificate.
+//
+// The attestation certificate will be signed by the attestation keys the secure
+// area has been provisioned with. The given |challenge| and |applicationId|
+// will be used as will |testCredential|.
+//
+// The generated certificate will be in X.509 format and returned in |cert|
+// and |certSize| must be set to the size of this array and this function will
+// set it to the size of the certification chain on successfully return.
+//
+// This may return either a single certificate or an entire certificate
+// chain. If it returns only a single certificate, the implementation of
+// SecureHardwareProvisioningProxy::createCredentialKey() should amend the
+// remainder of the certificate chain on the HAL side.
+//
+bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+ const uint8_t* challenge, size_t challengeSize,
+ const uint8_t* applicationId,
+ size_t applicationIdSize, bool testCredential,
+ uint8_t* cert,
+ size_t* certSize); // inout
+
+// Generate an X.509 certificate for the key identified by |publicKey| which
+// must be of the form returned by eicOpsCreateEcKey().
+//
+// If proofOfBinding is not NULL, it will be included as an OCTET_STRING
+// X.509 extension at OID 1.3.6.1.4.1.11129.2.1.26.
+//
+// The certificate will be signed by the key identified by |signingKey| which
+// must be of the form returned by eicOpsCreateEcKey().
+//
+bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+ const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE],
+ unsigned int serial, const char* issuerName,
+ const char* subjectName, time_t validityNotBefore,
+ time_t validityNotAfter, const uint8_t* proofOfBinding,
+ size_t proofOfBindingSize, uint8_t* cert,
+ size_t* certSize); // inout
+
+// Uses |privateKey| to create an ECDSA signature of some data (the SHA-256 must
+// be given by |digestOfData|). Returns the signature in |signature|.
+//
+bool eicOpsEcDsa(const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+ const uint8_t digestOfData[EIC_SHA256_DIGEST_SIZE],
+ uint8_t signature[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+// Performs Elliptic Curve Diffie-Helman.
+//
+bool eicOpsEcdh(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+ const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+ uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE]);
+
+// Performs HKDF.
+//
+bool eicOpsHkdf(const uint8_t* sharedSecret, size_t sharedSecretSize,
+ const uint8_t* salt, size_t saltSize, const uint8_t* info,
+ size_t infoSize, uint8_t* output, size_t outputSize);
+
+// SHA-256 functions.
+void eicOpsSha256Init(EicSha256Ctx* ctx);
+void eicOpsSha256Update(EicSha256Ctx* ctx, const uint8_t* data, size_t len);
+void eicOpsSha256Final(EicSha256Ctx* ctx,
+ uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
+
+// HMAC SHA-256 functions.
+void eicOpsHmacSha256Init(EicHmacSha256Ctx* ctx, const uint8_t* key,
+ size_t keySize);
+void eicOpsHmacSha256Update(EicHmacSha256Ctx* ctx, const uint8_t* data,
+ size_t len);
+void eicOpsHmacSha256Final(EicHmacSha256Ctx* ctx,
+ uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
+
+// Extracts the public key in the given X.509 certificate.
+//
+// If the key is not an EC key, this function fails.
+//
+// Otherwise the public key is stored in uncompressed form in |publicKey| which
+// size should be set in |publicKeySize|. On successful return |publicKeySize|
+// is set to the length of the key. If there is not enough space, the function
+// fails.
+//
+// (The public key returned is not necessarily a P-256 key, even if it is note
+// that its size is not EIC_P256_PUBLIC_KEY_SIZE because of the leading 0x04.)
+//
+bool eicOpsX509GetPublicKey(const uint8_t* x509Cert, size_t x509CertSize,
+ uint8_t* publicKey, size_t* publicKeySize);
+
+// Checks that the X.509 certificate given by |x509Cert| is signed by the public
+// key given by |publicKey| which must be an EC key in uncompressed form (e.g.
+// same formatt as returned by eicOpsX509GetPublicKey()).
+//
+bool eicOpsX509CertSignedByPublicKey(const uint8_t* x509Cert,
+ size_t x509CertSize,
+ const uint8_t* publicKey,
+ size_t publicKeySize);
+
+// Checks that |signature| is a signature of some data (given by |digest|),
+// signed by the public key given by |publicKey|.
+//
+// The key must be an EC key in uncompressed form (e.g. same format as returned
+// by eicOpsX509GetPublicKey()).
+//
+// The format of the signature is the same encoding as the 'signature' field of
+// COSE_Sign1 - that is, it's the R and S integers both with the same length as
+// the key-size.
+//
+// The size of digest must match the size of the key.
+//
+bool eicOpsEcDsaVerifyWithPublicKey(const uint8_t* digest, size_t digestSize,
+ const uint8_t* signature,
+ size_t signatureSize,
+ const uint8_t* publicKey,
+ size_t publicKeySize);
+
+// Validates that the passed in data constitutes a valid auth- and verification
+// tokens.
+//
+bool eicOpsValidateAuthToken(
+ uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+ int hardwareAuthenticatorType, uint64_t timeStamp, const uint8_t* mac,
+ size_t macSize, uint64_t verificationTokenChallenge,
+ uint64_t verificationTokenTimeStamp, int verificationTokenSecurityLevel,
+ const uint8_t* verificationTokenMac, size_t verificationTokenMacSize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_HARDWARE_IDENTITY_EIC_OPS_H
diff --git a/guest/hals/identity/libeic/EicOpsImpl.cc b/guest/hals/identity/libeic/EicOpsImpl.cc
new file mode 100644
index 000000000..0921c72cf
--- /dev/null
+++ b/guest/hals/identity/libeic/EicOpsImpl.cc
@@ -0,0 +1,546 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#define LOG_TAG "EicOpsImpl"
+
+#include <optional>
+#include <tuple>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <string.h>
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <openssl/sha.h>
+
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hkdf.h>
+#include <openssl/hmac.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+
+#include "EicOps.h"
+
+using ::std::map;
+using ::std::optional;
+using ::std::string;
+using ::std::tuple;
+using ::std::vector;
+
+void* eicMemSet(void* s, int c, size_t n) { return memset(s, c, n); }
+
+void* eicMemCpy(void* dest, const void* src, size_t n) {
+ return memcpy(dest, src, n);
+}
+
+size_t eicStrLen(const char* s) { return strlen(s); }
+
+int eicCryptoMemCmp(const void* s1, const void* s2, size_t n) {
+ return CRYPTO_memcmp(s1, s2, n);
+}
+
+void eicOpsHmacSha256Init(EicHmacSha256Ctx* ctx, const uint8_t* key,
+ size_t keySize) {
+ HMAC_CTX* realCtx = (HMAC_CTX*)ctx;
+ HMAC_CTX_init(realCtx);
+ if (HMAC_Init_ex(realCtx, key, keySize, EVP_sha256(), nullptr /* impl */) !=
+ 1) {
+ LOG(ERROR) << "Error initializing HMAC_CTX";
+ }
+}
+
+void eicOpsHmacSha256Update(EicHmacSha256Ctx* ctx, const uint8_t* data,
+ size_t len) {
+ HMAC_CTX* realCtx = (HMAC_CTX*)ctx;
+ if (HMAC_Update(realCtx, data, len) != 1) {
+ LOG(ERROR) << "Error updating HMAC_CTX";
+ }
+}
+
+void eicOpsHmacSha256Final(EicHmacSha256Ctx* ctx,
+ uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
+ HMAC_CTX* realCtx = (HMAC_CTX*)ctx;
+ unsigned int size = 0;
+ if (HMAC_Final(realCtx, digest, &size) != 1) {
+ LOG(ERROR) << "Error finalizing HMAC_CTX";
+ }
+ if (size != EIC_SHA256_DIGEST_SIZE) {
+ LOG(ERROR) << "Expected 32 bytes from HMAC_Final, got " << size;
+ }
+}
+
+void eicOpsSha256Init(EicSha256Ctx* ctx) {
+ SHA256_CTX* realCtx = (SHA256_CTX*)ctx;
+ SHA256_Init(realCtx);
+}
+
+void eicOpsSha256Update(EicSha256Ctx* ctx, const uint8_t* data, size_t len) {
+ SHA256_CTX* realCtx = (SHA256_CTX*)ctx;
+ SHA256_Update(realCtx, data, len);
+}
+
+void eicOpsSha256Final(EicSha256Ctx* ctx,
+ uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
+ SHA256_CTX* realCtx = (SHA256_CTX*)ctx;
+ SHA256_Final(digest, realCtx);
+}
+
+bool eicOpsRandom(uint8_t* buf, size_t numBytes) {
+ optional<vector<uint8_t>> bytes =
+ ::android::hardware::identity::support::getRandom(numBytes);
+ if (!bytes.has_value()) {
+ return false;
+ }
+ memcpy(buf, bytes.value().data(), numBytes);
+ return true;
+}
+
+bool eicOpsEncryptAes128Gcm(
+ const uint8_t* key, // Must be 16 bytes
+ const uint8_t* nonce, // Must be 12 bytes
+ const uint8_t* data, // May be NULL if size is 0
+ size_t dataSize,
+ const uint8_t* additionalAuthenticationData, // May be NULL if size is 0
+ size_t additionalAuthenticationDataSize, uint8_t* encryptedData) {
+ vector<uint8_t> cppKey;
+ cppKey.resize(16);
+ memcpy(cppKey.data(), key, 16);
+
+ vector<uint8_t> cppData;
+ cppData.resize(dataSize);
+ if (dataSize > 0) {
+ memcpy(cppData.data(), data, dataSize);
+ }
+
+ vector<uint8_t> cppAAD;
+ cppAAD.resize(additionalAuthenticationDataSize);
+ if (additionalAuthenticationDataSize > 0) {
+ memcpy(cppAAD.data(), additionalAuthenticationData,
+ additionalAuthenticationDataSize);
+ }
+
+ vector<uint8_t> cppNonce;
+ cppNonce.resize(12);
+ memcpy(cppNonce.data(), nonce, 12);
+
+ optional<vector<uint8_t>> cppEncryptedData =
+ android::hardware::identity::support::encryptAes128Gcm(cppKey, cppNonce,
+ cppData, cppAAD);
+ if (!cppEncryptedData.has_value()) {
+ return false;
+ }
+
+ memcpy(encryptedData, cppEncryptedData.value().data(),
+ cppEncryptedData.value().size());
+ return true;
+}
+
+// Decrypts |encryptedData| using |key| and |additionalAuthenticatedData|,
+// returns resulting plaintext in |data| must be of size |encryptedDataSize|
+// - 28.
+//
+// The format of |encryptedData| must be as specified in the
+// encryptAes128Gcm() function.
+bool eicOpsDecryptAes128Gcm(const uint8_t* key, // Must be 16 bytes
+ const uint8_t* encryptedData,
+ size_t encryptedDataSize,
+ const uint8_t* additionalAuthenticationData,
+ size_t additionalAuthenticationDataSize,
+ uint8_t* data) {
+ vector<uint8_t> keyVec;
+ keyVec.resize(16);
+ memcpy(keyVec.data(), key, 16);
+
+ vector<uint8_t> encryptedDataVec;
+ encryptedDataVec.resize(encryptedDataSize);
+ if (encryptedDataSize > 0) {
+ memcpy(encryptedDataVec.data(), encryptedData, encryptedDataSize);
+ }
+
+ vector<uint8_t> aadVec;
+ aadVec.resize(additionalAuthenticationDataSize);
+ if (additionalAuthenticationDataSize > 0) {
+ memcpy(aadVec.data(), additionalAuthenticationData,
+ additionalAuthenticationDataSize);
+ }
+
+ optional<vector<uint8_t>> decryptedDataVec =
+ android::hardware::identity::support::decryptAes128Gcm(
+ keyVec, encryptedDataVec, aadVec);
+ if (!decryptedDataVec.has_value()) {
+ eicDebug("Error decrypting data");
+ return false;
+ }
+ if (decryptedDataVec.value().size() != encryptedDataSize - 28) {
+ eicDebug("Decrypted data is size %zd, expected %zd",
+ decryptedDataVec.value().size(), encryptedDataSize - 28);
+ return false;
+ }
+
+ if (decryptedDataVec.value().size() > 0) {
+ memcpy(data, decryptedDataVec.value().data(),
+ decryptedDataVec.value().size());
+ }
+ return true;
+}
+
+bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+ uint8_t publicKey[EIC_P256_PUB_KEY_SIZE]) {
+ optional<vector<uint8_t>> keyPair =
+ android::hardware::identity::support::createEcKeyPair();
+ if (!keyPair) {
+ eicDebug("Error creating EC keypair");
+ return false;
+ }
+ optional<vector<uint8_t>> privKey =
+ android::hardware::identity::support::ecKeyPairGetPrivateKey(
+ keyPair.value());
+ if (!privKey) {
+ eicDebug("Error extracting private key");
+ return false;
+ }
+ if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
+ eicDebug("Private key is %zd bytes, expected %zd", privKey.value().size(),
+ (size_t)EIC_P256_PRIV_KEY_SIZE);
+ return false;
+ }
+
+ optional<vector<uint8_t>> pubKey =
+ android::hardware::identity::support::ecKeyPairGetPublicKey(
+ keyPair.value());
+ if (!pubKey) {
+ eicDebug("Error extracting public key");
+ return false;
+ }
+ // ecKeyPairGetPublicKey() returns 0x04 | x | y, we don't want the leading
+ // 0x04.
+ if (pubKey.value().size() != EIC_P256_PUB_KEY_SIZE + 1) {
+ eicDebug("Public key is %zd bytes long, expected %zd",
+ pubKey.value().size(), (size_t)EIC_P256_PRIV_KEY_SIZE + 1);
+ return false;
+ }
+
+ memcpy(privateKey, privKey.value().data(), EIC_P256_PRIV_KEY_SIZE);
+ memcpy(publicKey, pubKey.value().data() + 1, EIC_P256_PUB_KEY_SIZE);
+
+ return true;
+}
+
+bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+ const uint8_t* challenge, size_t challengeSize,
+ const uint8_t* applicationId,
+ size_t applicationIdSize, bool testCredential,
+ uint8_t* cert, size_t* certSize) {
+ vector<uint8_t> challengeVec(challengeSize);
+ memcpy(challengeVec.data(), challenge, challengeSize);
+
+ vector<uint8_t> applicationIdVec(applicationIdSize);
+ memcpy(applicationIdVec.data(), applicationId, applicationIdSize);
+
+ optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> ret =
+ android::hardware::identity::support::createEcKeyPairAndAttestation(
+ challengeVec, applicationIdVec, testCredential);
+ if (!ret) {
+ eicDebug("Error generating CredentialKey and attestation");
+ return false;
+ }
+
+ // Extract certificate chain.
+ vector<uint8_t> flatChain =
+ android::hardware::identity::support::certificateChainJoin(
+ ret.value().second);
+ if (*certSize < flatChain.size()) {
+ eicDebug("Buffer for certificate is only %zd bytes long, need %zd bytes",
+ *certSize, flatChain.size());
+ return false;
+ }
+ memcpy(cert, flatChain.data(), flatChain.size());
+ *certSize = flatChain.size();
+
+ // Extract private key.
+ optional<vector<uint8_t>> privKey =
+ android::hardware::identity::support::ecKeyPairGetPrivateKey(
+ ret.value().first);
+ if (!privKey) {
+ eicDebug("Error extracting private key");
+ return false;
+ }
+ if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
+ eicDebug("Private key is %zd bytes, expected %zd", privKey.value().size(),
+ (size_t)EIC_P256_PRIV_KEY_SIZE);
+ return false;
+ }
+
+ memcpy(privateKey, privKey.value().data(), EIC_P256_PRIV_KEY_SIZE);
+
+ return true;
+}
+
+bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+ const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE],
+ unsigned int serial, const char* issuerName,
+ const char* subjectName, time_t validityNotBefore,
+ time_t validityNotAfter, const uint8_t* proofOfBinding,
+ size_t proofOfBindingSize, uint8_t* cert,
+ size_t* certSize) { // inout
+ vector<uint8_t> signingKeyVec(EIC_P256_PRIV_KEY_SIZE);
+ memcpy(signingKeyVec.data(), signingKey, EIC_P256_PRIV_KEY_SIZE);
+
+ vector<uint8_t> pubKeyVec(EIC_P256_PUB_KEY_SIZE + 1);
+ pubKeyVec[0] = 0x04;
+ memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE);
+
+ string serialDecimal = android::base::StringPrintf("%d", serial);
+
+ map<string, vector<uint8_t>> extensions;
+ if (proofOfBinding != nullptr) {
+ vector<uint8_t> proofOfBindingVec(proofOfBinding,
+ proofOfBinding + proofOfBindingSize);
+ extensions["1.3.6.1.4.1.11129.2.1.26"] = proofOfBindingVec;
+ }
+
+ optional<vector<uint8_t>> certVec =
+ android::hardware::identity::support::ecPublicKeyGenerateCertificate(
+ pubKeyVec, signingKeyVec, serialDecimal, issuerName, subjectName,
+ validityNotBefore, validityNotAfter, extensions);
+ if (!certVec) {
+ eicDebug("Error generating certificate");
+ return false;
+ }
+
+ if (*certSize < certVec.value().size()) {
+ eicDebug("Buffer for certificate is only %zd bytes long, need %zd bytes",
+ *certSize, certVec.value().size());
+ return false;
+ }
+ memcpy(cert, certVec.value().data(), certVec.value().size());
+ *certSize = certVec.value().size();
+
+ return true;
+}
+
+bool eicOpsEcDsa(const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+ const uint8_t digestOfData[EIC_SHA256_DIGEST_SIZE],
+ uint8_t signature[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+ vector<uint8_t> privKeyVec(EIC_P256_PRIV_KEY_SIZE);
+ memcpy(privKeyVec.data(), privateKey, EIC_P256_PRIV_KEY_SIZE);
+
+ vector<uint8_t> digestVec(EIC_SHA256_DIGEST_SIZE);
+ memcpy(digestVec.data(), digestOfData, EIC_SHA256_DIGEST_SIZE);
+
+ optional<vector<uint8_t>> derSignature =
+ android::hardware::identity::support::signEcDsaDigest(privKeyVec,
+ digestVec);
+ if (!derSignature) {
+ eicDebug("Error signing data");
+ return false;
+ }
+
+ ECDSA_SIG* sig;
+ const unsigned char* p = derSignature.value().data();
+ sig = d2i_ECDSA_SIG(nullptr, &p, derSignature.value().size());
+ if (sig == nullptr) {
+ eicDebug("Error decoding DER signature");
+ return false;
+ }
+
+ if (BN_bn2binpad(sig->r, signature, 32) != 32) {
+ eicDebug("Error encoding r");
+ return false;
+ }
+ if (BN_bn2binpad(sig->s, signature + 32, 32) != 32) {
+ eicDebug("Error encoding s");
+ return false;
+ }
+
+ return true;
+}
+
+static const uint8_t hbkTest[16] = {0};
+static const uint8_t hbkReal[16] = {0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15};
+
+const uint8_t* eicOpsGetHardwareBoundKey(bool testCredential) {
+ if (testCredential) {
+ return hbkTest;
+ }
+ return hbkReal;
+}
+
+bool eicOpsValidateAuthToken(uint64_t /* challenge */,
+ uint64_t /* secureUserId */,
+ uint64_t /* authenticatorId */,
+ int /* hardwareAuthenticatorType */,
+ uint64_t /* timeStamp */, const uint8_t* /* mac */,
+ size_t /* macSize */,
+ uint64_t /* verificationTokenChallenge */,
+ uint64_t /* verificationTokenTimeStamp */,
+ int /* verificationTokenSecurityLevel */,
+ const uint8_t* /* verificationTokenMac */,
+ size_t /* verificationTokenMacSize */) {
+ // Here's where we would validate the passed-in |authToken| to assure
+ // ourselves that it comes from the e.g. biometric hardware and wasn't made up
+ // by an attacker.
+ //
+ // However this involves calculating the MAC which requires access to the to
+ // a pre-shared key which we don't have...
+ //
+ return true;
+}
+
+bool eicOpsX509GetPublicKey(const uint8_t* x509Cert, size_t x509CertSize,
+ uint8_t* publicKey, size_t* publicKeySize) {
+ vector<uint8_t> chain;
+ chain.resize(x509CertSize);
+ memcpy(chain.data(), x509Cert, x509CertSize);
+ optional<vector<uint8_t>> res =
+ android::hardware::identity::support::certificateChainGetTopMostKey(
+ chain);
+ if (!res) {
+ return false;
+ }
+ if (res.value().size() > *publicKeySize) {
+ eicDebug("Public key size is %zd but buffer only has room for %zd bytes",
+ res.value().size(), *publicKeySize);
+ return false;
+ }
+ *publicKeySize = res.value().size();
+ memcpy(publicKey, res.value().data(), *publicKeySize);
+ eicDebug("Extracted %zd bytes public key from %zd bytes X.509 cert",
+ *publicKeySize, x509CertSize);
+ return true;
+}
+
+bool eicOpsX509CertSignedByPublicKey(const uint8_t* x509Cert,
+ size_t x509CertSize,
+ const uint8_t* publicKey,
+ size_t publicKeySize) {
+ vector<uint8_t> certVec(x509Cert, x509Cert + x509CertSize);
+ vector<uint8_t> publicKeyVec(publicKey, publicKey + publicKeySize);
+ return android::hardware::identity::support::certificateSignedByPublicKey(
+ certVec, publicKeyVec);
+}
+
+bool eicOpsEcDsaVerifyWithPublicKey(const uint8_t* digest, size_t digestSize,
+ const uint8_t* signature,
+ size_t signatureSize,
+ const uint8_t* publicKey,
+ size_t publicKeySize) {
+ vector<uint8_t> digestVec(digest, digest + digestSize);
+ vector<uint8_t> signatureVec(signature, signature + signatureSize);
+ vector<uint8_t> publicKeyVec(publicKey, publicKey + publicKeySize);
+
+ vector<uint8_t> derSignature;
+ if (!android::hardware::identity::support::ecdsaSignatureCoseToDer(
+ signatureVec, derSignature)) {
+ LOG(ERROR) << "Error convering signature to DER format";
+ return false;
+ }
+
+ if (!android::hardware::identity::support::checkEcDsaSignature(
+ digestVec, derSignature, publicKeyVec)) {
+ LOG(ERROR) << "Signature check failed";
+ return false;
+ }
+ return true;
+}
+
+bool eicOpsEcdh(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+ const uint8_t privateKey[EIC_P256_PUB_KEY_SIZE],
+ uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE]) {
+ vector<uint8_t> pubKeyVec(EIC_P256_PUB_KEY_SIZE + 1);
+ pubKeyVec[0] = 0x04;
+ memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE);
+
+ vector<uint8_t> privKeyVec(EIC_P256_PRIV_KEY_SIZE);
+ memcpy(privKeyVec.data(), privateKey, EIC_P256_PRIV_KEY_SIZE);
+
+ optional<vector<uint8_t>> shared =
+ android::hardware::identity::support::ecdh(pubKeyVec, privKeyVec);
+ if (!shared) {
+ LOG(ERROR) << "Error performing ECDH";
+ return false;
+ }
+ if (shared.value().size() != EIC_P256_COORDINATE_SIZE) {
+ LOG(ERROR) << "Unexpected size of shared secret " << shared.value().size()
+ << " expected " << EIC_P256_COORDINATE_SIZE << " bytes";
+ return false;
+ }
+ memcpy(sharedSecret, shared.value().data(), EIC_P256_COORDINATE_SIZE);
+ return true;
+}
+
+bool eicOpsHkdf(const uint8_t* sharedSecret, size_t sharedSecretSize,
+ const uint8_t* salt, size_t saltSize, const uint8_t* info,
+ size_t infoSize, uint8_t* output, size_t outputSize) {
+ vector<uint8_t> sharedSecretVec(sharedSecretSize);
+ memcpy(sharedSecretVec.data(), sharedSecret, sharedSecretSize);
+ vector<uint8_t> saltVec(saltSize);
+ memcpy(saltVec.data(), salt, saltSize);
+ vector<uint8_t> infoVec(infoSize);
+ memcpy(infoVec.data(), info, infoSize);
+
+ optional<vector<uint8_t>> result = android::hardware::identity::support::hkdf(
+ sharedSecretVec, saltVec, infoVec, outputSize);
+ if (!result) {
+ LOG(ERROR) << "Error performing HKDF";
+ return false;
+ }
+ if (result.value().size() != outputSize) {
+ LOG(ERROR) << "Unexpected size of HKDF " << result.value().size()
+ << " expected " << outputSize;
+ return false;
+ }
+ memcpy(output, result.value().data(), outputSize);
+ return true;
+}
+
+#ifdef EIC_DEBUG
+
+void eicPrint(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+}
+
+void eicHexdump(const char* message, const uint8_t* data, size_t dataSize) {
+ vector<uint8_t> dataVec(dataSize);
+ memcpy(dataVec.data(), data, dataSize);
+ android::hardware::identity::support::hexdump(message, dataVec);
+}
+
+void eicCborPrettyPrint(const uint8_t* cborData, size_t cborDataSize,
+ size_t maxBStrSize) {
+ vector<uint8_t> cborDataVec(cborDataSize);
+ memcpy(cborDataVec.data(), cborData, cborDataSize);
+ string str = android::hardware::identity::support::cborPrettyPrint(
+ cborDataVec, maxBStrSize, {});
+ fprintf(stderr, "%s\n", str.c_str());
+}
+
+#endif // EIC_DEBUG
diff --git a/guest/hals/identity/libeic/EicOpsImpl.h b/guest/hals/identity/libeic/EicOpsImpl.h
new file mode 100644
index 000000000..333cdce63
--- /dev/null
+++ b/guest/hals/identity/libeic/EicOpsImpl.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_OPS_IMPL_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_OPS_IMPL_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+// Add whatever includes are needed for definitions below.
+//
+
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Set the following defines to match the implementation of the supplied
+// eicOps*() operations. See EicOps.h for details.
+//
+
+#define EIC_SHA256_CONTEXT_SIZE sizeof(SHA256_CTX)
+
+#define EIC_HMAC_SHA256_CONTEXT_SIZE sizeof(HMAC_CTX)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_HARDWARE_IDENTITY_EMBEDDED_IC_H
diff --git a/guest/hals/identity/libeic/EicPresentation.c b/guest/hals/identity/libeic/EicPresentation.c
new file mode 100644
index 000000000..520c2c2af
--- /dev/null
+++ b/guest/hals/identity/libeic/EicPresentation.c
@@ -0,0 +1,916 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#include "EicPresentation.h"
+#include "EicCommon.h"
+
+#include <inttypes.h>
+
+bool eicPresentationInit(EicPresentation* ctx, bool testCredential,
+ const char* docType, size_t docTypeLength,
+ const uint8_t* encryptedCredentialKeys,
+ size_t encryptedCredentialKeysSize) {
+ uint8_t credentialKeys[EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101];
+ bool expectPopSha256 = false;
+
+ // For feature version 202009 it's 52 bytes long and for feature version
+ // 202101 it's 86 bytes (the additional data is the ProofOfProvisioning
+ // SHA-256). We need to support loading all feature versions.
+ //
+ if (encryptedCredentialKeysSize ==
+ EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202009 + 28) {
+ /* do nothing */
+ } else if (encryptedCredentialKeysSize ==
+ EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101 + 28) {
+ expectPopSha256 = true;
+ } else {
+ eicDebug("Unexpected size %zd for encryptedCredentialKeys",
+ encryptedCredentialKeysSize);
+ return false;
+ }
+
+ eicMemSet(ctx, '\0', sizeof(EicPresentation));
+
+ if (!eicOpsDecryptAes128Gcm(
+ eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
+ encryptedCredentialKeysSize,
+ // DocType is the additionalAuthenticatedData
+ (const uint8_t*)docType, docTypeLength, credentialKeys)) {
+ eicDebug("Error decrypting CredentialKeys");
+ return false;
+ }
+
+ // It's supposed to look like this;
+ //
+ // Feature version 202009:
+ //
+ // CredentialKeys = [
+ // bstr, ; storageKey, a 128-bit AES key
+ // bstr, ; credentialPrivKey, the private key for credentialKey
+ // ]
+ //
+ // Feature version 202101:
+ //
+ // CredentialKeys = [
+ // bstr, ; storageKey, a 128-bit AES key
+ // bstr, ; credentialPrivKey, the private key for credentialKey
+ // bstr ; proofOfProvisioning SHA-256
+ // ]
+ //
+ // where storageKey is 16 bytes, credentialPrivateKey is 32 bytes, and
+ // proofOfProvisioning SHA-256 is 32 bytes.
+ //
+ if (credentialKeys[0] !=
+ (expectPopSha256 ? 0x83 : 0x82) || // array of two or three elements
+ credentialKeys[1] != 0x50 || // 16-byte bstr
+ credentialKeys[18] != 0x58 ||
+ credentialKeys[19] != 0x20) { // 32-byte bstr
+ eicDebug("Invalid CBOR for CredentialKeys");
+ return false;
+ }
+ if (expectPopSha256) {
+ if (credentialKeys[52] != 0x58 ||
+ credentialKeys[53] != 0x20) { // 32-byte bstr
+ eicDebug("Invalid CBOR for CredentialKeys");
+ return false;
+ }
+ }
+ eicMemCpy(ctx->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
+ eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20,
+ EIC_P256_PRIV_KEY_SIZE);
+ ctx->testCredential = testCredential;
+ if (expectPopSha256) {
+ eicMemCpy(ctx->proofOfProvisioningSha256, credentialKeys + 54,
+ EIC_SHA256_DIGEST_SIZE);
+ }
+ return true;
+}
+
+bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx,
+ const char* docType,
+ size_t docTypeLength, time_t now,
+ uint8_t* publicKeyCert,
+ size_t* publicKeyCertSize,
+ uint8_t signingKeyBlob[60]) {
+ uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
+ uint8_t signingKeyPub[EIC_P256_PUB_KEY_SIZE];
+ uint8_t cborBuf[64];
+
+ // Generate the ProofOfBinding CBOR to include in the X.509 certificate in
+ // IdentityCredentialAuthenticationKeyExtension CBOR. This CBOR is defined
+ // by the following CDDL
+ //
+ // ProofOfBinding = [
+ // "ProofOfBinding",
+ // bstr, // Contains the SHA-256 of ProofOfProvisioning
+ // ]
+ //
+ // This array may grow in the future if other information needs to be
+ // conveyed.
+ //
+ // The bytes of ProofOfBinding is is represented as an OCTET_STRING
+ // and stored at OID 1.3.6.1.4.1.11129.2.1.26.
+ //
+
+ EicCbor cbor;
+ eicCborInit(&cbor, cborBuf, sizeof cborBuf);
+ eicCborAppendArray(&cbor, 2);
+ eicCborAppendStringZ(&cbor, "ProofOfBinding");
+ eicCborAppendByteString(&cbor, ctx->proofOfProvisioningSha256,
+ EIC_SHA256_DIGEST_SIZE);
+ if (cbor.size > sizeof(cborBuf)) {
+ eicDebug("Exceeded buffer size");
+ return false;
+ }
+ const uint8_t* proofOfBinding = cborBuf;
+ size_t proofOfBindingSize = cbor.size;
+
+ if (!eicOpsCreateEcKey(signingKeyPriv, signingKeyPub)) {
+ eicDebug("Error creating signing key");
+ return false;
+ }
+
+ const int secondsInOneYear = 365 * 24 * 60 * 60;
+ time_t validityNotBefore = now;
+ time_t validityNotAfter = now + secondsInOneYear; // One year from now.
+ if (!eicOpsSignEcKey(
+ signingKeyPub, ctx->credentialPrivateKey, 1,
+ "Android Identity Credential Key", // issuer CN
+ "Android Identity Credential Authentication Key", // subject CN
+ validityNotBefore, validityNotAfter, proofOfBinding,
+ proofOfBindingSize, publicKeyCert, publicKeyCertSize)) {
+ eicDebug("Error creating certificate for signing key");
+ return false;
+ }
+
+ uint8_t nonce[12];
+ if (!eicOpsRandom(nonce, 12)) {
+ eicDebug("Error getting random");
+ return false;
+ }
+ if (!eicOpsEncryptAes128Gcm(
+ ctx->storageKey, nonce, signingKeyPriv, sizeof(signingKeyPriv),
+ // DocType is the additionalAuthenticatedData
+ (const uint8_t*)docType, docTypeLength, signingKeyBlob)) {
+ eicDebug("Error encrypting signing key");
+ return false;
+ }
+
+ return true;
+}
+
+bool eicPresentationCreateEphemeralKeyPair(
+ EicPresentation* ctx, uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]) {
+ uint8_t ephemeralPublicKey[EIC_P256_PUB_KEY_SIZE];
+ if (!eicOpsCreateEcKey(ctx->ephemeralPrivateKey, ephemeralPublicKey)) {
+ eicDebug("Error creating ephemeral key");
+ return false;
+ }
+ eicMemCpy(ephemeralPrivateKey, ctx->ephemeralPrivateKey,
+ EIC_P256_PRIV_KEY_SIZE);
+ return true;
+}
+
+bool eicPresentationCreateAuthChallenge(EicPresentation* ctx,
+ uint64_t* authChallenge) {
+ do {
+ if (!eicOpsRandom((uint8_t*)&(ctx->authChallenge), sizeof(uint64_t))) {
+ eicDebug("Failed generating random challenge");
+ return false;
+ }
+ } while (ctx->authChallenge == 0);
+ eicDebug("Created auth challenge %" PRIu64, ctx->authChallenge);
+ *authChallenge = ctx->authChallenge;
+ return true;
+}
+
+// From "COSE Algorithms" registry
+//
+#define COSE_ALG_ECDSA_256 -7
+
+bool eicPresentationValidateRequestMessage(
+ EicPresentation* ctx, const uint8_t* sessionTranscript,
+ size_t sessionTranscriptSize, const uint8_t* requestMessage,
+ size_t requestMessageSize, int coseSignAlg,
+ const uint8_t* readerSignatureOfToBeSigned,
+ size_t readerSignatureOfToBeSignedSize) {
+ if (ctx->readerPublicKeySize == 0) {
+ eicDebug("No public key for reader");
+ return false;
+ }
+
+ // Right now we only support ECDSA with SHA-256 (e.g. ES256).
+ //
+ if (coseSignAlg != COSE_ALG_ECDSA_256) {
+ eicDebug(
+ "COSE Signature algorithm for reader signature is %d, "
+ "only ECDSA with SHA-256 is supported right now",
+ coseSignAlg);
+ return false;
+ }
+
+ // What we're going to verify is the COSE ToBeSigned structure which
+ // looks like the following:
+ //
+ // Sig_structure = [
+ // context : "Signature" / "Signature1" / "CounterSignature",
+ // body_protected : empty_or_serialized_map,
+ // ? sign_protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ //
+ // So we're going to build that CBOR...
+ //
+ EicCbor cbor;
+ eicCborInit(&cbor, NULL, 0);
+ eicCborAppendArray(&cbor, 4);
+ eicCborAppendStringZ(&cbor, "Signature1");
+
+ // The COSE Encoded protected headers is just a single field with
+ // COSE_LABEL_ALG (1) -> coseSignAlg (e.g. -7). For simplicitly we just
+ // hard-code the CBOR encoding:
+ static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+ eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
+ sizeof(coseEncodedProtectedHeaders));
+
+ // External_aad is the empty bstr
+ static const uint8_t externalAad[0] = {};
+ eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
+
+ // For the payload, the _encoded_ form follows here. We handle this by simply
+ // opening a bstr, and then writing the CBOR. This requires us to know the
+ // size of said bstr, ahead of time... the CBOR to be written is
+ //
+ // ReaderAuthentication = [
+ // "ReaderAuthentication",
+ // SessionTranscript,
+ // ItemsRequestBytes
+ // ]
+ //
+ // ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
+ //
+ // ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
+ //
+ // which is easily calculated below
+ //
+ size_t calculatedSize = 0;
+ calculatedSize += 1; // Array of size 3
+ calculatedSize += 1; // "ReaderAuthentication" less than 24 bytes
+ calculatedSize +=
+ sizeof("ReaderAuthentication") - 1; // Don't include trailing NUL
+ calculatedSize += sessionTranscriptSize; // Already CBOR encoded
+ calculatedSize += 2; // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+ calculatedSize += 1 + eicCborAdditionalLengthBytesFor(requestMessageSize);
+ calculatedSize += requestMessageSize;
+
+ // However note that we're authenticating ReaderAuthenticationBytes which
+ // is a tagged bstr of the bytes of ReaderAuthentication. So need to get
+ // that in front.
+ size_t rabCalculatedSize = 0;
+ rabCalculatedSize +=
+ 2; // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+ rabCalculatedSize += 1 + eicCborAdditionalLengthBytesFor(calculatedSize);
+ rabCalculatedSize += calculatedSize;
+
+ // Begin the bytestring for ReaderAuthenticationBytes;
+ eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, rabCalculatedSize);
+
+ eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+
+ // Begins the bytestring for ReaderAuthentication;
+ eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, calculatedSize);
+
+ // And now that we know the size, let's fill it in...
+ //
+ size_t payloadOffset = cbor.size;
+ eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_ARRAY, 3);
+ eicCborAppendStringZ(&cbor, "ReaderAuthentication");
+ eicCborAppend(&cbor, sessionTranscript, sessionTranscriptSize);
+ eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+ eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, requestMessageSize);
+ eicCborAppend(&cbor, requestMessage, requestMessageSize);
+
+ if (cbor.size != payloadOffset + calculatedSize) {
+ eicDebug("CBOR size is %zd but we expected %zd", cbor.size,
+ payloadOffset + calculatedSize);
+ return false;
+ }
+ uint8_t toBeSignedDigest[EIC_SHA256_DIGEST_SIZE];
+ eicCborFinal(&cbor, toBeSignedDigest);
+
+ if (!eicOpsEcDsaVerifyWithPublicKey(
+ toBeSignedDigest, EIC_SHA256_DIGEST_SIZE, readerSignatureOfToBeSigned,
+ readerSignatureOfToBeSignedSize, ctx->readerPublicKey,
+ ctx->readerPublicKeySize)) {
+ eicDebug("Request message is not signed by public key");
+ return false;
+ }
+ ctx->requestMessageValidated = true;
+ return true;
+}
+
+// Validates the next certificate in the reader certificate chain.
+bool eicPresentationPushReaderCert(EicPresentation* ctx,
+ const uint8_t* certX509,
+ size_t certX509Size) {
+ // If we had a previous certificate, use its public key to validate this
+ // certificate.
+ if (ctx->readerPublicKeySize > 0) {
+ if (!eicOpsX509CertSignedByPublicKey(certX509, certX509Size,
+ ctx->readerPublicKey,
+ ctx->readerPublicKeySize)) {
+ eicDebug(
+ "Certificate is not signed by public key in the previous "
+ "certificate");
+ return false;
+ }
+ }
+
+ // Store the key of this certificate, this is used to validate the next
+ // certificate and also ACPs with certificates that use the same public key...
+ ctx->readerPublicKeySize = EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE;
+ if (!eicOpsX509GetPublicKey(certX509, certX509Size, ctx->readerPublicKey,
+ &ctx->readerPublicKeySize)) {
+ eicDebug("Error extracting public key from certificate");
+ return false;
+ }
+ if (ctx->readerPublicKeySize == 0) {
+ eicDebug("Zero-length public key in certificate");
+ return false;
+ }
+
+ return true;
+}
+
+bool eicPresentationSetAuthToken(
+ EicPresentation* ctx, uint64_t challenge, uint64_t secureUserId,
+ uint64_t authenticatorId, int hardwareAuthenticatorType, uint64_t timeStamp,
+ const uint8_t* mac, size_t macSize, uint64_t verificationTokenChallenge,
+ uint64_t verificationTokenTimestamp, int verificationTokenSecurityLevel,
+ const uint8_t* verificationTokenMac, size_t verificationTokenMacSize) {
+ // It doesn't make sense to accept any tokens if
+ // eicPresentationCreateAuthChallenge() was never called.
+ if (ctx->authChallenge == 0) {
+ eicDebug(
+ "Trying validate tokens when no auth-challenge was previously "
+ "generated");
+ return false;
+ }
+ // At least the verification-token must have the same challenge as what was
+ // generated.
+ if (verificationTokenChallenge != ctx->authChallenge) {
+ eicDebug(
+ "Challenge in verification token does not match the challenge "
+ "previously generated");
+ return false;
+ }
+ if (!eicOpsValidateAuthToken(
+ challenge, secureUserId, authenticatorId, hardwareAuthenticatorType,
+ timeStamp, mac, macSize, verificationTokenChallenge,
+ verificationTokenTimestamp, verificationTokenSecurityLevel,
+ verificationTokenMac, verificationTokenMacSize)) {
+ return false;
+ }
+ ctx->authTokenChallenge = challenge;
+ ctx->authTokenSecureUserId = secureUserId;
+ ctx->authTokenTimestamp = timeStamp;
+ ctx->verificationTokenTimestamp = verificationTokenTimestamp;
+ return true;
+}
+
+static bool checkUserAuth(EicPresentation* ctx, bool userAuthenticationRequired,
+ int timeoutMillis, uint64_t secureUserId) {
+ if (!userAuthenticationRequired) {
+ return true;
+ }
+
+ if (secureUserId != ctx->authTokenSecureUserId) {
+ eicDebug("secureUserId in profile differs from userId in authToken");
+ return false;
+ }
+
+ // Only ACP with auth-on-every-presentation - those with timeout == 0 - need
+ // the challenge to match...
+ if (timeoutMillis == 0) {
+ if (ctx->authTokenChallenge != ctx->authChallenge) {
+ eicDebug("Challenge in authToken (%" PRIu64
+ ") doesn't match the challenge "
+ "that was created (%" PRIu64 ") for this session",
+ ctx->authTokenChallenge, ctx->authChallenge);
+ return false;
+ }
+ }
+
+ uint64_t now = ctx->verificationTokenTimestamp;
+ if (ctx->authTokenTimestamp > now) {
+ eicDebug("Timestamp in authToken is in the future");
+ return false;
+ }
+
+ if (timeoutMillis > 0) {
+ if (now > ctx->authTokenTimestamp + timeoutMillis) {
+ eicDebug("Deadline for authToken is in the past");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool checkReaderAuth(EicPresentation* ctx,
+ const uint8_t* readerCertificate,
+ size_t readerCertificateSize) {
+ uint8_t publicKey[EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE];
+ size_t publicKeySize;
+
+ if (readerCertificateSize == 0) {
+ return true;
+ }
+
+ // Remember in this case certificate equality is done by comparing public
+ // keys, not bitwise comparison of the certificates.
+ //
+ publicKeySize = EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE;
+ if (!eicOpsX509GetPublicKey(readerCertificate, readerCertificateSize,
+ publicKey, &publicKeySize)) {
+ eicDebug("Error extracting public key from certificate");
+ return false;
+ }
+ if (publicKeySize == 0) {
+ eicDebug("Zero-length public key in certificate");
+ return false;
+ }
+
+ if ((ctx->readerPublicKeySize != publicKeySize) ||
+ (eicCryptoMemCmp(ctx->readerPublicKey, publicKey,
+ ctx->readerPublicKeySize) != 0)) {
+ return false;
+ }
+ return true;
+}
+
+// Note: This function returns false _only_ if an error occurred check for
+// access, _not_ whether access is granted. Whether access is granted is
+// returned in |accessGranted|.
+//
+bool eicPresentationValidateAccessControlProfile(
+ EicPresentation* ctx, int id, const uint8_t* readerCertificate,
+ size_t readerCertificateSize, bool userAuthenticationRequired,
+ int timeoutMillis, uint64_t secureUserId, const uint8_t mac[28],
+ bool* accessGranted, uint8_t* scratchSpace, size_t scratchSpaceSize) {
+ *accessGranted = false;
+ if (id < 0 || id >= 32) {
+ eicDebug("id value of %d is out of allowed range [0, 32[", id);
+ return false;
+ }
+
+ // Validate the MAC
+ EicCbor cborBuilder;
+ eicCborInit(&cborBuilder, scratchSpace, scratchSpaceSize);
+ if (!eicCborCalcAccessControl(
+ &cborBuilder, id, readerCertificate, readerCertificateSize,
+ userAuthenticationRequired, timeoutMillis, secureUserId)) {
+ return false;
+ }
+ if (!eicOpsDecryptAes128Gcm(ctx->storageKey, mac, 28, cborBuilder.buffer,
+ cborBuilder.size, NULL)) {
+ eicDebug("MAC for AccessControlProfile doesn't match");
+ return false;
+ }
+
+ bool passedUserAuth = checkUserAuth(ctx, userAuthenticationRequired,
+ timeoutMillis, secureUserId);
+ bool passedReaderAuth =
+ checkReaderAuth(ctx, readerCertificate, readerCertificateSize);
+
+ ctx->accessControlProfileMaskValidated |= (1U << id);
+ if (readerCertificateSize > 0) {
+ ctx->accessControlProfileMaskUsesReaderAuth |= (1U << id);
+ }
+ if (!passedReaderAuth) {
+ ctx->accessControlProfileMaskFailedReaderAuth |= (1U << id);
+ }
+ if (!passedUserAuth) {
+ ctx->accessControlProfileMaskFailedUserAuth |= (1U << id);
+ }
+
+ if (passedUserAuth && passedReaderAuth) {
+ *accessGranted = true;
+ eicDebug("Access granted for id %d", id);
+ }
+ return true;
+}
+
+bool eicPresentationCalcMacKey(
+ EicPresentation* ctx, const uint8_t* sessionTranscript,
+ size_t sessionTranscriptSize,
+ const uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE],
+ const uint8_t signingKeyBlob[60], const char* docType, size_t docTypeLength,
+ unsigned int numNamespacesWithValues, size_t expectedDeviceNamespacesSize) {
+ uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
+ if (!eicOpsDecryptAes128Gcm(ctx->storageKey, signingKeyBlob, 60,
+ (const uint8_t*)docType, docTypeLength,
+ signingKeyPriv)) {
+ eicDebug("Error decrypting signingKeyBlob");
+ return false;
+ }
+
+ uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE];
+ if (!eicOpsEcdh(readerEphemeralPublicKey, signingKeyPriv, sharedSecret)) {
+ eicDebug("ECDH failed");
+ return false;
+ }
+
+ EicCbor cbor;
+ eicCborInit(&cbor, NULL, 0);
+ eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+ eicCborAppendByteString(&cbor, sessionTranscript, sessionTranscriptSize);
+ uint8_t salt[EIC_SHA256_DIGEST_SIZE];
+ eicCborFinal(&cbor, salt);
+
+ const uint8_t info[7] = {'E', 'M', 'a', 'c', 'K', 'e', 'y'};
+ uint8_t derivedKey[32];
+ if (!eicOpsHkdf(sharedSecret, EIC_P256_COORDINATE_SIZE, salt, sizeof(salt),
+ info, sizeof(info), derivedKey, sizeof(derivedKey))) {
+ eicDebug("HKDF failed");
+ return false;
+ }
+
+ eicCborInitHmacSha256(&ctx->cbor, NULL, 0, derivedKey, sizeof(derivedKey));
+ ctx->buildCbor = true;
+
+ // What we're going to calculate the HMAC-SHA256 is the COSE ToBeMaced
+ // structure which looks like the following:
+ //
+ // MAC_structure = [
+ // context : "MAC" / "MAC0",
+ // protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ //
+ eicCborAppendArray(&ctx->cbor, 4);
+ eicCborAppendStringZ(&ctx->cbor, "MAC0");
+
+ // The COSE Encoded protected headers is just a single field with
+ // COSE_LABEL_ALG (1) -> COSE_ALG_HMAC_256_256 (5). For simplicitly we just
+ // hard-code the CBOR encoding:
+ static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x05};
+ eicCborAppendByteString(&ctx->cbor, coseEncodedProtectedHeaders,
+ sizeof(coseEncodedProtectedHeaders));
+
+ // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+ // so external_aad is the empty bstr
+ static const uint8_t externalAad[0] = {};
+ eicCborAppendByteString(&ctx->cbor, externalAad, sizeof(externalAad));
+
+ // For the payload, the _encoded_ form follows here. We handle this by simply
+ // opening a bstr, and then writing the CBOR. This requires us to know the
+ // size of said bstr, ahead of time... the CBOR to be written is
+ //
+ // DeviceAuthentication = [
+ // "DeviceAuthentication",
+ // SessionTranscript,
+ // DocType, ; DocType as used in Documents structure in
+ // OfflineResponse DeviceNameSpacesBytes
+ // ]
+ //
+ // DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
+ //
+ // DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
+ //
+ // which is easily calculated below
+ //
+ size_t calculatedSize = 0;
+ calculatedSize += 1; // Array of size 4
+ calculatedSize += 1; // "DeviceAuthentication" less than 24 bytes
+ calculatedSize +=
+ sizeof("DeviceAuthentication") - 1; // Don't include trailing NUL
+ calculatedSize += sessionTranscriptSize; // Already CBOR encoded
+ calculatedSize +=
+ 1 + eicCborAdditionalLengthBytesFor(docTypeLength) + docTypeLength;
+ calculatedSize += 2; // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+ calculatedSize +=
+ 1 + eicCborAdditionalLengthBytesFor(expectedDeviceNamespacesSize);
+ calculatedSize += expectedDeviceNamespacesSize;
+
+ // However note that we're authenticating DeviceAuthenticationBytes which
+ // is a tagged bstr of the bytes of DeviceAuthentication. So need to get
+ // that in front.
+ size_t dabCalculatedSize = 0;
+ dabCalculatedSize +=
+ 2; // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+ dabCalculatedSize += 1 + eicCborAdditionalLengthBytesFor(calculatedSize);
+ dabCalculatedSize += calculatedSize;
+
+ // Begin the bytestring for DeviceAuthenticationBytes;
+ eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, dabCalculatedSize);
+
+ eicCborAppendSemantic(&ctx->cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+
+ // Begins the bytestring for DeviceAuthentication;
+ eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, calculatedSize);
+
+ eicCborAppendArray(&ctx->cbor, 4);
+ eicCborAppendStringZ(&ctx->cbor, "DeviceAuthentication");
+ eicCborAppend(&ctx->cbor, sessionTranscript, sessionTranscriptSize);
+ eicCborAppendString(&ctx->cbor, docType, docTypeLength);
+
+ // For the payload, the _encoded_ form follows here. We handle this by simply
+ // opening a bstr, and then writing the CBOR. This requires us to know the
+ // size of said bstr, ahead of time.
+ eicCborAppendSemantic(&ctx->cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+ eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING,
+ expectedDeviceNamespacesSize);
+ ctx->expectedCborSizeAtEnd = expectedDeviceNamespacesSize + ctx->cbor.size;
+
+ eicCborAppendMap(&ctx->cbor, numNamespacesWithValues);
+ return true;
+}
+
+bool eicPresentationStartRetrieveEntries(EicPresentation* ctx) {
+ // HAL may use this object multiple times to retrieve data so need to reset
+ // various state objects here.
+ ctx->requestMessageValidated = false;
+ ctx->buildCbor = false;
+ ctx->accessControlProfileMaskValidated = 0;
+ ctx->accessControlProfileMaskUsesReaderAuth = 0;
+ ctx->accessControlProfileMaskFailedReaderAuth = 0;
+ ctx->accessControlProfileMaskFailedUserAuth = 0;
+ ctx->readerPublicKeySize = 0;
+ return true;
+}
+
+EicAccessCheckResult eicPresentationStartRetrieveEntryValue(
+ EicPresentation* ctx, const char* nameSpace, size_t nameSpaceLength,
+ const char* name, size_t nameLength, unsigned int newNamespaceNumEntries,
+ int32_t entrySize, const uint8_t* accessControlProfileIds,
+ size_t numAccessControlProfileIds, uint8_t* scratchSpace,
+ size_t scratchSpaceSize) {
+ (void)entrySize;
+ uint8_t* additionalDataCbor = scratchSpace;
+ size_t additionalDataCborBufferSize = scratchSpaceSize;
+ size_t additionalDataCborSize;
+
+ if (newNamespaceNumEntries > 0) {
+ eicCborAppendString(&ctx->cbor, nameSpace, nameSpaceLength);
+ eicCborAppendMap(&ctx->cbor, newNamespaceNumEntries);
+ }
+
+ // We'll need to calc and store a digest of additionalData to check that it's
+ // the same additionalData being passed in for every
+ // eicPresentationRetrieveEntryValue() call...
+ //
+ ctx->accessCheckOk = false;
+ if (!eicCborCalcEntryAdditionalData(
+ accessControlProfileIds, numAccessControlProfileIds, nameSpace,
+ nameSpaceLength, name, nameLength, additionalDataCbor,
+ additionalDataCborBufferSize, &additionalDataCborSize,
+ ctx->additionalDataSha256)) {
+ return EIC_ACCESS_CHECK_RESULT_FAILED;
+ }
+
+ if (numAccessControlProfileIds == 0) {
+ return EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES;
+ }
+
+ // Access is granted if at least one of the profiles grants access.
+ //
+ // If an item is configured without any profiles, access is denied.
+ //
+ EicAccessCheckResult result = EIC_ACCESS_CHECK_RESULT_FAILED;
+ for (size_t n = 0; n < numAccessControlProfileIds; n++) {
+ int id = accessControlProfileIds[n];
+ uint32_t idBitMask = (1 << id);
+
+ // If the access control profile wasn't validated, this is an error and we
+ // fail immediately.
+ bool validated =
+ ((ctx->accessControlProfileMaskValidated & idBitMask) != 0);
+ if (!validated) {
+ eicDebug("No ACP for profile id %d", id);
+ return EIC_ACCESS_CHECK_RESULT_FAILED;
+ }
+
+ // Otherwise, we _did_ validate the profile. If none of the checks
+ // failed, we're done
+ bool failedUserAuth =
+ ((ctx->accessControlProfileMaskFailedUserAuth & idBitMask) != 0);
+ bool failedReaderAuth =
+ ((ctx->accessControlProfileMaskFailedReaderAuth & idBitMask) != 0);
+ if (!failedUserAuth && !failedReaderAuth) {
+ result = EIC_ACCESS_CHECK_RESULT_OK;
+ break;
+ }
+ // One of the checks failed, convey which one
+ if (failedUserAuth) {
+ result = EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED;
+ } else {
+ result = EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED;
+ }
+ }
+ eicDebug("Result %d for name %s", result, name);
+
+ if (result == EIC_ACCESS_CHECK_RESULT_OK) {
+ eicCborAppendString(&ctx->cbor, name, nameLength);
+ ctx->accessCheckOk = true;
+ }
+ return result;
+}
+
+// Note: |content| must be big enough to hold |encryptedContentSize| - 28 bytes.
+bool eicPresentationRetrieveEntryValue(
+ EicPresentation* ctx, const uint8_t* encryptedContent,
+ size_t encryptedContentSize, uint8_t* content, const char* nameSpace,
+ size_t nameSpaceLength, const char* name, size_t nameLength,
+ const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds,
+ uint8_t* scratchSpace, size_t scratchSpaceSize) {
+ uint8_t* additionalDataCbor = scratchSpace;
+ size_t additionalDataCborBufferSize = scratchSpaceSize;
+ size_t additionalDataCborSize;
+
+ uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
+ if (!eicCborCalcEntryAdditionalData(
+ accessControlProfileIds, numAccessControlProfileIds, nameSpace,
+ nameSpaceLength, name, nameLength, additionalDataCbor,
+ additionalDataCborBufferSize, &additionalDataCborSize,
+ calculatedSha256)) {
+ return false;
+ }
+
+ if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256,
+ EIC_SHA256_DIGEST_SIZE) != 0) {
+ eicDebug("SHA-256 mismatch of additionalData");
+ return false;
+ }
+ if (!ctx->accessCheckOk) {
+ eicDebug("Attempting to retrieve a value for which access is not granted");
+ return false;
+ }
+
+ if (!eicOpsDecryptAes128Gcm(ctx->storageKey, encryptedContent,
+ encryptedContentSize, additionalDataCbor,
+ additionalDataCborSize, content)) {
+ eicDebug("Error decrypting content");
+ return false;
+ }
+
+ eicCborAppend(&ctx->cbor, content, encryptedContentSize - 28);
+
+ return true;
+}
+
+bool eicPresentationFinishRetrieval(EicPresentation* ctx,
+ uint8_t* digestToBeMaced,
+ size_t* digestToBeMacedSize) {
+ if (!ctx->buildCbor) {
+ *digestToBeMacedSize = 0;
+ return true;
+ }
+ if (*digestToBeMacedSize != 32) {
+ return false;
+ }
+
+ // This verifies that the correct expectedDeviceNamespacesSize value was
+ // passed in at eicPresentationCalcMacKey() time.
+ if (ctx->cbor.size != ctx->expectedCborSizeAtEnd) {
+ eicDebug("CBOR size is %zd, was expecting %zd", ctx->cbor.size,
+ ctx->expectedCborSizeAtEnd);
+ return false;
+ }
+ eicCborFinal(&ctx->cbor, digestToBeMaced);
+ return true;
+}
+
+bool eicPresentationDeleteCredential(
+ EicPresentation* ctx, const char* docType, size_t docTypeLength,
+ const uint8_t* challenge, size_t challengeSize, bool includeChallenge,
+ size_t proofOfDeletionCborSize,
+ uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+ EicCbor cbor;
+
+ eicCborInit(&cbor, NULL, 0);
+
+ // What we're going to sign is the COSE ToBeSigned structure which
+ // looks like the following:
+ //
+ // Sig_structure = [
+ // context : "Signature" / "Signature1" / "CounterSignature",
+ // body_protected : empty_or_serialized_map,
+ // ? sign_protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ //
+ eicCborAppendArray(&cbor, 4);
+ eicCborAppendStringZ(&cbor, "Signature1");
+
+ // The COSE Encoded protected headers is just a single field with
+ // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
+ // hard-code the CBOR encoding:
+ static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+ eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
+ sizeof(coseEncodedProtectedHeaders));
+
+ // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+ // so external_aad is the empty bstr
+ static const uint8_t externalAad[0] = {};
+ eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
+
+ // For the payload, the _encoded_ form follows here. We handle this by simply
+ // opening a bstr, and then writing the CBOR. This requires us to know the
+ // size of said bstr, ahead of time.
+ eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfDeletionCborSize);
+
+ // Finally, the CBOR that we're actually signing.
+ eicCborAppendArray(&cbor, includeChallenge ? 4 : 3);
+ eicCborAppendStringZ(&cbor, "ProofOfDeletion");
+ eicCborAppendString(&cbor, docType, docTypeLength);
+ if (includeChallenge) {
+ eicCborAppendByteString(&cbor, challenge, challengeSize);
+ }
+ eicCborAppendBool(&cbor, ctx->testCredential);
+
+ uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
+ eicCborFinal(&cbor, cborSha256);
+ if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256,
+ signatureOfToBeSigned)) {
+ eicDebug("Error signing proofOfDeletion");
+ return false;
+ }
+
+ return true;
+}
+
+bool eicPresentationProveOwnership(
+ EicPresentation* ctx, const char* docType, size_t docTypeLength,
+ bool testCredential, const uint8_t* challenge, size_t challengeSize,
+ size_t proofOfOwnershipCborSize,
+ uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+ EicCbor cbor;
+
+ eicCborInit(&cbor, NULL, 0);
+
+ // What we're going to sign is the COSE ToBeSigned structure which
+ // looks like the following:
+ //
+ // Sig_structure = [
+ // context : "Signature" / "Signature1" / "CounterSignature",
+ // body_protected : empty_or_serialized_map,
+ // ? sign_protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ //
+ eicCborAppendArray(&cbor, 4);
+ eicCborAppendStringZ(&cbor, "Signature1");
+
+ // The COSE Encoded protected headers is just a single field with
+ // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
+ // hard-code the CBOR encoding:
+ static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+ eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
+ sizeof(coseEncodedProtectedHeaders));
+
+ // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+ // so external_aad is the empty bstr
+ static const uint8_t externalAad[0] = {};
+ eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
+
+ // For the payload, the _encoded_ form follows here. We handle this by simply
+ // opening a bstr, and then writing the CBOR. This requires us to know the
+ // size of said bstr, ahead of time.
+ eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING,
+ proofOfOwnershipCborSize);
+
+ // Finally, the CBOR that we're actually signing.
+ eicCborAppendArray(&cbor, 4);
+ eicCborAppendStringZ(&cbor, "ProofOfOwnership");
+ eicCborAppendString(&cbor, docType, docTypeLength);
+ eicCborAppendByteString(&cbor, challenge, challengeSize);
+ eicCborAppendBool(&cbor, testCredential);
+
+ uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
+ eicCborFinal(&cbor, cborSha256);
+ if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256,
+ signatureOfToBeSigned)) {
+ eicDebug("Error signing proofOfDeletion");
+ return false;
+ }
+
+ return true;
+}
diff --git a/guest/hals/identity/libeic/EicPresentation.h b/guest/hals/identity/libeic/EicPresentation.h
new file mode 100644
index 000000000..72d840145
--- /dev/null
+++ b/guest/hals/identity/libeic/EicPresentation.h
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EicCbor.h"
+
+// The maximum size we support for public keys in reader certificates.
+#define EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE 65
+
+typedef struct {
+ int featureLevel;
+
+ uint8_t storageKey[EIC_AES_128_KEY_SIZE];
+ uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
+
+ uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE];
+
+ // The challenge generated with eicPresentationCreateAuthChallenge()
+ uint64_t authChallenge;
+
+ // Set by eicPresentationSetAuthToken() and contains the fields
+ // from the passed in authToken and verificationToken.
+ //
+ uint64_t authTokenChallenge;
+ uint64_t authTokenSecureUserId;
+ uint64_t authTokenTimestamp;
+ uint64_t verificationTokenTimestamp;
+
+ // The public key for the reader.
+ //
+ // (During the process of pushing reader certificates, this is also used to
+ // store the public key of the previously pushed certificate.)
+ //
+ uint8_t readerPublicKey[EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE];
+ size_t readerPublicKeySize;
+
+ // This is set to true only if eicPresentationValidateRequestMessage()
+ // successfully validated the requestMessage.
+ //
+ // Why even record this? Because there's no requirement the HAL actually calls
+ // that function and we validate ACPs before it's called... so it's possible
+ // that a compromised HAL could trick us into marking ACPs as authorized while
+ // they in fact aren't.
+ bool requestMessageValidated;
+ bool buildCbor;
+
+ // Set to true initialized as a test credential.
+ bool testCredential;
+
+ // Set to true if the evaluation of access control checks in
+ // eicPresentationStartRetrieveEntryValue() resulted
+ // EIC_ACCESS_CHECK_RESULT_OK
+ bool accessCheckOk;
+
+ // These are bitmasks indicating which of the possible 32 access control
+ // profiles are authorized. They are built up by
+ // eicPresentationValidateAccessControlProfile().
+ //
+ uint32_t
+ accessControlProfileMaskValidated; // True if the profile was validated.
+ uint32_t accessControlProfileMaskUsesReaderAuth; // True if the ACP is using
+ // reader auth
+ uint32_t
+ accessControlProfileMaskFailedReaderAuth; // True if failed reader auth
+ uint32_t accessControlProfileMaskFailedUserAuth; // True if failed user auth
+
+ // SHA-256 for AdditionalData, updated for each entry.
+ uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
+
+ // SHA-256 of ProofOfProvisioning. Set to NUL-bytes or initialized from
+ // CredentialKeys data if credential was created with feature version 202101
+ // or later.
+ uint8_t proofOfProvisioningSha256[EIC_SHA256_DIGEST_SIZE];
+
+ size_t expectedCborSizeAtEnd;
+ EicCbor cbor;
+} EicPresentation;
+
+bool eicPresentationInit(EicPresentation* ctx, bool testCredential,
+ const char* docType, size_t docTypeLength,
+ const uint8_t* encryptedCredentialKeys,
+ size_t encryptedCredentialKeysSize);
+
+bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx,
+ const char* docType,
+ size_t docTypeLength, time_t now,
+ uint8_t* publicKeyCert,
+ size_t* publicKeyCertSize,
+ uint8_t signingKeyBlob[60]);
+
+// Create an ephemeral key-pair.
+//
+// The private key is stored in |ctx->ephemeralPrivateKey| and also returned in
+// |ephemeralPrivateKey|.
+//
+bool eicPresentationCreateEphemeralKeyPair(
+ EicPresentation* ctx, uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]);
+
+// Returns a non-zero challenge in |authChallenge|.
+bool eicPresentationCreateAuthChallenge(EicPresentation* ctx,
+ uint64_t* authChallenge);
+
+// Starts retrieving entries.
+//
+bool eicPresentationStartRetrieveEntries(EicPresentation* ctx);
+
+// Sets the auth-token.
+bool eicPresentationSetAuthToken(
+ EicPresentation* ctx, uint64_t challenge, uint64_t secureUserId,
+ uint64_t authenticatorId, int hardwareAuthenticatorType, uint64_t timeStamp,
+ const uint8_t* mac, size_t macSize, uint64_t verificationTokenChallenge,
+ uint64_t verificationTokenTimeStamp, int verificationTokenSecurityLevel,
+ const uint8_t* verificationTokenMac, size_t verificationTokenMacSize);
+
+// Function to push certificates in the reader certificate chain.
+//
+// This should start with the root certificate (e.g. the last in the chain) and
+// continue up the chain, ending with the certificate for the reader.
+//
+// Calls to this function should be interleaved with calls to the
+// eicPresentationValidateAccessControlProfile() function, see below.
+//
+bool eicPresentationPushReaderCert(EicPresentation* ctx,
+ const uint8_t* certX509,
+ size_t certX509Size);
+
+// Checks an access control profile.
+//
+// Returns false if an error occurred while checking the profile (e.g. MAC
+// doesn't check out).
+//
+// Returns in |accessGranted| whether access is granted.
+//
+// If |readerCertificate| is non-empty and the public key of one of those
+// certificates appear in the chain presented by the reader, this function must
+// be called after pushing that certificate using
+// eicPresentationPushReaderCert().
+//
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done
+// this way to avoid allocating stack space.
+//
+bool eicPresentationValidateAccessControlProfile(
+ EicPresentation* ctx, int id, const uint8_t* readerCertificate,
+ size_t readerCertificateSize, bool userAuthenticationRequired,
+ int timeoutMillis, uint64_t secureUserId, const uint8_t mac[28],
+ bool* accessGranted, uint8_t* scratchSpace, size_t scratchSpaceSize);
+
+// Validates that the given requestMessage is signed by the public key in the
+// certificate last set with eicPresentationPushReaderCert().
+//
+// The format of the signature is the same encoding as the 'signature' field of
+// COSE_Sign1 - that is, it's the R and S integers both with the same length as
+// the key-size.
+//
+// Must be called after eicPresentationPushReaderCert() have been used to push
+// the final certificate. Which is the certificate of the reader itself.
+//
+bool eicPresentationValidateRequestMessage(
+ EicPresentation* ctx, const uint8_t* sessionTranscript,
+ size_t sessionTranscriptSize, const uint8_t* requestMessage,
+ size_t requestMessageSize, int coseSignAlg,
+ const uint8_t* readerSignatureOfToBeSigned,
+ size_t readerSignatureOfToBeSignedSize);
+
+typedef enum {
+ // Returned if access is granted.
+ EIC_ACCESS_CHECK_RESULT_OK,
+
+ // Returned if an error occurred checking for access.
+ EIC_ACCESS_CHECK_RESULT_FAILED,
+
+ // Returned if access was denied because item is configured without any
+ // access control profiles.
+ EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES,
+
+ // Returned if access was denied because of user authentication.
+ EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED,
+
+ // Returned if access was denied because of reader authentication.
+ EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED,
+} EicAccessCheckResult;
+
+// Passes enough information to calculate the MACing key
+//
+bool eicPresentationCalcMacKey(
+ EicPresentation* ctx, const uint8_t* sessionTranscript,
+ size_t sessionTranscriptSize,
+ const uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE],
+ const uint8_t signingKeyBlob[60], const char* docType, size_t docTypeLength,
+ unsigned int numNamespacesWithValues, size_t expectedDeviceNamespacesSize);
+
+// The scratchSpace should be set to a buffer at least 512 bytes (ideally 1024
+// bytes, the bigger the better). It's done this way to avoid allocating stack
+// space.
+//
+EicAccessCheckResult eicPresentationStartRetrieveEntryValue(
+ EicPresentation* ctx, const char* nameSpace, size_t nameSpaceLength,
+ const char* name, size_t nameLength, unsigned int newNamespaceNumEntries,
+ int32_t entrySize, const uint8_t* accessControlProfileIds,
+ size_t numAccessControlProfileIds, uint8_t* scratchSpace,
+ size_t scratchSpaceSize);
+
+// Note: |content| must be big enough to hold |encryptedContentSize| - 28 bytes.
+//
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done this
+// way to avoid allocating stack space.
+//
+bool eicPresentationRetrieveEntryValue(
+ EicPresentation* ctx, const uint8_t* encryptedContent,
+ size_t encryptedContentSize, uint8_t* content, const char* nameSpace,
+ size_t nameSpaceLength, const char* name, size_t nameLength,
+ const uint8_t* accessControlProfileIds, size_t numAccessControlProfileIds,
+ uint8_t* scratchSpace, size_t scratchSpaceSize);
+
+// Returns the HMAC-SHA256 of |ToBeMaced| as per RFC 8051 "6.3. How to Compute
+// and Verify a MAC".
+bool eicPresentationFinishRetrieval(EicPresentation* ctx,
+ uint8_t* digestToBeMaced,
+ size_t* digestToBeMacedSize);
+
+// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
+// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
+// where content is set to the ProofOfDeletion CBOR.
+//
+bool eicPresentationDeleteCredential(
+ EicPresentation* ctx, const char* docType, size_t docTypeLength,
+ const uint8_t* challenge, size_t challengeSize, bool includeChallenge,
+ size_t proofOfDeletionCborSize,
+ uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
+// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
+// where content is set to the ProofOfOwnership CBOR.
+//
+bool eicPresentationProveOwnership(
+ EicPresentation* ctx, const char* docType, size_t docTypeLength,
+ bool testCredential, const uint8_t* challenge, size_t challengeSize,
+ size_t proofOfOwnershipCborSize,
+ uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
diff --git a/guest/hals/identity/libeic/EicProvisioning.c b/guest/hals/identity/libeic/EicProvisioning.c
new file mode 100644
index 000000000..1f691cd7e
--- /dev/null
+++ b/guest/hals/identity/libeic/EicProvisioning.c
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#include "EicProvisioning.h"
+#include "EicCommon.h"
+
+bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential) {
+ eicMemSet(ctx, '\0', sizeof(EicProvisioning));
+ ctx->testCredential = testCredential;
+ if (!eicOpsRandom(ctx->storageKey, EIC_AES_128_KEY_SIZE)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential,
+ const char* docType, size_t docTypeLength,
+ const uint8_t* encryptedCredentialKeys,
+ size_t encryptedCredentialKeysSize) {
+ uint8_t credentialKeys[EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101];
+
+ // For feature version 202009 it's 52 bytes long and for feature version
+ // 202101 it's 86 bytes (the additional data is the ProofOfProvisioning
+ // SHA-256). We need to support loading all feature versions.
+ //
+ bool expectPopSha256 = false;
+ if (encryptedCredentialKeysSize ==
+ EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202009 + 28) {
+ /* do nothing */
+ } else if (encryptedCredentialKeysSize ==
+ EIC_CREDENTIAL_KEYS_CBOR_SIZE_FEATURE_VERSION_202101 + 28) {
+ expectPopSha256 = true;
+ } else {
+ eicDebug("Unexpected size %zd for encryptedCredentialKeys",
+ encryptedCredentialKeysSize);
+ return false;
+ }
+
+ eicMemSet(ctx, '\0', sizeof(EicProvisioning));
+ ctx->testCredential = testCredential;
+
+ if (!eicOpsDecryptAes128Gcm(
+ eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
+ encryptedCredentialKeysSize,
+ // DocType is the additionalAuthenticatedData
+ (const uint8_t*)docType, docTypeLength, credentialKeys)) {
+ eicDebug("Error decrypting CredentialKeys");
+ return false;
+ }
+
+ // It's supposed to look like this;
+ //
+ // Feature version 202009:
+ //
+ // CredentialKeys = [
+ // bstr, ; storageKey, a 128-bit AES key
+ // bstr, ; credentialPrivKey, the private key for credentialKey
+ // ]
+ //
+ // Feature version 202101:
+ //
+ // CredentialKeys = [
+ // bstr, ; storageKey, a 128-bit AES key
+ // bstr, ; credentialPrivKey, the private key for credentialKey
+ // bstr ; proofOfProvisioning SHA-256
+ // ]
+ //
+ // where storageKey is 16 bytes, credentialPrivateKey is 32 bytes, and
+ // proofOfProvisioning SHA-256 is 32 bytes.
+ //
+ if (credentialKeys[0] !=
+ (expectPopSha256 ? 0x83 : 0x82) || // array of two or three elements
+ credentialKeys[1] != 0x50 || // 16-byte bstr
+ credentialKeys[18] != 0x58 ||
+ credentialKeys[19] != 0x20) { // 32-byte bstr
+ eicDebug("Invalid CBOR for CredentialKeys");
+ return false;
+ }
+ if (expectPopSha256) {
+ if (credentialKeys[52] != 0x58 ||
+ credentialKeys[53] != 0x20) { // 32-byte bstr
+ eicDebug("Invalid CBOR for CredentialKeys");
+ return false;
+ }
+ }
+ eicMemCpy(ctx->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
+ eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20,
+ EIC_P256_PRIV_KEY_SIZE);
+ // Note: We don't care about the previous ProofOfProvisioning SHA-256
+ ctx->isUpdate = true;
+ return true;
+}
+
+bool eicProvisioningCreateCredentialKey(
+ EicProvisioning* ctx, const uint8_t* challenge, size_t challengeSize,
+ const uint8_t* applicationId, size_t applicationIdSize,
+ uint8_t* publicKeyCert, size_t* publicKeyCertSize) {
+ if (ctx->isUpdate) {
+ eicDebug("Cannot create CredentialKey on update");
+ return false;
+ }
+
+ if (!eicOpsCreateCredentialKey(ctx->credentialPrivateKey, challenge,
+ challengeSize, applicationId,
+ applicationIdSize, ctx->testCredential,
+ publicKeyCert, publicKeyCertSize)) {
+ return false;
+ }
+ return true;
+}
+
+bool eicProvisioningStartPersonalization(
+ EicProvisioning* ctx, int accessControlProfileCount, const int* entryCounts,
+ size_t numEntryCounts, const char* docType, size_t docTypeLength,
+ size_t expectedProofOfProvisioningSize) {
+ if (numEntryCounts >= EIC_MAX_NUM_NAMESPACES) {
+ return false;
+ }
+ if (accessControlProfileCount >= EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS) {
+ return false;
+ }
+
+ ctx->numEntryCounts = numEntryCounts;
+ if (numEntryCounts > EIC_MAX_NUM_NAMESPACES) {
+ return false;
+ }
+ for (size_t n = 0; n < numEntryCounts; n++) {
+ if (entryCounts[n] >= 256) {
+ return false;
+ }
+ ctx->entryCounts[n] = entryCounts[n];
+ }
+ ctx->curNamespace = -1;
+ ctx->curNamespaceNumProcessed = 0;
+
+ eicCborInit(&ctx->cbor, NULL, 0);
+
+ // What we're going to sign is the COSE ToBeSigned structure which
+ // looks like the following:
+ //
+ // Sig_structure = [
+ // context : "Signature" / "Signature1" / "CounterSignature",
+ // body_protected : empty_or_serialized_map,
+ // ? sign_protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ //
+ eicCborAppendArray(&ctx->cbor, 4);
+ eicCborAppendStringZ(&ctx->cbor, "Signature1");
+
+ // The COSE Encoded protected headers is just a single field with
+ // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
+ // hard-code the CBOR encoding:
+ static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+ eicCborAppendByteString(&ctx->cbor, coseEncodedProtectedHeaders,
+ sizeof(coseEncodedProtectedHeaders));
+
+ // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+ // so external_aad is the empty bstr
+ static const uint8_t externalAad[0] = {};
+ eicCborAppendByteString(&ctx->cbor, externalAad, sizeof(externalAad));
+
+ // For the payload, the _encoded_ form follows here. We handle this by simply
+ // opening a bstr, and then writing the CBOR. This requires us to know the
+ // size of said bstr, ahead of time.
+ eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING,
+ expectedProofOfProvisioningSize);
+ ctx->expectedCborSizeAtEnd = expectedProofOfProvisioningSize + ctx->cbor.size;
+
+ eicOpsSha256Init(&ctx->proofOfProvisioningDigester);
+ eicCborEnableSecondaryDigesterSha256(&ctx->cbor,
+ &ctx->proofOfProvisioningDigester);
+
+ eicCborAppendArray(&ctx->cbor, 5);
+ eicCborAppendStringZ(&ctx->cbor, "ProofOfProvisioning");
+ eicCborAppendString(&ctx->cbor, docType, docTypeLength);
+
+ eicCborAppendArray(&ctx->cbor, accessControlProfileCount);
+
+ return true;
+}
+
+bool eicProvisioningAddAccessControlProfile(
+ EicProvisioning* ctx, int id, const uint8_t* readerCertificate,
+ size_t readerCertificateSize, bool userAuthenticationRequired,
+ uint64_t timeoutMillis, uint64_t secureUserId, uint8_t outMac[28],
+ uint8_t* scratchSpace, size_t scratchSpaceSize) {
+ EicCbor cborBuilder;
+ eicCborInit(&cborBuilder, scratchSpace, scratchSpaceSize);
+
+ if (!eicCborCalcAccessControl(
+ &cborBuilder, id, readerCertificate, readerCertificateSize,
+ userAuthenticationRequired, timeoutMillis, secureUserId)) {
+ return false;
+ }
+
+ // Calculate and return MAC
+ uint8_t nonce[12];
+ if (!eicOpsRandom(nonce, 12)) {
+ return false;
+ }
+ if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, NULL, 0,
+ cborBuilder.buffer, cborBuilder.size, outMac)) {
+ return false;
+ }
+
+ // The ACP CBOR in the provisioning receipt doesn't include secureUserId so
+ // build it again.
+ eicCborInit(&cborBuilder, scratchSpace, scratchSpaceSize);
+ if (!eicCborCalcAccessControl(
+ &cborBuilder, id, readerCertificate, readerCertificateSize,
+ userAuthenticationRequired, timeoutMillis, 0 /* secureUserId */)) {
+ return false;
+ }
+
+ // Append the CBOR from the local builder to the digester.
+ eicCborAppend(&ctx->cbor, cborBuilder.buffer, cborBuilder.size);
+
+ return true;
+}
+
+bool eicProvisioningBeginAddEntry(EicProvisioning* ctx,
+ const uint8_t* accessControlProfileIds,
+ size_t numAccessControlProfileIds,
+ const char* nameSpace, size_t nameSpaceLength,
+ const char* name, size_t nameLength,
+ uint64_t entrySize, uint8_t* scratchSpace,
+ size_t scratchSpaceSize) {
+ uint8_t* additionalDataCbor = scratchSpace;
+ const size_t additionalDataCborBufSize = scratchSpaceSize;
+ size_t additionalDataCborSize;
+
+ // We'll need to calc and store a digest of additionalData to check that it's
+ // the same additionalData being passed in for every
+ // eicProvisioningAddEntryValue() call...
+ if (!eicCborCalcEntryAdditionalData(
+ accessControlProfileIds, numAccessControlProfileIds, nameSpace,
+ nameSpaceLength, name, nameLength, additionalDataCbor,
+ additionalDataCborBufSize, &additionalDataCborSize,
+ ctx->additionalDataSha256)) {
+ return false;
+ }
+
+ if (ctx->curNamespace == -1) {
+ ctx->curNamespace = 0;
+ ctx->curNamespaceNumProcessed = 0;
+ // Opens the main map: { * Namespace => [ + Entry ] }
+ eicCborAppendMap(&ctx->cbor, ctx->numEntryCounts);
+ eicCborAppendString(&ctx->cbor, nameSpace, nameSpaceLength);
+ // Opens the per-namespace array: [ + Entry ]
+ eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
+ }
+
+ if (ctx->curNamespaceNumProcessed == ctx->entryCounts[ctx->curNamespace]) {
+ ctx->curNamespace += 1;
+ ctx->curNamespaceNumProcessed = 0;
+ eicCborAppendString(&ctx->cbor, nameSpace, nameSpaceLength);
+ // Opens the per-namespace array: [ + Entry ]
+ eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
+ }
+
+ eicCborAppendMap(&ctx->cbor, 3);
+ eicCborAppendStringZ(&ctx->cbor, "name");
+ eicCborAppendString(&ctx->cbor, name, nameLength);
+
+ ctx->curEntrySize = entrySize;
+ ctx->curEntryNumBytesReceived = 0;
+
+ eicCborAppendStringZ(&ctx->cbor, "value");
+
+ ctx->curNamespaceNumProcessed += 1;
+ return true;
+}
+
+bool eicProvisioningAddEntryValue(
+ EicProvisioning* ctx, const uint8_t* accessControlProfileIds,
+ size_t numAccessControlProfileIds, const char* nameSpace,
+ size_t nameSpaceLength, const char* name, size_t nameLength,
+ const uint8_t* content, size_t contentSize, uint8_t* outEncryptedContent,
+ uint8_t* scratchSpace, size_t scratchSpaceSize) {
+ uint8_t* additionalDataCbor = scratchSpace;
+ const size_t additionalDataCborBufSize = scratchSpaceSize;
+ size_t additionalDataCborSize;
+ uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
+
+ if (!eicCborCalcEntryAdditionalData(
+ accessControlProfileIds, numAccessControlProfileIds, nameSpace,
+ nameSpaceLength, name, nameLength, additionalDataCbor,
+ additionalDataCborBufSize, &additionalDataCborSize,
+ calculatedSha256)) {
+ return false;
+ }
+ if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256,
+ EIC_SHA256_DIGEST_SIZE) != 0) {
+ eicDebug("SHA-256 mismatch of additionalData");
+ return false;
+ }
+
+ eicCborAppend(&ctx->cbor, content, contentSize);
+
+ uint8_t nonce[12];
+ if (!eicOpsRandom(nonce, 12)) {
+ return false;
+ }
+ if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, content, contentSize,
+ additionalDataCbor, additionalDataCborSize,
+ outEncryptedContent)) {
+ return false;
+ }
+
+ // If done with this entry, close the map
+ ctx->curEntryNumBytesReceived += contentSize;
+ if (ctx->curEntryNumBytesReceived == ctx->curEntrySize) {
+ eicCborAppendStringZ(&ctx->cbor, "accessControlProfiles");
+ eicCborAppendArray(&ctx->cbor, numAccessControlProfileIds);
+ for (size_t n = 0; n < numAccessControlProfileIds; n++) {
+ eicCborAppendNumber(&ctx->cbor, accessControlProfileIds[n]);
+ }
+ }
+ return true;
+}
+
+bool eicProvisioningFinishAddingEntries(
+ EicProvisioning* ctx,
+ uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+ uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
+
+ eicCborAppendBool(&ctx->cbor, ctx->testCredential);
+ eicCborFinal(&ctx->cbor, cborSha256);
+
+ // This verifies that the correct expectedProofOfProvisioningSize value was
+ // passed in at eicStartPersonalization() time.
+ if (ctx->cbor.size != ctx->expectedCborSizeAtEnd) {
+ eicDebug("CBOR size is %zd, was expecting %zd", ctx->cbor.size,
+ ctx->expectedCborSizeAtEnd);
+ return false;
+ }
+
+ if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256,
+ signatureOfToBeSigned)) {
+ eicDebug("Error signing proofOfProvisioning");
+ return false;
+ }
+
+ return true;
+}
+
+bool eicProvisioningFinishGetCredentialData(
+ EicProvisioning* ctx, const char* docType, size_t docTypeLength,
+ uint8_t* encryptedCredentialKeys, size_t* encryptedCredentialKeysSize) {
+ EicCbor cbor;
+ uint8_t cborBuf[86];
+
+ if (*encryptedCredentialKeysSize < 86 + 28) {
+ eicDebug("encryptedCredentialKeysSize is %zd which is insufficient");
+ return false;
+ }
+
+ eicCborInit(&cbor, cborBuf, sizeof(cborBuf));
+ eicCborAppendArray(&cbor, 3);
+ eicCborAppendByteString(&cbor, ctx->storageKey, EIC_AES_128_KEY_SIZE);
+ eicCborAppendByteString(&cbor, ctx->credentialPrivateKey,
+ EIC_P256_PRIV_KEY_SIZE);
+ uint8_t popSha256[EIC_SHA256_DIGEST_SIZE];
+ eicOpsSha256Final(&ctx->proofOfProvisioningDigester, popSha256);
+ eicCborAppendByteString(&cbor, popSha256, EIC_SHA256_DIGEST_SIZE);
+ if (cbor.size > sizeof(cborBuf)) {
+ eicDebug("Exceeded buffer size");
+ return false;
+ }
+
+ uint8_t nonce[12];
+ if (!eicOpsRandom(nonce, 12)) {
+ eicDebug("Error getting random");
+ return false;
+ }
+ if (!eicOpsEncryptAes128Gcm(eicOpsGetHardwareBoundKey(ctx->testCredential),
+ nonce, cborBuf, cbor.size,
+ // DocType is the additionalAuthenticatedData
+ (const uint8_t*)docType, docTypeLength,
+ encryptedCredentialKeys)) {
+ eicDebug("Error encrypting CredentialKeys");
+ return false;
+ }
+ *encryptedCredentialKeysSize = cbor.size + 28;
+
+ return true;
+}
diff --git a/guest/hals/identity/libeic/EicProvisioning.h b/guest/hals/identity/libeic/EicProvisioning.h
new file mode 100644
index 000000000..245a6e6d8
--- /dev/null
+++ b/guest/hals/identity/libeic/EicProvisioning.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_PROVISIONING_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_PROVISIONING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EicCbor.h"
+
+#define EIC_MAX_NUM_NAMESPACES 32
+#define EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS 32
+
+typedef struct {
+ // Set by eicCreateCredentialKey() OR eicProvisioningInitForUpdate()
+ uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
+
+ int numEntryCounts;
+ uint8_t entryCounts[EIC_MAX_NUM_NAMESPACES];
+
+ int curNamespace;
+ int curNamespaceNumProcessed;
+
+ size_t curEntrySize;
+ size_t curEntryNumBytesReceived;
+
+ // Set by eicProvisioningInit() OR eicProvisioningInitForUpdate()
+ uint8_t storageKey[EIC_AES_128_KEY_SIZE];
+
+ size_t expectedCborSizeAtEnd;
+
+ // SHA-256 for AdditionalData, updated for each entry.
+ uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
+
+ // Digester just for ProofOfProvisioning (without Sig_structure).
+ EicSha256Ctx proofOfProvisioningDigester;
+
+ EicCbor cbor;
+
+ bool testCredential;
+
+ // Set to true if this is an update.
+ bool isUpdate;
+} EicProvisioning;
+
+bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential);
+
+bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential,
+ const char* docType, size_t docTypeLength,
+ const uint8_t* encryptedCredentialKeys,
+ size_t encryptedCredentialKeysSize);
+
+bool eicProvisioningCreateCredentialKey(
+ EicProvisioning* ctx, const uint8_t* challenge, size_t challengeSize,
+ const uint8_t* applicationId, size_t applicationIdSize,
+ uint8_t* publicKeyCert, size_t* publicKeyCertSize);
+
+bool eicProvisioningStartPersonalization(
+ EicProvisioning* ctx, int accessControlProfileCount, const int* entryCounts,
+ size_t numEntryCounts, const char* docType, size_t docTypeLength,
+ size_t expectedProofOfProvisioningingSize);
+
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done this
+// way to avoid allocating stack space.
+//
+bool eicProvisioningAddAccessControlProfile(
+ EicProvisioning* ctx, int id, const uint8_t* readerCertificate,
+ size_t readerCertificateSize, bool userAuthenticationRequired,
+ uint64_t timeoutMillis, uint64_t secureUserId, uint8_t outMac[28],
+ uint8_t* scratchSpace, size_t scratchSpaceSize);
+
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done this
+// way to avoid allocating stack space.
+//
+bool eicProvisioningBeginAddEntry(EicProvisioning* ctx,
+ const uint8_t* accessControlProfileIds,
+ size_t numAccessControlProfileIds,
+ const char* nameSpace, size_t nameSpaceLength,
+ const char* name, size_t nameLength,
+ uint64_t entrySize, uint8_t* scratchSpace,
+ size_t scratchSpaceSize);
+
+// The outEncryptedContent array must be contentSize + 28 bytes long.
+//
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done this
+// way to avoid allocating stack space.
+//
+bool eicProvisioningAddEntryValue(
+ EicProvisioning* ctx, const uint8_t* accessControlProfileIds,
+ size_t numAccessControlProfileIds, const char* nameSpace,
+ size_t nameSpaceLength, const char* name, size_t nameLength,
+ const uint8_t* content, size_t contentSize, uint8_t* outEncryptedContent,
+ uint8_t* scratchSpace, size_t scratchSpaceSize);
+
+// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
+// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
+// where content is set to the ProofOfProvisioninging CBOR.
+//
+bool eicProvisioningFinishAddingEntries(
+ EicProvisioning* ctx,
+ uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+//
+//
+// The |encryptedCredentialKeys| array is set to AES-GCM-ENC(HBK, R,
+// CredentialKeys, docType) where
+//
+// CredentialKeys = [
+// bstr, ; storageKey, a 128-bit AES key
+// bstr ; credentialPrivKey, the private key for credentialKey
+// bstr ; SHA-256(ProofOfProvisioning)
+// ]
+//
+// for feature version 202101. For feature version 202009 the third field was
+// not present.
+//
+// Since |storageKey| is 16 bytes and |credentialPrivKey| is 32 bytes, the
+// encoded CBOR for CredentialKeys is 86 bytes and consequently
+// |encryptedCredentialKeys| will be no longer than 86 + 28 = 114 bytes.
+//
+bool eicProvisioningFinishGetCredentialData(
+ EicProvisioning* ctx, const char* docType, size_t docTypeLength,
+ uint8_t* encryptedCredentialKeys, size_t* encryptedCredentialKeysSize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_HARDWARE_IDENTITY_EIC_PROVISIONING_H
diff --git a/guest/hals/identity/libeic/libeic.h b/guest/hals/identity/libeic/libeic.h
new file mode 100644
index 000000000..8674ce36f
--- /dev/null
+++ b/guest/hals/identity/libeic/libeic.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IDENTITY_LIBEIC_H
+#define ANDROID_HARDWARE_IDENTITY_LIBEIC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The EIC_INSIDE_LIBEIC_H preprocessor symbol is used to enforce
+ * library users to include only this file. All public interfaces, and
+ * only public interfaces, must be included here.
+ */
+#define EIC_INSIDE_LIBEIC_H
+#include "EicCbor.h"
+#include "EicCommon.h"
+#include "EicOps.h"
+#include "EicPresentation.h"
+#include "EicProvisioning.h"
+#undef EIC_INSIDE_LIBEIC_H
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ANDROID_HARDWARE_IDENTITY_LIBEIC_H
diff --git a/guest/hals/identity/service.cpp b/guest/hals/identity/service.cpp
new file mode 100644
index 000000000..b40dba467
--- /dev/null
+++ b/guest/hals/identity/service.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2021, 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.
+ */
+
+#define LOG_TAG "android.hardware.identity-service"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "IdentityCredentialStore.h"
+
+#include "RemoteSecureHardwareProxy.h"
+
+using ::android::sp;
+using ::android::base::InitLogging;
+using ::android::base::StderrLogger;
+
+using ::aidl::android::hardware::identity::IdentityCredentialStore;
+using ::android::hardware::identity::RemoteSecureHardwareProxyFactory;
+using ::android::hardware::identity::SecureHardwareProxyFactory;
+
+int main(int /*argc*/, char* argv[]) {
+ InitLogging(argv, StderrLogger);
+
+ sp<SecureHardwareProxyFactory> hwProxyFactory =
+ new RemoteSecureHardwareProxyFactory();
+
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ std::shared_ptr<IdentityCredentialStore> store =
+ ndk::SharedRefBase::make<IdentityCredentialStore>(hwProxyFactory);
+
+ const std::string instance =
+ std::string(IdentityCredentialStore::descriptor) + "/default";
+ binder_status_t status =
+ AServiceManager_addService(store->asBinder().get(), instance.c_str());
+ CHECK_EQ(status, STATUS_OK);
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/guest/hals/keymint/remote/Android.bp b/guest/hals/keymint/remote/Android.bp
index f4e7a8f2a..4944d3720 100644
--- a/guest/hals/keymint/remote/Android.bp
+++ b/guest/hals/keymint/remote/Android.bp
@@ -32,7 +32,6 @@ cc_binary {
"-Wextra",
],
shared_libs: [
- "android.hardware.security.keymint-V1-ndk",
"android.hardware.security.secureclock-V1-ndk",
"android.hardware.security.sharedsecret-V1-ndk",
"lib_android_keymaster_keymint_utils",
@@ -59,6 +58,7 @@ cc_binary {
],
defaults: [
"cuttlefish_guest_only",
+ "keymint_use_latest_hal_aidl_ndk_shared",
],
required: [
"RemoteProvisioner",
diff --git a/guest/hals/keymint/remote/android.hardware.security.keymint-service.remote.xml b/guest/hals/keymint/remote/android.hardware.security.keymint-service.remote.xml
index 4aa05efe1..a4d030206 100644
--- a/guest/hals/keymint/remote/android.hardware.security.keymint-service.remote.xml
+++ b/guest/hals/keymint/remote/android.hardware.security.keymint-service.remote.xml
@@ -1,10 +1,12 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.security.keymint</name>
+ <version>2</version>
<fqname>IKeyMintDevice/default</fqname>
</hal>
<hal format="aidl">
<name>android.hardware.security.keymint</name>
+ <version>2</version>
<fqname>IRemotelyProvisionedComponent/default</fqname>
</hal>
</manifest>
diff --git a/guest/hals/keymint/remote/remote_keymaster.cpp b/guest/hals/keymint/remote/remote_keymaster.cpp
index d67b83780..9739a6b7f 100644
--- a/guest/hals/keymint/remote/remote_keymaster.cpp
+++ b/guest/hals/keymint/remote/remote_keymaster.cpp
@@ -17,6 +17,8 @@
#include "remote_keymaster.h"
#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <keymaster/android_keymaster_messages.h>
#include <keymaster/keymaster_configuration.h>
@@ -79,9 +81,14 @@ bool RemoteKeymaster::Initialize() {
return false;
}
- // Set the boot patchlevel to zero for Cuttlefish.
+ // Set the boot patchlevel to value retrieved from system property (which
+ // requires SELinux permission).
ConfigureBootPatchlevelRequest boot_req(message_version());
- boot_req.boot_patchlevel = 0;
+ static constexpr char boot_prop_name[] = "ro.vendor.boot_security_patch";
+ auto boot_prop_value = android::base::GetProperty(boot_prop_name, "");
+ boot_prop_value = android::base::StringReplace(boot_prop_value.data(), "-",
+ "", /* all */ true);
+ boot_req.boot_patchlevel = std::stoi(boot_prop_value);
ConfigureBootPatchlevelResponse boot_rsp = ConfigureBootPatchlevel(boot_req);
if (boot_rsp.error != KM_ERROR_OK) {
LOG(ERROR) << "Failed to configure keymaster boot patchlevel: "
diff --git a/guest/hals/keymint/remote/service.cpp b/guest/hals/keymint/remote/service.cpp
index f27b0dcb7..1fb0143de 100644
--- a/guest/hals/keymint/remote/service.cpp
+++ b/guest/hals/keymint/remote/service.cpp
@@ -93,7 +93,7 @@ int main(int, char** argv) {
keymaster::RemoteKeymaster remote_keymaster(
&keymasterChannel, keymaster::MessageVersion(
- keymaster::KmVersion::KEYMINT_1, 0 /* km_date */));
+ keymaster::KmVersion::KEYMINT_2, 0 /* km_date */));
if (!remote_keymaster.Initialize()) {
LOG(FATAL) << "Could not initialize keymaster";
diff --git a/guest/hals/ril/reference-libril/ril.h b/guest/hals/ril/reference-libril/ril.h
index 586de42ba..f7bc2c541 100644
--- a/guest/hals/ril/reference-libril/ril.h
+++ b/guest/hals/ril/reference-libril/ril.h
@@ -6202,7 +6202,7 @@ typedef enum {
*
* "data" is NULL
*
- * "response" is an array of RIL_CellInfo_v12.
+ * "response" is an array of RIL_CellInfo_v12.
*
* Valid errors:
* SUCCESS
@@ -7543,8 +7543,12 @@ typedef enum {
#define RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORDS 172
+/**
+ * Same as RIL_REQUEST_GET_CELL_INFO_LIST but "response" is an array of RIL_CellInfo_v16.
+ */
+#define RIL_REQUEST_GET_CELL_INFO_LIST_1_6 173
-#define RIL_REQUEST_LAST RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORDS
+#define RIL_REQUEST_LAST RIL_REQUEST_GET_CELL_INFO_LIST_1_6
/***********************************************************************/
diff --git a/guest/hals/ril/reference-libril/ril_commands.h b/guest/hals/ril/reference-libril/ril_commands.h
index a100b484e..4b1c48dc7 100644
--- a/guest/hals/ril/reference-libril/ril_commands.h
+++ b/guest/hals/ril/reference-libril/ril_commands.h
@@ -14,176 +14,193 @@
** See the License for the specific language governing permissions and
** limitations under the License.
*/
- {0, NULL}, //none
- {RIL_REQUEST_GET_SIM_STATUS, radio_1_6::getIccCardStatusResponse},
- {RIL_REQUEST_ENTER_SIM_PIN, radio_1_6::supplyIccPinForAppResponse},
- {RIL_REQUEST_ENTER_SIM_PUK, radio_1_6::supplyIccPukForAppResponse},
- {RIL_REQUEST_ENTER_SIM_PIN2, radio_1_6::supplyIccPin2ForAppResponse},
- {RIL_REQUEST_ENTER_SIM_PUK2, radio_1_6::supplyIccPuk2ForAppResponse},
- {RIL_REQUEST_CHANGE_SIM_PIN, radio_1_6::changeIccPinForAppResponse},
- {RIL_REQUEST_CHANGE_SIM_PIN2, radio_1_6::changeIccPin2ForAppResponse},
- {RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION, radio_1_6::supplyNetworkDepersonalizationResponse},
- {RIL_REQUEST_GET_CURRENT_CALLS, radio_1_6::getCurrentCallsResponse},
- {RIL_REQUEST_DIAL, radio_1_6::dialResponse},
- {RIL_REQUEST_GET_IMSI, radio_1_6::getIMSIForAppResponse},
- {RIL_REQUEST_HANGUP, radio_1_6::hangupConnectionResponse},
- {RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, radio_1_6::hangupWaitingOrBackgroundResponse},
- {RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND, radio_1_6::hangupForegroundResumeBackgroundResponse},
- {RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, radio_1_6::switchWaitingOrHoldingAndActiveResponse},
- {RIL_REQUEST_CONFERENCE, radio_1_6::conferenceResponse},
- {RIL_REQUEST_UDUB, radio_1_6::rejectCallResponse},
- {RIL_REQUEST_LAST_CALL_FAIL_CAUSE, radio_1_6::getLastCallFailCauseResponse},
- {RIL_REQUEST_SIGNAL_STRENGTH, radio_1_6::getSignalStrengthResponse},
- {RIL_REQUEST_VOICE_REGISTRATION_STATE, radio_1_6::getVoiceRegistrationStateResponse},
- {RIL_REQUEST_DATA_REGISTRATION_STATE, radio_1_6::getDataRegistrationStateResponse},
- {RIL_REQUEST_OPERATOR, radio_1_6::getOperatorResponse},
- {RIL_REQUEST_RADIO_POWER, radio_1_6::setRadioPowerResponse},
- {RIL_REQUEST_DTMF, radio_1_6::sendDtmfResponse},
- {RIL_REQUEST_SEND_SMS, radio_1_6::sendSmsResponse},
- {RIL_REQUEST_SEND_SMS_EXPECT_MORE, radio_1_6::sendSmsExpectMoreResponse},
- {RIL_REQUEST_SETUP_DATA_CALL, radio_1_6::setupDataCallResponse},
- {RIL_REQUEST_SIM_IO, radio_1_6::iccIOForAppResponse},
- {RIL_REQUEST_SEND_USSD, radio_1_6::sendUssdResponse},
- {RIL_REQUEST_CANCEL_USSD, radio_1_6::cancelPendingUssdResponse},
- {RIL_REQUEST_GET_CLIR, radio_1_6::getClirResponse},
- {RIL_REQUEST_SET_CLIR, radio_1_6::setClirResponse},
- {RIL_REQUEST_QUERY_CALL_FORWARD_STATUS, radio_1_6::getCallForwardStatusResponse},
- {RIL_REQUEST_SET_CALL_FORWARD, radio_1_6::setCallForwardResponse},
- {RIL_REQUEST_QUERY_CALL_WAITING, radio_1_6::getCallWaitingResponse},
- {RIL_REQUEST_SET_CALL_WAITING, radio_1_6::setCallWaitingResponse},
- {RIL_REQUEST_SMS_ACKNOWLEDGE, radio_1_6::acknowledgeLastIncomingGsmSmsResponse},
- {RIL_REQUEST_GET_IMEI, NULL},
- {RIL_REQUEST_GET_IMEISV, NULL},
- {RIL_REQUEST_ANSWER, radio_1_6::acceptCallResponse},
- {RIL_REQUEST_DEACTIVATE_DATA_CALL, radio_1_6::deactivateDataCallResponse},
- {RIL_REQUEST_QUERY_FACILITY_LOCK, radio_1_6::getFacilityLockForAppResponse},
- {RIL_REQUEST_SET_FACILITY_LOCK, radio_1_6::setFacilityLockForAppResponse},
- {RIL_REQUEST_CHANGE_BARRING_PASSWORD, radio_1_6::setBarringPasswordResponse},
- {RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE, radio_1_6::getNetworkSelectionModeResponse},
- {RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, radio_1_6::setNetworkSelectionModeAutomaticResponse},
- {RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL, radio_1_6::setNetworkSelectionModeManualResponse},
- {RIL_REQUEST_QUERY_AVAILABLE_NETWORKS , radio_1_6::getAvailableNetworksResponse},
- {RIL_REQUEST_DTMF_START, radio_1_6::startDtmfResponse},
- {RIL_REQUEST_DTMF_STOP, radio_1_6::stopDtmfResponse},
- {RIL_REQUEST_BASEBAND_VERSION, radio_1_6::getBasebandVersionResponse},
- {RIL_REQUEST_SEPARATE_CONNECTION, radio_1_6::separateConnectionResponse},
- {RIL_REQUEST_SET_MUTE, radio_1_6::setMuteResponse},
- {RIL_REQUEST_GET_MUTE, radio_1_6::getMuteResponse},
- {RIL_REQUEST_QUERY_CLIP, radio_1_6::getClipResponse},
- {RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE, NULL},
- {RIL_REQUEST_DATA_CALL_LIST, radio_1_6::getDataCallListResponse},
- {RIL_REQUEST_RESET_RADIO, NULL},
- {RIL_REQUEST_OEM_HOOK_RAW, radio_1_6::sendRequestRawResponse},
- {RIL_REQUEST_OEM_HOOK_STRINGS, radio_1_6::sendRequestStringsResponse},
- {RIL_REQUEST_SCREEN_STATE, radio_1_6::sendDeviceStateResponse}, // Note the response function is different.
- {RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION, radio_1_6::setSuppServiceNotificationsResponse},
- {RIL_REQUEST_WRITE_SMS_TO_SIM, radio_1_6::writeSmsToSimResponse},
- {RIL_REQUEST_DELETE_SMS_ON_SIM, radio_1_6::deleteSmsOnSimResponse},
- {RIL_REQUEST_SET_BAND_MODE, radio_1_6::setBandModeResponse},
- {RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE, radio_1_6::getAvailableBandModesResponse},
- {RIL_REQUEST_STK_GET_PROFILE, NULL},
- {RIL_REQUEST_STK_SET_PROFILE, NULL},
- {RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND, radio_1_6::sendEnvelopeResponse},
- {RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, radio_1_6::sendTerminalResponseToSimResponse},
- {RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM, radio_1_6::handleStkCallSetupRequestFromSimResponse},
- {RIL_REQUEST_EXPLICIT_CALL_TRANSFER, radio_1_6::explicitCallTransferResponse},
- {RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, radio_1_6::setPreferredNetworkTypeResponse},
- {RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, radio_1_6::getPreferredNetworkTypeResponse},
- {RIL_REQUEST_GET_NEIGHBORING_CELL_IDS, radio_1_6::getNeighboringCidsResponse},
- {RIL_REQUEST_SET_LOCATION_UPDATES, radio_1_6::setLocationUpdatesResponse},
- {RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE, radio_1_6::setCdmaSubscriptionSourceResponse},
- {RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE, radio_1_6::setCdmaRoamingPreferenceResponse},
- {RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE, radio_1_6::getCdmaRoamingPreferenceResponse},
- {RIL_REQUEST_SET_TTY_MODE, radio_1_6::setTTYModeResponse},
- {RIL_REQUEST_QUERY_TTY_MODE, radio_1_6::getTTYModeResponse},
- {RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE, radio_1_6::setPreferredVoicePrivacyResponse},
- {RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE, radio_1_6::getPreferredVoicePrivacyResponse},
- {RIL_REQUEST_CDMA_FLASH, radio_1_6::sendCDMAFeatureCodeResponse},
- {RIL_REQUEST_CDMA_BURST_DTMF, radio_1_6::sendBurstDtmfResponse},
- {RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY, NULL},
- {RIL_REQUEST_CDMA_SEND_SMS, radio_1_6::sendCdmaSmsResponse},
- {RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, radio_1_6::acknowledgeLastIncomingCdmaSmsResponse},
- {RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG, radio_1_6::getGsmBroadcastConfigResponse},
- {RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG, radio_1_6::setGsmBroadcastConfigResponse},
- {RIL_REQUEST_GSM_SMS_BROADCAST_ACTIVATION, radio_1_6::setGsmBroadcastActivationResponse},
- {RIL_REQUEST_CDMA_GET_BROADCAST_SMS_CONFIG, radio_1_6::getCdmaBroadcastConfigResponse},
- {RIL_REQUEST_CDMA_SET_BROADCAST_SMS_CONFIG, radio_1_6::setCdmaBroadcastConfigResponse},
- {RIL_REQUEST_CDMA_SMS_BROADCAST_ACTIVATION, radio_1_6::setCdmaBroadcastActivationResponse},
- {RIL_REQUEST_CDMA_SUBSCRIPTION, radio_1_6::getCDMASubscriptionResponse},
- {RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM, radio_1_6::writeSmsToRuimResponse},
- {RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM, radio_1_6::deleteSmsOnRuimResponse},
- {RIL_REQUEST_DEVICE_IDENTITY, radio_1_6::getDeviceIdentityResponse},
- {RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, radio_1_6::exitEmergencyCallbackModeResponse},
- {RIL_REQUEST_GET_SMSC_ADDRESS, radio_1_6::getSmscAddressResponse},
- {RIL_REQUEST_SET_SMSC_ADDRESS, radio_1_6::setSmscAddressResponse},
- {RIL_REQUEST_REPORT_SMS_MEMORY_STATUS, radio_1_6::reportSmsMemoryStatusResponse},
- {RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING, radio_1_6::reportStkServiceIsRunningResponse},
- {RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE, radio_1_6::getCdmaSubscriptionSourceResponse},
- {RIL_REQUEST_ISIM_AUTHENTICATION, radio_1_6::requestIsimAuthenticationResponse},
- {RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU, radio_1_6::acknowledgeIncomingGsmSmsWithPduResponse},
- {RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS, radio_1_6::sendEnvelopeWithStatusResponse},
- {RIL_REQUEST_VOICE_RADIO_TECH, radio_1_6::getVoiceRadioTechnologyResponse},
- {RIL_REQUEST_GET_CELL_INFO_LIST, radio_1_6::getCellInfoListResponse},
- {RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE, radio_1_6::setCellInfoListRateResponse},
- {RIL_REQUEST_SET_INITIAL_ATTACH_APN, radio_1_6::setInitialAttachApnResponse},
- {RIL_REQUEST_IMS_REGISTRATION_STATE, radio_1_6::getImsRegistrationStateResponse},
- {RIL_REQUEST_IMS_SEND_SMS, radio_1_6::sendImsSmsResponse},
- {RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC, radio_1_6::iccTransmitApduBasicChannelResponse},
- {RIL_REQUEST_SIM_OPEN_CHANNEL, radio_1_6::iccOpenLogicalChannelResponse},
- {RIL_REQUEST_SIM_CLOSE_CHANNEL, radio_1_6::iccCloseLogicalChannelResponse},
- {RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL, radio_1_6::iccTransmitApduLogicalChannelResponse},
- {RIL_REQUEST_NV_READ_ITEM, radio_1_6::nvReadItemResponse},
- {RIL_REQUEST_NV_WRITE_ITEM, radio_1_6::nvWriteItemResponse},
- {RIL_REQUEST_NV_WRITE_CDMA_PRL, radio_1_6::nvWriteCdmaPrlResponse},
- {RIL_REQUEST_NV_RESET_CONFIG, radio_1_6::nvResetConfigResponse},
- {RIL_REQUEST_SET_UICC_SUBSCRIPTION, radio_1_6::setUiccSubscriptionResponse},
- {RIL_REQUEST_ALLOW_DATA, radio_1_6::setDataAllowedResponse},
- {RIL_REQUEST_GET_HARDWARE_CONFIG, radio_1_6::getHardwareConfigResponse},
- {RIL_REQUEST_SIM_AUTHENTICATION, radio_1_6::requestIccSimAuthenticationResponse},
- {RIL_REQUEST_GET_DC_RT_INFO, NULL},
- {RIL_REQUEST_SET_DC_RT_INFO_RATE, NULL},
- {RIL_REQUEST_SET_DATA_PROFILE, radio_1_6::setDataProfileResponse},
- {RIL_REQUEST_SHUTDOWN, radio_1_6::requestShutdownResponse},
- {RIL_REQUEST_GET_RADIO_CAPABILITY, radio_1_6::getRadioCapabilityResponse},
- {RIL_REQUEST_SET_RADIO_CAPABILITY, radio_1_6::setRadioCapabilityResponse},
- {RIL_REQUEST_START_LCE, radio_1_6::startLceServiceResponse},
- {RIL_REQUEST_STOP_LCE, radio_1_6::stopLceServiceResponse},
- {RIL_REQUEST_PULL_LCEDATA, radio_1_6::pullLceDataResponse},
- {RIL_REQUEST_GET_ACTIVITY_INFO, radio_1_6::getModemActivityInfoResponse},
- {RIL_REQUEST_SET_CARRIER_RESTRICTIONS, radio_1_6::setAllowedCarriersResponse},
- {RIL_REQUEST_GET_CARRIER_RESTRICTIONS, radio_1_6::getAllowedCarriersResponse},
- {RIL_REQUEST_SEND_DEVICE_STATE, radio_1_6::sendDeviceStateResponse},
- {RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER, radio_1_6::setIndicationFilterResponse},
- {RIL_REQUEST_SET_SIM_CARD_POWER, radio_1_6::setSimCardPowerResponse},
- {RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION, radio_1_6::setCarrierInfoForImsiEncryptionResponse},
- {RIL_REQUEST_START_NETWORK_SCAN, radio_1_6::startNetworkScanResponse},
- {RIL_REQUEST_STOP_NETWORK_SCAN, radio_1_6::stopNetworkScanResponse},
- {RIL_REQUEST_START_KEEPALIVE, radio_1_6::startKeepaliveResponse},
- {RIL_REQUEST_STOP_KEEPALIVE, radio_1_6::stopKeepaliveResponse},
- {RIL_REQUEST_GET_MODEM_STACK_STATUS, radio_1_6::getModemStackStatusResponse},
- {RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE_BITMAP, radio_1_6::getPreferredNetworkTypeBitmapResponse},
- {RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE_BITMAP, radio_1_6::setPreferredNetworkTypeBitmapResponse},
- {RIL_REQUEST_EMERGENCY_DIAL, radio_1_6::emergencyDialResponse},
- {RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS, radio_1_6::setSystemSelectionChannelsResponse},
- {RIL_REQUEST_ENABLE_MODEM, radio_1_6::enableModemResponse},
- {RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA, radio_1_6::setSignalStrengthReportingCriteriaResponse},
- {RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA, radio_1_6::setLinkCapacityReportingCriteriaResponse},
- {RIL_REQUEST_ENABLE_UICC_APPLICATIONS, radio_1_6::enableUiccApplicationsResponse},
- {RIL_REQUEST_ARE_UICC_APPLICATIONS_ENABLED, radio_1_6::areUiccApplicationsEnabledResponse},
- {RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION, radio_1_6::supplySimDepersonalizationResponse},
- {RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE, radio_1_6::sendCdmaSmsExpectMoreResponse},
- {RIL_REQUEST_GET_BARRING_INFO, radio_1_6::getBarringInfoResponse},
- {RIL_REQUEST_ENABLE_NR_DUAL_CONNECTIVITY, radio_1_6::setNrDualConnectivityStateResponse},
- {RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED, radio_1_6::isNrDualConnectivityEnabledResponse},
- {RIL_REQUEST_ALLOCATE_PDU_SESSION_ID, radio_1_6::allocatePduSessionIdResponse},
- {RIL_REQUEST_RELEASE_PDU_SESSION_ID, radio_1_6::releasePduSessionIdResponse},
- {RIL_REQUEST_START_HANDOVER, radio_1_6::startHandoverResponse},
- {RIL_REQUEST_CANCEL_HANDOVER, radio_1_6::cancelHandoverResponse},
- {RIL_REQUEST_SET_ALLOWED_NETWORK_TYPES_BITMAP, radio_1_6::setAllowedNetworkTypesBitmapResponse},
- {RIL_REQUEST_SET_DATA_THROTTLING, radio_1_6::setDataThrottlingResponse},
- {RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS, radio_1_6::getSystemSelectionChannelsResponse},
- {RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP, radio_1_6::getAllowedNetworkTypesBitmapResponse},
- {RIL_REQUEST_GET_SLICING_CONFIG, radio_1_6::getSlicingConfigResponse},
- {RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS, radio_1_6::getSimPhonebookRecordsResponse},
- {RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY, radio_1_6::getSimPhonebookCapacityResponse},
- {RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORDS, radio_1_6::updateSimPhonebookRecordsResponse}
+{0, NULL}, // none
+ {RIL_REQUEST_GET_SIM_STATUS, radio_1_6::getIccCardStatusResponse},
+ {RIL_REQUEST_ENTER_SIM_PIN, radio_1_6::supplyIccPinForAppResponse},
+ {RIL_REQUEST_ENTER_SIM_PUK, radio_1_6::supplyIccPukForAppResponse},
+ {RIL_REQUEST_ENTER_SIM_PIN2, radio_1_6::supplyIccPin2ForAppResponse},
+ {RIL_REQUEST_ENTER_SIM_PUK2, radio_1_6::supplyIccPuk2ForAppResponse},
+ {RIL_REQUEST_CHANGE_SIM_PIN, radio_1_6::changeIccPinForAppResponse},
+ {RIL_REQUEST_CHANGE_SIM_PIN2, radio_1_6::changeIccPin2ForAppResponse},
+ {RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION,
+ radio_1_6::supplyNetworkDepersonalizationResponse},
+ {RIL_REQUEST_GET_CURRENT_CALLS, radio_1_6::getCurrentCallsResponse},
+ {RIL_REQUEST_DIAL, radio_1_6::dialResponse},
+ {RIL_REQUEST_GET_IMSI, radio_1_6::getIMSIForAppResponse},
+ {RIL_REQUEST_HANGUP, radio_1_6::hangupConnectionResponse},
+ {RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, radio_1_6::hangupWaitingOrBackgroundResponse},
+ {RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND,
+ radio_1_6::hangupForegroundResumeBackgroundResponse},
+ {RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE,
+ radio_1_6::switchWaitingOrHoldingAndActiveResponse},
+ {RIL_REQUEST_CONFERENCE, radio_1_6::conferenceResponse},
+ {RIL_REQUEST_UDUB, radio_1_6::rejectCallResponse},
+ {RIL_REQUEST_LAST_CALL_FAIL_CAUSE, radio_1_6::getLastCallFailCauseResponse},
+ {RIL_REQUEST_SIGNAL_STRENGTH, radio_1_6::getSignalStrengthResponse},
+ {RIL_REQUEST_VOICE_REGISTRATION_STATE, radio_1_6::getVoiceRegistrationStateResponse},
+ {RIL_REQUEST_DATA_REGISTRATION_STATE, radio_1_6::getDataRegistrationStateResponse},
+ {RIL_REQUEST_OPERATOR, radio_1_6::getOperatorResponse},
+ {RIL_REQUEST_RADIO_POWER, radio_1_6::setRadioPowerResponse},
+ {RIL_REQUEST_DTMF, radio_1_6::sendDtmfResponse},
+ {RIL_REQUEST_SEND_SMS, radio_1_6::sendSmsResponse},
+ {RIL_REQUEST_SEND_SMS_EXPECT_MORE, radio_1_6::sendSmsExpectMoreResponse},
+ {RIL_REQUEST_SETUP_DATA_CALL, radio_1_6::setupDataCallResponse},
+ {RIL_REQUEST_SIM_IO, radio_1_6::iccIOForAppResponse},
+ {RIL_REQUEST_SEND_USSD, radio_1_6::sendUssdResponse},
+ {RIL_REQUEST_CANCEL_USSD, radio_1_6::cancelPendingUssdResponse},
+ {RIL_REQUEST_GET_CLIR, radio_1_6::getClirResponse},
+ {RIL_REQUEST_SET_CLIR, radio_1_6::setClirResponse},
+ {RIL_REQUEST_QUERY_CALL_FORWARD_STATUS, radio_1_6::getCallForwardStatusResponse},
+ {RIL_REQUEST_SET_CALL_FORWARD, radio_1_6::setCallForwardResponse},
+ {RIL_REQUEST_QUERY_CALL_WAITING, radio_1_6::getCallWaitingResponse},
+ {RIL_REQUEST_SET_CALL_WAITING, radio_1_6::setCallWaitingResponse},
+ {RIL_REQUEST_SMS_ACKNOWLEDGE, radio_1_6::acknowledgeLastIncomingGsmSmsResponse},
+ {RIL_REQUEST_GET_IMEI, NULL}, {RIL_REQUEST_GET_IMEISV, NULL},
+ {RIL_REQUEST_ANSWER, radio_1_6::acceptCallResponse},
+ {RIL_REQUEST_DEACTIVATE_DATA_CALL, radio_1_6::deactivateDataCallResponse},
+ {RIL_REQUEST_QUERY_FACILITY_LOCK, radio_1_6::getFacilityLockForAppResponse},
+ {RIL_REQUEST_SET_FACILITY_LOCK, radio_1_6::setFacilityLockForAppResponse},
+ {RIL_REQUEST_CHANGE_BARRING_PASSWORD, radio_1_6::setBarringPasswordResponse},
+ {RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE, radio_1_6::getNetworkSelectionModeResponse},
+ {RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC,
+ radio_1_6::setNetworkSelectionModeAutomaticResponse},
+ {RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL,
+ radio_1_6::setNetworkSelectionModeManualResponse},
+ {RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, radio_1_6::getAvailableNetworksResponse},
+ {RIL_REQUEST_DTMF_START, radio_1_6::startDtmfResponse},
+ {RIL_REQUEST_DTMF_STOP, radio_1_6::stopDtmfResponse},
+ {RIL_REQUEST_BASEBAND_VERSION, radio_1_6::getBasebandVersionResponse},
+ {RIL_REQUEST_SEPARATE_CONNECTION, radio_1_6::separateConnectionResponse},
+ {RIL_REQUEST_SET_MUTE, radio_1_6::setMuteResponse},
+ {RIL_REQUEST_GET_MUTE, radio_1_6::getMuteResponse},
+ {RIL_REQUEST_QUERY_CLIP, radio_1_6::getClipResponse},
+ {RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE, NULL},
+ {RIL_REQUEST_DATA_CALL_LIST, radio_1_6::getDataCallListResponse},
+ {RIL_REQUEST_RESET_RADIO, NULL},
+ {RIL_REQUEST_OEM_HOOK_RAW, radio_1_6::sendRequestRawResponse},
+ {RIL_REQUEST_OEM_HOOK_STRINGS, radio_1_6::sendRequestStringsResponse},
+ {RIL_REQUEST_SCREEN_STATE,
+ radio_1_6::sendDeviceStateResponse}, // Note the response function is different.
+ {RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION, radio_1_6::setSuppServiceNotificationsResponse},
+ {RIL_REQUEST_WRITE_SMS_TO_SIM, radio_1_6::writeSmsToSimResponse},
+ {RIL_REQUEST_DELETE_SMS_ON_SIM, radio_1_6::deleteSmsOnSimResponse},
+ {RIL_REQUEST_SET_BAND_MODE, radio_1_6::setBandModeResponse},
+ {RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE, radio_1_6::getAvailableBandModesResponse},
+ {RIL_REQUEST_STK_GET_PROFILE, NULL}, {RIL_REQUEST_STK_SET_PROFILE, NULL},
+ {RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND, radio_1_6::sendEnvelopeResponse},
+ {RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, radio_1_6::sendTerminalResponseToSimResponse},
+ {RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM,
+ radio_1_6::handleStkCallSetupRequestFromSimResponse},
+ {RIL_REQUEST_EXPLICIT_CALL_TRANSFER, radio_1_6::explicitCallTransferResponse},
+ {RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, radio_1_6::setPreferredNetworkTypeResponse},
+ {RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, radio_1_6::getPreferredNetworkTypeResponse},
+ {RIL_REQUEST_GET_NEIGHBORING_CELL_IDS, radio_1_6::getNeighboringCidsResponse},
+ {RIL_REQUEST_SET_LOCATION_UPDATES, radio_1_6::setLocationUpdatesResponse},
+ {RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE, radio_1_6::setCdmaSubscriptionSourceResponse},
+ {RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE, radio_1_6::setCdmaRoamingPreferenceResponse},
+ {RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE, radio_1_6::getCdmaRoamingPreferenceResponse},
+ {RIL_REQUEST_SET_TTY_MODE, radio_1_6::setTTYModeResponse},
+ {RIL_REQUEST_QUERY_TTY_MODE, radio_1_6::getTTYModeResponse},
+ {RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE,
+ radio_1_6::setPreferredVoicePrivacyResponse},
+ {RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE,
+ radio_1_6::getPreferredVoicePrivacyResponse},
+ {RIL_REQUEST_CDMA_FLASH, radio_1_6::sendCDMAFeatureCodeResponse},
+ {RIL_REQUEST_CDMA_BURST_DTMF, radio_1_6::sendBurstDtmfResponse},
+ {RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY, NULL},
+ {RIL_REQUEST_CDMA_SEND_SMS, radio_1_6::sendCdmaSmsResponse},
+ {RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, radio_1_6::acknowledgeLastIncomingCdmaSmsResponse},
+ {RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG, radio_1_6::getGsmBroadcastConfigResponse},
+ {RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG, radio_1_6::setGsmBroadcastConfigResponse},
+ {RIL_REQUEST_GSM_SMS_BROADCAST_ACTIVATION, radio_1_6::setGsmBroadcastActivationResponse},
+ {RIL_REQUEST_CDMA_GET_BROADCAST_SMS_CONFIG, radio_1_6::getCdmaBroadcastConfigResponse},
+ {RIL_REQUEST_CDMA_SET_BROADCAST_SMS_CONFIG, radio_1_6::setCdmaBroadcastConfigResponse},
+ {RIL_REQUEST_CDMA_SMS_BROADCAST_ACTIVATION, radio_1_6::setCdmaBroadcastActivationResponse},
+ {RIL_REQUEST_CDMA_SUBSCRIPTION, radio_1_6::getCDMASubscriptionResponse},
+ {RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM, radio_1_6::writeSmsToRuimResponse},
+ {RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM, radio_1_6::deleteSmsOnRuimResponse},
+ {RIL_REQUEST_DEVICE_IDENTITY, radio_1_6::getDeviceIdentityResponse},
+ {RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, radio_1_6::exitEmergencyCallbackModeResponse},
+ {RIL_REQUEST_GET_SMSC_ADDRESS, radio_1_6::getSmscAddressResponse},
+ {RIL_REQUEST_SET_SMSC_ADDRESS, radio_1_6::setSmscAddressResponse},
+ {RIL_REQUEST_REPORT_SMS_MEMORY_STATUS, radio_1_6::reportSmsMemoryStatusResponse},
+ {RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING, radio_1_6::reportStkServiceIsRunningResponse},
+ {RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE, radio_1_6::getCdmaSubscriptionSourceResponse},
+ {RIL_REQUEST_ISIM_AUTHENTICATION, radio_1_6::requestIsimAuthenticationResponse},
+ {RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU,
+ radio_1_6::acknowledgeIncomingGsmSmsWithPduResponse},
+ {RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS, radio_1_6::sendEnvelopeWithStatusResponse},
+ {RIL_REQUEST_VOICE_RADIO_TECH, radio_1_6::getVoiceRadioTechnologyResponse},
+ {RIL_REQUEST_GET_CELL_INFO_LIST, radio_1_6::getCellInfoListResponse},
+ {RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE, radio_1_6::setCellInfoListRateResponse},
+ {RIL_REQUEST_SET_INITIAL_ATTACH_APN, radio_1_6::setInitialAttachApnResponse},
+ {RIL_REQUEST_IMS_REGISTRATION_STATE, radio_1_6::getImsRegistrationStateResponse},
+ {RIL_REQUEST_IMS_SEND_SMS, radio_1_6::sendImsSmsResponse},
+ {RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC, radio_1_6::iccTransmitApduBasicChannelResponse},
+ {RIL_REQUEST_SIM_OPEN_CHANNEL, radio_1_6::iccOpenLogicalChannelResponse},
+ {RIL_REQUEST_SIM_CLOSE_CHANNEL, radio_1_6::iccCloseLogicalChannelResponse},
+ {RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL, radio_1_6::iccTransmitApduLogicalChannelResponse},
+ {RIL_REQUEST_NV_READ_ITEM, radio_1_6::nvReadItemResponse},
+ {RIL_REQUEST_NV_WRITE_ITEM, radio_1_6::nvWriteItemResponse},
+ {RIL_REQUEST_NV_WRITE_CDMA_PRL, radio_1_6::nvWriteCdmaPrlResponse},
+ {RIL_REQUEST_NV_RESET_CONFIG, radio_1_6::nvResetConfigResponse},
+ {RIL_REQUEST_SET_UICC_SUBSCRIPTION, radio_1_6::setUiccSubscriptionResponse},
+ {RIL_REQUEST_ALLOW_DATA, radio_1_6::setDataAllowedResponse},
+ {RIL_REQUEST_GET_HARDWARE_CONFIG, radio_1_6::getHardwareConfigResponse},
+ {RIL_REQUEST_SIM_AUTHENTICATION, radio_1_6::requestIccSimAuthenticationResponse},
+ {RIL_REQUEST_GET_DC_RT_INFO, NULL}, {RIL_REQUEST_SET_DC_RT_INFO_RATE, NULL},
+ {RIL_REQUEST_SET_DATA_PROFILE, radio_1_6::setDataProfileResponse},
+ {RIL_REQUEST_SHUTDOWN, radio_1_6::requestShutdownResponse},
+ {RIL_REQUEST_GET_RADIO_CAPABILITY, radio_1_6::getRadioCapabilityResponse},
+ {RIL_REQUEST_SET_RADIO_CAPABILITY, radio_1_6::setRadioCapabilityResponse},
+ {RIL_REQUEST_START_LCE, radio_1_6::startLceServiceResponse},
+ {RIL_REQUEST_STOP_LCE, radio_1_6::stopLceServiceResponse},
+ {RIL_REQUEST_PULL_LCEDATA, radio_1_6::pullLceDataResponse},
+ {RIL_REQUEST_GET_ACTIVITY_INFO, radio_1_6::getModemActivityInfoResponse},
+ {RIL_REQUEST_SET_CARRIER_RESTRICTIONS, radio_1_6::setAllowedCarriersResponse},
+ {RIL_REQUEST_GET_CARRIER_RESTRICTIONS, radio_1_6::getAllowedCarriersResponse},
+ {RIL_REQUEST_SEND_DEVICE_STATE, radio_1_6::sendDeviceStateResponse},
+ {RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER, radio_1_6::setIndicationFilterResponse},
+ {RIL_REQUEST_SET_SIM_CARD_POWER, radio_1_6::setSimCardPowerResponse},
+ {RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION,
+ radio_1_6::setCarrierInfoForImsiEncryptionResponse},
+ {RIL_REQUEST_START_NETWORK_SCAN, radio_1_6::startNetworkScanResponse},
+ {RIL_REQUEST_STOP_NETWORK_SCAN, radio_1_6::stopNetworkScanResponse},
+ {RIL_REQUEST_START_KEEPALIVE, radio_1_6::startKeepaliveResponse},
+ {RIL_REQUEST_STOP_KEEPALIVE, radio_1_6::stopKeepaliveResponse},
+ {RIL_REQUEST_GET_MODEM_STACK_STATUS, radio_1_6::getModemStackStatusResponse},
+ {RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE_BITMAP,
+ radio_1_6::getPreferredNetworkTypeBitmapResponse},
+ {RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE_BITMAP,
+ radio_1_6::setPreferredNetworkTypeBitmapResponse},
+ {RIL_REQUEST_EMERGENCY_DIAL, radio_1_6::emergencyDialResponse},
+ {RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS, radio_1_6::setSystemSelectionChannelsResponse},
+ {RIL_REQUEST_ENABLE_MODEM, radio_1_6::enableModemResponse},
+ {RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA,
+ radio_1_6::setSignalStrengthReportingCriteriaResponse},
+ {RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA,
+ radio_1_6::setLinkCapacityReportingCriteriaResponse},
+ {RIL_REQUEST_ENABLE_UICC_APPLICATIONS, radio_1_6::enableUiccApplicationsResponse},
+ {RIL_REQUEST_ARE_UICC_APPLICATIONS_ENABLED, radio_1_6::areUiccApplicationsEnabledResponse},
+ {RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION, radio_1_6::supplySimDepersonalizationResponse},
+ {RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE, radio_1_6::sendCdmaSmsExpectMoreResponse},
+ {RIL_REQUEST_GET_BARRING_INFO, radio_1_6::getBarringInfoResponse},
+ {RIL_REQUEST_ENABLE_NR_DUAL_CONNECTIVITY, radio_1_6::setNrDualConnectivityStateResponse},
+ {RIL_REQUEST_IS_NR_DUAL_CONNECTIVITY_ENABLED,
+ radio_1_6::isNrDualConnectivityEnabledResponse},
+ {RIL_REQUEST_ALLOCATE_PDU_SESSION_ID, radio_1_6::allocatePduSessionIdResponse},
+ {RIL_REQUEST_RELEASE_PDU_SESSION_ID, radio_1_6::releasePduSessionIdResponse},
+ {RIL_REQUEST_START_HANDOVER, radio_1_6::startHandoverResponse},
+ {RIL_REQUEST_CANCEL_HANDOVER, radio_1_6::cancelHandoverResponse},
+ {RIL_REQUEST_SET_ALLOWED_NETWORK_TYPES_BITMAP,
+ radio_1_6::setAllowedNetworkTypesBitmapResponse},
+ {RIL_REQUEST_SET_DATA_THROTTLING, radio_1_6::setDataThrottlingResponse},
+ {RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS, radio_1_6::getSystemSelectionChannelsResponse},
+ {RIL_REQUEST_GET_ALLOWED_NETWORK_TYPES_BITMAP,
+ radio_1_6::getAllowedNetworkTypesBitmapResponse},
+ {RIL_REQUEST_GET_SLICING_CONFIG, radio_1_6::getSlicingConfigResponse},
+ {RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS, radio_1_6::getSimPhonebookRecordsResponse},
+ {RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY, radio_1_6::getSimPhonebookCapacityResponse},
+ {RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORDS, radio_1_6::updateSimPhonebookRecordsResponse},
+ {RIL_REQUEST_GET_CELL_INFO_LIST_1_6, radio_1_6::getCellInfoListResponse
+}
diff --git a/guest/hals/ril/reference-libril/ril_service.cpp b/guest/hals/ril/reference-libril/ril_service.cpp
index c7b80a6d9..8f2b9487a 100644
--- a/guest/hals/ril/reference-libril/ril_service.cpp
+++ b/guest/hals/ril/reference-libril/ril_service.cpp
@@ -119,26 +119,46 @@ void convertRilLceDataInfoToHal(void *response, size_t responseLen, LceDataInfo&
void convertRilSignalStrengthToHal(void *response, size_t responseLen,
SignalStrength& signalStrength);
-void convertRilDataCallToHal(RIL_Data_Call_Response_v11 *dcResponse,
- SetupDataCallResult& dcResult);
+void convertRilSignalStrengthToHal_1_2(void* response, size_t responseLen,
+ V1_2::SignalStrength& signalStrength);
void convertRilSignalStrengthToHal_1_4(void *response, size_t responseLen,
V1_4::SignalStrength& signalStrength);
-void convertRilDataCallToHal(RIL_Data_Call_Response_v11 *dcResponse,
- ::android::hardware::radio::V1_4::SetupDataCallResult& dcResult);
+void convertRilSignalStrengthToHal_1_6(void* response, size_t responseLen,
+ V1_6::SignalStrength& signalStrength);
-void convertRilDataCallToHal(RIL_Data_Call_Response_v12 *dcResponse,
- ::android::hardware::radio::V1_5::SetupDataCallResult& dcResult);
+void convertRilDataCallToHal(RIL_Data_Call_Response_v11* dcResponse, SetupDataCallResult& dcResult);
-void convertRilDataCallToHal(RIL_Data_Call_Response_v12 *dcResponse,
- ::android::hardware::radio::V1_6::SetupDataCallResult& dcResult);
+void convertRilDataCallToHal(RIL_Data_Call_Response_v11* dcResponse,
+ V1_4::SetupDataCallResult& dcResult);
+
+void convertRilDataCallToHal(RIL_Data_Call_Response_v12* dcResponse,
+ V1_5::SetupDataCallResult& dcResult);
+
+void convertRilDataCallToHal(RIL_Data_Call_Response_v12* dcResponse,
+ V1_6::SetupDataCallResult& dcResult);
void convertRilDataCallListToHal(void *response, size_t responseLen,
hidl_vec<SetupDataCallResult>& dcResultList);
+void convertRilDataCallListToHal_1_4(void* response, size_t responseLen,
+ hidl_vec<V1_4::SetupDataCallResult>& dcResultList);
+
+void convertRilDataCallListToHal_1_5(void* response, size_t responseLen,
+ hidl_vec<V1_5::SetupDataCallResult>& dcResultList);
+
+void convertRilDataCallListToHal_1_6(void* response, size_t responseLen,
+ hidl_vec<V1_6::SetupDataCallResult>& dcResultList);
+
void convertRilCellInfoListToHal(void *response, size_t responseLen, hidl_vec<CellInfo>& records);
void convertRilCellInfoListToHal_1_2(void *response, size_t responseLen, hidl_vec<V1_2::CellInfo>& records);
+void convertRilCellInfoListToHal_1_4(void* response, size_t responseLen,
+ hidl_vec<V1_4::CellInfo>& records);
+void convertRilCellInfoListToHal_1_5(void* response, size_t responseLen,
+ hidl_vec<V1_5::CellInfo>& records);
+void convertRilCellInfoListToHal_1_6(void* response, size_t responseLen,
+ hidl_vec<V1_6::CellInfo>& records);
void populateResponseInfo(RadioResponseInfo& responseInfo, int serial, int responseType,
RIL_Errno e);
@@ -2428,7 +2448,7 @@ Return<void> RadioImpl_1_6::getCellInfoList_1_6(int32_t serial) {
#if VDBG
RLOGD("getCellInfoList_1_6: serial %d", serial);
#endif
- dispatchVoid(serial, mSlotId, RIL_REQUEST_GET_CELL_INFO_LIST);
+ dispatchVoid(serial, mSlotId, RIL_REQUEST_GET_CELL_INFO_LIST_1_6);
return Void();
}
@@ -3501,12 +3521,14 @@ Return<void> RadioImpl_1_6::setSignalStrengthReportingCriteria(int32_t serial,
e = RIL_E_SUCCESS;
}
populateResponseInfo(responseInfo, serial, RESPONSE_SOLICITED, e);
- Return<void> retStatus
- = radioService[mSlotId]->mRadioResponseV1_2->setSignalStrengthReportingCriteriaResponse(responseInfo);
+ Return<void> retStatus =
+ radioService[mSlotId]
+ ->mRadioResponseV1_2->setSignalStrengthReportingCriteriaResponse(
+ responseInfo);
radioService[mSlotId]->checkReturnStatus(retStatus);
} else {
- RLOGE("setIndicationFilterResponse: radioService[%d]->mRadioResponse == NULL",
- mSlotId);
+ RLOGE("setSignalStrengthReportingCriteria: radioService[%d]->mRadioResponse == NULL",
+ mSlotId);
}
return Void();
}
@@ -4458,8 +4480,9 @@ Return<void> RadioImpl_1_6::setInitialAttachApn_1_5(int32_t serial ,
populateResponseInfo(responseInfo, serial, RESPONSE_SOLICITED, RIL_E_SUCCESS);
if (radioService[mSlotId]->mRadioResponseV1_5 != NULL) {
- Return<void> retStatus
- = radioService[mSlotId]->mRadioResponseV1_5->setInitialAttachApnResponse(responseInfo);
+ Return<void> retStatus =
+ radioService[mSlotId]->mRadioResponseV1_5->setInitialAttachApnResponse_1_5(
+ responseInfo);
} else if (radioService[mSlotId]->mRadioResponseV1_4 != NULL) {
Return<void> retStatus
= radioService[mSlotId]->mRadioResponseV1_4->setInitialAttachApnResponse(responseInfo);
@@ -4487,8 +4510,8 @@ Return<void> RadioImpl_1_6::setDataProfile_1_5(int32_t serial ,
populateResponseInfo(responseInfo, serial, RESPONSE_SOLICITED, RIL_E_SUCCESS);
if (radioService[mSlotId]->mRadioResponseV1_5 != NULL) {
- Return<void> retStatus
- = radioService[mSlotId]->mRadioResponseV1_5->setDataProfileResponse(responseInfo);
+ Return<void> retStatus =
+ radioService[mSlotId]->mRadioResponseV1_5->setDataProfileResponse_1_5(responseInfo);
} else if (radioService[mSlotId]->mRadioResponseV1_4 != NULL) {
Return<void> retStatus
= radioService[mSlotId]->mRadioResponseV1_4->setDataProfileResponse(responseInfo);
@@ -5123,57 +5146,134 @@ int radio_1_6::supplyNetworkDepersonalizationResponse(int slotId,
return 0;
}
-int radio_1_6::getCurrentCallsResponse(int slotId,
- int responseType, int serial, RIL_Errno e,
- void *response, size_t responseLen) {
+int radio_1_6::getCurrentCallsResponse(int slotId, int responseType, int serial, RIL_Errno e,
+ void* response, size_t responseLen) {
#if VDBG
RLOGD("getCurrentCallsResponse: serial %d", serial);
#endif
- if (radioService[slotId]->mRadioResponse != NULL) {
+ if (radioService[slotId]->mRadioResponseV1_6 != NULL ||
+ radioService[slotId]->mRadioResponseV1_2 != NULL ||
+ radioService[slotId]->mRadioResponse != NULL) {
+ V1_6::RadioResponseInfo responseInfo16 = {};
RadioResponseInfo responseInfo = {};
- populateResponseInfo(responseInfo, serial, responseType, e);
-
- hidl_vec<Call> calls;
- if ((response == NULL && responseLen != 0)
- || (responseLen % sizeof(RIL_Call *)) != 0) {
+ if (radioService[slotId]->mRadioResponseV1_6 != NULL) {
+ populateResponseInfo_1_6(responseInfo16, serial, responseType, e);
+ } else {
+ populateResponseInfo(responseInfo, serial, responseType, e);
+ }
+ if ((response == NULL && responseLen != 0) || (responseLen % sizeof(RIL_Call*)) != 0) {
RLOGE("getCurrentCallsResponse: Invalid response");
- if (e == RIL_E_SUCCESS) responseInfo.error = RadioError::INVALID_RESPONSE;
+ if (e == RIL_E_SUCCESS) {
+ responseInfo16.error = V1_6::RadioError::INVALID_RESPONSE;
+ responseInfo.error = RadioError::INVALID_RESPONSE;
+ }
+ return 0;
} else {
- int num = responseLen / sizeof(RIL_Call *);
- calls.resize(num);
-
- for (int i = 0 ; i < num ; i++) {
- RIL_Call *p_cur = ((RIL_Call **) response)[i];
- /* each call info */
- calls[i].state = (CallState) p_cur->state;
- calls[i].index = p_cur->index;
- calls[i].toa = p_cur->toa;
- calls[i].isMpty = p_cur->isMpty;
- calls[i].isMT = p_cur->isMT;
- calls[i].als = p_cur->als;
- calls[i].isVoice = p_cur->isVoice;
- calls[i].isVoicePrivacy = p_cur->isVoicePrivacy;
- calls[i].number = convertCharPtrToHidlString(p_cur->number);
- calls[i].numberPresentation = (CallPresentation) p_cur->numberPresentation;
- calls[i].name = convertCharPtrToHidlString(p_cur->name);
- calls[i].namePresentation = (CallPresentation) p_cur->namePresentation;
- if (p_cur->uusInfo != NULL && p_cur->uusInfo->uusData != NULL) {
- RIL_UUS_Info *uusInfo = p_cur->uusInfo;
- calls[i].uusInfo.resize(1);
- calls[i].uusInfo[0].uusType = (UusType) uusInfo->uusType;
- calls[i].uusInfo[0].uusDcs = (UusDcs) uusInfo->uusDcs;
- // convert uusInfo->uusData to a null-terminated string
- char *nullTermStr = strndup(uusInfo->uusData, uusInfo->uusLength);
- calls[i].uusInfo[0].uusData = nullTermStr;
- free(nullTermStr);
+ Return<void> retStatus;
+ if (radioService[slotId]->mRadioResponseV1_6 != NULL) {
+ hidl_vec<V1_6::Call> calls;
+ int num = responseLen / sizeof(RIL_Call*);
+ calls.resize(num);
+
+ for (int i = 0; i < num; i++) {
+ RIL_Call* p_cur = ((RIL_Call**)response)[i];
+ /* each call info */
+ calls[i].base.base.state = (CallState)p_cur->state;
+ calls[i].base.base.index = p_cur->index;
+ calls[i].base.base.toa = p_cur->toa;
+ calls[i].base.base.isMpty = p_cur->isMpty;
+ calls[i].base.base.isMT = p_cur->isMT;
+ calls[i].base.base.als = p_cur->als;
+ calls[i].base.base.isVoice = p_cur->isVoice;
+ calls[i].base.base.isVoicePrivacy = p_cur->isVoicePrivacy;
+ calls[i].base.base.number = convertCharPtrToHidlString(p_cur->number);
+ calls[i].base.base.numberPresentation =
+ (CallPresentation)p_cur->numberPresentation;
+ calls[i].base.base.name = convertCharPtrToHidlString(p_cur->name);
+ calls[i].base.base.namePresentation = (CallPresentation)p_cur->namePresentation;
+ if (p_cur->uusInfo != NULL && p_cur->uusInfo->uusData != NULL) {
+ RIL_UUS_Info* uusInfo = p_cur->uusInfo;
+ calls[i].base.base.uusInfo.resize(1);
+ calls[i].base.base.uusInfo[0].uusType = (UusType)uusInfo->uusType;
+ calls[i].base.base.uusInfo[0].uusDcs = (UusDcs)uusInfo->uusDcs;
+ // convert uusInfo->uusData to a null-terminated string
+ char* nullTermStr = strndup(uusInfo->uusData, uusInfo->uusLength);
+ calls[i].base.base.uusInfo[0].uusData = nullTermStr;
+ free(nullTermStr);
+ }
+ }
+ retStatus = radioService[slotId]->mRadioResponseV1_6->getCurrentCallsResponse_1_6(
+ responseInfo16, calls);
+ } else if (radioService[slotId]->mRadioResponseV1_2 != NULL) {
+ hidl_vec<V1_2::Call> calls;
+ int num = responseLen / sizeof(RIL_Call*);
+ calls.resize(num);
+
+ for (int i = 0; i < num; i++) {
+ RIL_Call* p_cur = ((RIL_Call**)response)[i];
+ /* each call info */
+ calls[i].base.state = (CallState)p_cur->state;
+ calls[i].base.index = p_cur->index;
+ calls[i].base.toa = p_cur->toa;
+ calls[i].base.isMpty = p_cur->isMpty;
+ calls[i].base.isMT = p_cur->isMT;
+ calls[i].base.als = p_cur->als;
+ calls[i].base.isVoice = p_cur->isVoice;
+ calls[i].base.isVoicePrivacy = p_cur->isVoicePrivacy;
+ calls[i].base.number = convertCharPtrToHidlString(p_cur->number);
+ calls[i].base.numberPresentation = (CallPresentation)p_cur->numberPresentation;
+ calls[i].base.name = convertCharPtrToHidlString(p_cur->name);
+ calls[i].base.namePresentation = (CallPresentation)p_cur->namePresentation;
+ if (p_cur->uusInfo != NULL && p_cur->uusInfo->uusData != NULL) {
+ RIL_UUS_Info* uusInfo = p_cur->uusInfo;
+ calls[i].base.uusInfo.resize(1);
+ calls[i].base.uusInfo[0].uusType = (UusType)uusInfo->uusType;
+ calls[i].base.uusInfo[0].uusDcs = (UusDcs)uusInfo->uusDcs;
+ // convert uusInfo->uusData to a null-terminated string
+ char* nullTermStr = strndup(uusInfo->uusData, uusInfo->uusLength);
+ calls[i].base.uusInfo[0].uusData = nullTermStr;
+ free(nullTermStr);
+ }
+ }
+ retStatus = radioService[slotId]->mRadioResponseV1_2->getCurrentCallsResponse_1_2(
+ responseInfo, calls);
+ } else {
+ hidl_vec<Call> calls;
+ int num = responseLen / sizeof(RIL_Call*);
+ calls.resize(num);
+
+ for (int i = 0; i < num; i++) {
+ RIL_Call* p_cur = ((RIL_Call**)response)[i];
+ /* each call info */
+ calls[i].state = (CallState)p_cur->state;
+ calls[i].index = p_cur->index;
+ calls[i].toa = p_cur->toa;
+ calls[i].isMpty = p_cur->isMpty;
+ calls[i].isMT = p_cur->isMT;
+ calls[i].als = p_cur->als;
+ calls[i].isVoice = p_cur->isVoice;
+ calls[i].isVoicePrivacy = p_cur->isVoicePrivacy;
+ calls[i].number = convertCharPtrToHidlString(p_cur->number);
+ calls[i].numberPresentation = (CallPresentation)p_cur->numberPresentation;
+ calls[i].name = convertCharPtrToHidlString(p_cur->name);
+ calls[i].namePresentation = (CallPresentation)p_cur->namePresentation;
+ if (p_cur->uusInfo != NULL && p_cur->uusInfo->uusData != NULL) {
+ RIL_UUS_Info* uusInfo = p_cur->uusInfo;
+ calls[i].uusInfo.resize(1);
+ calls[i].uusInfo[0].uusType = (UusType)uusInfo->uusType;
+ calls[i].uusInfo[0].uusDcs = (UusDcs)uusInfo->uusDcs;
+ // convert uusInfo->uusData to a null-terminated string
+ char* nullTermStr = strndup(uusInfo->uusData, uusInfo->uusLength);
+ calls[i].uusInfo[0].uusData = nullTermStr;
+ free(nullTermStr);
+ }
}
+ retStatus = radioService[slotId]->mRadioResponse->getCurrentCallsResponse(
+ responseInfo, calls);
}
+ radioService[slotId]->checkReturnStatus(retStatus);
}
-
- Return<void> retStatus = radioService[slotId]->mRadioResponse->
- getCurrentCallsResponse(responseInfo, calls);
- radioService[slotId]->checkReturnStatus(retStatus);
} else {
RLOGE("getCurrentCallsResponse: radioService[%d]->mRadioResponse == NULL", slotId);
}
@@ -5268,19 +5368,19 @@ int radio_1_6::hangupForegroundResumeBackgroundResponse(int slotId, int response
RIL_Errno e, void *response,
size_t responseLen) {
#if VDBG
- RLOGD("hangupWaitingOrBackgroundResponse: serial %d", serial);
+ RLOGD("hangupForegroundResumeBackgroundResponse: serial %d", serial);
#endif
if (radioService[slotId]->mRadioResponse != NULL) {
RadioResponseInfo responseInfo = {};
populateResponseInfo(responseInfo, serial, responseType, e);
Return<void> retStatus =
- radioService[slotId]->mRadioResponse->hangupWaitingOrBackgroundResponse(
- responseInfo);
+ radioService[slotId]->mRadioResponse->hangupForegroundResumeBackgroundResponse(
+ responseInfo);
radioService[slotId]->checkReturnStatus(retStatus);
} else {
- RLOGE("hangupWaitingOrBackgroundResponse: radioService[%d]->mRadioResponse == NULL",
- slotId);
+ RLOGE("hangupForegroundResumeBackgroundResponse: radioService[%d]->mRadioResponse == NULL",
+ slotId);
}
return 0;
@@ -5362,7 +5462,7 @@ int radio_1_6::getLastCallFailCauseResponse(int slotId,
LastCallFailCauseInfo info = {};
info.vendorCause = hidl_string();
if (response == NULL) {
- RLOGE("getCurrentCallsResponse Invalid response: NULL");
+ RLOGE("getLastCallFailCauseResponse Invalid response: NULL");
if (e == RIL_E_SUCCESS) responseInfo.error = RadioError::INVALID_RESPONSE;
} else if (responseLen == sizeof(int)) {
int *pInt = (int *) response;
@@ -5372,7 +5472,7 @@ int radio_1_6::getLastCallFailCauseResponse(int slotId,
info.causeCode = (LastCallFailCause) pFailCauseInfo->cause_code;
info.vendorCause = convertCharPtrToHidlString(pFailCauseInfo->vendor_cause);
} else {
- RLOGE("getCurrentCallsResponse Invalid response: NULL");
+ RLOGE("getLastCallFailCauseResponse Invalid response: NULL");
if (e == RIL_E_SUCCESS) responseInfo.error = RadioError::INVALID_RESPONSE;
}
@@ -5387,46 +5487,59 @@ int radio_1_6::getLastCallFailCauseResponse(int slotId,
return 0;
}
-int radio_1_6::getSignalStrengthResponse(int slotId,
- int responseType, int serial, RIL_Errno e,
- void *response, size_t responseLen) {
+int radio_1_6::getSignalStrengthResponse(int slotId, int responseType, int serial, RIL_Errno e,
+ void* response, size_t responseLen) {
#if VDBG
RLOGD("getSignalStrengthResponse: serial %d", serial);
#endif
- if (radioService[slotId]->mRadioResponseV1_4 != NULL) {
- RadioResponseInfo responseInfo = {};
+ V1_6::RadioResponseInfo responseInfo16 = {};
+ RadioResponseInfo responseInfo = {};
+ if (radioService[slotId]->mRadioResponseV1_6 != NULL) {
+ populateResponseInfo_1_6(responseInfo16, serial, responseType, e);
+ } else {
populateResponseInfo(responseInfo, serial, responseType, e);
- ::android::hardware::radio::V1_4::SignalStrength signalStrength_1_4 = {};
- if (response == NULL || responseLen != sizeof(RIL_SignalStrength_v12)) {
- RLOGE("getSignalStrengthResponse: Invalid response");
- if (e == RIL_E_SUCCESS) responseInfo.error = RadioError::INVALID_RESPONSE;
- } else {
- convertRilSignalStrengthToHal_1_4(response, responseLen, signalStrength_1_4);
- }
-
- //TODO: future implementation needs to fill tdScdma, wcdma and nr signal strength.
+ }
- Return<void> retStatus = radioService[slotId]->mRadioResponseV1_4->
- getSignalStrengthResponse_1_4(responseInfo, signalStrength_1_4);
- radioService[slotId]->checkReturnStatus(retStatus);
- } else if (radioService[slotId]->mRadioResponse != NULL) {
- RadioResponseInfo responseInfo = {};
- populateResponseInfo(responseInfo, serial, responseType, e);
- SignalStrength signalStrength = {};
- if (response == NULL || responseLen != sizeof(RIL_SignalStrength_v12)) {
- RLOGE("getSignalStrengthResponse: Invalid response");
- if (e == RIL_E_SUCCESS) responseInfo.error = RadioError::INVALID_RESPONSE;
- } else {
- convertRilSignalStrengthToHal(response, responseLen, signalStrength);
+ if (response == NULL || responseLen != sizeof(RIL_SignalStrength_v12)) {
+ RLOGE("getSignalStrengthResponse: Invalid response");
+ if (e == RIL_E_SUCCESS) {
+ responseInfo16.error = V1_6::RadioError::INVALID_RESPONSE;
+ responseInfo.error = RadioError::INVALID_RESPONSE;
}
-
- Return<void> retStatus = radioService[slotId]->mRadioResponse->getSignalStrengthResponse(
- responseInfo, signalStrength);
- radioService[slotId]->checkReturnStatus(retStatus);
} else {
- RLOGE("getSignalStrengthResponse: radioService[%d]->mRadioResponse == NULL",
- slotId);
+ if (radioService[slotId]->mRadioResponseV1_6 != NULL) {
+ V1_6::SignalStrength signalStrength_1_6 = {};
+ convertRilSignalStrengthToHal_1_6(response, responseLen, signalStrength_1_6);
+ Return<void> retStatus =
+ radioService[slotId]->mRadioResponseV1_6->getSignalStrengthResponse_1_6(
+ responseInfo16, signalStrength_1_6);
+ radioService[slotId]->checkReturnStatus(retStatus);
+ } else if (radioService[slotId]->mRadioResponseV1_4 != NULL) {
+ V1_4::SignalStrength signalStrength_1_4 = {};
+ convertRilSignalStrengthToHal_1_4(response, responseLen, signalStrength_1_4);
+ // TODO: future implementation needs to fill tdScdma, wcdma and nr signal strength.
+ Return<void> retStatus =
+ radioService[slotId]->mRadioResponseV1_4->getSignalStrengthResponse_1_4(
+ responseInfo, signalStrength_1_4);
+ radioService[slotId]->checkReturnStatus(retStatus);
+ } else if (radioService[slotId]->mRadioResponseV1_2 != NULL) {
+ V1_2::SignalStrength signalStrength_1_2 = {};
+ convertRilSignalStrengthToHal_1_2(response, responseLen, signalStrength_1_2);
+ Return<void> retStatus =
+ radioService[slotId]->mRadioResponseV1_2->getSignalStrengthResponse_1_2(
+ responseInfo, signalStrength_1_2);
+ radioService[slotId]->checkReturnStatus(retStatus);
+ } else if (radioService[slotId]->mRadioResponse != NULL) {
+ SignalStrength signalStrength = {};
+ convertRilSignalStrengthToHal(response, responseLen, signalStrength);
+ Return<void> retStatus =
+ radioService[slotId]->mRadioResponse->getSignalStrengthResponse(responseInfo,
+ signalStrength);
+ radioService[slotId]->checkReturnStatus(retStatus);
+ } else {
+ RLOGE("getSignalStrengthResponse: radioService[%d]->mRadioResponse == NULL", slotId);
+ }
}
return 0;
@@ -6483,9 +6596,8 @@ int radio_1_6::getVoiceRegistrationStateResponse(int slotId,
return 0;
}
-int radio_1_6::getDataRegistrationStateResponse(int slotId,
- int responseType, int serial, RIL_Errno e,
- void *response, size_t responseLen) {
+int radio_1_6::getDataRegistrationStateResponse(int slotId, int responseType, int serial,
+ RIL_Errno e, void* response, size_t responseLen) {
#if VDBG
RLOGD("getDataRegistrationStateResponse: serial %d", serial);
#endif
@@ -6547,11 +6659,11 @@ int radio_1_6::getDataRegistrationStateResponse(int slotId,
}
Return<void> retStatus =
- radioService[slotId]
- ->mRadioResponseV1_6
- ->getVoiceRegistrationStateResponse_1_6(
- responseInfo_1_6, regResponse);
+ radioService[slotId]
+ ->mRadioResponseV1_6->getDataRegistrationStateResponse_1_6(
+ responseInfo_1_6, regResponse);
radioService[slotId]->checkReturnStatus(retStatus);
+ return 0;
}
} else if (s_vendorFunctions->version <= 14 &&
radioService[slotId]->mRadioResponseV1_5 != NULL) {
@@ -6587,11 +6699,11 @@ int radio_1_6::getDataRegistrationStateResponse(int slotId,
numStrings, resp);
Return<void> retStatus =
- radioService[slotId]
- ->mRadioResponseV1_5
- ->getDataRegistrationStateResponse_1_5(
- responseInfo, regResponse);
+ radioService[slotId]
+ ->mRadioResponseV1_5->getDataRegistrationStateResponse_1_5(
+ responseInfo, regResponse);
radioService[slotId]->checkReturnStatus(retStatus);
+ return 0;
}
} else if (s_vendorFunctions->version <= 14 &&
radioService[slotId]->mRadioResponseV1_2 != NULL) {
@@ -6611,6 +6723,7 @@ int radio_1_6::getDataRegistrationStateResponse(int slotId,
Return<void> retStatus = radioService[slotId]->mRadioResponseV1_2->
getDataRegistrationStateResponse_1_2(responseInfo, dataRegResponse);
radioService[slotId]->checkReturnStatus(retStatus);
+ return 0;
}
} else if (s_vendorFunctions->version <= 14) {
int numStrings = responseLen / sizeof(char *);
@@ -7605,29 +7718,56 @@ int radio_1_6::getClipResponse(int slotId,
return 0;
}
-int radio_1_6::getDataCallListResponse(int slotId,
- int responseType, int serial, RIL_Errno e,
- void *response, size_t responseLen) {
+int radio_1_6::getDataCallListResponse(int slotId, int responseType, int serial, RIL_Errno e,
+ void* response, size_t responseLen) {
#if VDBG
RLOGD("getDataCallListResponse: serial %d", serial);
#endif
- if (radioService[slotId]->mRadioResponse != NULL) {
+ if (radioService[slotId]->mRadioResponse != NULL ||
+ radioService[slotId]->mRadioResponseV1_4 != NULL ||
+ radioService[slotId]->mRadioResponseV1_5 != NULL ||
+ radioService[slotId]->mRadioResponseV1_6 != NULL) {
+ V1_6::RadioResponseInfo responseInfo16 = {};
RadioResponseInfo responseInfo = {};
- populateResponseInfo(responseInfo, serial, responseType, e);
+ if (radioService[slotId]->mRadioResponseV1_6 != NULL) {
+ populateResponseInfo_1_6(responseInfo16, serial, responseType, e);
+ } else {
+ populateResponseInfo(responseInfo, serial, responseType, e);
+ }
- hidl_vec<SetupDataCallResult> ret;
if ((response == NULL && responseLen != 0)
|| responseLen % sizeof(RIL_Data_Call_Response_v11) != 0) {
RLOGE("getDataCallListResponse: invalid response");
- if (e == RIL_E_SUCCESS) responseInfo.error = RadioError::INVALID_RESPONSE;
+ if (e == RIL_E_SUCCESS) {
+ responseInfo16.error = V1_6::RadioError::INVALID_RESPONSE;
+ responseInfo.error = RadioError::INVALID_RESPONSE;
+ }
} else {
- convertRilDataCallListToHal(response, responseLen, ret);
+ Return<void> retStatus;
+ if (radioService[slotId]->mRadioResponseV1_6 != NULL) {
+ hidl_vec<V1_6::SetupDataCallResult> ret;
+ convertRilDataCallListToHal_1_6(response, responseLen, ret);
+ retStatus = radioService[slotId]->mRadioResponseV1_6->getDataCallListResponse_1_6(
+ responseInfo16, ret);
+ } else if (radioService[slotId]->mRadioResponseV1_5 != NULL) {
+ hidl_vec<V1_5::SetupDataCallResult> ret;
+ convertRilDataCallListToHal_1_5(response, responseLen, ret);
+ retStatus = radioService[slotId]->mRadioResponseV1_5->getDataCallListResponse_1_5(
+ responseInfo, ret);
+ } else if (radioService[slotId]->mRadioResponseV1_4 != NULL) {
+ hidl_vec<V1_4::SetupDataCallResult> ret;
+ convertRilDataCallListToHal_1_4(response, responseLen, ret);
+ retStatus = radioService[slotId]->mRadioResponseV1_4->getDataCallListResponse_1_4(
+ responseInfo, ret);
+ } else {
+ hidl_vec<SetupDataCallResult> ret;
+ convertRilDataCallListToHal(response, responseLen, ret);
+ retStatus = radioService[slotId]->mRadioResponse->getDataCallListResponse(
+ responseInfo, ret);
+ }
+ radioService[slotId]->checkReturnStatus(retStatus);
}
-
- Return<void> retStatus = radioService[slotId]->mRadioResponse->getDataCallListResponse(
- responseInfo, ret);
- radioService[slotId]->checkReturnStatus(retStatus);
} else {
RLOGE("getDataCallListResponse: radioService[%d]->mRadioResponse == NULL", slotId);
}
@@ -8794,47 +8934,72 @@ int radio_1_6::getVoiceRadioTechnologyResponse(int slotId,
return 0;
}
-int radio_1_6::getCellInfoListResponse(int slotId,
- int responseType,
- int serial, RIL_Errno e, void *response,
- size_t responseLen) {
+int radio_1_6::getCellInfoListResponse(int slotId, int responseType, int serial, RIL_Errno e,
+ void* response, size_t responseLen) {
#if VDBG
RLOGD("getCellInfoListResponse: serial %d", serial);
#endif
-
- if (radioService[slotId]->mRadioResponse != NULL ||
- radioService[slotId]->mRadioResponseV1_2 != NULL) {
+ if (radioService[slotId]->mRadioResponseV1_6 != NULL) {
+ V1_6::RadioResponseInfo responseInfo = {};
+ populateResponseInfo_1_6(responseInfo, serial, responseType, e);
+ hidl_vec<V1_6::CellInfo> ret;
+ Return<void> retStatus;
+ if (response != NULL && responseLen != 0 && responseLen % sizeof(RIL_CellInfo_v16) == 0) {
+ convertRilCellInfoListToHal_1_6(response, responseLen, ret);
+ } else {
+ RLOGE("getCellInfoListResponse_1_6: Invalid response");
+ if (e == RIL_E_SUCCESS) responseInfo.error = V1_6::RadioError::INVALID_RESPONSE;
+ }
+ retStatus = radioService[slotId]->mRadioResponseV1_6->getCellInfoListResponse_1_6(
+ responseInfo, ret);
+ radioService[slotId]->checkReturnStatus(retStatus);
+ } else if (radioService[slotId]->mRadioResponse != NULL ||
+ radioService[slotId]->mRadioResponseV1_2 != NULL ||
+ radioService[slotId]->mRadioResponseV1_4 != NULL ||
+ radioService[slotId]->mRadioResponseV1_5 != NULL) {
RadioResponseInfo responseInfo = {};
populateResponseInfo(responseInfo, serial, responseType, e);
-
+ bool error = response == NULL && responseLen != 0;
Return<void> retStatus;
- hidl_vec<CellInfo> ret;
- if ((response == NULL && responseLen != 0)
- || responseLen % sizeof(RIL_CellInfo_v12) != 0) {
- RLOGE("getCellInfoListResponse: Invalid response");
- if (e == RIL_E_SUCCESS) responseInfo.error = RadioError::INVALID_RESPONSE;
-
- if (radioService[slotId]->mRadioResponseV1_2 != NULL) {
- hidl_vec<V1_2::CellInfo> ret;
- retStatus = radioService[slotId]->mRadioResponseV1_2->
- getCellInfoListResponse_1_2(responseInfo, ret);
+ if (radioService[slotId]->mRadioResponseV1_5 != NULL) {
+ hidl_vec<V1_5::CellInfo> ret;
+ if (!error && responseLen % sizeof(RIL_CellInfo_v16) != 0) {
+ convertRilCellInfoListToHal_1_5(response, responseLen, ret);
} else {
- hidl_vec<CellInfo> ret;
- retStatus = radioService[slotId]->mRadioResponse->
- getCellInfoListResponse(responseInfo, ret);
+ RLOGE("getCellInfoListResponse_1_5: Invalid response");
+ if (e == RIL_E_SUCCESS) responseInfo.error = RadioError::INVALID_RESPONSE;
}
- } else {
- if (radioService[slotId]->mRadioResponseV1_2 != NULL) {
- hidl_vec<V1_2::CellInfo> ret;
+ retStatus = radioService[slotId]->mRadioResponseV1_5->getCellInfoListResponse_1_5(
+ responseInfo, ret);
+ } else if (radioService[slotId]->mRadioResponseV1_4 != NULL) {
+ hidl_vec<V1_4::CellInfo> ret;
+ if (!error && responseLen % sizeof(RIL_CellInfo_v16) != 0) {
+ convertRilCellInfoListToHal_1_4(response, responseLen, ret);
+ } else {
+ RLOGE("getCellInfoListResponse_1_4: Invalid response");
+ if (e == RIL_E_SUCCESS) responseInfo.error = RadioError::INVALID_RESPONSE;
+ }
+ radioService[slotId]->mRadioResponseV1_4->getCellInfoListResponse_1_4(responseInfo,
+ ret);
+ } else if (radioService[slotId]->mRadioResponseV1_2 != NULL) {
+ hidl_vec<V1_2::CellInfo> ret;
+ if (!error && responseLen % sizeof(RIL_CellInfo_v12) != 0) {
convertRilCellInfoListToHal_1_2(response, responseLen, ret);
- retStatus = radioService[slotId]->mRadioResponseV1_2->
- getCellInfoListResponse_1_2(responseInfo, ret);
} else {
- hidl_vec<CellInfo> ret;
+ RLOGE("getCellInfoListResponse_1_2: Invalid response");
+ if (e == RIL_E_SUCCESS) responseInfo.error = RadioError::INVALID_RESPONSE;
+ }
+ radioService[slotId]->mRadioResponseV1_2->getCellInfoListResponse_1_2(responseInfo,
+ ret);
+ } else {
+ hidl_vec<CellInfo> ret;
+ if (!error && responseLen % sizeof(RIL_CellInfo) != 0) {
convertRilCellInfoListToHal(response, responseLen, ret);
- retStatus = radioService[slotId]->mRadioResponse->
- getCellInfoListResponse(responseInfo, ret);
+ } else {
+ RLOGE("getCellInfoListResponse: Invalid response");
+ if (e == RIL_E_SUCCESS) responseInfo.error = RadioError::INVALID_RESPONSE;
}
+ radioService[slotId]->mRadioResponse->getCellInfoListResponse(responseInfo, ret);
}
radioService[slotId]->checkReturnStatus(retStatus);
} else {
@@ -8866,9 +9031,8 @@ int radio_1_6::setCellInfoListRateResponse(int slotId,
return 0;
}
-int radio_1_6::setInitialAttachApnResponse(int slotId,
- int responseType, int serial, RIL_Errno e,
- void *response, size_t responseLen) {
+int radio_1_6::setInitialAttachApnResponse(int slotId, int responseType, int serial, RIL_Errno e,
+ void* response, size_t responseLen) {
#if VDBG
RLOGD("setInitialAttachApnResponse: serial %d", serial);
#endif
@@ -8876,18 +9040,18 @@ int radio_1_6::setInitialAttachApnResponse(int slotId,
if (radioService[slotId]->mRadioResponseV1_5 != NULL) {
RadioResponseInfo responseInfo = {};
populateResponseInfo(responseInfo, serial, responseType, e);
- Return<void> retStatus
- = radioService[slotId]->mRadioResponseV1_5->setInitialAttachApnResponse_1_5(
- responseInfo);
+ Return<void> retStatus =
+ radioService[slotId]->mRadioResponseV1_5->setInitialAttachApnResponse_1_5(
+ responseInfo);
+ radioService[slotId]->checkReturnStatus(retStatus);
} else if (radioService[slotId]->mRadioResponse != NULL) {
RadioResponseInfo responseInfo = {};
populateResponseInfo(responseInfo, serial, responseType, e);
- Return<void> retStatus
- = radioService[slotId]->mRadioResponse->setInitialAttachApnResponse(responseInfo);
+ Return<void> retStatus =
+ radioService[slotId]->mRadioResponse->setInitialAttachApnResponse(responseInfo);
radioService[slotId]->checkReturnStatus(retStatus);
} else {
- RLOGE("setInitialAttachApnResponse: radioService[%d]->mRadioResponse == NULL",
- slotId);
+ RLOGE("setInitialAttachApnResponse: radioService[%d]->mRadioResponse == NULL", slotId);
}
return 0;
@@ -10638,6 +10802,26 @@ void convertRilSignalStrengthToHal(void *response, size_t responseLen,
signalStrength.tdScdma.rscp = rilSignalStrength->TD_SCDMA_SignalStrength.rscp;
}
+void convertRilSignalStrengthToHal_1_2(void* response, size_t responseLen,
+ V1_2::SignalStrength& signalStrength_1_2) {
+ SignalStrength signalStrength = {};
+ convertRilSignalStrengthToHal(response, responseLen, signalStrength);
+ signalStrength_1_2.gsm = signalStrength.gw;
+ signalStrength_1_2.cdma = signalStrength.cdma;
+ signalStrength_1_2.evdo = signalStrength.evdo;
+ signalStrength_1_2.lte = signalStrength.lte;
+
+ RIL_SignalStrength_v12* rilSignalStrength = (RIL_SignalStrength_v12*)response;
+ signalStrength_1_2.wcdma.base.signalStrength =
+ rilSignalStrength->WCDMA_SignalStrength.signalStrength;
+ signalStrength_1_2.wcdma.base.bitErrorRate =
+ rilSignalStrength->WCDMA_SignalStrength.bitErrorRate;
+ signalStrength_1_2.wcdma.rscp = INT_MAX;
+ signalStrength_1_2.wcdma.ecno = INT_MAX;
+
+ signalStrength_1_2.tdScdma.rscp = INT_MAX;
+}
+
void convertRilSignalStrengthToHal_1_4(void *response, size_t responseLen,
V1_4::SignalStrength& signalStrength_1_4) {
SignalStrength signalStrength = {};
@@ -10667,12 +10851,41 @@ void convertRilSignalStrengthToHal_1_4(void *response, size_t responseLen,
signalStrength_1_4.nr.csiSinr = rilSignalStrength->NR_SignalStrength.ssSinr;
}
-int radio_1_6::currentSignalStrengthInd(int slotId,
- int indicationType, int token, RIL_Errno e,
- void *response, size_t responseLen) {
- if (radioService[slotId] != NULL &&
- (radioService[slotId]->mRadioIndication != NULL ||
- radioService[slotId]->mRadioIndicationV1_4 != NULL)) {
+void convertRilSignalStrengthToHal_1_6(void* response, size_t responseLen,
+ V1_6::SignalStrength& signalStrength_1_6) {
+ SignalStrength signalStrength = {};
+ convertRilSignalStrengthToHal(response, responseLen, signalStrength);
+ signalStrength_1_6.gsm = signalStrength.gw;
+ signalStrength_1_6.cdma = signalStrength.cdma;
+ signalStrength_1_6.evdo = signalStrength.evdo;
+ signalStrength_1_6.lte.base = signalStrength.lte;
+
+ RIL_SignalStrength_v12* rilSignalStrength = (RIL_SignalStrength_v12*)response;
+ signalStrength_1_6.wcdma.base.signalStrength =
+ rilSignalStrength->WCDMA_SignalStrength.signalStrength;
+ signalStrength_1_6.wcdma.base.bitErrorRate =
+ rilSignalStrength->WCDMA_SignalStrength.bitErrorRate;
+ signalStrength_1_6.wcdma.rscp = INT_MAX;
+ signalStrength_1_6.wcdma.ecno = INT_MAX;
+
+ signalStrength_1_6.tdscdma.signalStrength = INT_MAX;
+ signalStrength_1_6.tdscdma.bitErrorRate = INT_MAX;
+ signalStrength_1_6.tdscdma.rscp = INT_MAX;
+
+ signalStrength_1_6.nr.base.ssRsrp = rilSignalStrength->NR_SignalStrength.ssRsrp;
+ signalStrength_1_6.nr.base.ssRsrq = rilSignalStrength->NR_SignalStrength.ssRsrq;
+ signalStrength_1_6.nr.base.ssSinr = rilSignalStrength->NR_SignalStrength.ssSinr;
+ signalStrength_1_6.nr.base.csiRsrp = rilSignalStrength->NR_SignalStrength.csiRsrp;
+ signalStrength_1_6.nr.base.csiRsrq = rilSignalStrength->NR_SignalStrength.csiRsrq;
+ signalStrength_1_6.nr.base.csiSinr = rilSignalStrength->NR_SignalStrength.ssSinr;
+}
+
+int radio_1_6::currentSignalStrengthInd(int slotId, int indicationType, int token, RIL_Errno e,
+ void* response, size_t responseLen) {
+ if (radioService[slotId] != NULL && (radioService[slotId]->mRadioIndication != NULL ||
+ radioService[slotId]->mRadioIndicationV1_2 != NULL ||
+ radioService[slotId]->mRadioIndicationV1_4 != NULL) ||
+ radioService[slotId]->mRadioIndicationV1_6 != NULL) {
if (response == NULL || responseLen != sizeof(RIL_SignalStrength_v12)) {
RLOGE("currentSignalStrengthInd: invalid response");
return 0;
@@ -10682,16 +10895,26 @@ int radio_1_6::currentSignalStrengthInd(int slotId,
RLOGD("currentSignalStrengthInd");
#endif
Return<void> retStatus;
- if (radioService[slotId]->mRadioIndicationV1_4 != NULL) {
- V1_4::SignalStrength signalStrength_1_4 = {};
- convertRilSignalStrengthToHal_1_4(response, responseLen, signalStrength_1_4);
- retStatus = radioService[slotId]->mRadioIndicationV1_4->currentSignalStrength_1_4(
- convertIntToRadioIndicationType(indicationType), signalStrength_1_4);
+ if (radioService[slotId]->mRadioIndicationV1_6 != NULL) {
+ V1_6::SignalStrength signalStrength_1_6 = {};
+ convertRilSignalStrengthToHal_1_6(response, responseLen, signalStrength_1_6);
+ retStatus = radioService[slotId]->mRadioIndicationV1_6->currentSignalStrength_1_6(
+ convertIntToRadioIndicationType(indicationType), signalStrength_1_6);
+ } else if (radioService[slotId]->mRadioIndicationV1_4 != NULL) {
+ V1_4::SignalStrength signalStrength_1_4 = {};
+ convertRilSignalStrengthToHal_1_4(response, responseLen, signalStrength_1_4);
+ retStatus = radioService[slotId]->mRadioIndicationV1_4->currentSignalStrength_1_4(
+ convertIntToRadioIndicationType(indicationType), signalStrength_1_4);
+ } else if (radioService[slotId]->mRadioIndicationV1_2 != NULL) {
+ V1_2::SignalStrength signalStrength_1_2 = {};
+ convertRilSignalStrengthToHal_1_2(response, responseLen, signalStrength_1_2);
+ retStatus = radioService[slotId]->mRadioIndicationV1_2->currentSignalStrength_1_2(
+ convertIntToRadioIndicationType(indicationType), signalStrength_1_2);
} else {
- SignalStrength signalStrength = {};
- convertRilSignalStrengthToHal(response, responseLen, signalStrength);
- retStatus = radioService[slotId]->mRadioIndication->currentSignalStrength(
- convertIntToRadioIndicationType(indicationType), signalStrength);
+ SignalStrength signalStrength = {};
+ convertRilSignalStrengthToHal(response, responseLen, signalStrength);
+ retStatus = radioService[slotId]->mRadioIndication->currentSignalStrength(
+ convertIntToRadioIndicationType(indicationType), signalStrength);
}
radioService[slotId]->checkReturnStatus(retStatus);
} else {
@@ -10841,6 +11064,39 @@ void convertRilDataCallListToHal(void *response, size_t responseLen,
}
}
+void convertRilDataCallListToHal_1_4(void* response, size_t responseLen,
+ hidl_vec<V1_4::SetupDataCallResult>& dcResultList) {
+ int num = responseLen / sizeof(RIL_Data_Call_Response_v11);
+
+ RIL_Data_Call_Response_v11* dcResponse = (RIL_Data_Call_Response_v11*)response;
+ dcResultList.resize(num);
+ for (int i = 0; i < num; i++) {
+ convertRilDataCallToHal(&dcResponse[i], dcResultList[i]);
+ }
+}
+
+void convertRilDataCallListToHal_1_5(void* response, size_t responseLen,
+ hidl_vec<V1_5::SetupDataCallResult>& dcResultList) {
+ int num = responseLen / sizeof(RIL_Data_Call_Response_v12);
+
+ RIL_Data_Call_Response_v12* dcResponse = (RIL_Data_Call_Response_v12*)response;
+ dcResultList.resize(num);
+ for (int i = 0; i < num; i++) {
+ convertRilDataCallToHal(&dcResponse[i], dcResultList[i]);
+ }
+}
+
+void convertRilDataCallListToHal_1_6(void* response, size_t responseLen,
+ hidl_vec<V1_6::SetupDataCallResult>& dcResultList) {
+ int num = responseLen / sizeof(RIL_Data_Call_Response_v12);
+
+ RIL_Data_Call_Response_v12* dcResponse = (RIL_Data_Call_Response_v12*)response;
+ dcResultList.resize(num);
+ for (int i = 0; i < num; i++) {
+ convertRilDataCallToHal(&dcResponse[i], dcResultList[i]);
+ }
+}
+
int radio_1_6::dataCallListChangedInd(int slotId,
int indicationType, int token, RIL_Errno e, void *response,
size_t responseLen) {
@@ -12045,6 +12301,346 @@ void convertRilCellInfoListToHal_1_4(void *response, size_t responseLen, hidl_ve
}
}
+void convertRilCellInfoListToHal_1_5(void* response, size_t responseLen,
+ hidl_vec<V1_5::CellInfo>& records) {
+ int num = responseLen / sizeof(RIL_CellInfo_v16);
+ records.resize(num);
+ RIL_CellInfo_v16* rillCellInfo = (RIL_CellInfo_v16*)response;
+ for (int i = 0; i < num; i++) {
+ records[i].registered = rillCellInfo->registered;
+ records[i].connectionStatus = (V1_2::CellConnectionStatus)rillCellInfo->connectionStatus;
+
+ switch (rillCellInfo->cellInfoType) {
+ case RIL_CELL_INFO_TYPE_GSM: {
+ V1_5::CellInfoGsm cellInfoGsm;
+ cellInfoGsm.cellIdentityGsm.base.base.mcc =
+ std::to_string(rillCellInfo->CellInfo.gsm.cellIdentityGsm.mcc);
+ cellInfoGsm.cellIdentityGsm.base.base.mnc =
+ ril::util::mnc::decode(rillCellInfo->CellInfo.gsm.cellIdentityGsm.mnc);
+ cellInfoGsm.cellIdentityGsm.base.base.lac =
+ rillCellInfo->CellInfo.gsm.cellIdentityGsm.lac;
+ cellInfoGsm.cellIdentityGsm.base.base.cid =
+ rillCellInfo->CellInfo.gsm.cellIdentityGsm.cid;
+ cellInfoGsm.cellIdentityGsm.base.base.arfcn =
+ rillCellInfo->CellInfo.gsm.cellIdentityGsm.arfcn;
+ cellInfoGsm.cellIdentityGsm.base.base.bsic =
+ rillCellInfo->CellInfo.gsm.cellIdentityGsm.bsic;
+ cellInfoGsm.signalStrengthGsm.signalStrength =
+ rillCellInfo->CellInfo.gsm.signalStrengthGsm.signalStrength;
+ cellInfoGsm.signalStrengthGsm.bitErrorRate =
+ rillCellInfo->CellInfo.gsm.signalStrengthGsm.bitErrorRate;
+ cellInfoGsm.signalStrengthGsm.timingAdvance =
+ rillCellInfo->CellInfo.gsm.signalStrengthGsm.timingAdvance;
+ records[i].ratSpecificInfo.gsm(cellInfoGsm);
+ break;
+ }
+
+ case RIL_CELL_INFO_TYPE_WCDMA: {
+ V1_5::CellInfoWcdma cellInfoWcdma;
+ cellInfoWcdma.cellIdentityWcdma.base.base.mcc =
+ std::to_string(rillCellInfo->CellInfo.wcdma.cellIdentityWcdma.mcc);
+ cellInfoWcdma.cellIdentityWcdma.base.base.mnc =
+ ril::util::mnc::decode(rillCellInfo->CellInfo.wcdma.cellIdentityWcdma.mnc);
+ cellInfoWcdma.cellIdentityWcdma.base.base.lac =
+ rillCellInfo->CellInfo.wcdma.cellIdentityWcdma.lac;
+ cellInfoWcdma.cellIdentityWcdma.base.base.cid =
+ rillCellInfo->CellInfo.wcdma.cellIdentityWcdma.cid;
+ cellInfoWcdma.cellIdentityWcdma.base.base.psc =
+ rillCellInfo->CellInfo.wcdma.cellIdentityWcdma.psc;
+ cellInfoWcdma.cellIdentityWcdma.base.base.uarfcn =
+ rillCellInfo->CellInfo.wcdma.cellIdentityWcdma.uarfcn;
+ cellInfoWcdma.signalStrengthWcdma.base.signalStrength =
+ rillCellInfo->CellInfo.wcdma.signalStrengthWcdma.signalStrength;
+ cellInfoWcdma.signalStrengthWcdma.base.bitErrorRate =
+ rillCellInfo->CellInfo.wcdma.signalStrengthWcdma.bitErrorRate;
+ records[i].ratSpecificInfo.wcdma(cellInfoWcdma);
+ break;
+ }
+
+ case RIL_CELL_INFO_TYPE_CDMA: {
+ V1_2::CellInfoCdma cellInfoCdma;
+ cellInfoCdma.cellIdentityCdma.base.networkId =
+ rillCellInfo->CellInfo.cdma.cellIdentityCdma.networkId;
+ cellInfoCdma.cellIdentityCdma.base.systemId =
+ rillCellInfo->CellInfo.cdma.cellIdentityCdma.systemId;
+ cellInfoCdma.cellIdentityCdma.base.baseStationId =
+ rillCellInfo->CellInfo.cdma.cellIdentityCdma.basestationId;
+ cellInfoCdma.cellIdentityCdma.base.longitude =
+ rillCellInfo->CellInfo.cdma.cellIdentityCdma.longitude;
+ cellInfoCdma.cellIdentityCdma.base.latitude =
+ rillCellInfo->CellInfo.cdma.cellIdentityCdma.latitude;
+ cellInfoCdma.signalStrengthCdma.dbm =
+ rillCellInfo->CellInfo.cdma.signalStrengthCdma.dbm;
+ cellInfoCdma.signalStrengthCdma.ecio =
+ rillCellInfo->CellInfo.cdma.signalStrengthCdma.ecio;
+ cellInfoCdma.signalStrengthEvdo.dbm =
+ rillCellInfo->CellInfo.cdma.signalStrengthEvdo.dbm;
+ cellInfoCdma.signalStrengthEvdo.ecio =
+ rillCellInfo->CellInfo.cdma.signalStrengthEvdo.ecio;
+ cellInfoCdma.signalStrengthEvdo.signalNoiseRatio =
+ rillCellInfo->CellInfo.cdma.signalStrengthEvdo.signalNoiseRatio;
+ records[i].ratSpecificInfo.cdma(cellInfoCdma);
+ break;
+ }
+
+ case RIL_CELL_INFO_TYPE_LTE: {
+ V1_5::CellInfoLte cellInfoLte;
+ cellInfoLte.cellIdentityLte.base.base.mcc =
+ std::to_string(rillCellInfo->CellInfo.lte.cellIdentityLte.mcc);
+ cellInfoLte.cellIdentityLte.base.base.mnc =
+ ril::util::mnc::decode(rillCellInfo->CellInfo.lte.cellIdentityLte.mnc);
+ cellInfoLte.cellIdentityLte.base.base.ci =
+ rillCellInfo->CellInfo.lte.cellIdentityLte.ci;
+ cellInfoLte.cellIdentityLte.base.base.pci =
+ rillCellInfo->CellInfo.lte.cellIdentityLte.pci;
+ cellInfoLte.cellIdentityLte.base.base.tac =
+ rillCellInfo->CellInfo.lte.cellIdentityLte.tac;
+ cellInfoLte.cellIdentityLte.base.base.earfcn =
+ rillCellInfo->CellInfo.lte.cellIdentityLte.earfcn;
+ cellInfoLte.signalStrengthLte.signalStrength =
+ rillCellInfo->CellInfo.lte.signalStrengthLte.signalStrength;
+ cellInfoLte.signalStrengthLte.rsrp =
+ rillCellInfo->CellInfo.lte.signalStrengthLte.rsrp;
+ cellInfoLte.signalStrengthLte.rsrq =
+ rillCellInfo->CellInfo.lte.signalStrengthLte.rsrq;
+ cellInfoLte.signalStrengthLte.rssnr =
+ rillCellInfo->CellInfo.lte.signalStrengthLte.rssnr;
+ cellInfoLte.signalStrengthLte.cqi =
+ rillCellInfo->CellInfo.lte.signalStrengthLte.cqi;
+ cellInfoLte.signalStrengthLte.timingAdvance =
+ rillCellInfo->CellInfo.lte.signalStrengthLte.timingAdvance;
+ records[i].ratSpecificInfo.lte(cellInfoLte);
+ break;
+ }
+
+ case RIL_CELL_INFO_TYPE_TD_SCDMA: {
+ V1_5::CellInfoTdscdma cellInfoTdscdma;
+ cellInfoTdscdma.cellIdentityTdscdma.base.base.mcc =
+ std::to_string(rillCellInfo->CellInfo.tdscdma.cellIdentityTdscdma.mcc);
+ cellInfoTdscdma.cellIdentityTdscdma.base.base.mnc = ril::util::mnc::decode(
+ rillCellInfo->CellInfo.tdscdma.cellIdentityTdscdma.mnc);
+ cellInfoTdscdma.cellIdentityTdscdma.base.base.lac =
+ rillCellInfo->CellInfo.tdscdma.cellIdentityTdscdma.lac;
+ cellInfoTdscdma.cellIdentityTdscdma.base.base.cid =
+ rillCellInfo->CellInfo.tdscdma.cellIdentityTdscdma.cid;
+ cellInfoTdscdma.cellIdentityTdscdma.base.base.cpid =
+ rillCellInfo->CellInfo.tdscdma.cellIdentityTdscdma.cpid;
+ cellInfoTdscdma.signalStrengthTdscdma.rscp =
+ rillCellInfo->CellInfo.tdscdma.signalStrengthTdscdma.rscp;
+ records[i].ratSpecificInfo.tdscdma(cellInfoTdscdma);
+ break;
+ }
+
+ case RIL_CELL_INFO_TYPE_NR: {
+ V1_5::CellInfoNr cellInfoNr;
+ cellInfoNr.cellIdentityNr.base.mcc =
+ std::to_string(rillCellInfo->CellInfo.nr.cellidentity.mcc);
+ cellInfoNr.cellIdentityNr.base.mnc =
+ ril::util::mnc::decode(rillCellInfo->CellInfo.nr.cellidentity.mnc);
+ cellInfoNr.cellIdentityNr.base.nci = rillCellInfo->CellInfo.nr.cellidentity.nci;
+ cellInfoNr.cellIdentityNr.base.pci = rillCellInfo->CellInfo.nr.cellidentity.pci;
+ cellInfoNr.cellIdentityNr.base.tac = rillCellInfo->CellInfo.nr.cellidentity.tac;
+ cellInfoNr.cellIdentityNr.base.nrarfcn =
+ rillCellInfo->CellInfo.nr.cellidentity.nrarfcn;
+ cellInfoNr.cellIdentityNr.base.operatorNames.alphaLong = convertCharPtrToHidlString(
+ rillCellInfo->CellInfo.nr.cellidentity.operatorNames.alphaLong);
+ cellInfoNr.cellIdentityNr.base.operatorNames.alphaShort =
+ convertCharPtrToHidlString(
+ rillCellInfo->CellInfo.nr.cellidentity.operatorNames.alphaShort);
+
+ cellInfoNr.signalStrengthNr.ssRsrp =
+ rillCellInfo->CellInfo.nr.signalStrength.ssRsrp;
+ cellInfoNr.signalStrengthNr.ssRsrq =
+ rillCellInfo->CellInfo.nr.signalStrength.ssRsrq;
+ cellInfoNr.signalStrengthNr.ssSinr =
+ rillCellInfo->CellInfo.nr.signalStrength.ssSinr;
+ cellInfoNr.signalStrengthNr.csiRsrp =
+ rillCellInfo->CellInfo.nr.signalStrength.csiRsrp;
+ cellInfoNr.signalStrengthNr.csiRsrq =
+ rillCellInfo->CellInfo.nr.signalStrength.csiRsrq;
+ cellInfoNr.signalStrengthNr.csiSinr =
+ rillCellInfo->CellInfo.nr.signalStrength.csiSinr;
+ records[i].ratSpecificInfo.nr(cellInfoNr);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ rillCellInfo += 1;
+ }
+}
+
+void convertRilCellInfoListToHal_1_6(void* response, size_t responseLen,
+ hidl_vec<V1_6::CellInfo>& records) {
+ int num = responseLen / sizeof(RIL_CellInfo_v16);
+ records.resize(num);
+ RIL_CellInfo_v16* rillCellInfo = (RIL_CellInfo_v16*)response;
+ for (int i = 0; i < num; i++) {
+ records[i].registered = rillCellInfo->registered;
+ records[i].connectionStatus = (V1_2::CellConnectionStatus)rillCellInfo->connectionStatus;
+
+ switch (rillCellInfo->cellInfoType) {
+ case RIL_CELL_INFO_TYPE_GSM: {
+ V1_5::CellInfoGsm cellInfoGsm;
+ cellInfoGsm.cellIdentityGsm.base.base.mcc =
+ std::to_string(rillCellInfo->CellInfo.gsm.cellIdentityGsm.mcc);
+ cellInfoGsm.cellIdentityGsm.base.base.mnc =
+ ril::util::mnc::decode(rillCellInfo->CellInfo.gsm.cellIdentityGsm.mnc);
+ cellInfoGsm.cellIdentityGsm.base.base.lac =
+ rillCellInfo->CellInfo.gsm.cellIdentityGsm.lac;
+ cellInfoGsm.cellIdentityGsm.base.base.cid =
+ rillCellInfo->CellInfo.gsm.cellIdentityGsm.cid;
+ cellInfoGsm.cellIdentityGsm.base.base.arfcn =
+ rillCellInfo->CellInfo.gsm.cellIdentityGsm.arfcn;
+ cellInfoGsm.cellIdentityGsm.base.base.bsic =
+ rillCellInfo->CellInfo.gsm.cellIdentityGsm.bsic;
+ cellInfoGsm.signalStrengthGsm.signalStrength =
+ rillCellInfo->CellInfo.gsm.signalStrengthGsm.signalStrength;
+ cellInfoGsm.signalStrengthGsm.bitErrorRate =
+ rillCellInfo->CellInfo.gsm.signalStrengthGsm.bitErrorRate;
+ cellInfoGsm.signalStrengthGsm.timingAdvance =
+ rillCellInfo->CellInfo.gsm.signalStrengthGsm.timingAdvance;
+ records[i].ratSpecificInfo.gsm(cellInfoGsm);
+ break;
+ }
+
+ case RIL_CELL_INFO_TYPE_WCDMA: {
+ V1_5::CellInfoWcdma cellInfoWcdma;
+ cellInfoWcdma.cellIdentityWcdma.base.base.mcc =
+ std::to_string(rillCellInfo->CellInfo.wcdma.cellIdentityWcdma.mcc);
+ cellInfoWcdma.cellIdentityWcdma.base.base.mnc =
+ ril::util::mnc::decode(rillCellInfo->CellInfo.wcdma.cellIdentityWcdma.mnc);
+ cellInfoWcdma.cellIdentityWcdma.base.base.lac =
+ rillCellInfo->CellInfo.wcdma.cellIdentityWcdma.lac;
+ cellInfoWcdma.cellIdentityWcdma.base.base.cid =
+ rillCellInfo->CellInfo.wcdma.cellIdentityWcdma.cid;
+ cellInfoWcdma.cellIdentityWcdma.base.base.psc =
+ rillCellInfo->CellInfo.wcdma.cellIdentityWcdma.psc;
+ cellInfoWcdma.cellIdentityWcdma.base.base.uarfcn =
+ rillCellInfo->CellInfo.wcdma.cellIdentityWcdma.uarfcn;
+ cellInfoWcdma.signalStrengthWcdma.base.signalStrength =
+ rillCellInfo->CellInfo.wcdma.signalStrengthWcdma.signalStrength;
+ cellInfoWcdma.signalStrengthWcdma.base.bitErrorRate =
+ rillCellInfo->CellInfo.wcdma.signalStrengthWcdma.bitErrorRate;
+ records[i].ratSpecificInfo.wcdma(cellInfoWcdma);
+ break;
+ }
+
+ case RIL_CELL_INFO_TYPE_CDMA: {
+ V1_2::CellInfoCdma cellInfoCdma;
+ cellInfoCdma.cellIdentityCdma.base.networkId =
+ rillCellInfo->CellInfo.cdma.cellIdentityCdma.networkId;
+ cellInfoCdma.cellIdentityCdma.base.systemId =
+ rillCellInfo->CellInfo.cdma.cellIdentityCdma.systemId;
+ cellInfoCdma.cellIdentityCdma.base.baseStationId =
+ rillCellInfo->CellInfo.cdma.cellIdentityCdma.basestationId;
+ cellInfoCdma.cellIdentityCdma.base.longitude =
+ rillCellInfo->CellInfo.cdma.cellIdentityCdma.longitude;
+ cellInfoCdma.cellIdentityCdma.base.latitude =
+ rillCellInfo->CellInfo.cdma.cellIdentityCdma.latitude;
+ cellInfoCdma.signalStrengthCdma.dbm =
+ rillCellInfo->CellInfo.cdma.signalStrengthCdma.dbm;
+ cellInfoCdma.signalStrengthCdma.ecio =
+ rillCellInfo->CellInfo.cdma.signalStrengthCdma.ecio;
+ cellInfoCdma.signalStrengthEvdo.dbm =
+ rillCellInfo->CellInfo.cdma.signalStrengthEvdo.dbm;
+ cellInfoCdma.signalStrengthEvdo.ecio =
+ rillCellInfo->CellInfo.cdma.signalStrengthEvdo.ecio;
+ cellInfoCdma.signalStrengthEvdo.signalNoiseRatio =
+ rillCellInfo->CellInfo.cdma.signalStrengthEvdo.signalNoiseRatio;
+ records[i].ratSpecificInfo.cdma(cellInfoCdma);
+ break;
+ }
+
+ case RIL_CELL_INFO_TYPE_LTE: {
+ V1_6::CellInfoLte cellInfoLte;
+ cellInfoLte.cellIdentityLte.base.base.mcc =
+ std::to_string(rillCellInfo->CellInfo.lte.cellIdentityLte.mcc);
+ cellInfoLte.cellIdentityLte.base.base.mnc =
+ ril::util::mnc::decode(rillCellInfo->CellInfo.lte.cellIdentityLte.mnc);
+ cellInfoLte.cellIdentityLte.base.base.ci =
+ rillCellInfo->CellInfo.lte.cellIdentityLte.ci;
+ cellInfoLte.cellIdentityLte.base.base.pci =
+ rillCellInfo->CellInfo.lte.cellIdentityLte.pci;
+ cellInfoLte.cellIdentityLte.base.base.tac =
+ rillCellInfo->CellInfo.lte.cellIdentityLte.tac;
+ cellInfoLte.cellIdentityLte.base.base.earfcn =
+ rillCellInfo->CellInfo.lte.cellIdentityLte.earfcn;
+ cellInfoLte.signalStrengthLte.base.signalStrength =
+ rillCellInfo->CellInfo.lte.signalStrengthLte.signalStrength;
+ cellInfoLte.signalStrengthLte.base.rsrp =
+ rillCellInfo->CellInfo.lte.signalStrengthLte.rsrp;
+ cellInfoLte.signalStrengthLte.base.rsrq =
+ rillCellInfo->CellInfo.lte.signalStrengthLte.rsrq;
+ cellInfoLte.signalStrengthLte.base.rssnr =
+ rillCellInfo->CellInfo.lte.signalStrengthLte.rssnr;
+ cellInfoLte.signalStrengthLte.base.cqi =
+ rillCellInfo->CellInfo.lte.signalStrengthLte.cqi;
+ cellInfoLte.signalStrengthLte.base.timingAdvance =
+ rillCellInfo->CellInfo.lte.signalStrengthLte.timingAdvance;
+ records[i].ratSpecificInfo.lte(cellInfoLte);
+ break;
+ }
+
+ case RIL_CELL_INFO_TYPE_TD_SCDMA: {
+ V1_5::CellInfoTdscdma cellInfoTdscdma;
+ cellInfoTdscdma.cellIdentityTdscdma.base.base.mcc =
+ std::to_string(rillCellInfo->CellInfo.tdscdma.cellIdentityTdscdma.mcc);
+ cellInfoTdscdma.cellIdentityTdscdma.base.base.mnc = ril::util::mnc::decode(
+ rillCellInfo->CellInfo.tdscdma.cellIdentityTdscdma.mnc);
+ cellInfoTdscdma.cellIdentityTdscdma.base.base.lac =
+ rillCellInfo->CellInfo.tdscdma.cellIdentityTdscdma.lac;
+ cellInfoTdscdma.cellIdentityTdscdma.base.base.cid =
+ rillCellInfo->CellInfo.tdscdma.cellIdentityTdscdma.cid;
+ cellInfoTdscdma.cellIdentityTdscdma.base.base.cpid =
+ rillCellInfo->CellInfo.tdscdma.cellIdentityTdscdma.cpid;
+ cellInfoTdscdma.signalStrengthTdscdma.rscp =
+ rillCellInfo->CellInfo.tdscdma.signalStrengthTdscdma.rscp;
+ records[i].ratSpecificInfo.tdscdma(cellInfoTdscdma);
+ break;
+ }
+
+ case RIL_CELL_INFO_TYPE_NR: {
+ V1_6::CellInfoNr cellInfoNr;
+ cellInfoNr.cellIdentityNr.base.mcc =
+ std::to_string(rillCellInfo->CellInfo.nr.cellidentity.mcc);
+ cellInfoNr.cellIdentityNr.base.mnc =
+ ril::util::mnc::decode(rillCellInfo->CellInfo.nr.cellidentity.mnc);
+ cellInfoNr.cellIdentityNr.base.nci = rillCellInfo->CellInfo.nr.cellidentity.nci;
+ cellInfoNr.cellIdentityNr.base.pci = rillCellInfo->CellInfo.nr.cellidentity.pci;
+ cellInfoNr.cellIdentityNr.base.tac = rillCellInfo->CellInfo.nr.cellidentity.tac;
+ cellInfoNr.cellIdentityNr.base.nrarfcn =
+ rillCellInfo->CellInfo.nr.cellidentity.nrarfcn;
+ cellInfoNr.cellIdentityNr.base.operatorNames.alphaLong = convertCharPtrToHidlString(
+ rillCellInfo->CellInfo.nr.cellidentity.operatorNames.alphaLong);
+ cellInfoNr.cellIdentityNr.base.operatorNames.alphaShort =
+ convertCharPtrToHidlString(
+ rillCellInfo->CellInfo.nr.cellidentity.operatorNames.alphaShort);
+
+ cellInfoNr.signalStrengthNr.base.ssRsrp =
+ rillCellInfo->CellInfo.nr.signalStrength.ssRsrp;
+ cellInfoNr.signalStrengthNr.base.ssRsrq =
+ rillCellInfo->CellInfo.nr.signalStrength.ssRsrq;
+ cellInfoNr.signalStrengthNr.base.ssSinr =
+ rillCellInfo->CellInfo.nr.signalStrength.ssSinr;
+ cellInfoNr.signalStrengthNr.base.csiRsrp =
+ rillCellInfo->CellInfo.nr.signalStrength.csiRsrp;
+ cellInfoNr.signalStrengthNr.base.csiRsrq =
+ rillCellInfo->CellInfo.nr.signalStrength.csiRsrq;
+ cellInfoNr.signalStrengthNr.base.csiSinr =
+ rillCellInfo->CellInfo.nr.signalStrength.csiSinr;
+ records[i].ratSpecificInfo.nr(cellInfoNr);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ rillCellInfo += 1;
+ }
+}
+
int radio_1_6::cellInfoListInd(int slotId,
int indicationType, int token, RIL_Errno e, void *response,
size_t responseLen) {
@@ -12426,111 +13022,197 @@ int radio_1_6::modemResetInd(int slotId,
return 0;
}
-int radio_1_6::networkScanResultInd(int slotId,
- int indicationType, int token, RIL_Errno e, void *response,
- size_t responseLen) {
+int radio_1_6::networkScanResultInd(int slotId, int indicationType, int token, RIL_Errno e,
+ void* response, size_t responseLen) {
#if VDBG
RLOGD("networkScanResultInd");
#endif
- if (radioService[slotId] != NULL && radioService[slotId]->mRadioIndicationV1_4 != NULL) {
+ if (radioService[slotId] != NULL && (radioService[slotId]->mRadioIndicationV1_6 != NULL ||
+ radioService[slotId]->mRadioIndicationV1_5 != NULL ||
+ radioService[slotId]->mRadioIndicationV1_4 != NULL ||
+ radioService[slotId]->mRadioIndicationV1_2 != NULL ||
+ radioService[slotId]->mRadioIndicationV1_1 != NULL)) {
if (response == NULL || responseLen == 0) {
RLOGE("networkScanResultInd: invalid response");
return 0;
}
RLOGD("networkScanResultInd");
-#if VDBG
- RLOGD("networkScanResultInd");
-#endif
-
RIL_NetworkScanResult *networkScanResult = (RIL_NetworkScanResult *) response;
-
- V1_1::NetworkScanResult result;
- result.status = (V1_1::ScanStatus) networkScanResult->status;
- result.error = (RadioError) networkScanResult->error;
- convertRilCellInfoListToHal(
- networkScanResult->network_infos,
- networkScanResult->network_infos_length * sizeof(RIL_CellInfo_v12),
- result.networkInfos);
-
- Return<void> retStatus = radioService[slotId]->mRadioIndicationV1_4->networkScanResult(
- convertIntToRadioIndicationType(indicationType), result);
+ Return<void> retStatus;
+ if (radioService[slotId]->mRadioIndicationV1_6 != NULL) {
+ V1_6::NetworkScanResult result;
+ result.status = (V1_1::ScanStatus)networkScanResult->status;
+ result.error = (V1_6::RadioError)networkScanResult->error;
+ convertRilCellInfoListToHal_1_6(
+ networkScanResult->network_infos,
+ networkScanResult->network_infos_length * sizeof(RIL_CellInfo_v16),
+ result.networkInfos);
+ retStatus = radioService[slotId]->mRadioIndicationV1_6->networkScanResult_1_6(
+ convertIntToRadioIndicationType(indicationType), result);
+ } else if (radioService[slotId]->mRadioIndicationV1_5 != NULL) {
+ V1_5::NetworkScanResult result;
+ result.status = (V1_1::ScanStatus)networkScanResult->status;
+ result.error = (RadioError)networkScanResult->error;
+ convertRilCellInfoListToHal_1_5(
+ networkScanResult->network_infos,
+ networkScanResult->network_infos_length * sizeof(RIL_CellInfo_v12),
+ result.networkInfos);
+ retStatus = radioService[slotId]->mRadioIndicationV1_5->networkScanResult_1_5(
+ convertIntToRadioIndicationType(indicationType), result);
+ } else if (radioService[slotId]->mRadioIndicationV1_4 != NULL) {
+ V1_4::NetworkScanResult result;
+ result.status = (V1_1::ScanStatus)networkScanResult->status;
+ result.error = (RadioError)networkScanResult->error;
+ convertRilCellInfoListToHal_1_4(
+ networkScanResult->network_infos,
+ networkScanResult->network_infos_length * sizeof(RIL_CellInfo_v12),
+ result.networkInfos);
+ retStatus = radioService[slotId]->mRadioIndicationV1_4->networkScanResult_1_4(
+ convertIntToRadioIndicationType(indicationType), result);
+ } else if (radioService[slotId]->mRadioIndicationV1_2 != NULL) {
+ V1_2::NetworkScanResult result;
+ result.status = (V1_1::ScanStatus)networkScanResult->status;
+ result.error = (RadioError)networkScanResult->error;
+ convertRilCellInfoListToHal_1_2(
+ networkScanResult->network_infos,
+ networkScanResult->network_infos_length * sizeof(RIL_CellInfo_v12),
+ result.networkInfos);
+ retStatus = radioService[slotId]->mRadioIndicationV1_2->networkScanResult_1_2(
+ convertIntToRadioIndicationType(indicationType), result);
+ } else {
+ V1_1::NetworkScanResult result;
+ result.status = (V1_1::ScanStatus)networkScanResult->status;
+ result.error = (RadioError)networkScanResult->error;
+ convertRilCellInfoListToHal(
+ networkScanResult->network_infos,
+ networkScanResult->network_infos_length * sizeof(RIL_CellInfo),
+ result.networkInfos);
+ retStatus = radioService[slotId]->mRadioIndicationV1_1->networkScanResult(
+ convertIntToRadioIndicationType(indicationType), result);
+ }
radioService[slotId]->checkReturnStatus(retStatus);
} else {
- RLOGE("networkScanResultInd: radioService[%d]->mRadioIndicationV1_4 == NULL", slotId);
+ RLOGE("networkScanResultInd: radioService[%d]->mRadioIndication == NULL", slotId);
}
return 0;
}
-int radio_1_6::carrierInfoForImsiEncryption(int slotId,
- int indicationType, int token, RIL_Errno e, void *response,
- size_t responseLen) {
- if (radioService[slotId] != NULL && radioService[slotId]->mRadioIndicationV1_4 != NULL) {
+int radio_1_6::carrierInfoForImsiEncryption(int slotId, int indicationType, int token, RIL_Errno e,
+ void* response, size_t responseLen) {
+ if (radioService[slotId] != NULL && (radioService[slotId]->mRadioIndicationV1_2 != NULL)) {
if (response == NULL || responseLen == 0) {
RLOGE("carrierInfoForImsiEncryption: invalid response");
return 0;
}
RLOGD("carrierInfoForImsiEncryption");
- Return<void> retStatus = radioService[slotId]->mRadioIndicationV1_4->
- carrierInfoForImsiEncryption(convertIntToRadioIndicationType(indicationType));
+ Return<void> retStatus =
+ radioService[slotId]->mRadioIndicationV1_2->carrierInfoForImsiEncryption(
+ convertIntToRadioIndicationType(indicationType));
radioService[slotId]->checkReturnStatus(retStatus);
} else {
- RLOGE("carrierInfoForImsiEncryption: radioService[%d]->mRadioIndicationV1_4 == NULL",
- slotId);
+ RLOGE("carrierInfoForImsiEncryption: radioService[%d]->mRadioIndication == NULL", slotId);
}
return 0;
}
-int radio_1_6::reportPhysicalChannelConfigs(int slotId, int indicationType,
- int token, RIL_Errno e,
- void *response,
- size_t responseLen) {
- if (radioService[slotId] != NULL &&
- radioService[slotId]->mRadioIndicationV1_4 != NULL) {
- int *configs = (int *)response;
- ::android::hardware::hidl_vec<PhysicalChannelConfigV1_4> physChanConfig;
- physChanConfig.resize(1);
- physChanConfig[0].base.status =
- (::android::hardware::radio::V1_2::CellConnectionStatus)configs[0];
- physChanConfig[0].base.cellBandwidthDownlink = configs[1];
- physChanConfig[0].rat =
- (::android::hardware::radio::V1_4::RadioTechnology)configs[2];
- physChanConfig[0].rfInfo.range(
- (::android::hardware::radio::V1_4::FrequencyRange)configs[3]);
- physChanConfig[0].contextIds.resize(1);
- physChanConfig[0].contextIds[0] = configs[4];
- RLOGD("reportPhysicalChannelConfigs: %d %d %d %d %d", configs[0],
- configs[1], configs[2], configs[3], configs[4]);
- Return<void> retStatus = radioService[slotId]
- ->mRadioIndicationV1_4->currentPhysicalChannelConfigs_1_4(
- RadioIndicationType::UNSOLICITED, physChanConfig);
- radioService[slotId]->checkReturnStatus(retStatus);
- {
- // just send the link estimate along with physical channel
- // config, as it has at least the downlink bandwidth.
- // Note: the bandwidth is just some hardcoded
- // value, as there is not way to get that reliably on
- // virtual devices, as of now.
- V1_2::LinkCapacityEstimate lce = {
- .downlinkCapacityKbps = static_cast<uint32_t>(configs[1]),
- .uplinkCapacityKbps = static_cast<uint32_t>(configs[1])
- };
- RLOGD("reporting link capacity estimate download: %d upload: %d",
- lce.downlinkCapacityKbps, lce.uplinkCapacityKbps );
- Return<void> retStatus = radioService[slotId]->mRadioIndicationV1_4->
- currentLinkCapacityEstimate(RadioIndicationType::UNSOLICITED, lce);
- radioService[slotId]->checkReturnStatus(retStatus);
- }
+int radio_1_6::reportPhysicalChannelConfigs(int slotId, int indicationType, int token, RIL_Errno e,
+ void* response, size_t responseLen) {
+ if (radioService[slotId] != NULL && (radioService[slotId]->mRadioIndicationV1_6 != NULL ||
+ radioService[slotId]->mRadioIndicationV1_4 != NULL ||
+ radioService[slotId]->mRadioIndicationV1_2 != NULL)) {
+ int* configs = (int*)response;
+ if (radioService[slotId]->mRadioIndicationV1_6 != NULL) {
+ hidl_vec<V1_6::PhysicalChannelConfig> physChanConfig;
+ physChanConfig.resize(1);
+ physChanConfig[0].status = (V1_2::CellConnectionStatus)configs[0];
+ physChanConfig[0].cellBandwidthDownlinkKhz = configs[1];
+ physChanConfig[0].rat = (V1_4::RadioTechnology)configs[2];
+ physChanConfig[0].contextIds.resize(1);
+ physChanConfig[0].contextIds[0] = configs[4];
+ RLOGD("reportPhysicalChannelConfigs_1_6: %d %d %d %d %d", configs[0], configs[1],
+ configs[2], configs[3], configs[4]);
+ Return<void> retStatus =
+ radioService[slotId]->mRadioIndicationV1_6->currentPhysicalChannelConfigs_1_6(
+ RadioIndicationType::UNSOLICITED, physChanConfig);
+ radioService[slotId]->checkReturnStatus(retStatus);
+ {
+ // Just send the link estimate along with physical channel config, as it has
+ // at least the downlink bandwidth.
+ // Note: the bandwidth is just some hardcoded value, as there is not way to get
+ // that reliably on virtual devices, as of now.
+ V1_6::LinkCapacityEstimate lce = {
+ .downlinkCapacityKbps = static_cast<uint32_t>(configs[1]),
+ .uplinkCapacityKbps = static_cast<uint32_t>(configs[1])};
+ RLOGD("reporting link capacity estimate download: %d upload: %d",
+ lce.downlinkCapacityKbps, lce.uplinkCapacityKbps);
+ Return<void> retStatus =
+ radioService[slotId]->mRadioIndicationV1_6->currentLinkCapacityEstimate_1_6(
+ RadioIndicationType::UNSOLICITED, lce);
+ radioService[slotId]->checkReturnStatus(retStatus);
+ }
+ } else if (radioService[slotId]->mRadioIndicationV1_4 != NULL) {
+ hidl_vec<PhysicalChannelConfigV1_4> physChanConfig;
+ physChanConfig.resize(1);
+ physChanConfig[0].base.status = (V1_2::CellConnectionStatus)configs[0];
+ physChanConfig[0].base.cellBandwidthDownlink = configs[1];
+ physChanConfig[0].rat = (V1_4::RadioTechnology)configs[2];
+ physChanConfig[0].rfInfo.range((V1_4::FrequencyRange)configs[3]);
+ physChanConfig[0].contextIds.resize(1);
+ physChanConfig[0].contextIds[0] = configs[4];
+ RLOGD("reportPhysicalChannelConfigs_1_4: %d %d %d %d %d", configs[0], configs[1],
+ configs[2], configs[3], configs[4]);
+ Return<void> retStatus =
+ radioService[slotId]->mRadioIndicationV1_4->currentPhysicalChannelConfigs_1_4(
+ RadioIndicationType::UNSOLICITED, physChanConfig);
+ radioService[slotId]->checkReturnStatus(retStatus);
+ {
+ // Just send the link estimate along with physical channel config, as it has
+ // at least the downlink bandwidth.
+ // Note: the bandwidth is just some hardcoded value, as there is not way to get
+ // that reliably on virtual devices, as of now.
+ V1_2::LinkCapacityEstimate lce = {
+ .downlinkCapacityKbps = static_cast<uint32_t>(configs[1]),
+ .uplinkCapacityKbps = static_cast<uint32_t>(configs[1])};
+ RLOGD("reporting link capacity estimate download: %d upload: %d",
+ lce.downlinkCapacityKbps, lce.uplinkCapacityKbps);
+ Return<void> retStatus =
+ radioService[slotId]->mRadioIndicationV1_4->currentLinkCapacityEstimate(
+ RadioIndicationType::UNSOLICITED, lce);
+ radioService[slotId]->checkReturnStatus(retStatus);
+ }
+ } else {
+ hidl_vec<V1_2::PhysicalChannelConfig> physChanConfig;
+ physChanConfig.resize(1);
+ physChanConfig[0].status = (V1_2::CellConnectionStatus)configs[0];
+ physChanConfig[0].cellBandwidthDownlink = configs[1];
+ RLOGD("reportPhysicalChannelConfigs_1_2: %d %d", configs[0], configs[1]);
+ Return<void> retStatus =
+ radioService[slotId]->mRadioIndicationV1_2->currentPhysicalChannelConfigs(
+ RadioIndicationType::UNSOLICITED, physChanConfig);
+ radioService[slotId]->checkReturnStatus(retStatus);
+ {
+ // Just send the link estimate along with physical channel config, as it has
+ // at least the downlink bandwidth.
+ // Note: the bandwidth is just some hardcoded value, as there is not way to get
+ // that reliably on virtual devices, as of now.
+ V1_2::LinkCapacityEstimate lce = {
+ .downlinkCapacityKbps = static_cast<uint32_t>(configs[1]),
+ .uplinkCapacityKbps = static_cast<uint32_t>(configs[1])};
+ RLOGD("reporting link capacity estimate download: %d upload: %d",
+ lce.downlinkCapacityKbps, lce.uplinkCapacityKbps);
+ Return<void> retStatus =
+ radioService[slotId]->mRadioIndicationV1_2->currentLinkCapacityEstimate(
+ RadioIndicationType::UNSOLICITED, lce);
+ radioService[slotId]->checkReturnStatus(retStatus);
+ }
+ }
} else {
- RLOGE(
- "reportPhysicalChannelConfigs: radioService[%d]->mRadioIndicationV1_4 "
- "== NULL",
- slotId);
- return -1;
+ RLOGE("reportPhysicalChannelConfigs: radioService[%d]->mRadioIndication == NULL", slotId);
+ return -1;
}
-
- return 0;
+ return 0;
}
int radio_1_6::keepaliveStatusInd(int slotId,
diff --git a/guest/hals/ril/reference-ril/reference-ril.c b/guest/hals/ril/reference-ril/reference-ril.c
index 08ef49468..9064b1f43 100644
--- a/guest/hals/ril/reference-ril/reference-ril.c
+++ b/guest/hals/ril/reference-ril/reference-ril.c
@@ -1283,6 +1283,7 @@ static void requestSignalStrength(void *data __unused, size_t datalen __unused,
memset(response, 0, sizeof(response));
+ // TODO(b/206814247): Rename AT+CSQ command.
err = at_send_command_singleline("AT+CSQ", "+CSQ:", &p_response);
if (err < 0 || p_response->success == 0) {
@@ -1668,6 +1669,9 @@ static int parseRegistrationState(char *str, int *type, int *items, int **respon
int skip;
int commas;
+ s_lac = -1;
+ s_cid = -1;
+
RLOGD("parseRegistrationState. Parsing: %s",str);
err = at_tok_start(&line);
if (err < 0) goto error;
@@ -1705,19 +1709,14 @@ static int parseRegistrationState(char *str, int *type, int *items, int **respon
case 0: /* +CREG: <stat> */
err = at_tok_nextint(&line, &resp[0]);
if (err < 0) goto error;
- resp[1] = -1;
- resp[2] = -1;
- break;
+ break;
case 1: /* +CREG: <n>, <stat> */
err = at_tok_nextint(&line, &skip);
if (err < 0) goto error;
err = at_tok_nextint(&line, &resp[0]);
if (err < 0) goto error;
- resp[1] = -1;
- resp[2] = -1;
- if (err < 0) goto error;
- break;
+ break;
case 2: /* +CREG: <stat>, <lac>, <cid> */
err = at_tok_nextint(&line, &resp[0]);
@@ -1755,8 +1754,12 @@ static int parseRegistrationState(char *str, int *type, int *items, int **respon
default:
goto error;
}
- s_lac = resp[1];
- s_cid = resp[2];
+
+ if (commas >= 2) {
+ s_lac = resp[1];
+ s_cid = resp[2];
+ }
+
if (response)
*response = resp;
if (items)
@@ -3262,6 +3265,33 @@ static void requestGetCellInfoList(void *data __unused, size_t datalen __unused,
RIL_onRequestComplete(t, RIL_E_SUCCESS, ci, sizeof(ci));
}
+static void requestGetCellInfoList_1_6(void* data __unused, size_t datalen __unused, RIL_Token t) {
+ uint64_t curTime = ril_nano_time();
+ RIL_CellInfo_v16 ci[1] = {{ // ci[0]
+ 1, // cellInfoType
+ 1, // registered
+ CELL_CONNECTION_PRIMARY_SERVING,
+ { // union CellInfo
+ {// RIL_CellInfoGsm gsm
+ {
+ // gsm.cellIdneityGsm
+ s_mcc, // mcc
+ s_mnc, // mnc
+ s_lac, // lac
+ s_cid, // cid
+ 0, // arfcn unknown
+ 0x1, // Base Station Identity Code set to arbitrarily 1
+ },
+ {
+ // gsm.signalStrengthGsm
+ 10, // signalStrength
+ 0 // bitErrorRate
+ ,
+ INT_MAX // timingAdvance invalid value
+ }}}}};
+
+ RIL_onRequestComplete(t, RIL_E_SUCCESS, ci, sizeof(ci));
+}
static void requestSetCellInfoListRate(void *data, size_t datalen __unused, RIL_Token t)
{
@@ -4661,6 +4691,10 @@ onRequest (int request, void *data, size_t datalen, RIL_Token t)
requestGetCellInfoList(data, datalen, t);
break;
+ case RIL_REQUEST_GET_CELL_INFO_LIST_1_6:
+ requestGetCellInfoList_1_6(data, datalen, t);
+ break;
+
case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE:
requestSetCellInfoListRate(data, datalen, t);
break;
@@ -6003,7 +6037,7 @@ static void onUnsolicited (const char *s, const char *sms_pdu)
response, sizeof(response));
free(line);
} else if (strStartsWith(s, "+CUSATEND")) { // session end
- RIL_onUnsolicitedResponse(RIL_UNSOL_STK_SESSION_END, NULL, 0);
+ RIL_onUnsolicitedResponse(RIL_UNSOL_STK_SESSION_END, NULL, 0);
} else if (strStartsWith(s, "+CUSATP:")) {
line = p = strdup(s);
if (!line) {
diff --git a/guest/services/wifi/Android.bp b/guest/services/wifi/Android.bp
new file mode 100644
index 000000000..f895cef7d
--- /dev/null
+++ b/guest/services/wifi/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+genrule {
+ name: "init.wifi.sh_apex_srcs",
+ srcs: ["init.wifi.sh"],
+ out: ["init.wifi.sh_apex"],
+ cmd: "sed -E 's/\\/vendor\\/bin\\/mac802/\\/apex\\/com.android.wifi.hal\\/bin\\/mac802/' $(in) > $(out)",
+}
+
+sh_binary {
+ name: "init.wifi.sh_apex",
+ src: ":init.wifi.sh_apex_srcs",
+ filename: "init.wifi.sh",
+ vendor: true,
+ init_rc: [
+ "init.wifi.sh.rc",
+ ],
+}
+
+sh_binary {
+ name: "init.wifi.sh",
+ src: "init.wifi.sh",
+ filename: "init_wifi.sh",
+ vendor: true,
+ init_rc: [
+ "init.wifi.sh.rc",
+ ],
+}
diff --git a/guest/services/wifi/init.wifi.sh.rc b/guest/services/wifi/init.wifi.sh.rc
new file mode 100644
index 000000000..bdef59859
--- /dev/null
+++ b/guest/services/wifi/init.wifi.sh.rc
@@ -0,0 +1,8 @@
+
+service init_wifi_sh /vendor/bin/init_wifi.sh
+ class late_start
+ user root
+ group root wakelock wifi
+ oneshot
+ disabled # Started on post-fs-data
+
diff --git a/host/commands/append_squashfs_overlay/Android.bp b/host/commands/append_squashfs_overlay/Android.bp
index 1dd8a287c..882bcb9a6 100644
--- a/host/commands/append_squashfs_overlay/Android.bp
+++ b/host/commands/append_squashfs_overlay/Android.bp
@@ -6,7 +6,6 @@ rust_binary_host {
name: "append_squashfs_overlay",
crate_name: "append_squashfs_overlay",
srcs: ["src/main.rs"],
- edition: "2018",
rustlibs: [
"libclap",
],
diff --git a/host/commands/append_squashfs_overlay/Cargo.toml b/host/commands/append_squashfs_overlay/Cargo.toml
index 0084994bd..547258082 100644
--- a/host/commands/append_squashfs_overlay/Cargo.toml
+++ b/host/commands/append_squashfs_overlay/Cargo.toml
@@ -2,7 +2,7 @@
name = "append_squashfs_overlay"
version = "0.1.0"
authors = ["Jeongik Cha <jeongik@google.com>"]
-edition = "2018"
+edition = 2021
[dependencies]
clap = "2.33" \ No newline at end of file
diff --git a/host/commands/assemble_cvd/assemble_cvd.cc b/host/commands/assemble_cvd/assemble_cvd.cc
index f3bc6a8d6..77d0337f7 100644
--- a/host/commands/assemble_cvd/assemble_cvd.cc
+++ b/host/commands/assemble_cvd/assemble_cvd.cc
@@ -38,8 +38,9 @@ using cuttlefish::StringFromEnv;
DEFINE_string(assembly_dir, StringFromEnv("HOME", ".") + "/cuttlefish_assembly",
"A directory to put generated files common between instances");
-DEFINE_string(instance_dir, StringFromEnv("HOME", ".") + "/cuttlefish_runtime",
- "A directory to put all instance specific files");
+DEFINE_string(instance_dir, StringFromEnv("HOME", ".") + "/cuttlefish",
+ "This is a directory that will hold the cuttlefish generated"
+ "files, including both instance-specific and common files");
DEFINE_bool(resume, true, "Resume using the disk from the last session, if "
"possible. i.e., if --noresume is passed, the disk "
"will be reset to the state it was initially launched "
@@ -97,21 +98,53 @@ bool SaveConfig(const CuttlefishConfig& tmp_config_obj) {
# define O_TMPFILE (020000000 | O_DIRECTORY)
#endif
+void CreateLegacySymlinks(const CuttlefishConfig::InstanceSpecific& instance) {
+ std::string log_files[] = {
+ "kernel.log", "launcher.log", "logcat",
+ "metrics.log", "modem_simulator.log", "crosvm_openwrt.log",
+ };
+ for (const auto& log_file : log_files) {
+ auto symlink_location = instance.PerInstancePath(log_file.c_str());
+ auto log_target = "logs/" + log_file; // Relative path
+ if (symlink(log_target.c_str(), symlink_location.c_str()) != 0) {
+ PLOG(FATAL) << "symlink(\"" << log_target << ", " << symlink_location
+ << ") failed";
+ }
+ }
+
+ std::stringstream legacy_instance_path_stream;
+ legacy_instance_path_stream << FLAGS_instance_dir;
+ if (gflags::GetCommandLineFlagInfoOrDie("instance_dir").is_default) {
+ legacy_instance_path_stream << "_runtime";
+ }
+ legacy_instance_path_stream << "." << instance.id();
+ auto legacy_instance_path = legacy_instance_path_stream.str();
+
+ if (DirectoryExists(legacy_instance_path, /* follow_symlinks */ false)) {
+ CHECK(RecursivelyRemoveDirectory(legacy_instance_path))
+ << "Failed to remove legacy directory " << legacy_instance_path;
+ } else if (FileExists(legacy_instance_path, /* follow_symlinks */ false)) {
+ CHECK(RemoveFile(legacy_instance_path))
+ << "Failed to remove instance_dir symlink " << legacy_instance_path;
+ }
+ if (symlink(instance.instance_dir().c_str(), legacy_instance_path.c_str())) {
+ PLOG(FATAL) << "symlink(\"" << instance.instance_dir() << "\", \""
+ << legacy_instance_path << "\") failed";
+ }
+}
+
const CuttlefishConfig* InitFilesystemAndCreateConfig(
FetcherConfig fetcher_config, KernelConfig kernel_config,
fruit::Injector<>& injector) {
- std::string assembly_dir_parent = AbsolutePath(FLAGS_assembly_dir);
- while (assembly_dir_parent[assembly_dir_parent.size() - 1] == '/') {
- assembly_dir_parent =
- assembly_dir_parent.substr(0, FLAGS_assembly_dir.rfind('/'));
+ std::string runtime_dir_parent = AbsolutePath(FLAGS_instance_dir);
+ while (runtime_dir_parent[runtime_dir_parent.size() - 1] == '/') {
+ runtime_dir_parent =
+ runtime_dir_parent.substr(0, FLAGS_instance_dir.rfind('/'));
}
- assembly_dir_parent =
- assembly_dir_parent.substr(0, FLAGS_assembly_dir.rfind('/'));
- auto log =
- SharedFD::Open(
- assembly_dir_parent,
- O_WRONLY | O_TMPFILE,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ runtime_dir_parent =
+ runtime_dir_parent.substr(0, FLAGS_instance_dir.rfind('/'));
+ auto log = SharedFD::Open(runtime_dir_parent, O_WRONLY | O_TMPFILE,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (!log->IsOpen()) {
LOG(ERROR) << "Could not open O_TMPFILE precursor to assemble_cvd.log: "
<< log->StrError();
@@ -166,12 +199,13 @@ const CuttlefishConfig* InitFilesystemAndCreateConfig(
ss.str("");
}
}
- CHECK(
- CleanPriorFiles(preserving, FLAGS_assembly_dir, config.instance_dirs()))
+ CHECK(CleanPriorFiles(preserving, config.assembly_dir(),
+ config.instance_dirs()))
<< "Failed to clean prior files";
- // Create assembly directory if it doesn't exist.
- CHECK(EnsureDirectoryExists(FLAGS_assembly_dir));
+ CHECK(EnsureDirectoryExists(config.root_dir()));
+ CHECK(EnsureDirectoryExists(config.assembly_dir()));
+ CHECK(EnsureDirectoryExists(config.instances_dir()));
if (log->LinkAtCwd(config.AssemblyPath("assemble_cvd.log"))) {
LOG(ERROR) << "Unable to persist assemble_cvd log at "
<< config.AssemblyPath("assemble_cvd.log")
@@ -198,23 +232,40 @@ const CuttlefishConfig* InitFilesystemAndCreateConfig(
CHECK(EnsureDirectoryExists(shared_dir));
auto recording_dir = instance.instance_dir() + "/recording";
CHECK(EnsureDirectoryExists(recording_dir));
+ CHECK(EnsureDirectoryExists(instance.PerInstanceLogPath("")));
+ // TODO(schuffelen): Move this code somewhere better
+ CreateLegacySymlinks(instance);
}
CHECK(SaveConfig(config)) << "Failed to initialize configuration";
}
- std::string first_instance = FLAGS_instance_dir + "." + std::to_string(GetInstance());
- if (FileExists(FLAGS_instance_dir)) {
- CHECK(RemoveFile(FLAGS_instance_dir))
- << "Failed to remove instance_dir symlink " << FLAGS_instance_dir;
- }
- CHECK_EQ(symlink(first_instance.c_str(), FLAGS_instance_dir.c_str()), 0)
- << "Could not symlink \"" << first_instance << "\" to \""
- << FLAGS_instance_dir << "\"";
-
// Do this early so that the config object is ready for anything that needs it
auto config = CuttlefishConfig::Get();
CHECK(config) << "Failed to obtain config singleton";
+ if (DirectoryExists(FLAGS_assembly_dir, /* follow_symlinks */ false)) {
+ CHECK(RecursivelyRemoveDirectory(FLAGS_assembly_dir))
+ << "Failed to remove directory " << FLAGS_assembly_dir;
+ } else if (FileExists(FLAGS_assembly_dir, /* follow_symlinks */ false)) {
+ CHECK(RemoveFile(FLAGS_assembly_dir))
+ << "Failed to remove file" << FLAGS_assembly_dir;
+ }
+ if (symlink(config->assembly_dir().c_str(), FLAGS_assembly_dir.c_str())) {
+ PLOG(FATAL) << "symlink(\"" << config->assembly_dir() << "\", \""
+ << FLAGS_assembly_dir << "\") failed";
+ }
+
+ std::string first_instance = config->Instances()[0].instance_dir();
+ std::string double_legacy_instance_dir = FLAGS_instance_dir + "_runtime";
+ if (FileExists(double_legacy_instance_dir, /* follow_symlinks */ false)) {
+ CHECK(RemoveFile(double_legacy_instance_dir))
+ << "Failed to remove symlink " << double_legacy_instance_dir;
+ }
+ if (symlink(first_instance.c_str(), double_legacy_instance_dir.c_str())) {
+ PLOG(FATAL) << "Could not symlink \"" << first_instance << "\" to \""
+ << double_legacy_instance_dir << "\"";
+ }
+
CreateDynamicDiskFiles(fetcher_config, *config);
return config;
@@ -293,7 +344,7 @@ int AssembleCvdMain(int argc, char** argv) {
for (const auto& help_flag : help_flags) {
if (!help_flag.Parse(args)) {
LOG(ERROR) << "Failed to process help flag.";
- return false;
+ return 1;
}
}
@@ -301,13 +352,13 @@ int AssembleCvdMain(int argc, char** argv) {
auto flag_features = injector.getMultibindings<FlagFeature>();
if (!FlagFeature::ProcessFlags(flag_features, args)) {
LOG(ERROR) << "Failed to parse flags.";
- return false;
+ return 1;
}
if (help || help_str != "") {
LOG(WARNING) << "TODO(schuffelen): Implement `--help` for assemble_cvd.";
LOG(WARNING) << "In the meantime, call `launch_cvd --help`";
- return false;
+ return 1;
} else if (helpxml) {
if (!FlagFeature::WriteGflagsHelpXml(flag_features, std::cout)) {
LOG(ERROR) << "Failure in writing gflags helpxml output";
diff --git a/host/commands/assemble_cvd/disk_flags.cc b/host/commands/assemble_cvd/disk_flags.cc
index 7c6ede198..b046339a5 100644
--- a/host/commands/assemble_cvd/disk_flags.cc
+++ b/host/commands/assemble_cvd/disk_flags.cc
@@ -45,6 +45,10 @@ DECLARE_string(system_image_dir);
DEFINE_string(boot_image, "",
"Location of cuttlefish boot image. If empty it is assumed to be "
"boot.img in the directory specified by -system_image_dir.");
+DEFINE_string(
+ init_boot_image, "",
+ "Location of cuttlefish init boot image. If empty it is assumed to "
+ "be init_boot.img in the directory specified by -system_image_dir.");
DEFINE_string(data_image, "", "Location of the data partition image.");
DEFINE_string(super_image, "", "Location of the super partition image.");
DEFINE_string(misc_image, "",
@@ -100,6 +104,11 @@ bool ResolveInstanceFiles() {
std::string default_boot_image = FLAGS_system_image_dir + "/boot.img";
SetCommandLineOptionWithMode("boot_image", default_boot_image.c_str(),
google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ std::string default_init_boot_image =
+ FLAGS_system_image_dir + "/init_boot.img";
+ SetCommandLineOptionWithMode("init_boot_image",
+ default_init_boot_image.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
std::string default_data_image = FLAGS_system_image_dir + "/userdata.img";
SetCommandLineOptionWithMode("data_image", default_data_image.c_str(),
google::FlagSettingMode::SET_FLAGS_DEFAULT);
@@ -159,6 +168,16 @@ std::vector<ImagePartition> GetOsCompositeDiskConfig() {
.read_only = true,
});
partitions.push_back(ImagePartition{
+ .label = "init_boot_a",
+ .image_file_path = FLAGS_init_boot_image,
+ .read_only = true,
+ });
+ partitions.push_back(ImagePartition{
+ .label = "init_boot_b",
+ .image_file_path = FLAGS_init_boot_image,
+ .read_only = true,
+ });
+ partitions.push_back(ImagePartition{
.label = "vendor_boot_a",
.image_file_path = FLAGS_vendor_boot_image,
.read_only = true,
@@ -410,6 +429,11 @@ class BootImageRepacker : public Feature {
LOG(ERROR) << "File not found: " << FLAGS_boot_image;
return false;
}
+ // The init_boot partition is be optional for testing boot.img
+ // with the ramdisk inside.
+ if (!FileHasContent(FLAGS_init_boot_image)) {
+ LOG(WARNING) << "File not found: " << FLAGS_init_boot_image;
+ }
if (!FileHasContent(FLAGS_vendor_boot_image)) {
LOG(ERROR) << "File not found: " << FLAGS_vendor_boot_image;
@@ -467,41 +491,70 @@ class BootImageRepacker : public Feature {
const CuttlefishConfig& config_;
};
-static void GeneratePersistentBootconfig(
- const CuttlefishConfig& config,
- const CuttlefishConfig::InstanceSpecific& instance) {
- const auto bootconfig_path = instance.persistent_bootconfig_path();
- if (!FileExists(bootconfig_path)) {
- CreateBlankImage(bootconfig_path, 1 /* mb */, "none");
- }
+class GeneratePersistentBootconfig : public Feature {
+ public:
+ INJECT(GeneratePersistentBootconfig(
+ const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific& instance))
+ : config_(config), instance_(instance) {}
+
+ // Feature
+ std::string Name() const override { return "GeneratePersistentBootconfig"; }
+ bool Enabled() const override { return !config_.protected_vm(); }
- auto bootconfig_fd = SharedFD::Open(bootconfig_path, O_RDWR);
- CHECK(bootconfig_fd->IsOpen())
- << "Unable to open bootconfig file: " << bootconfig_fd->StrError();
-
- // Cuttlefish for the time being won't be able to support OTA from a
- // non-bootconfig kernel to a bootconfig-kernel (or vice versa) IF the device
- // is stopped (via stop_cvd). This is rarely an issue since OTA testing run
- // on cuttlefish is done within one launch cycle of the device. If this ever
- // becomes an issue, this code will have to be rewritten.
- if (!config.bootconfig_supported()) {
- return;
+ private:
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+ bool Setup() override {
+ const auto bootconfig_path = instance_.persistent_bootconfig_path();
+ if (!FileExists(bootconfig_path)) {
+ if (!CreateBlankImage(bootconfig_path, 1 /* mb */, "none")) {
+ LOG(ERROR) << "Failed to create image at " << bootconfig_path;
+ return false;
+ }
+ }
+
+ auto bootconfig_fd = SharedFD::Open(bootconfig_path, O_RDWR);
+ if (!bootconfig_fd->IsOpen()) {
+ LOG(ERROR) << "Unable to open bootconfig file: "
+ << bootconfig_fd->StrError();
+ return false;
+ }
+
+ // Cuttlefish for the time being won't be able to support OTA from a
+ // non-bootconfig kernel to a bootconfig-kernel (or vice versa) IF the
+ // device is stopped (via stop_cvd). This is rarely an issue since OTA
+ // testing run on cuttlefish is done within one launch cycle of the device.
+ // If this ever becomes an issue, this code will have to be rewritten.
+ if (!config_.bootconfig_supported()) {
+ return true;
+ }
+
+ const std::string bootconfig =
+ android::base::Join(BootconfigArgsFromConfig(config_, instance_),
+ "\n") +
+ "\n";
+ ssize_t bytesWritten = WriteAll(bootconfig_fd, bootconfig);
+ if (bytesWritten != bootconfig.size()) {
+ LOG(ERROR) << "Failed to write contents of bootconfig to \""
+ << bootconfig_path << "\"";
+ return false;
+ }
+ LOG(DEBUG) << "Bootconfig parameters from vendor boot image and config are "
+ << ReadFile(bootconfig_path);
+
+ const off_t bootconfig_size_bytes =
+ AlignToPowerOf2(bootconfig.size(), PARTITION_SIZE_SHIFT);
+ if (bootconfig_fd->Truncate(bootconfig_size_bytes) != 0) {
+ LOG(ERROR) << "`truncate --size=" << bootconfig_size_bytes << " bytes "
+ << bootconfig_path << "` failed:" << bootconfig_fd->StrError();
+ return false;
+ }
+ return true;
}
- const std::string bootconfig =
- android::base::Join(BootconfigArgsFromConfig(config, instance), "\n") +
- "\n";
- ssize_t bytesWritten = WriteAll(bootconfig_fd, bootconfig);
- CHECK(bytesWritten == bootconfig.size());
- LOG(DEBUG) << "Bootconfig parameters from vendor boot image and config are "
- << ReadFile(bootconfig_path);
-
- const off_t bootconfig_size_bytes =
- AlignToPowerOf2(bootconfig.size(), PARTITION_SIZE_SHIFT);
- CHECK(bootconfig_fd->Truncate(bootconfig_size_bytes) == 0)
- << "`truncate --size=" << bootconfig_size_bytes << " bytes "
- << bootconfig_path << "` failed:" << bootconfig_fd->StrError();
-}
+ const CuttlefishConfig& config_;
+ const CuttlefishConfig::InstanceSpecific& instance_;
+};
class InitializeMetadataImage : public Feature {
public:
@@ -661,7 +714,11 @@ static fruit::Component<> DiskChangesComponent(const FetcherConfig* fetcher,
.install(FixedMiscImagePathComponent, &FLAGS_misc_image)
.install(InitializeMiscImageComponent)
.install(FixedDataImagePathComponent, &FLAGS_data_image)
- .install(InitializeDataImageComponent);
+ .install(InitializeDataImageComponent)
+ // Create esp if necessary
+ .install(InitializeEspImageComponent, &FLAGS_otheros_esp_image,
+ &FLAGS_otheros_kernel_path, &FLAGS_otheros_initramfs_path,
+ &FLAGS_otheros_root_image);
}
static fruit::Component<> DiskChangesPerInstanceComponent(
@@ -675,6 +732,7 @@ static fruit::Component<> DiskChangesPerInstanceComponent(
.addMultibinding<Feature, InitializePstore>()
.addMultibinding<Feature, InitializeSdCard>()
.addMultibinding<Feature, InitializeFactoryResetProtected>()
+ .addMultibinding<Feature, GeneratePersistentBootconfig>()
.install(InitBootloaderEnvPartitionComponent);
}
@@ -687,13 +745,6 @@ void CreateDynamicDiskFiles(const FetcherConfig& fetcher_config,
const auto& features = injector.getMultibindings<Feature>();
CHECK(Feature::RunSetup(features)) << "Failed to run feature setup.";
- // Create esp if necessary
- if (!FLAGS_otheros_root_image.empty()) {
- CHECK(InitializeEspImage(FLAGS_otheros_esp_image, FLAGS_otheros_kernel_path,
- FLAGS_otheros_initramfs_path))
- << "Failed to create esp image";
- }
-
for (const auto& instance : config.Instances()) {
fruit::Injector<> instance_injector(DiskChangesPerInstanceComponent,
&fetcher_config, &config, &instance);
@@ -703,16 +754,6 @@ void CreateDynamicDiskFiles(const FetcherConfig& fetcher_config,
<< "Failed to run instance feature setup.";
}
- // If we are booting a protected VM, for now, assume we want a super minimal
- // environment with no userdata encryption, limited debug, no FRP emulation, a
- // static env for the bootloader, no SD-Card and no resume-on-reboot HAL
- // support.
- if (!FLAGS_protected_vm) {
- for (const auto& instance : config.Instances()) {
- GeneratePersistentBootconfig(config, instance);
- }
- }
-
for (const auto& instance : config.Instances()) {
bool compositeMatchesDiskConfig = DoesCompositeMatchCurrentDiskConfig(
instance.PerInstancePath("persistent_composite_disk_config.txt"),
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index d4db312f4..eb08d9334 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -100,9 +100,14 @@ DEFINE_string(vm_manager, "",
DEFINE_string(gpu_mode, cuttlefish::kGpuModeAuto,
"What gpu configuration to use, one of {auto, drm_virgl, "
"gfxstream, guest_swiftshader}");
+DEFINE_string(hwcomposer, cuttlefish::kHwComposerAuto,
+ "What hardware composer to use, one of {auto, drm, ranchu} ");
DEFINE_string(gpu_capture_binary, "",
"Path to the GPU capture binary to use when capturing GPU traces"
"(ngfx, renderdoc, etc)");
+DEFINE_bool(enable_gpu_udmabuf,
+ false,
+ "Use the udmabuf driver for zero-copy virtio-gpu");
DEFINE_bool(deprecated_boot_completed, false, "Log boot completed message to"
" host kernel. This is only used during transition of our clients."
@@ -174,13 +179,17 @@ DEFINE_bool(
false,
"[Experimental] If enabled, exposes local adb service through a websocket.");
+static constexpr auto HOST_OPERATOR_SOCKET_PATH = "/run/cuttlefish/operator";
+
DEFINE_bool(
- start_webrtc_sig_server, false,
+ // The actual default for this flag is set with SetCommandLineOption() in
+ // GetKernelConfigsAndSetDefaults() at the end of this file.
+ start_webrtc_sig_server, true,
"Whether to start the webrtc signaling server. This option only applies to "
"the first instance, if multiple instances are launched they'll share the "
"same signaling server, which is owned by the first one.");
-DEFINE_string(webrtc_sig_server_addr, "0.0.0.0",
+DEFINE_string(webrtc_sig_server_addr, "",
"The address of the webrtc signaling server.");
DEFINE_int32(
@@ -458,7 +467,7 @@ void ReadKernelConfig(KernelConfig* kernel_config) {
} // namespace
CuttlefishConfig InitializeCuttlefishConfiguration(
- const std::string& instance_dir, int modem_simulator_count,
+ const std::string& root_dir, int modem_simulator_count,
KernelConfig kernel_config, fruit::Injector<>& injector) {
CuttlefishConfig tmp_config_obj;
@@ -467,7 +476,8 @@ CuttlefishConfig InitializeCuttlefishConfiguration(
<< "Failed to save fragment " << fragment->Name();
}
- tmp_config_obj.set_assembly_dir(FLAGS_assembly_dir);
+ tmp_config_obj.set_root_dir(root_dir);
+
tmp_config_obj.set_target_arch(kernel_config.target_arch);
tmp_config_obj.set_bootconfig_supported(kernel_config.bootconfig_supported);
auto vmm = GetVmManager(FLAGS_vm_manager, kernel_config.target_arch);
@@ -563,15 +573,35 @@ CuttlefishConfig InitializeCuttlefishConfiguration(
<< "GPU capture only supported with --norestart_subprocesses";
}
+ tmp_config_obj.set_hwcomposer(FLAGS_hwcomposer);
+ if (!tmp_config_obj.hwcomposer().empty()) {
+ if (tmp_config_obj.hwcomposer() == kHwComposerRanchu) {
+ CHECK(tmp_config_obj.gpu_mode() != kGpuModeDrmVirgl)
+ << "ranchu hwcomposer not supported with --gpu_mode=drm_virgl";
+ }
+ }
+
+ if (tmp_config_obj.hwcomposer() == kHwComposerAuto) {
+ if (tmp_config_obj.gpu_mode() == kGpuModeDrmVirgl) {
+ tmp_config_obj.set_hwcomposer(kHwComposerDrmMinigbm);
+ } else {
+ tmp_config_obj.set_hwcomposer(kHwComposerRanchu);
+ }
+ }
+
+ tmp_config_obj.set_enable_gpu_udmabuf(FLAGS_enable_gpu_udmabuf);
+
// Sepolicy rules need to be updated to support gpu mode. Temporarily disable
// auto-enabling sandbox when gpu is enabled (b/152323505).
if (tmp_config_obj.gpu_mode() != kGpuModeGuestSwiftshader) {
SetCommandLineOptionWithMode("enable_sandbox", "false", SET_FLAGS_DEFAULT);
}
- if (vmm->ConfigureGpuMode(tmp_config_obj.gpu_mode()).empty()) {
- LOG(FATAL) << "Invalid gpu_mode=" << FLAGS_gpu_mode <<
- " does not work with vm_manager=" << FLAGS_vm_manager;
+ if (vmm->ConfigureGraphics(tmp_config_obj.gpu_mode(),
+ tmp_config_obj.hwcomposer()).empty()) {
+ LOG(FATAL) << "Invalid (gpu_mode=," << FLAGS_gpu_mode <<
+ " hwcomposer= " << FLAGS_hwcomposer <<
+ ") does not work with vm_manager=" << FLAGS_vm_manager;
}
CHECK(!FLAGS_smt || FLAGS_cpus % 2 == 0)
@@ -702,10 +732,7 @@ CuttlefishConfig InitializeCuttlefishConfiguration(
auto instance = tmp_config_obj.ForInstance(num);
auto const_instance =
- const_cast<const CuttlefishConfig&>(tmp_config_obj)
- .ForInstance(num);
- // Set this first so that calls to PerInstancePath below are correct
- instance.set_instance_dir(instance_dir + "." + std::to_string(num));
+ const_cast<const CuttlefishConfig&>(tmp_config_obj).ForInstance(num);
instance.set_use_allocd(FLAGS_use_allocd);
if (FLAGS_use_random_serial) {
instance.set_serial_number(
@@ -796,13 +823,20 @@ CuttlefishConfig InitializeCuttlefishConfiguration(
}
instance.set_webrtc_device_id(device_id);
}
- if (FLAGS_start_webrtc_sig_server && is_first_instance) {
+ if (!is_first_instance || !FLAGS_start_webrtc) {
+ // Only the first instance starts the signaling server or proxy
+ instance.set_start_webrtc_signaling_server(false);
+ instance.set_start_webrtc_sig_server_proxy(false);
+ } else {
auto port = 8443 + num - 1;
// Change the signaling server port for all instances
tmp_config_obj.set_sig_server_port(port);
- instance.set_start_webrtc_signaling_server(true);
- } else {
- instance.set_start_webrtc_signaling_server(false);
+ // Either the signaling server or the proxy is started, never both
+ instance.set_start_webrtc_signaling_server(FLAGS_start_webrtc_sig_server);
+ // The proxy is only started if the host operator is available
+ instance.set_start_webrtc_sig_server_proxy(
+ cuttlefish::FileIsSocket(HOST_OPERATOR_SOCKET_PATH) &&
+ !FLAGS_start_webrtc_sig_server);
}
// Start wmediumd process for the first instance if
@@ -933,11 +967,18 @@ bool GetKernelConfigAndSetDefaults(KernelConfig* kernel_config) {
<< std::endl;
invalid_manager = true;
}
- // The default for starting signaling server is whether or not webrt is to be
- // started.
- SetCommandLineOptionWithMode("start_webrtc_sig_server",
- FLAGS_start_webrtc ? "true" : "false",
- SET_FLAGS_DEFAULT);
+ auto host_operator_present =
+ cuttlefish::FileIsSocket(HOST_OPERATOR_SOCKET_PATH);
+ // The default for starting signaling server depends on whether or not webrtc
+ // is to be started and the presence of the host orchestrator.
+ SetCommandLineOptionWithMode(
+ "start_webrtc_sig_server",
+ FLAGS_start_webrtc && !host_operator_present ? "true" : "false",
+ SET_FLAGS_DEFAULT);
+ SetCommandLineOptionWithMode(
+ "webrtc_sig_server_addr",
+ host_operator_present ? HOST_OPERATOR_SOCKET_PATH : "0.0.0.0",
+ SET_FLAGS_DEFAULT);
if (invalid_manager) {
return false;
}
diff --git a/host/commands/assemble_cvd/flags.h b/host/commands/assemble_cvd/flags.h
index 63abe50e9..961ac2377 100644
--- a/host/commands/assemble_cvd/flags.h
+++ b/host/commands/assemble_cvd/flags.h
@@ -19,9 +19,10 @@ struct KernelConfig {
bool GetKernelConfigAndSetDefaults(KernelConfig* kernel_config);
// Must be called after ParseCommandLineFlags.
-CuttlefishConfig InitializeCuttlefishConfiguration(
- const std::string& instance_dir, int modem_simulator_count,
- KernelConfig kernel_config, fruit::Injector<>& injector);
+CuttlefishConfig InitializeCuttlefishConfiguration(const std::string& root_dir,
+ int modem_simulator_count,
+ KernelConfig kernel_config,
+ fruit::Injector<>& injector);
std::string GetConfigFilePath(const CuttlefishConfig& config);
std::string GetCuttlefishEnvPath();
diff --git a/host/commands/assemble_cvd/super_image_mixer.cc b/host/commands/assemble_cvd/super_image_mixer.cc
index c1e7eac0e..847800609 100644
--- a/host/commands/assemble_cvd/super_image_mixer.cc
+++ b/host/commands/assemble_cvd/super_image_mixer.cc
@@ -55,14 +55,9 @@ std::string TargetFilesZip(const FetcherConfig& fetcher_config,
const std::string kMiscInfoPath = "META/misc_info.txt";
const std::set<std::string> kDefaultTargetImages = {
- "IMAGES/boot.img",
- "IMAGES/odm.img",
- "IMAGES/odm_dlkm.img",
- "IMAGES/recovery.img",
- "IMAGES/userdata.img",
- "IMAGES/vbmeta.img",
- "IMAGES/vendor.img",
- "IMAGES/vendor_dlkm.img",
+ "IMAGES/boot.img", "IMAGES/init_boot.img", "IMAGES/odm.img",
+ "IMAGES/odm_dlkm.img", "IMAGES/recovery.img", "IMAGES/userdata.img",
+ "IMAGES/vbmeta.img", "IMAGES/vendor.img", "IMAGES/vendor_dlkm.img",
};
const std::set<std::string> kDefaultTargetBuildProp = {
"ODM/build.prop",
diff --git a/host/commands/cvd/Android.bp b/host/commands/cvd/Android.bp
index 917e4d3ec..3d2f85316 100644
--- a/host/commands/cvd/Android.bp
+++ b/host/commands/cvd/Android.bp
@@ -19,6 +19,7 @@ package {
cc_binary_host {
name: "cvd",
+ symlinks: ["acloud"],
srcs: [
"main.cc",
],
@@ -31,6 +32,7 @@ cc_binary_host {
"libprotobuf-cpp-lite",
],
static_libs: [
+ "libbuildversion",
"libcuttlefish_cvd_proto",
"libcuttlefish_host_config",
],
@@ -44,6 +46,7 @@ cc_binary_host {
defaults: [
"cuttlefish_host",
],
+ use_version_lib: true,
}
cc_binary_host {
@@ -59,10 +62,12 @@ cc_binary_host {
"libprotobuf-cpp-lite",
],
static_libs: [
+ "libbuildversion",
"libcuttlefish_cvd_proto",
"libcuttlefish_host_config",
],
defaults: [
"cuttlefish_host",
],
+ use_version_lib: true,
}
diff --git a/host/commands/cvd/main.cc b/host/commands/cvd/main.cc
index f47135144..e226e45e7 100644
--- a/host/commands/cvd/main.cc
+++ b/host/commands/cvd/main.cc
@@ -26,6 +26,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/result.h>
+#include <build/version.h>
#include "cvd_server.pb.h"
@@ -85,10 +86,17 @@ class CvdClient {
return EnsureCvdServerRunning(host_tool_directory, num_retries - 1);
} else {
std::cout << "Unable to start the cvd_server with version "
- << cvd::kVersionMajor << "." << cvd::kVersionMinor;
+ << cvd::kVersionMajor << "." << cvd::kVersionMinor
+ << std::endl;
return false;
}
}
+ if (server_version.build() != android::build::GetBuildNumber()) {
+ std::cout << "WARNING: cvd_server client version ("
+ << android::build::GetBuildNumber()
+ << ") does not match server version ("
+ << server_version.build() << std::endl;
+ }
return true;
}
@@ -241,6 +249,31 @@ int CvdMain(int argc, char** argv, char** envp) {
std::vector<std::string> args = ArgsToVec(argc, argv);
std::vector<Flag> flags;
+ // TODO(b/206893146): Make this decision inside the server.
+ if (args[0] == "acloud") {
+ bool passthrough = true;
+ ParseFlags({GflagsCompatFlag("acloud_passthrough", passthrough)}, args);
+ if (passthrough) {
+ auto android_top = StringFromEnv("ANDROID_BUILD_TOP", "");
+ if (android_top == "") {
+ LOG(ERROR) << "Could not find android environment. Please run "
+ << "\"source build/envsetup.sh\".";
+ return 1;
+ }
+ // TODO(b/206893146): Detect what the platform actually is.
+ auto py_acloud_path =
+ android_top + "/prebuilts/asuite/acloud/linux-x86/acloud";
+ char** new_argv = new char*[args.size() + 1];
+ for (size_t i = 0; i < args.size(); i++) {
+ new_argv[i] = args[i].data();
+ }
+ new_argv[args.size()] = nullptr;
+ execv(py_acloud_path.data(), new_argv);
+ delete[] new_argv;
+ PLOG(ERROR) << "execv(" << py_acloud_path << ", ...) failed";
+ return 1;
+ }
+ }
bool clean = false;
flags.emplace_back(GflagsCompatFlag("clean", clean));
diff --git a/host/commands/cvd/proto/cvd_server.proto b/host/commands/cvd/proto/cvd_server.proto
index 73fb4db91..91194bc54 100644
--- a/host/commands/cvd/proto/cvd_server.proto
+++ b/host/commands/cvd/proto/cvd_server.proto
@@ -52,6 +52,7 @@ message Response {
message Version {
int32 major = 1;
int32 minor = 2;
+ string build = 3;
}
message VersionRequest {}
diff --git a/host/commands/cvd/server.cc b/host/commands/cvd/server.cc
index 978b43cb5..1c5b2ead6 100644
--- a/host/commands/cvd/server.cc
+++ b/host/commands/cvd/server.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "host/commands/cvd/server.h"
+
#include <future>
#include <map>
#include <optional>
@@ -21,9 +23,9 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <build/version.h>
#include "cvd_server.pb.h"
-#include "host/commands/cvd/server.h"
#include "common/libs/fs/shared_buf.h"
#include "common/libs/fs/shared_fd.h"
@@ -145,6 +147,8 @@ class CvdServer {
cvd::kVersionMajor);
response.mutable_version_response()->mutable_version()->set_minor(
cvd::kVersionMinor);
+ response.mutable_version_response()->mutable_version()->set_build(
+ android::build::GetBuildNumber());
response.mutable_status()->set_code(cvd::Status::OK);
return SendResponse(client, response);
}
@@ -268,7 +272,7 @@ class CvdServer {
// Handle `cvd help <subcommand>` by calling the subcommand with --help.
bin = it->second;
- args = {"--help"};
+ args_copy.push_back("--help");
} else if (bin == kClearBin) {
*response.mutable_status() = CvdClear(out, err);
return SendResponse(client, response);
diff --git a/host/commands/cvd/server.h b/host/commands/cvd/server.h
index 05dadecd9..300c8c43e 100644
--- a/host/commands/cvd/server.h
+++ b/host/commands/cvd/server.h
@@ -20,7 +20,7 @@ namespace cvd {
// Major version uprevs are backwards incompatible.
// Minor version uprevs are backwards compatible within major version.
constexpr int kVersionMajor = 1;
-constexpr int kVersionMinor = 0;
+constexpr int kVersionMinor = 1;
// Pathname of the abstract cvd_server socket.
constexpr char kServerSocketPath[] = "cvd_server";
diff --git a/host/commands/fetcher/build_api.cc b/host/commands/fetcher/build_api.cc
index 8189270f0..90eb64099 100644
--- a/host/commands/fetcher/build_api.cc
+++ b/host/commands/fetcher/build_api.cc
@@ -93,9 +93,11 @@ std::vector<std::string> BuildApi::Headers() {
std::string BuildApi::LatestBuildId(const std::string& branch,
const std::string& target) {
- std::string url = BUILD_API + "/builds?branch=" + branch
- + "&buildAttemptStatus=complete"
- + "&buildType=submitted&maxResults=1&successful=true&target=" + target;
+ std::string url =
+ BUILD_API + "/builds?branch=" + curl.UrlEscape(branch) +
+ "&buildAttemptStatus=complete" +
+ "&buildType=submitted&maxResults=1&successful=true&target=" +
+ curl.UrlEscape(target);
auto curl_response = curl.DownloadToJson(url, Headers());
const auto& json = curl_response.data;
if (!curl_response.HttpSuccess()) {
@@ -117,7 +119,8 @@ std::string BuildApi::LatestBuildId(const std::string& branch,
}
std::string BuildApi::BuildStatus(const DeviceBuild& build) {
- std::string url = BUILD_API + "/builds/" + build.id + "/" + build.target;
+ std::string url = BUILD_API + "/builds/" + curl.UrlEscape(build.id) + "/" +
+ curl.UrlEscape(build.target);
auto curl_response = curl.DownloadToJson(url, Headers());
const auto& json = curl_response.data;
if (!curl_response.HttpSuccess()) {
@@ -133,7 +136,8 @@ std::string BuildApi::BuildStatus(const DeviceBuild& build) {
}
std::string BuildApi::ProductName(const DeviceBuild& build) {
- std::string url = BUILD_API + "/builds/" + build.id + "/" + build.target;
+ std::string url = BUILD_API + "/builds/" + curl.UrlEscape(build.id) + "/" +
+ curl.UrlEscape(build.target);
auto curl_response = curl.DownloadToJson(url, Headers());
const auto& json = curl_response.data;
if (!curl_response.HttpSuccess()) {
@@ -153,10 +157,11 @@ std::vector<Artifact> BuildApi::Artifacts(const DeviceBuild& build) {
std::string page_token = "";
std::vector<Artifact> artifacts;
do {
- std::string url = BUILD_API + "/builds/" + build.id + "/" + build.target +
+ std::string url = BUILD_API + "/builds/" + curl.UrlEscape(build.id) + "/" +
+ curl.UrlEscape(build.target) +
"/attempts/latest/artifacts?maxResults=100";
if (page_token != "") {
- url += "&pageToken=" + page_token;
+ url += "&pageToken=" + curl.UrlEscape(page_token);
}
auto curl_response = curl.DownloadToJson(url, Headers());
const auto& json = curl_response.data;
@@ -204,8 +209,9 @@ bool BuildApi::ArtifactToFile(const DeviceBuild& build,
const std::string& artifact,
const std::string& path) {
std::string download_url_endpoint =
- BUILD_API + "/builds/" + build.id + "/" + build.target +
- "/attempts/latest/artifacts/" + artifact + "/url";
+ BUILD_API + "/builds/" + curl.UrlEscape(build.id) + "/" +
+ curl.UrlEscape(build.target) + "/attempts/latest/artifacts/" +
+ curl.UrlEscape(artifact) + "/url";
auto curl_response = curl.DownloadToJson(download_url_endpoint, Headers());
const auto& json = curl_response.data;
if (!(curl_response.HttpSuccess() || curl_response.HttpRedirect())) {
diff --git a/host/commands/fetcher/curl_wrapper.cc b/host/commands/fetcher/curl_wrapper.cc
index 676bba879..60b2d239c 100644
--- a/host/commands/fetcher/curl_wrapper.cc
+++ b/host/commands/fetcher/curl_wrapper.cc
@@ -156,6 +156,13 @@ class CurlWrapperImpl : public CurlWrapper {
return {json, response.http_code};
}
+ std::string UrlEscape(const std::string& text) override {
+ char* escaped_str = curl_easy_escape(curl_, text.c_str(), text.size());
+ std::string ret{escaped_str};
+ curl_free(escaped_str);
+ return ret;
+ }
+
private:
CURL* curl_;
std::mutex mutex_;
@@ -188,6 +195,10 @@ class CurlServerErrorRetryingWrapper : public CurlWrapper {
[&, this]() { return inner_curl_.DownloadToJson(url, headers); });
}
+ std::string UrlEscape(const std::string& text) override {
+ return inner_curl_.UrlEscape(text);
+ }
+
private:
template <typename T>
CurlResponse<T> RetryImpl(std::function<CurlResponse<T>()> attempt_fn) {
diff --git a/host/commands/fetcher/curl_wrapper.h b/host/commands/fetcher/curl_wrapper.h
index ce5a04377..6262a0abb 100644
--- a/host/commands/fetcher/curl_wrapper.h
+++ b/host/commands/fetcher/curl_wrapper.h
@@ -49,6 +49,8 @@ class CurlWrapper {
const std::string& url, const std::vector<std::string>& headers = {}) = 0;
virtual CurlResponse<Json::Value> DownloadToJson(
const std::string& url, const std::vector<std::string>& headers = {}) = 0;
+
+ virtual std::string UrlEscape(const std::string&) = 0;
};
}
diff --git a/host/commands/kernel_log_monitor/main.cc b/host/commands/kernel_log_monitor/main.cc
index 3f708b791..c7269b0ca 100644
--- a/host/commands/kernel_log_monitor/main.cc
+++ b/host/commands/kernel_log_monitor/main.cc
@@ -94,7 +94,7 @@ int main(int argc, char** argv) {
return 2;
}
- monitor::KernelLogServer klog{pipe, instance.PerInstancePath("kernel.log"),
+ monitor::KernelLogServer klog{pipe, instance.PerInstanceLogPath("kernel.log"),
config->deprecated_boot_completed()};
for (auto subscriber_fd: subscriber_fds) {
diff --git a/host/commands/log_tee/log_tee.cpp b/host/commands/log_tee/log_tee.cpp
index 7021a994b..b63c2acbd 100644
--- a/host/commands/log_tee/log_tee.cpp
+++ b/host/commands/log_tee/log_tee.cpp
@@ -64,7 +64,7 @@ int main(int argc, char** argv) {
// There is no guarantee of success all the time since log line boundaries
// could be out sync with the reads, but that's ok.
if (android::base::StartsWith(trimmed, "[INFO")) {
- LOG(INFO) << trimmed;
+ LOG(DEBUG) << trimmed;
} else if (android::base::StartsWith(trimmed, "[ERROR")) {
LOG(ERROR) << trimmed;
} else if (android::base::StartsWith(trimmed, "[WARNING")) {
diff --git a/host/commands/metrics/metrics.cc b/host/commands/metrics/metrics.cc
index 378155cf6..5fcf58e17 100644
--- a/host/commands/metrics/metrics.cc
+++ b/host/commands/metrics/metrics.cc
@@ -32,7 +32,7 @@ int main(int argc, char** argv) {
CHECK(config) << "Could not open cuttlefish config";
auto instance = config->ForDefaultInstance();
- auto metrics_log_path = instance.PerInstancePath("metrics.log");
+ auto metrics_log_path = instance.PerInstanceLogPath("metrics.log");
if (config->run_as_daemon()) {
android::base::SetLogger(
diff --git a/host/commands/modem_simulator/call_service.cpp b/host/commands/modem_simulator/call_service.cpp
index 3dd6076c2..bfb17c6a4 100644
--- a/host/commands/modem_simulator/call_service.cpp
+++ b/host/commands/modem_simulator/call_service.cpp
@@ -219,11 +219,13 @@ void CallService::HandleDial(const Client& client, const std::string& command) {
int index = last_active_call_index_++;
auto call_token = std::make_pair(index, call_status.number);
- call_status.timeout_serial = thread_looper_->PostWithDelay(
- std::chrono::minutes(1),
- makeSafeCallback<CallService>(this, [call_token](CallService* me) {
- me->TimerWaitingRemoteCallResponse(call_token);
- }));
+ call_status.timeout_serial = thread_looper_->Post(
+ makeSafeCallback<CallService>(this,
+ [call_token](CallService* me) {
+ me->TimerWaitingRemoteCallResponse(
+ call_token);
+ }),
+ std::chrono::minutes(1));
active_calls_[index] = call_status;
} else {
@@ -237,8 +239,9 @@ void CallService::HandleDial(const Client& client, const std::string& command) {
in_emergency_mode_ = true;
SendUnsolicitedCommand("+WSOS: 1");
}
- thread_looper_->PostWithDelay(std::chrono::seconds(1),
- makeSafeCallback(this, &CallService::SimulatePendingCallsAnswered));
+ thread_looper_->Post(
+ makeSafeCallback(this, &CallService::SimulatePendingCallsAnswered),
+ std::chrono::seconds(1));
}
client.SendCommandResponse("OK");
diff --git a/host/commands/modem_simulator/data_service.cpp b/host/commands/modem_simulator/data_service.cpp
index b48a777fe..1e11e690d 100644
--- a/host/commands/modem_simulator/data_service.cpp
+++ b/host/commands/modem_simulator/data_service.cpp
@@ -335,10 +335,10 @@ void DataService::updatePhysicalChannelconfigs(int modem_tech, int freq,
// call again after 1 sec delay
count--;
- thread_looper_->PostWithDelay(
- std::chrono::seconds(1),
+ thread_looper_->Post(
makeSafeCallback(this, &DataService::updatePhysicalChannelconfigs,
- modem_tech, freq, cellBandwidthDownlink, count));
+ modem_tech, freq, cellBandwidthDownlink, count),
+ std::chrono::seconds(1));
}
} // namespace cuttlefish
diff --git a/host/commands/modem_simulator/main.cpp b/host/commands/modem_simulator/main.cpp
index 8bfcd9719..41ab6ce54 100644
--- a/host/commands/modem_simulator/main.cpp
+++ b/host/commands/modem_simulator/main.cpp
@@ -63,7 +63,7 @@ int main(int argc, char** argv) {
auto config = cuttlefish::CuttlefishConfig::Get();
auto instance = config->ForDefaultInstance();
- auto modem_log_path = instance.PerInstancePath("modem_simulator.log");
+ auto modem_log_path = instance.PerInstanceLogPath("modem_simulator.log");
{
auto log_path = instance.launcher_log_path();
diff --git a/host/commands/modem_simulator/network_service.cpp b/host/commands/modem_simulator/network_service.cpp
index 56f14e514..af9c59ef4 100644
--- a/host/commands/modem_simulator/network_service.cpp
+++ b/host/commands/modem_simulator/network_service.cpp
@@ -31,15 +31,6 @@ static const std::string kAreaCode = "2142";
// string type; four byte GERAN/UTRAN cell ID in hexadecimal format
static const std::string kCellId = "0000B804";
-// Check SignalStrength.java file for more details on how these map to
-// signal strength bars
-const std::pair<int, int> kGSMSignalStrength = std::make_pair(4, 30);
-const std::pair<int, int> kCDMASignalStrength = std::make_pair(4, 120);
-const std::pair<int, int> kEVDOSignalStrength = std::make_pair(4, 120);
-const std::pair<int, int> kLTESignalStrength = std::make_pair(4, 30);
-const std::pair<int, int> kWCDMASignalStrength = std::make_pair(4, 30);
-const std::pair<int, int> kNRSignalStrength = std::make_pair(45, 135);
-
NetworkService::NetworkService(int32_t service_id,
ChannelMonitor* channel_monitor,
ThreadLooper* thread_looper)
@@ -255,9 +246,10 @@ void NetworkService::OnSimStatusChanged(SimService::SimStatus sim_status) {
// Note: not saved to nvram config due to sim status may change after reboot
current_network_mode_ = M_MODEM_TECH_WCDMA;
}
- thread_looper_->PostWithDelay(std::chrono::seconds(1),
+ thread_looper_->Post(
makeSafeCallback(this, &NetworkService::UpdateRegisterState,
- voice_registration_status_.registration_state));
+ voice_registration_status_.registration_state),
+ std::chrono::seconds(1));
}
/**
@@ -318,7 +310,6 @@ void NetworkService::HandleRadioPower(const Client& client, std::string& command
client.SendCommandResponse(kCmeErrorOperationNotSupported);
return;
}
- signal_strength_.Reset();
client.SendCommandResponse("OK");
}
@@ -335,39 +326,87 @@ bool NetworkService::WakeupFromSleep() {
return wakeup_from_sleep;
}
-void NetworkService::SetSignalStrengthValue(int& value,
- const std::pair<int, int>& range,
- double percentd) {
- value = range.first + percentd * (range.second - range.first);
- AdjustSignalStrengthValue(value, range);
-}
-
-void NetworkService::AdjustSignalStrengthValue(int& value,
- const std::pair<int, int>& range) {
- if (value < range.first) {
- value = range.first;
- } else if (value > range.second) {
- value = range.second;
- }
-}
/**
+ * IMPORTANT NOTE: Current implementation of AT+CSQ differs from standards
+ * described in TS 27.007 8.5 which only only supports RSSI and BER.
+ *
+ * TODO(b/206814247): Rename AT+CSQ command.
+ *
* AT+CSQ
- * Execution command returns received signal strength indication <rssi>
- * and channel bit error rate <ber> from the MT.
+ * Execution command returns received signal strength indication. This is a
+ * Cuttlefish specific command.
*
- * command Possible response(s)
- * AT+CSQ +CSQ: <rssi>,<ber>
- * +CME ERROR: <err>
+ * Command Possible response(s)
+ * AT+CSQ +CSQ: <gsm_rssi>,<gsm_ber>,<cdma_dbm>,
+ * <cdma_ecio>,<evdo_dbm>,<evdo_ecio>,<evdo_snr>,
+ * <lte_rssi>,<lte_rsrp>,<lte_rsrq>,<lte_rssnr>,
+ * <lte_cqi>,<lte_ta>,<tdscdma_rscp>,<wcdma_rssi>,
+ * <wcdma_ber>,<nr_ss_rsrp>,<nr_ss_rsrq>,<nr_ss_sinr>,
+ * <nr_csi_rsrp>,<nr_csi_rsrq>,<nr_csi_sinr>
+ * +CME ERROR: <err>
*
- * <rssi>: integer type
- * 0 ‑113 dBm or less
- * 1 ‑111 dBm
- * 2...30 ‑109... ‑53 dBm
- * 31 ‑51 dBm or greater
- * 99 not known or not detectable
- * <ber>: integer type; channel bit error rate (in percent)
- * 0...7 as RXQUAL values in the table in 3GPP TS 45.008 [20] subclause 8.2.4
- * 99 not known or not detectable
+ * <gsm_rssi>: Valid values are (0-31, 99) as defined in TS 27.007 8.5.
+ * <gsm_ber>: Bit error rate (0-7, 99) as defined in TS 27.007 8.5.
+ * <cdma_dbm>: Valid values are positive integers.
+ * This value is the actual RSSI value multiplied by -1.
+ * Example: If the actual RSSI is -75, then this response value will be 75.
+ * <cdma_ecio>: Valid values are positive integers.
+ * This value is the actual Ec/Io multiplied by -10.
+ * Example: If the actual Ec/Io is -12.5 dB, then this response value will
+ * be 125.
+ * <evdo_dbm>: Refer cdma_dbm.
+ * <evdo_ecio>: Refer cdma_ecio.
+ * <evdo_snr>: Valid values are 0-8.
+ * 8 is the highest signal to noise ratio.
+ * <lte_rssi>: Refer gsm_rssi.
+ * <lte_rsrp>:
+ * The current Reference Signal Receive Power in dBm multiplied by -1.
+ * Range: 44 to 140 dBm.
+ * INT_MAX: 0x7FFFFFFF denotes invalid value.
+ * Reference: 3GPP TS 36.133 9.1.4.
+ * <lte_rsrq>:
+ * The current Reference Signal Receive Quality in dB multiplied by -1.
+ * Range: 20 to 3 dB.
+ * INT_MAX: 0x7FFFFFFF denotes invalid value.
+ * Reference: 3GPP TS 36.133 9.1.7.
+ * <lte_rssnr>:
+ * The current reference signal signal-to-noise ratio in 0.1 dB units.
+ * Range: -200 to +300 (-200 = -20.0 dB, +300 = 30dB).
+ * INT_MAX : 0x7FFFFFFF denotes invalid value.
+ * Reference: 3GPP TS 36.101 8.1.1.
+ * <lte_cqi>: The current Channel Quality Indicator.
+ * Range: 0 to 15.
+ * INT_MAX : 0x7FFFFFFF denotes invalid value.
+ * Reference: 3GPP TS 36.101 9.2, 9.3, A.4.
+ * <lte_ta>:
+ * Timing advance in micro seconds for a one way trip from cell to device.
+ * Approximate distance can be calculated using 300m/us * timingAdvance.
+ * Range: 0 to 0x7FFFFFFE.
+ * INT_MAX : 0x7FFFFFFF denotes invalid value.
+ * Reference: 3GPP 36.321 section 6.1.3.5.
+ * <tdscdma_rscp>: P-CCPCH RSCP as defined in TS 25.225 5.1.1.
+ * Valid values are (0-96, 255) as defined in TS 27.007 8.69.
+ * INT_MAX denotes that the value is invalid/unreported.
+ * <wcdma_rssi>: Refer gsm_rssi.
+ * <wcdma_ber>: Refer gsm_ber.
+ * <nr_ss_rsrp>: SS reference signal received power, multiplied by -1.
+ * Reference: 3GPP TS 38.215.
+ * Range [44, 140], INT_MAX means invalid/unreported.
+ * <nr_ss_rsrq>: SS reference signal received quality, multiplied by -1.
+ * Reference: 3GPP TS 38.215.
+ * Range [3, 20], INT_MAX means invalid/unreported.
+ * <nr_ss_sinr>: SS signal-to-noise and interference ratio.
+ * Reference: 3GPP TS 38.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1.
+ * Range [-23, 40], INT_MAX means invalid/unreported.
+ * <nr_csi_rsrp>: CSI reference signal received power, multiplied by -1.
+ * Reference: 3GPP TS 38.215.
+ * Range [44, 140], INT_MAX means invalid/unreported.
+ * <nr_csi_rsrq>: CSI reference signal received quality, multiplied by -1.
+ * Reference: 3GPP TS 38.215.
+ * Range [3, 20], INT_MAX means invalid/unreported.
+ * <nr_csi_sinr>: CSI signal-to-noise and interference ratio.
+ * Reference: 3GPP TS 138.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1.
+ * Range [-23, 40], INT_MAX means invalid/unreported.
*
* see RIL_REQUEST_SIGNAL_STRENGTH in RIL
*/
@@ -384,7 +423,7 @@ void NetworkService::HandleSignalStrength(const Client& client) {
android_last_signal_time_ = time(0);
- auto response = GetSignalStrength();
+ auto response = BuildCSQCommandResponse(GetCurrentSignalStrength());
responses.push_back(response);
responses.push_back("OK");
@@ -392,11 +431,9 @@ void NetworkService::HandleSignalStrength(const Client& client) {
}
bool NetworkService::IsHasNetwork() {
- if (radio_state_ == RADIO_STATE_OFF ||
- oper_selection_mode_ == OperatorSelectionMode::OPER_SELECTION_DEREGISTRATION) {
- return false;
- }
- return true;
+ return radio_state_ != RADIO_STATE_OFF &&
+ oper_selection_mode_ !=
+ OperatorSelectionMode::OPER_SELECTION_DEREGISTRATION;
}
/**
@@ -676,8 +713,10 @@ void NetworkService::HandleSetNetworkSelectionMode(const Client& client, std::st
NvramConfig::SaveToFile();
- thread_looper_->PostWithDelay(std::chrono::seconds(1),
- makeSafeCallback(this, &NetworkService::UpdateRegisterState, registration_state));
+ thread_looper_->Post(
+ makeSafeCallback(this, &NetworkService::UpdateRegisterState,
+ registration_state),
+ std::chrono::seconds(1));
}
NetworkService::NetworkRegistrationStatus::AccessTechnoloy
@@ -987,13 +1026,13 @@ void NetworkService::HandleSetPreferredNetworkType(const Client& client, std::st
if (current != current_network_mode_) {
UpdateRegisterState(NET_REGISTRATION_UNREGISTERED);
- signal_strength_.Reset();
ss << "+CTEC: "<< current_network_mode_;
- thread_looper_->PostWithDelay(std::chrono::milliseconds(200),
+ thread_looper_->Post(
makeSafeCallback(this, &NetworkService::UpdateRegisterState,
- NET_REGISTRATION_HOME));
+ NET_REGISTRATION_HOME),
+ std::chrono::milliseconds(200));
} else {
ss << "+CTEC: DONE";
}
@@ -1071,60 +1110,75 @@ void NetworkService::OnDataRegisterStateChanged() {
SendUnsolicitedCommand(ss.str());
}
-std::string NetworkService::GetSignalStrength() {
+int NetworkService::GetValueInRange(const std::pair<int, int>& range,
+ int percent) {
+ int range_size = range.second - range.first + 1;
+ return range.first + (int)((percent / 101.0) * range_size);
+}
+
+std::string NetworkService::BuildCSQCommandResponse(
+ const SignalStrength& signal_strength) {
+ std::stringstream ss;
+ // clang-format off
+ ss << "+CSQ: "
+ << signal_strength.gsm_rssi << ","
+ << signal_strength.gsm_ber << ","
+ << signal_strength.cdma_dbm << ","
+ << signal_strength.cdma_ecio << ","
+ << signal_strength.evdo_dbm << ","
+ << signal_strength.evdo_ecio << ","
+ << signal_strength.evdo_snr << ","
+ << signal_strength.lte_rssi << ","
+ << signal_strength.lte_rsrp << ","
+ << signal_strength.lte_rsrq << ","
+ << signal_strength.lte_rssnr << ","
+ << signal_strength.lte_cqi << ","
+ << signal_strength.lte_ta << ","
+ << signal_strength.tdscdma_rscp << ","
+ << signal_strength.wcdma_rssi << ","
+ << signal_strength.wcdma_ber << ","
+ << signal_strength.nr_ss_rsrp << ","
+ << signal_strength.nr_ss_rsrq << ","
+ << signal_strength.nr_ss_sinr << ","
+ << signal_strength.nr_csi_rsrp << ","
+ << signal_strength.nr_csi_rsrq << ","
+ << signal_strength.nr_csi_sinr;
+ // clang-format on
+ return ss.str();
+}
+
+NetworkService::SignalStrength NetworkService::GetCurrentSignalStrength() {
+ NetworkService::SignalStrength result;
+ if (!IsHasNetwork()) {
+ return result;
+ }
+ int percent = signal_strength_percent_;
switch (current_network_mode_) {
case M_MODEM_TECH_GSM:
- signal_strength_.gsm_rssi += (rand() % 3 - 1);
- AdjustSignalStrengthValue(signal_strength_.gsm_rssi, kGSMSignalStrength);
+ result.gsm_rssi = GetValueInRange(kRssiRange, percent);
break;
case M_MODEM_TECH_CDMA:
- signal_strength_.cdma_dbm += (rand() % 3 - 1);
- AdjustSignalStrengthValue(signal_strength_.cdma_dbm, kCDMASignalStrength);
+ result.cdma_dbm = GetValueInRange(kDbmRange, percent) * -1;
break;
case M_MODEM_TECH_EVDO:
- signal_strength_.evdo_dbm += (rand() % 3 - 1);
- AdjustSignalStrengthValue(signal_strength_.evdo_dbm, kEVDOSignalStrength);
+ result.evdo_dbm = GetValueInRange(kDbmRange, percent) * -1;
break;
case M_MODEM_TECH_LTE:
- signal_strength_.lte_rssi += (rand() % 3 - 1);
- AdjustSignalStrengthValue(signal_strength_.lte_rssi, kLTESignalStrength);
+ result.lte_rsrp = GetValueInRange(kRsrpRange, percent) * -1;
break;
case M_MODEM_TECH_WCDMA:
- signal_strength_.wcdma_rssi += (rand() % 3 - 1);
- AdjustSignalStrengthValue(signal_strength_.wcdma_rssi, kWCDMASignalStrength);
+ result.wcdma_rssi = GetValueInRange(kRssiRange, percent);
break;
case M_MODEM_TECH_NR:
- signal_strength_.nr_ss_rsrp += (rand() % 3 - 1);
- AdjustSignalStrengthValue(signal_strength_.nr_ss_rsrp, kNRSignalStrength);
+ // special for NR: it uses LTE as primary, so LTE signal strength is
+ // needed as well
+ result.lte_rsrp = GetValueInRange(kRsrpRange, percent) * -1;
+ result.nr_ss_rsrp = GetValueInRange(kRsrpRange, percent) * -1;
break;
default:
break;
}
-
- std::stringstream ss;
- ss << "+CSQ: " << signal_strength_.gsm_rssi << ","
- << signal_strength_.gsm_ber << ","
- << signal_strength_.cdma_dbm << ","
- << signal_strength_.cdma_ecio << ","
- << signal_strength_.evdo_dbm << ","
- << signal_strength_.evdo_ecio << ","
- << signal_strength_.evdo_snr << ","
- << signal_strength_.lte_rssi << ","
- << signal_strength_.lte_rsrp << ","
- << signal_strength_.lte_rsrq << ","
- << signal_strength_.lte_rssnr << ","
- << signal_strength_.lte_cqi << ","
- << signal_strength_.lte_ta << ","
- << signal_strength_.tdscdma_rscp << ","
- << signal_strength_.wcdma_rssi << ","
- << signal_strength_.wcdma_ber << ","
- << signal_strength_.nr_ss_rsrp << ","
- << signal_strength_.nr_ss_rsrq << ","
- << signal_strength_.nr_ss_sinr << ","
- << signal_strength_.nr_csi_rsrp << ","
- << signal_strength_.nr_csi_rsrq << ","
- << signal_strength_.nr_csi_sinr;;
- return ss.str();
+ return result;
}
/* AT+REMOTEREG: state*/
@@ -1136,12 +1190,11 @@ void NetworkService::HandleReceiveRemoteVoiceDataReg(const Client& client,
int stated = std::stoi(states, nullptr, 10);
UpdateRegisterState(NET_REGISTRATION_UNREGISTERED);
- signal_strength_.Reset();
- thread_looper_->PostWithDelay(
- std::chrono::seconds(1),
+ thread_looper_->Post(
makeSafeCallback(this, &NetworkService::UpdateRegisterState,
- (cuttlefish::NetworkService::RegistrationState)stated));
+ (cuttlefish::NetworkService::RegistrationState)stated),
+ std::chrono::seconds(1));
}
/* AT+REMOTECTEC: ctec */
@@ -1162,77 +1215,13 @@ void NetworkService::HandleReceiveRemoteCTEC(const Client& client,
current_network_mode_ = current_network_mode_new;
auto saved_state = voice_registration_status_.registration_state;
UpdateRegisterState(NET_REGISTRATION_UNREGISTERED);
- signal_strength_.Reset();
ss << "+CTEC: " << current_network_mode_;
- thread_looper_->PostWithDelay(
- std::chrono::seconds(1),
+ thread_looper_->Post(
makeSafeCallback(this, &NetworkService::UpdateRegisterState,
- saved_state));
- }
-}
-
-void NetworkService::applySignalPercentage(double percentd) {
- switch (current_network_mode_) {
- case M_MODEM_TECH_GSM:
- signal_strength_.gsm_rssi = 99;
- signal_strength_.gsm_ber = 0;
- SetSignalStrengthValue(signal_strength_.gsm_rssi, kGSMSignalStrength,
- percentd);
- break;
- case M_MODEM_TECH_CDMA:
- signal_strength_.cdma_dbm = 125;
- signal_strength_.cdma_ecio = 165;
- SetSignalStrengthValue(signal_strength_.cdma_dbm, kCDMASignalStrength,
- percentd);
- break;
- case M_MODEM_TECH_EVDO:
- signal_strength_.evdo_dbm = 125;
- signal_strength_.evdo_ecio = 165;
- signal_strength_.evdo_snr = -1;
- SetSignalStrengthValue(signal_strength_.evdo_dbm, kEVDOSignalStrength,
- percentd);
- break;
- case M_MODEM_TECH_LTE:
- signal_strength_.lte_rssi = 99;
- signal_strength_.lte_rsrp = -1;
- signal_strength_.lte_rsrq = -5;
- signal_strength_.lte_rssnr = -205;
- signal_strength_.lte_cqi = -1;
- signal_strength_.lte_ta = -1;
- SetSignalStrengthValue(signal_strength_.lte_rssi, kLTESignalStrength,
- percentd);
- break;
- case M_MODEM_TECH_WCDMA:
- signal_strength_.tdscdma_rscp = 99;
- signal_strength_.wcdma_rssi = 99;
- signal_strength_.wcdma_ber = 0;
- SetSignalStrengthValue(signal_strength_.wcdma_rssi, kWCDMASignalStrength,
- percentd);
- break;
- case M_MODEM_TECH_NR:
- // special for NR: it uses LTE as primary, so LTE signal strength is
- // needed as well
- signal_strength_.lte_rssi = 99;
- signal_strength_.lte_rsrp = -1;
- signal_strength_.lte_rsrq = -5;
- signal_strength_.lte_rssnr = -205;
- signal_strength_.lte_cqi = -1;
- signal_strength_.lte_ta = -1;
- SetSignalStrengthValue(signal_strength_.lte_rssi, kLTESignalStrength,
- percentd);
- signal_strength_.nr_ss_rsrp = 0;
- signal_strength_.nr_ss_rsrq = 0;
- signal_strength_.nr_ss_sinr = 45;
- signal_strength_.nr_csi_rsrp = 0;
- signal_strength_.nr_csi_rsrq = 0;
- signal_strength_.nr_csi_sinr = 30;
- SetSignalStrengthValue(signal_strength_.nr_ss_rsrp, kNRSignalStrength,
- percentd);
- break;
- default:
- break;
+ saved_state),
+ std::chrono::seconds(1));
}
}
@@ -1242,12 +1231,12 @@ void NetworkService::HandleReceiveRemoteSignal(const Client& client,
(void)client;
std::stringstream ss;
std::string percents = command.substr(std::string("AT+REMOTESIGNAL:").size());
- double percentd = std::stoi(percents, nullptr, 10) / 100.0;
+ int percent = std::stoi(percents, nullptr, 10);
- if (percentd >= 0 && percentd <= 1.0) {
- percentd_ = percentd;
+ if (percent >= 0 && percent <= 100) {
+ signal_strength_percent_ = percent;
} else {
- LOG(DEBUG) << "out of bound signal strength: " << percentd;
+ LOG(DEBUG) << "out of bound signal strength percent: " << percent;
return;
}
@@ -1255,9 +1244,7 @@ void NetworkService::HandleReceiveRemoteSignal(const Client& client,
}
void NetworkService::OnSignalStrengthChanged() {
- applySignalPercentage(percentd_);
- auto command = GetSignalStrength();
- SendUnsolicitedCommand(command);
+ SendUnsolicitedCommand(BuildCSQCommandResponse(GetCurrentSignalStrength()));
}
NetworkService::RegistrationState NetworkService::GetVoiceRegistrationState() const {
diff --git a/host/commands/modem_simulator/network_service.h b/host/commands/modem_simulator/network_service.h
index b64d4b981..68f0d5d00 100644
--- a/host/commands/modem_simulator/network_service.h
+++ b/host/commands/modem_simulator/network_service.h
@@ -20,6 +20,7 @@
#include "host/commands/modem_simulator/data_service.h"
#include "host/commands/modem_simulator/misc_service.h"
#include "host/commands/modem_simulator/modem_service.h"
+#include "host/commands/modem_simulator/network_service_constants.h"
#include "host/commands/modem_simulator/sim_service.h"
namespace cuttlefish {
@@ -82,10 +83,6 @@ class NetworkService : public ModemService, public std::enable_shared_from_this<
bool IsHasNetwork();
void UpdateRegisterState(RegistrationState state);
void AdjustSignalStrengthValue(int& value, const std::pair<int, int>& range);
- void SetSignalStrengthValue(int& value, const std::pair<int, int>& range,
- double percentd);
- std::string GetSignalStrength();
- void applySignalPercentage(double percentd);
MiscService* misc_service_ = nullptr;
SimService* sim_service_ = nullptr;
@@ -202,61 +199,37 @@ class NetworkService : public ModemService, public std::enable_shared_from_this<
* Reference: 3GPP TS 138.215 section 5.1.*, 3GPP TS 38.133 section 10.1.16.1.
* Range [-23, 40], INT_MAX means invalid/unreported. */
- // Default invalid value
- SignalStrength():
- gsm_rssi(99), // [0, 31]
- gsm_ber(0), // [7, 99]
- cdma_dbm(125), // [0, 120]
- cdma_ecio(165), // [0, 160]
- evdo_dbm(125), // [0, 120]
- evdo_ecio(165), // [0, 160]
- evdo_snr(-1), // [0, 8]
- lte_rssi(99), // [0, 31]
- lte_rsrp(-1), // [43,140]
- lte_rsrq(-5), // [-3,34]
- lte_rssnr(-205), // [-200, 300]
- lte_cqi(-1), // [0, 15]
- lte_ta(-1), // [0, 1282]
- tdscdma_rscp(99), // [0, 96]
- wcdma_rssi(99), // [0, 31]
- wcdma_ber(0), // [7, 99]
- nr_ss_rsrp(0), // [44, 140]
- nr_ss_rsrq(0), // [3, 10]
- nr_ss_sinr(45), // [-23,40]
- nr_csi_rsrp(0), // [44, 140]
- nr_csi_rsrq(0), // [3, 20]
- nr_csi_sinr(30) // [-23, 23]
- {}
-
- // After radio power on, off, or set network mode, reset to invalid value
- void Reset() {
- gsm_rssi = INT_MAX;
- gsm_ber = INT_MAX;
- cdma_dbm = INT_MAX;
- cdma_ecio = INT_MAX;
- evdo_dbm = INT_MAX;
- evdo_ecio = INT_MAX;
- evdo_snr = INT_MAX;
- lte_rssi = INT_MAX;
- lte_rsrp = INT_MAX;
- lte_rsrq = INT_MAX;
- lte_rssnr = INT_MAX;
- lte_cqi = INT_MAX;
- lte_ta = INT_MAX;
- tdscdma_rscp = INT_MAX;
- wcdma_rssi = INT_MAX;
- wcdma_ber = INT_MAX;
- nr_ss_rsrp = INT_MAX;
- nr_ss_rsrq = INT_MAX;
- nr_ss_sinr = INT_MAX;
- nr_csi_rsrp = INT_MAX;
- nr_csi_rsrq = INT_MAX;
- nr_csi_sinr = INT_MAX;
- }
+ SignalStrength()
+ : gsm_rssi(kRssiUnknownValue),
+ gsm_ber(kBerUnknownValue),
+ cdma_dbm(kDbmUnknownValue),
+ cdma_ecio(kEcioUnknownValue),
+ evdo_dbm(kDbmUnknownValue),
+ evdo_ecio(kEcioUnknownValue),
+ evdo_snr(kSnrUnknownValue),
+ lte_rssi(kRssiUnknownValue),
+ lte_rsrp(INT_MAX),
+ lte_rsrq(INT_MAX),
+ lte_rssnr(INT_MAX),
+ lte_cqi(INT_MAX),
+ lte_ta(INT_MAX),
+ tdscdma_rscp(INT_MAX),
+ wcdma_rssi(kRssiUnknownValue),
+ wcdma_ber(kBerUnknownValue),
+ nr_ss_rsrp(INT_MAX),
+ nr_ss_rsrq(INT_MAX),
+ nr_ss_sinr(INT_MAX),
+ nr_csi_rsrp(INT_MAX),
+ nr_csi_rsrq(INT_MAX),
+ nr_csi_sinr(INT_MAX) {}
};
- double percentd_{0.8};
- SignalStrength signal_strength_;
+ int signal_strength_percent_{80};
+
+ static int GetValueInRange(const std::pair<int, int>& range, int percent);
+ static std::string BuildCSQCommandResponse(
+ const SignalStrength& signal_strength);
+ SignalStrength GetCurrentSignalStrength();
/* Data / voice Registration State */
struct NetworkRegistrationStatus {
diff --git a/host/commands/modem_simulator/network_service_constants.h b/host/commands/modem_simulator/network_service_constants.h
new file mode 100644
index 000000000..49d03190e
--- /dev/null
+++ b/host/commands/modem_simulator/network_service_constants.h
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2021 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.
+
+#pragma once
+
+namespace cuttlefish {
+
+// Constants representing a not known or not detectable value for different
+// signal strength parameters.
+constexpr int kRssiUnknownValue = 99;
+constexpr int kBerUnknownValue = 99;
+constexpr int kDbmUnknownValue = -1;
+constexpr int kEcioUnknownValue = -1;
+constexpr int kSnrUnknownValue = -1;
+
+// Constants representing the range of values of different signal strength
+// parameters.
+constexpr auto kRssiRange = std::make_pair(4, 30);
+constexpr auto kDbmRange = std::make_pair(-120, -4);
+constexpr auto kRsrpRange = std::make_pair(-140, -44);
+
+} // namespace cuttlefish
diff --git a/host/commands/modem_simulator/sms_service.cpp b/host/commands/modem_simulator/sms_service.cpp
index 6a61e7b8b..0caee166c 100644
--- a/host/commands/modem_simulator/sms_service.cpp
+++ b/host/commands/modem_simulator/sms_service.cpp
@@ -294,21 +294,20 @@ void SmsService::HandleSendSMSPDU(const Client& client, std::string& command) {
port <= kRemotePortRange.second) {
auto remote_host_port = std::to_string(port);
if (GetHostId() == remote_host_port) { // Send SMS to local host port
- thread_looper_->PostWithDelay(
- std::chrono::seconds(1),
- makeSafeCallback<SmsService>(this, [&sms_pdu](SmsService* me) {
- me->HandleReceiveSMS(sms_pdu);
- }));
+ thread_looper_->Post(
+ makeSafeCallback<SmsService>(
+ this,
+ [&sms_pdu](SmsService* me) { me->HandleReceiveSMS(sms_pdu); }),
+ std::chrono::seconds(1));
} else { // Send SMS to remote host port
SendSmsToRemote(remote_host_port, sms_pdu);
}
} else if (sim_service_ && phone_number == sim_service_->GetPhoneNumber()) {
/* Local phone number */
- thread_looper_->PostWithDelay(
- std::chrono::seconds(1),
- makeSafeCallback<SmsService>(this, [sms_pdu](SmsService* me) {
- me->HandleReceiveSMS(sms_pdu);
- }));
+ thread_looper_->Post(
+ makeSafeCallback<SmsService>(
+ this, [sms_pdu](SmsService* me) { me->HandleReceiveSMS(sms_pdu); }),
+ std::chrono::seconds(1));
} /* else pretend send SMS success */
std::stringstream ss;
@@ -319,11 +318,12 @@ void SmsService::HandleSendSMSPDU(const Client& client, std::string& command) {
if (sms_pdu.IsNeededStatuReport()) {
int ref = message_reference_;
- thread_looper_->PostWithDelay(
- std::chrono::seconds(1),
- makeSafeCallback<SmsService>(this, [sms_pdu, ref](SmsService* me) {
- me->HandleSMSStatuReport(sms_pdu, ref);
- }));
+ thread_looper_->Post(
+ makeSafeCallback<SmsService>(this,
+ [sms_pdu, ref](SmsService* me) {
+ me->HandleSMSStatuReport(sms_pdu, ref);
+ }),
+ std::chrono::seconds(1));
}
}
diff --git a/host/commands/modem_simulator/thread_looper.cpp b/host/commands/modem_simulator/thread_looper.cpp
index d9df9df2d..cc6c39e95 100644
--- a/host/commands/modem_simulator/thread_looper.cpp
+++ b/host/commands/modem_simulator/thread_looper.cpp
@@ -42,8 +42,8 @@ ThreadLooper::Serial ThreadLooper::Post(Callback cb) {
return serial;
}
-ThreadLooper::Serial ThreadLooper::PostWithDelay(
- std::chrono::steady_clock::duration delay, Callback cb) {
+ThreadLooper::Serial ThreadLooper::Post(
+ Callback cb, std::chrono::steady_clock::duration delay) {
CHECK(cb != nullptr);
auto serial = next_serial_++;
diff --git a/host/commands/modem_simulator/thread_looper.h b/host/commands/modem_simulator/thread_looper.h
index dbfd20107..dcc4efbbe 100644
--- a/host/commands/modem_simulator/thread_looper.h
+++ b/host/commands/modem_simulator/thread_looper.h
@@ -60,7 +60,7 @@ class ThreadLooper {
typedef int32_t Serial;
Serial Post(Callback cb);
- Serial PostWithDelay(std::chrono::steady_clock::duration delay, Callback cb);
+ Serial Post(Callback cb, std::chrono::steady_clock::duration delay);
void Stop();
diff --git a/host/commands/modem_simulator/unittest/service_test.cpp b/host/commands/modem_simulator/unittest/service_test.cpp
index 58015864d..7808122ac 100644
--- a/host/commands/modem_simulator/unittest/service_test.cpp
+++ b/host/commands/modem_simulator/unittest/service_test.cpp
@@ -41,31 +41,31 @@ class ModemServiceTest : public ::testing::Test {
{
cuttlefish::CuttlefishConfig tmp_config_obj;
std::string config_file = tmp_test_dir + "/.cuttlefish_config.json";
- std::string instance_dir = tmp_test_dir + "/cuttlefish_runtime.1";
- fs::create_directories(instance_dir);
+ tmp_config_obj.set_root_dir(tmp_test_dir + "/cuttlefish");
tmp_config_obj.set_ril_dns("8.8.8.8");
std::vector<int> instance_nums;
for (int i = 0; i < 1; i++) {
instance_nums.push_back(cuttlefish::GetInstance() + i);
}
for (const auto &num : instance_nums) {
- auto instance = tmp_config_obj.ForInstance(num);
- instance.set_instance_dir(instance_dir);
+ tmp_config_obj.ForInstance(num); // Trigger creation in map
}
for (auto instance : tmp_config_obj.Instances()) {
+ fs::create_directories(instance.instance_dir());
if (!tmp_config_obj.SaveToFile(
instance.PerInstancePath("cuttlefish_config.json"))) {
LOG(ERROR) << "Unable to save copy config object";
return;
}
+ std::string icfilename =
+ instance.PerInstancePath("/iccprofile_for_sim0.xml");
+ std::ofstream offile(icfilename, std::ofstream::out);
+ offile << std::string(myiccfile);
+ offile.close();
+ fs::copy_file(instance.PerInstancePath("/cuttlefish_config.json"),
+ config_file, fs::copy_options::overwrite_existing);
}
- fs::copy_file(instance_dir + "/cuttlefish_config.json", config_file,
- fs::copy_options::overwrite_existing);
- std::string icfilename = instance_dir + "/iccprofile_for_sim0.xml";
- std::ofstream offile(icfilename, std::ofstream::out);
- offile << std::string(myiccfile);
- offile.close();
::setenv("CUTTLEFISH_CONFIG_FILE", config_file.c_str(), 1);
}
diff --git a/host/commands/run_cvd/launch.cc b/host/commands/run_cvd/launch.cc
index c8498180a..af48064f5 100644
--- a/host/commands/run_cvd/launch.cc
+++ b/host/commands/run_cvd/launch.cc
@@ -32,6 +32,7 @@
#include "host/libs/config/inject.h"
#include "host/libs/config/known_paths.h"
#include "host/libs/vm_manager/crosvm_builder.h"
+#include "host/libs/vm_manager/crosvm_manager.h"
#include "host/libs/vm_manager/vm_manager.h"
namespace cuttlefish {
@@ -134,11 +135,40 @@ class KernelLogMonitor : public CommandSource,
std::vector<SharedFD> event_pipe_read_ends_;
};
+class LogTeeCreator {
+ public:
+ INJECT(LogTeeCreator(const CuttlefishConfig::InstanceSpecific& instance))
+ : instance_(instance) {}
+
+ Command CreateLogTee(Command& cmd, const std::string& process_name) {
+ auto name_with_ext = process_name + "_logs.fifo";
+ auto logs_path = instance_.PerInstanceInternalPath(name_with_ext.c_str());
+ auto logs = SharedFD::Fifo(logs_path, 0666);
+ if (!logs->IsOpen()) {
+ LOG(FATAL) << "Failed to create fifo for " << process_name
+ << " output: " << logs->StrError();
+ }
+
+ cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, logs);
+ cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdErr, logs);
+
+ Command log_tee_cmd(HostBinaryPath("log_tee"));
+ log_tee_cmd.AddParameter("--process_name=", process_name);
+ log_tee_cmd.AddParameter("--log_fd_in=", logs);
+
+ return log_tee_cmd;
+ }
+
+ private:
+ const CuttlefishConfig::InstanceSpecific& instance_;
+};
+
class RootCanal : public CommandSource {
public:
INJECT(RootCanal(const CuttlefishConfig& config,
- const CuttlefishConfig::InstanceSpecific& instance))
- : config_(config), instance_(instance) {}
+ const CuttlefishConfig::InstanceSpecific& instance,
+ LogTeeCreator& log_tee))
+ : config_(config), instance_(instance), log_tee_(log_tee) {}
// CommandSource
std::vector<Command> Commands() override {
@@ -160,7 +190,10 @@ class RootCanal : public CommandSource {
command.AddParameter("--default_commands_file=",
instance_.rootcanal_default_commands_file());
- return single_element_emplace(std::move(command));
+ std::vector<Command> commands;
+ commands.emplace_back(log_tee_.CreateLogTee(command, "rootcanal"));
+ commands.emplace_back(std::move(command));
+ return commands;
}
// Feature
@@ -173,6 +206,7 @@ class RootCanal : public CommandSource {
const CuttlefishConfig& config_;
const CuttlefishConfig::InstanceSpecific& instance_;
+ LogTeeCreator& log_tee_;
};
class LogcatReceiver : public CommandSource, public DiagnosticInformation {
@@ -396,7 +430,7 @@ class BluetoothConnector : public CommandSource {
// CommandSource
std::vector<Command> Commands() override {
- Command command(HostBinaryPath("bt_connector"));
+ Command command(DefaultHostArtifactsPath("bin/bt_connector"));
command.AddParameter("-bt_out=", fifos_[0]);
command.AddParameter("-bt_in=", fifos_[1]);
command.AddParameter("-hci_port=", instance_.rootcanal_hci_port());
@@ -628,8 +662,9 @@ class ConsoleForwarder : public CommandSource, public DiagnosticInformation {
class WmediumdServer : public CommandSource {
public:
INJECT(WmediumdServer(const CuttlefishConfig& config,
- const CuttlefishConfig::InstanceSpecific& instance))
- : config_(config), instance_(instance) {}
+ const CuttlefishConfig::InstanceSpecific& instance,
+ LogTeeCreator& log_tee))
+ : config_(config), instance_(instance), log_tee_(log_tee) {}
// CommandSource
std::vector<Command> Commands() override {
@@ -637,7 +672,11 @@ class WmediumdServer : public CommandSource {
cmd.AddParameter("-u", config_.vhost_user_mac80211_hwsim());
cmd.AddParameter("-a", config_.wmediumd_api_server_socket());
cmd.AddParameter("-c", config_path_);
- return single_element_emplace(std::move(cmd));
+
+ std::vector<Command> commands;
+ commands.emplace_back(log_tee_.CreateLogTee(cmd, "wmediumd"));
+ commands.emplace_back(std::move(cmd));
+ return commands;
}
// Feature
@@ -676,6 +715,7 @@ class WmediumdServer : public CommandSource {
const CuttlefishConfig& config_;
const CuttlefishConfig::InstanceSpecific& instance_;
+ LogTeeCreator& log_tee_;
std::string config_path_;
};
@@ -704,12 +744,13 @@ class VmmCommands : public CommandSource {
class OpenWrt : public CommandSource {
public:
INJECT(OpenWrt(const CuttlefishConfig& config,
- const CuttlefishConfig::InstanceSpecific& instance))
- : config_(config), instance_(instance) {}
+ const CuttlefishConfig::InstanceSpecific& instance,
+ LogTeeCreator& log_tee))
+ : config_(config), instance_(instance), log_tee_(log_tee) {}
// CommandSource
std::vector<Command> Commands() override {
- constexpr auto crosvm_for_ap_socket = "crosvm_for_ap_control.sock";
+ constexpr auto crosvm_for_ap_socket = "ap_control.sock";
CrosvmBuilder ap_cmd;
ap_cmd.SetBinary(config_.crosvm_binary());
@@ -752,9 +793,15 @@ class OpenWrt : public CommandSource {
ap_cmd.Cmd().AddParameter("--params=\"root=" + config_.ap_image_dev_path() +
"\"");
+ auto kernel_logs_path = instance_.PerInstanceLogPath("crosvm_openwrt.log");
+ ap_cmd.AddSerialConsoleReadOnly(kernel_logs_path);
+
ap_cmd.Cmd().AddParameter(config_.ap_kernel_image());
- return single_element_emplace(std::move(ap_cmd.Cmd()));
+ std::vector<Command> commands;
+ commands.emplace_back(log_tee_.CreateLogTee(ap_cmd.Cmd(), "openwrt"));
+ commands.emplace_back(std::move(ap_cmd.Cmd()));
+ return commands;
}
// Feature
@@ -763,7 +810,8 @@ class OpenWrt : public CommandSource {
#ifndef ENFORCE_MAC80211_HWSIM
return false;
#else
- return instance_.start_ap();
+ return instance_.start_ap() &&
+ config_.vm_manager() == vm_manager::CrosvmManager::name();
#endif
}
@@ -773,6 +821,7 @@ class OpenWrt : public CommandSource {
const CuttlefishConfig& config_;
const CuttlefishConfig::InstanceSpecific& instance_;
+ LogTeeCreator& log_tee_;
};
using PublicDeps = fruit::Required<const CuttlefishConfig, VmManager,
diff --git a/host/commands/run_cvd/launch_streamer.cpp b/host/commands/run_cvd/launch_streamer.cpp
index 1b1b05583..ab6a5f68f 100644
--- a/host/commands/run_cvd/launch_streamer.cpp
+++ b/host/commands/run_cvd/launch_streamer.cpp
@@ -213,6 +213,17 @@ class WebRtcServer : public virtual CommandSource,
commands.emplace_back(std::move(sig_server));
}
+ if (instance_.start_webrtc_sig_server_proxy()) {
+ Command sig_proxy(WebRtcSigServerProxyBinary());
+ sig_proxy.AddParameter("-use_secure_http=",
+ config_.sig_server_secure() ? "true" : "false");
+ if (!config_.webrtc_certs_dir().empty()) {
+ sig_proxy.AddParameter("-certs_dir=", config_.webrtc_certs_dir());
+ }
+ sig_proxy.AddParameter("-http_server_port=", config_.sig_server_port());
+ commands.emplace_back(std::move(sig_proxy));
+ }
+
auto stopper = [host_socket = std::move(host_socket_)](Subprocess* proc) {
struct timeval timeout;
timeout.tv_sec = 3;
diff --git a/host/commands/secure_env/secure_env.cpp b/host/commands/secure_env/secure_env.cpp
index 2c96a392e..114a7bd05 100644
--- a/host/commands/secure_env/secure_env.cpp
+++ b/host/commands/secure_env/secure_env.cpp
@@ -110,9 +110,9 @@ std::thread StartKernelEventMonitor(SharedFD kernel_events_fd) {
});
}
-fruit::Component<Tpm> SecureEnvComponent() {
- return fruit::createComponent().registerProvider(
- []() -> Tpm* { // fruit will take ownership
+fruit::Component<TpmResourceManager> SecureEnvComponent() {
+ return fruit::createComponent()
+ .registerProvider([]() -> Tpm* { // fruit will take ownership
if (FLAGS_tpm_impl == "in_memory") {
return new InProcessTpm();
} else if (FLAGS_tpm_impl == "host_device") {
@@ -121,7 +121,27 @@ fruit::Component<Tpm> SecureEnvComponent() {
LOG(FATAL) << "Unknown TPM implementation: " << FLAGS_tpm_impl;
abort();
}
- });
+ })
+ .registerProvider([](Tpm* tpm) {
+ if (tpm->TctiContext() == nullptr) {
+ LOG(FATAL) << "Unable to connect to TPM implementation.";
+ }
+ ESYS_CONTEXT* esys_ptr = nullptr;
+ std::unique_ptr<ESYS_CONTEXT, void (*)(ESYS_CONTEXT*)> esys(
+ nullptr, [](ESYS_CONTEXT* esys) { Esys_Finalize(&esys); });
+ auto rc = Esys_Initialize(&esys_ptr, tpm->TctiContext(), nullptr);
+ if (rc != TPM2_RC_SUCCESS) {
+ LOG(FATAL) << "Could not initialize esys: " << Tss2_RC_Decode(rc)
+ << " (" << rc << ")";
+ }
+ esys.reset(esys_ptr);
+ return esys;
+ })
+ .registerProvider(
+ [](std::unique_ptr<ESYS_CONTEXT, void (*)(ESYS_CONTEXT*)>& esys) {
+ return new TpmResourceManager(
+ esys.get()); // fruit will take ownership
+ });
}
} // namespace
@@ -131,26 +151,8 @@ int SecureEnvMain(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
keymaster::SoftKeymasterLogger km_logger;
- fruit::Injector<Tpm> injector(SecureEnvComponent);
- Tpm* tpm = injector.get<Tpm*>();
-
- if (tpm->TctiContext() == nullptr) {
- LOG(FATAL) << "Unable to connect to TPM implementation.";
- }
-
- std::unique_ptr<TpmResourceManager> resource_manager;
- std::unique_ptr<ESYS_CONTEXT, void(*)(ESYS_CONTEXT*)> esys(
- nullptr, [](ESYS_CONTEXT* esys) { Esys_Finalize(&esys); });
- if (FLAGS_keymint_impl == "tpm" || FLAGS_gatekeeper_impl == "tpm") {
- ESYS_CONTEXT* esys_ptr = nullptr;
- auto rc = Esys_Initialize(&esys_ptr, tpm->TctiContext(), nullptr);
- if (rc != TPM2_RC_SUCCESS) {
- LOG(FATAL) << "Could not initialize esys: " << Tss2_RC_Decode(rc)
- << " (" << rc << ")";
- }
- esys.reset(esys_ptr);
- resource_manager.reset(new TpmResourceManager(esys.get()));
- }
+ fruit::Injector<TpmResourceManager> injector(SecureEnvComponent);
+ TpmResourceManager* resource_manager = injector.get<TpmResourceManager*>();
std::unique_ptr<GatekeeperStorage> secure_storage;
std::unique_ptr<GatekeeperStorage> insecure_storage;
@@ -176,7 +178,7 @@ int SecureEnvMain(int argc, char** argv) {
if (FLAGS_keymint_impl == "software") {
// TODO: See if this is the right KM version.
keymaster_context.reset(new keymaster::PureSoftKeymasterContext(
- keymaster::KmVersion::KEYMINT_1, KM_SECURITY_LEVEL_SOFTWARE));
+ keymaster::KmVersion::KEYMINT_2, KM_SECURITY_LEVEL_SOFTWARE));
} else if (FLAGS_keymint_impl == "tpm") {
keymaster_context.reset(
new TpmKeymasterContext(*resource_manager, *keymaster_enforcement));
@@ -188,7 +190,7 @@ int SecureEnvMain(int argc, char** argv) {
// taking ownership.
keymaster::AndroidKeymaster keymaster{
new ProxyKeymasterContext(*keymaster_context), kOperationTableSize,
- keymaster::MessageVersion(keymaster::KmVersion::KEYMINT_1,
+ keymaster::MessageVersion(keymaster::KmVersion::KEYMINT_2,
0 /* km_date */)};
auto keymaster_in = DupFdFlag(FLAGS_keymaster_fd_in);
diff --git a/host/commands/secure_env/tpm_attestation_record.cpp b/host/commands/secure_env/tpm_attestation_record.cpp
index 39d8383f3..70239c9d8 100644
--- a/host/commands/secure_env/tpm_attestation_record.cpp
+++ b/host/commands/secure_env/tpm_attestation_record.cpp
@@ -43,7 +43,7 @@ VerifiedBootParams MakeVbParams() {
} // namespace
TpmAttestationRecordContext::TpmAttestationRecordContext()
- : keymaster::AttestationContext(::keymaster::KmVersion::KEYMINT_1),
+ : keymaster::AttestationContext(::keymaster::KmVersion::KEYMINT_2),
vb_params_(MakeVbParams()),
unique_id_hbk_(16) {
RAND_bytes(unique_id_hbk_.data(), unique_id_hbk_.size());
diff --git a/host/commands/stop/main.cc b/host/commands/stop/main.cc
index 671f97b06..feeca9eda 100644
--- a/host/commands/stop/main.cc
+++ b/host/commands/stop/main.cc
@@ -53,11 +53,10 @@
namespace cuttlefish {
namespace {
-std::set<std::string> FallbackPaths() {
+std::set<std::string> FallbackDirs() {
std::set<std::string> paths;
std::string parent_path = StringFromEnv("HOME", ".");
paths.insert(parent_path + "/cuttlefish_assembly");
- paths.insert(parent_path + "/cuttlefish_assembly/*");
std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(parent_path.c_str()), closedir);
for (auto entity = readdir(dir.get()); entity != nullptr; entity = readdir(dir.get())) {
@@ -65,43 +64,26 @@ std::set<std::string> FallbackPaths() {
if (!android::base::StartsWith(subdir, "cuttlefish_runtime.")) {
continue;
}
- auto instance_dir = parent_path + "/" + subdir;
- // Add the instance directory
- paths.insert(instance_dir);
- // Add files in instance dir
- paths.insert(instance_dir + "/*");
- // Add files in the tombstone directory
- paths.insert(instance_dir + "/tombstones/*");
- // Add files in the internal directory
- paths.insert(instance_dir + "/" + std::string(kInternalDirName) + "/*");
- // Add files in the shared directory
- paths.insert(instance_dir + "/" + std::string(kSharedDirName) + "/*");
+ paths.insert(parent_path + "/" + subdir);
}
return paths;
}
-std::set<std::string> PathsForInstance(const CuttlefishConfig& config,
- const CuttlefishConfig::InstanceSpecific instance) {
+std::set<std::string> DirsForInstance(
+ const CuttlefishConfig& config,
+ const CuttlefishConfig::InstanceSpecific instance) {
return {
- config.assembly_dir(),
- config.assembly_dir() + "/*",
- instance.instance_dir(),
- instance.PerInstancePath("*"),
- instance.PerInstancePath("tombstones"),
- instance.PerInstancePath("tombstones/*"),
- instance.instance_internal_dir(),
- instance.PerInstanceInternalPath("*"),
- instance.PerInstancePath(kSharedDirName),
- instance.PerInstancePath(kSharedDirName) + "/*",
+ config.assembly_dir(),
+ instance.instance_dir(),
};
}
// Gets a set of the possible process groups of a previous launch
-std::set<pid_t> GetCandidateProcessGroups(const std::set<std::string>& paths) {
+std::set<pid_t> GetCandidateProcessGroups(const std::set<std::string>& dirs) {
std::stringstream cmd;
cmd << "lsof -t 2>/dev/null";
- for (const auto& path : paths) {
- cmd << " " << path;
+ for (const auto& dir : dirs) {
+ cmd << " +D " << dir;
}
std::string cmd_str = cmd.str();
std::shared_ptr<FILE> cmd_out(popen(cmd_str.c_str(), "r"), pclose);
@@ -125,10 +107,10 @@ std::set<pid_t> GetCandidateProcessGroups(const std::set<std::string>& paths) {
return ret;
}
-int FallBackStop(const std::set<std::string>& paths) {
+int FallBackStop(const std::set<std::string>& dirs) {
auto exit_code = 1; // Having to fallback is an error
- auto process_groups = GetCandidateProcessGroups(paths);
+ auto process_groups = GetCandidateProcessGroups(dirs);
for (auto pgid: process_groups) {
LOG(INFO) << "Sending SIGKILL to process group " << pgid;
auto retval = killpg(pgid, SIGKILL);
@@ -199,7 +181,7 @@ int StopInstance(const CuttlefishConfig& config,
std::int32_t wait_for_launcher) {
bool res = CleanStopInstance(instance, wait_for_launcher);
if (!res) {
- return FallBackStop(PathsForInstance(config, instance));
+ return FallBackStop(DirsForInstance(config, instance));
}
return 0;
}
@@ -251,7 +233,7 @@ int StopCvdMain(int argc, char** argv) {
auto config = CuttlefishConfig::Get();
if (!config) {
LOG(ERROR) << "Failed to obtain config object";
- return FallBackStop(FallbackPaths());
+ return FallBackStop(FallbackDirs());
}
int ret = 0;
diff --git a/host/commands/wmediumd_control/main.cpp b/host/commands/wmediumd_control/main.cpp
index cf202387d..205d91804 100644
--- a/host/commands/wmediumd_control/main.cpp
+++ b/host/commands/wmediumd_control/main.cpp
@@ -36,7 +36,12 @@ const std::string usageMessage =
" set SNR between two nodes. (0 <= snr <= 255)\n\n"
" reload_config [path]\n"
" force reload wmediumd configuration file\n\n"
- " if path is not specified, reload current configuration file\n\n";
+ " if path is not specified, reload current configuration file\n\n"
+ " start_pcap path\n"
+ " start packet capture and save capture result to file.\n"
+ " file format is pcap capture format.\n\n"
+ " stop_pcap\n"
+ " stop packet capture\n\n";
DEFINE_string(wmediumd_api_server, "",
"Unix socket path of wmediumd api server");
@@ -120,6 +125,26 @@ bool HandleReloadConfigCommand(cuttlefish::WmediumdController& client,
}
}
+bool HandleStartPcapCommand(cuttlefish::WmediumdController& client,
+ const std::vector<std::string>& args) {
+ if (args.size() != 2) {
+ LOG(ERROR) << "error: you must provide only 1 option(path)";
+ return false;
+ }
+
+ return client.StartPcap(args[1]);
+}
+
+bool HandleStopPcapCommand(cuttlefish::WmediumdController& client,
+ const std::vector<std::string>& args) {
+ if (args.size() != 1) {
+ LOG(ERROR) << "error: you must not provide option";
+ return false;
+ }
+
+ return client.StopPcap();
+}
+
int main(int argc, char** argv) {
gflags::SetUsageMessage(usageMessage);
gflags::ParseCommandLineFlags(&argc, &argv, true);
@@ -162,6 +187,8 @@ int main(int argc, char** argv) {
const std::vector<std::string>&)>>{{
{"set_snr", HandleSetSnrCommand},
{"reload_config", HandleReloadConfigCommand},
+ {"start_pcap", HandleStartPcapCommand},
+ {"stop_pcap", HandleStopPcapCommand},
}};
if (commandMap.find(args[0]) == std::end(commandMap)) {
diff --git a/host/frontend/operator_proxy/Android.bp b/host/frontend/operator_proxy/Android.bp
new file mode 100644
index 000000000..9f6bc6ed1
--- /dev/null
+++ b/host/frontend/operator_proxy/Android.bp
@@ -0,0 +1,43 @@
+//
+// Copyright (C) 2021 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary_host {
+ name: "operator_proxy",
+ srcs: [
+ "main.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libcrypto",
+ "libjsoncpp",
+ "libssl",
+ "libcuttlefish_fs",
+ ],
+ static_libs: [
+ "libcap",
+ "libgflags",
+ "libcuttlefish_utils",
+ "libcuttlefish_host_config",
+ "libwebsockets",
+ ],
+ defaults: ["cuttlefish_buildhost_only"],
+}
+
+
diff --git a/host/frontend/operator_proxy/main.cpp b/host/frontend/operator_proxy/main.cpp
new file mode 100644
index 000000000..77b143aed
--- /dev/null
+++ b/host/frontend/operator_proxy/main.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include <android-base/logging.h>
+#include <gflags/gflags.h>
+#include <libwebsockets.h>
+
+#include "common/libs/utils/files.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/config/logging.h"
+
+DEFINE_int32(http_server_port, 8443, "The port for the http server");
+DEFINE_bool(use_secure_http, true, "Whether to use HTTPS or HTTP.");
+DEFINE_string(certs_dir,
+ cuttlefish::DefaultHostArtifactsPath("usr/share/webrtc/certs"),
+ "Directory to certificates. It must contain a server.crt file, a "
+ "server.key file and (optionally) a CA.crt file.");
+DEFINE_string(operator_addr, "localhost:1080/",
+ "The address of the operator server to proxy");
+
+int main(int argc, char** argv) {
+ cuttlefish::DefaultSubprocessLogging(argv);
+ ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+ struct lws_context_creation_info info;
+ struct lws_context* context;
+
+ lws_set_log_level(LLL_ERR, NULL);
+
+ struct lws_http_mount mount = {
+ .mount_next = nullptr,
+ .mountpoint = "/",
+ .mountpoint_len = static_cast<uint8_t>(1),
+ .origin = FLAGS_operator_addr.c_str(),
+ .def = nullptr,
+ .protocol = nullptr,
+ .cgienv = nullptr,
+ .extra_mimetypes = nullptr,
+ .interpret = nullptr,
+ .cgi_timeout = 0,
+ .cache_max_age = 0,
+ .auth_mask = 0,
+ .cache_reusable = 0,
+ .cache_revalidate = 0,
+ .cache_intermediaries = 0,
+ .origin_protocol = LWSMPRO_HTTP, // reverse proxy
+ .basic_auth_login_file = nullptr,
+ };
+
+ memset(&info, 0, sizeof info);
+ info.port = FLAGS_http_server_port;
+ info.mounts = &mount;
+
+ // These vars need to be in scope for the call to lws_create-context below
+ std::string cert_file = FLAGS_certs_dir + "/server.crt";
+ std::string key_file = FLAGS_certs_dir + "/server.key";
+ std::string ca_file = FLAGS_certs_dir + "/CA.crt";
+ if (FLAGS_use_secure_http) {
+ info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
+ info.ssl_cert_filepath = cert_file.c_str();
+ info.ssl_private_key_filepath = key_file.c_str();
+ if (cuttlefish::FileExists(ca_file)) {
+ info.ssl_ca_filepath = ca_file.c_str();
+ }
+ }
+
+ context = lws_create_context(&info);
+ CHECK(context) << "Unable to create reverse proxy";
+ LOG(VERBOSE) << "Started reverse proxy to signaling server";
+ while (lws_service(context, 0) >= 0) {
+ }
+ lws_context_destroy(context);
+ return 0;
+}
diff --git a/host/frontend/webrtc/client/client.html b/host/frontend/webrtc/client/client.html
index ac56bf62c..4039c2691 100644
--- a/host/frontend/webrtc/client/client.html
+++ b/host/frontend/webrtc/client/client.html
@@ -25,8 +25,12 @@
<body>
<div id="loader"></div>
+ <div id="error-message-div">
+ <h3 id="error-message" class="hidden">
+ <span class="material-icons close-btn">close</span>
+ </h3>
+ </div>
<section id="device-connection">
- <div id="error-message-div"><h3 id="error-message" class="error"></h3></div>
<div id='header'>
<div id='app-controls'>
<div id="keyboard-capture-control" title="Capture Keyboard"></div>
@@ -44,7 +48,7 @@
<button id='device-details-button' title='Device Details' class='material-icons'>
settings
</button>
- <button id='bluetooth-console-button' title='Bluetooth console' class='material-icons'>
+ <button id='bluetooth-modal-button' title='Bluetooth console' class='material-icons'>
settings_bluetooth
</button>
</div>
@@ -62,16 +66,85 @@
<h3>Hardware Configuration</h3>
<span id='device-details-hardware'>unknown</span>
</div>
- <div id='bluetooth-console-modal' class='modal'>
- <div id='bluetooth-console-modal-header' class='modal-header'>
- <h2>Bluetooth Console</h2>
- <button id='bluetooth-console-close' title='Close' class='material-icons modal-close'>close</button>
+
+ <div id='bluetooth-modal' class='modal-wrapper'>
+ <div id='bluetooth-prompt' class='modal'>
+ <div id='bluetooth-prompt-header' class='modal-header'>
+ <h2>Bluetooth</h2>
+ <button id='bluetooth-prompt-close' title='Close' class='material-icons modal-close'>close</button>
+ </div>
+ <div>
+ <div id='bluetooth-prompt-text' class='bluetooth-text'>
+ We have enabled a BT Wizard to simplify adding a<br>bluetooth device.<br>
+ Alternatively, you can enter the BT Console if you<br>want to exercise full control.</div><br>
+ <div class='bluetooth-button'>
+ <button id='bluetooth-prompt-wizard' title='Start Wizard' class='modal-button-highlight'>Start Wizard</button>
+ <button id='bluetooth-prompt-list' title='Device List' class='modal-button'>Device List</button>
+ <button id='bluetooth-prompt-console' title='BT Console' class='modal-button'>BT Console</button>
+ </div>
+ </div>
</div>
- <div>
- <table>
- <tr><td colspan='2'><textarea id='bluetooth-console-view' readonly rows='10' cols='60'></textarea></td></tr>
- <tr><td width='1'><p id='bluetooth-console-cmd-label'>Command:</p></td><td width='100'><input id='bluetooth-console-input' type='text'></input></td></tr>
- </table>
+ <div id='bluetooth-wizard' class='modal'>
+ <div id='bluetooth-wizard-modal-header' class='modal-header'>
+ <h2>BT Wizard</h2>
+ <button id='bluetooth-wizard-close' title='Close' class='material-icons modal-close'>close</button>
+ </div>
+ <div>
+ <div class='bluetooth-text-field'><input type="text" id='bluetooth-wizard-name' placeholder="Device Name"></input></div>
+ <div class='bluetooth-drop-down'>
+ <select id='bluetooth-wizard-type' validate-mac="true" required>
+ <option value="beacon">Beacon</option>
+ <option value="beacon_swarm">Beacon Swarm</option>
+ <!-- Disabled because they were "started but never finished" (according to mylesgw@)
+ <option value="car_kit">Car Kit</option>
+ <option value="classic">Classic</option> -->
+ <option value="keyboard">Keyboard</option>
+ <option value="remote_loopback">Remote Loopback</option>
+ <option value="scripted_beacon">Scripted Beacon</option>
+ <!-- Disabled because it will never show up in the UI
+ <option value="sniffer">Sniffer</option> -->
+ </select>
+ </div>
+ <div class='bluetooth-text-field'><input type="text" id='bluetooth-wizard-mac' placeholder="Device MAC" validate-mac="true" required></input><span></span></div>
+ <div class='bluetooth-button'>
+ <button id='bluetooth-wizard-device' title='Add Device' class='modal-button-highlight' disabled>Add Device</button>
+ <button id='bluetooth-wizard-cancel' title='Cancel' class='modal-button'>Cancel</button>
+ </div>
+ </div>
+ </div>
+ <div id='bluetooth-wizard-confirm' class='modal'>
+ <div id='bluetooth-wizard-confirm-header' class='modal-header'>
+ <h2>BT Wizard</h2>
+ <button id='bluetooth-wizard-confirm-close' title='Close' class='material-icons modal-close'>close</button>
+ </div>
+ <div id='bluetooth-wizard-text' class='bluetooth-text'>Device added. See device details below.</div><br>
+ <div class='bluetooth-text'>
+ <p>Name: <b>GKeyboard</b></p>
+ <p>Type: <b>Keyboard</b></p>
+ <p>MAC Addr: <b>be:ac:01:55:00:03</b></p>
+ </div>
+ <div class='bluetooth-button'><button id='bluetooth-wizard-another' title='Add Another' class='modal-button-highlight'>Add Another</button></div>
+ </div>
+ <div id='bluetooth-list' class='modal'>
+ <div id='bluetooth-list-header' class='modal-header'>
+ <h2>Device List</h2>
+ <button id='bluetooth-list-close' title='Close' class='material-icons modal-close'>close</button>
+ </div>
+ <div class='bluetooth-text'>
+ <div><button title="Delete" data-device-id="delete" class="bluetooth-list-trash material-icons">delete</button>GKeyboard | Keyboard | be:ac:01:55:00:03</div>
+ <div><button title="Delete" data-device-id="delete" class="bluetooth-list-trash material-icons">delete</button>GHeadphones | Audio | dc:fa:32:00:55:02</div>
+ </div>
+ </div>
+ <div id='bluetooth-console' class='modal'>
+ <div id='bluetooth-console-modal-header' class='modal-header'>
+ <h2>BT Console</h2>
+ <button id='bluetooth-console-close' title='Close' class='material-icons modal-close'>close</button>
+ </div>
+ <div>
+ <div colspan='2'><textarea id='bluetooth-console-view' readonly rows='10' cols='60'></textarea></div>
+ <div width='1'><p id='bluetooth-console-cmd-label'>Command:</p></div>
+ <div width='100'><input id='bluetooth-console-input' type='text'></input></div>
+ </div>
</div>
</div>
<script src="js/adb.js"></script>
diff --git a/host/frontend/webrtc/client/js/app.js b/host/frontend/webrtc/client/js/app.js
index 2d6975701..2b9a178e9 100644
--- a/host/frontend/webrtc/client/js/app.js
+++ b/host/frontend/webrtc/client/js/app.js
@@ -40,18 +40,30 @@ async function ConnectDevice(deviceId, serverConnector) {
return deviceConnection;
}
-function showWarning(msg) {
+function setupMessages() {
+ let closeBtn = document.querySelector('#error-message .close-btn');
+ closeBtn.addEventListener('click', evt => {
+ evt.target.parentElement.className = 'hidden';
+ });
+}
+
+function showMessage(msg, className) {
let element = document.getElementById('error-message');
- element.className = 'warning';
- element.textContent = msg;
- element.style.visibility = 'visible';
+ if (element.childNodes.length < 2) {
+ // First time, no text node yet
+ element.insertAdjacentText('afterBegin', msg);
+ } else {
+ element.childNodes[0].data = msg;
+ }
+ element.className = className;
+}
+
+function showWarning(msg) {
+ showMessage(msg, 'warning');
}
function showError(msg) {
- let element = document.getElementById('error-message');
- element.className = 'error';
- element.textContent = msg;
- element.style.visibility = 'visible';
+ showMessage(msg, 'error');
}
@@ -88,6 +100,8 @@ class DeviceControlApp {
#displayDescriptions = [];
#buttons = {};
#recording = {};
+ #phys = {};
+ #deviceCount = 0;
constructor(deviceConnection) {
this.#deviceConnection = deviceConnection;
@@ -138,12 +152,49 @@ class DeviceControlApp {
this.#buttons['volumeup'] = createControlPanelButton(
'volumeup', 'Volume Up', 'volume_up',
evt => this.#onControlPanelButton(evt));
+
createModalButton(
'device-details-button', 'device-details-modal',
'device-details-close');
createModalButton(
- 'bluetooth-console-button', 'bluetooth-console-modal',
- 'bluetooth-console-close');
+ 'bluetooth-modal-button', 'bluetooth-prompt',
+ 'bluetooth-prompt-close');
+ createModalButton(
+ 'bluetooth-prompt-wizard', 'bluetooth-wizard',
+ 'bluetooth-wizard-close', 'bluetooth-prompt');
+ createModalButton(
+ 'bluetooth-wizard-device', 'bluetooth-wizard-confirm',
+ 'bluetooth-wizard-confirm-close', 'bluetooth-wizard');
+ createModalButton(
+ 'bluetooth-wizard-another', 'bluetooth-wizard',
+ 'bluetooth-wizard-close', 'bluetooth-wizard-confirm');
+ createModalButton(
+ 'bluetooth-prompt-list', 'bluetooth-list',
+ 'bluetooth-list-close', 'bluetooth-prompt');
+ createModalButton(
+ 'bluetooth-prompt-console', 'bluetooth-console',
+ 'bluetooth-console-close', 'bluetooth-prompt');
+ createModalButton(
+ 'bluetooth-wizard-cancel', 'bluetooth-prompt',
+ 'bluetooth-wizard-close', 'bluetooth-wizard');
+
+ positionModal('device-details-button', 'bluetooth-modal');
+ positionModal('device-details-button', 'bluetooth-prompt');
+ positionModal('device-details-button', 'bluetooth-wizard');
+ positionModal('device-details-button', 'bluetooth-wizard-confirm');
+ positionModal('device-details-button', 'bluetooth-list');
+ positionModal('device-details-button', 'bluetooth-console');
+
+ createButtonListener('bluetooth-prompt-list', null, this.#deviceConnection,
+ evt => this.#onRootCanalCommand(this.#deviceConnection, "list", evt));
+ createButtonListener('bluetooth-wizard-device', null, this.#deviceConnection,
+ evt => this.#onRootCanalCommand(this.#deviceConnection, "add", evt));
+ createButtonListener('bluetooth-list-trash', null, this.#deviceConnection,
+ evt => this.#onRootCanalCommand(this.#deviceConnection, "del", evt));
+ createButtonListener('bluetooth-prompt-wizard', null, this.#deviceConnection,
+ evt => this.#onRootCanalCommand(this.#deviceConnection, "list", evt));
+ createButtonListener('bluetooth-wizard-another', null, this.#deviceConnection,
+ evt => this.#onRootCanalCommand(this.#deviceConnection, "list", evt));
if (this.#deviceConnection.description.custom_control_panel_buttons.length >
0) {
@@ -194,10 +245,8 @@ class DeviceControlApp {
let playPromise = deviceAudio.play();
if (playPromise !== undefined) {
playPromise.catch(error => {
- // Autoplay not allowed, mute and try again
- deviceAudio.muted = true;
- deviceAudio.play();
- showWarning("Audio playback is muted");
+ showWarning(
+ 'Audio failed to play automatically, click on the play button to activate it');
});
}
})
@@ -229,10 +278,50 @@ class DeviceControlApp {
createRootcanalMessage(command, args));
});
this.#deviceConnection.onBluetoothMessage(msg => {
- bluetoothConsole.addLine(decodeRootcanalMessage(msg));
+ let decoded = decodeRootcanalMessage(msg);
+ let deviceCount = btUpdateDeviceList(decoded);
+ if (deviceCount > 0) {
+ this.#deviceCount = deviceCount;
+ createButtonListener('bluetooth-list-trash', null, this.#deviceConnection,
+ evt => this.#onRootCanalCommand(this.#deviceConnection, "del", evt));
+ }
+ btUpdateAdded(decoded);
+ let phyList = btParsePhys(decoded);
+ if (phyList) {
+ this.#phys = phyList;
+ }
+ bluetoothConsole.addLine(decoded);
});
}
+ #onRootCanalCommand(deviceConnection, cmd, evt) {
+ if (cmd == "list") {
+ deviceConnection.sendBluetoothMessage(createRootcanalMessage("list", []));
+ }
+ if (cmd == "del") {
+ let id = evt.srcElement.getAttribute("data-device-id");
+ deviceConnection.sendBluetoothMessage(createRootcanalMessage("del", [id]));
+ deviceConnection.sendBluetoothMessage(createRootcanalMessage("list", []));
+ }
+ if (cmd == "add") {
+ let name = document.getElementById('bluetooth-wizard-name').value;
+ let type = document.getElementById('bluetooth-wizard-type').value;
+ if (type == "remote_loopback") {
+ deviceConnection.sendBluetoothMessage(createRootcanalMessage("add", [type]));
+ } else {
+ let mac = document.getElementById('bluetooth-wizard-mac').value;
+ deviceConnection.sendBluetoothMessage(createRootcanalMessage("add", [type, mac]));
+ }
+ let phyId = this.#phys["LOW_ENERGY"].toString();
+ if (type == "remote_loopback") {
+ phyId = this.#phys["BR_EDR"].toString();
+ }
+ let devId = this.#deviceCount.toString();
+ this.#deviceCount++;
+ deviceConnection.sendBluetoothMessage(createRootcanalMessage("add_device_to_phy", [devId, phyId]));
+ }
+ }
+
#showWebrtcError() {
document.getElementById('status-message').className = 'error';
document.getElementById('status-message').textContent =
@@ -911,18 +1000,18 @@ class DeviceControlApp {
window.addEventListener("load", async evt => {
try {
+ setupMessages();
let connectorModule = await import('./server_connector.js');
let deviceConnection = await ConnectDevice(
connectorModule.deviceId(), await connectorModule.createConnector());
let deviceControlApp = new DeviceControlApp(deviceConnection);
deviceControlApp.start();
- document.getElementById('loader').style.display = 'none';
document.getElementById('device-connection').style.display = 'block';
} catch(err) {
console.error('Unable to connect: ', err);
showError(
'No connection to the guest device. ' +
'Please ensure the WebRTC process on the host machine is active.');
- throw err;
}
+ document.getElementById('loader').style.display = 'none';
});
diff --git a/host/frontend/webrtc/client/js/controls.js b/host/frontend/webrtc/client/js/controls.js
index f4bb10511..268c7d830 100644
--- a/host/frontend/webrtc/client/js/controls.js
+++ b/host/frontend/webrtc/client/js/controls.js
@@ -49,6 +49,152 @@ function createToggleControl(elm, iconName, onChangeCb) {
};
}
+function createButtonListener(button_id_class, func,
+ deviceConnection, listener) {
+ let buttons = [];
+ let ele = document.getElementById(button_id_class);
+ if (ele != null) {
+ buttons.push(ele);
+ } else {
+ buttons = document.getElementsByClassName(button_id_class);
+ }
+ for (var button of buttons) {
+ if (func != null) {
+ button.onclick = func;
+ }
+ button.addEventListener('mousedown', listener);
+ }
+}
+
+function createInputListener(input_id, func, listener) {
+ input = document.getElementById(input_id);
+ if (func != null) {
+ input.oninput = func;
+ }
+ input.addEventListener('input', listener);
+}
+
+function validateMacAddress(val) {
+ var regex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
+ return (regex.test(val));
+}
+
+function validateMacWrapper() {
+ let type = document.getElementById('bluetooth-wizard-type').value;
+ let button = document.getElementById("bluetooth-wizard-device");
+ let macField = document.getElementById('bluetooth-wizard-mac');
+ if (this.id == 'bluetooth-wizard-type') {
+ if (type == "remote_loopback") {
+ button.disabled = false;
+ macField.setCustomValidity('');
+ macField.disabled = true;
+ macField.required = false;
+ macField.placeholder = 'N/A';
+ macField.value = '';
+ return;
+ }
+ }
+ macField.disabled = false;
+ macField.required = true;
+ macField.placeholder = 'Device MAC';
+ if (validateMacAddress($(macField).val())) {
+ button.disabled = false;
+ macField.setCustomValidity('');
+ } else {
+ button.disabled = true;
+ macField.setCustomValidity('MAC address invalid');
+ }
+}
+
+$('[validate-mac]').bind('input', validateMacWrapper);
+$('[validate-mac]').bind('select', validateMacWrapper);
+
+function parseDevice(device) {
+ let id, name, mac;
+ var regex = /([0-9]+):([^@ ]*)(@(([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})))?/;
+ if (regex.test(device)) {
+ let regexMatches = device.match(regex);
+ id = regexMatches[1];
+ name = regexMatches[2];
+ mac = regexMatches[4];
+ }
+ if (mac === undefined) {
+ mac = "";
+ }
+ return [id, name, mac];
+}
+
+function btUpdateAdded(devices) {
+ let deviceArr = devices.split('\r\n');
+ let [id, name, mac] = parseDevice(deviceArr[0]);
+ if (name) {
+ let div = document.getElementById('bluetooth-wizard-confirm').getElementsByClassName('bluetooth-text')[1];
+ div.innerHTML = "";
+ div.innerHTML += "<p>Name: <b>" + id + "</b></p>";
+ div.innerHTML += "<p>Type: <b>" + name + "</b></p>";
+ div.innerHTML += "<p>MAC Addr: <b>" + mac + "</b></p>";
+ return true;
+ }
+ return false;
+}
+
+function parsePhy(phy) {
+ let id = phy.substring(0, phy.indexOf(":"));
+ phy = phy.substring(phy.indexOf(":") + 1);
+ let name = phy.substring(0, phy.indexOf(":"));
+ let devices = phy.substring(phy.indexOf(":") + 1);
+ return [id, name, devices];
+}
+
+function btParsePhys(phys) {
+ if (phys.indexOf("Phys:") < 0) {
+ return null;
+ }
+ let phyDict = {};
+ phys = phys.split('Phys:')[1];
+ let phyArr = phys.split('\r\n');
+ for (var phy of phyArr.slice(1)) {
+ phy = phy.trim();
+ if (phy.length == 0 || phy.indexOf("deleted") >= 0) {
+ continue;
+ }
+ let [id, name, devices] = parsePhy(phy);
+ phyDict[name] = id;
+ }
+ return phyDict;
+}
+
+function btUpdateDeviceList(devices) {
+ let deviceArr = devices.split('\r\n');
+ if (deviceArr[0].indexOf("Devices:") >= 0) {
+ let div = document.getElementById('bluetooth-list').getElementsByClassName('bluetooth-text')[0];
+ div.innerHTML = "";
+ let count = 0;
+ for (var device of deviceArr.slice(1)) {
+ if (device.indexOf("Phys:") >= 0) {
+ break;
+ }
+ count++;
+ if (device.indexOf("deleted") >= 0) {
+ continue;
+ }
+ let [id, name, mac] = parseDevice(device);
+ let innerDiv = '<div><button title="Delete" data-device-id="'
+ innerDiv += id;
+ innerDiv += '" class="bluetooth-list-trash material-icons">delete</button>';
+ innerDiv += name;
+ if (mac) {
+ innerDiv += " | "
+ innerDiv += mac;
+ }
+ innerDiv += '</div>';
+ div.innerHTML += innerDiv;
+ }
+ return count;
+ }
+ return -1;
+}
+
function createControlPanelButton(
command, title, icon_name, listener,
parent_id = 'control-panel-default-buttons') {
@@ -71,15 +217,23 @@ function createControlPanelButton(
return button;
}
-function createModalButton(button_id, modal_id, close_id) {
+function positionModal(button_id, modal_id) {
const modalButton = document.getElementById(button_id);
const modalDiv = document.getElementById(modal_id);
- const modalHeader = modalDiv.querySelector('.modal-header');
- const modalClose = document.getElementById(close_id);
// Position the modal to the right of the show modal button.
modalDiv.style.top = modalButton.offsetTop;
modalDiv.style.left = modalButton.offsetWidth + 30;
+}
+
+function createModalButton(button_id, modal_id, close_id, hide_id) {
+ const modalButton = document.getElementById(button_id);
+ const modalDiv = document.getElementById(modal_id);
+ const modalHeader = modalDiv.querySelector('.modal-header');
+ const modalClose = document.getElementById(close_id);
+ const modalDivHide = document.getElementById(hide_id);
+
+ positionModal(button_id, modal_id);
function showHideModal(show) {
if (show) {
@@ -89,6 +243,9 @@ function createModalButton(button_id, modal_id, close_id) {
modalButton.classList.remove('modal-button-opened')
modalDiv.style.display = 'none';
}
+ if (modalDivHide != null) {
+ modalDivHide.style.display = 'none';
+ }
}
// Allow the show modal button to toggle the modal,
modalButton.addEventListener(
diff --git a/host/frontend/webrtc/client/style.css b/host/frontend/webrtc/client/style.css
index fa0055a07..93aaa05fa 100644
--- a/host/frontend/webrtc/client/style.css
+++ b/host/frontend/webrtc/client/style.css
@@ -79,6 +79,13 @@ body {
font-family: 'Open Sans', sans-serif;
padding: 10px;
margin: 10px;
+ border-radius: 10px;
+}
+#error-message .close-btn {
+ float: right;
+ cursor: pointer;
+}
+#error-message.hidden {
display: none;
}
#error-message.warning {
@@ -150,6 +157,38 @@ body {
outline: none;
background-color: transparent;
}
+.modal-button, .modal-button-highlight {
+ background: #e8eaed; /* Google grey 200 */
+ border-radius: 10px;
+ box-shadow: 1px 1px #444444;
+ padding: 10px 20px;
+ color: #000000;
+ display: inline-block;
+ font: normal bold 14px/1 "Open Sans", sans-serif;
+ text-align: center;
+}
+#bluetooth-wizard-mac:valid {
+ border: 2px solid black;
+}
+#bluetooth-wizard-mac:invalid {
+ border: 2px solid red;
+}
+#bluetooth-wizard-mac:invalid + span::before {
+ font-weight: bold;
+ content: 'X';
+ color: red;
+}
+#bluetooth-wizard-mac:valid + span::before {
+ font-weight: bold;
+ content: 'OK';
+ color: green;
+}
+.modal-button {
+ background: #e8eaed; /* Google grey 200 */
+}
+.modal-button-highlight {
+ background: #f4cccc;
+}
#device-details-modal span {
white-space: pre;
}
@@ -159,6 +198,38 @@ body {
#bluetooth-console-cmd-label {
color: white;
}
+.bluetooth-text, .bluetooth-text-bold, .bluetooth-text-field input {
+ font: normal 18px/1 "Open Sans", sans-serif;
+}
+.bluetooth-text, .bluetooth-text-bold {
+ color: white;
+}
+.bluetooth-text-bold {
+ font: bold;
+}
+.bluetooth-button {
+ text-align: center;
+}
+.bluetooth-drop-down select {
+ font: normal 18px/1 "Open Sans", sans-serif;
+ color: black;
+ width: 500px;
+ margin: 5px;
+ rows: 10;
+ columns: 60;
+}
+.bluetooth-text-field input {
+ color: black;
+ width: 500px;
+ margin: 5px;
+ rows: 10;
+ columns: 60;
+}
+.bluetooth-list-trash {
+ background: #00000000;
+ border: 0px;
+ color: #ffffff;
+}
.control-panel-column {
width: 50px;
diff --git a/host/frontend/webrtc/lib/server_connection.cpp b/host/frontend/webrtc/lib/server_connection.cpp
index b04c90f36..b9c90d72c 100644
--- a/host/frontend/webrtc/lib/server_connection.cpp
+++ b/host/frontend/webrtc/lib/server_connection.cpp
@@ -18,29 +18,59 @@
#include <android-base/logging.h>
#include <libwebsockets.h>
-class WsConnectionContextImpl;
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/fs/shared_select.h"
+#include "common/libs/utils/files.h"
-class WsConnectionImpl : public WsConnection,
- public std::enable_shared_from_this<WsConnectionImpl> {
+namespace cuttlefish {
+namespace webrtc_streaming {
+
+// ServerConnection over Unix socket
+class UnixServerConnection : public ServerConnection {
+ public:
+ UnixServerConnection(SharedFD conn,
+ std::weak_ptr<ServerConnectionObserver> observer,
+ SharedFD thread_notifier_);
+ ~UnixServerConnection() override;
+
+ bool Send(const Json::Value& msg) override;
+
+ private:
+ void Connect() override;
+ void ReadLoop();
+
+ SharedFD conn_;
+ std::mutex write_mtx_;
+ std::weak_ptr<ServerConnectionObserver> observer_;
+ // The event fd must be declared before the thread to ensure it's initialized
+ // before the thread starts and is safe to be accessed from it.
+ SharedFD thread_notifier_;
+ std::atomic_bool running_ = true;
+ std::thread thread_;
+};
+
+// ServerConnection using websockets
+class WsConnectionContext;
+
+class WsConnection : public std::enable_shared_from_this<WsConnection> {
public:
struct CreateConnectionSul {
lws_sorted_usec_list_t sul = {};
- std::weak_ptr<WsConnectionImpl> weak_this;
+ std::weak_ptr<WsConnection> weak_this;
};
- WsConnectionImpl(
- int port, const std::string& addr, const std::string& path,
- Security secure,
- const std::vector<std::pair<std::string, std::string>>& headers,
- std::weak_ptr<WsConnectionObserver> observer,
- std::shared_ptr<WsConnectionContextImpl> context);
+ WsConnection(int port, const std::string& addr, const std::string& path,
+ ServerConfig::Security secure,
+ const std::vector<std::pair<std::string, std::string>>& headers,
+ std::weak_ptr<ServerConnectionObserver> observer,
+ std::shared_ptr<WsConnectionContext> context);
- ~WsConnectionImpl() override;
+ ~WsConnection();
- void Connect() override;
- void ConnectInner();
+ void Connect();
+ bool Send(const Json::Value& msg);
- bool Send(const uint8_t* data, size_t len, bool binary = false) override;
+ void ConnectInner();
void OnError(const std::string& error);
void OnReceive(const uint8_t* data, size_t len, bool is_binary);
@@ -66,41 +96,43 @@ class WsConnectionImpl : public WsConnection,
std::vector<uint8_t> buffer_;
bool is_binary_;
};
+ bool Send(const uint8_t* data, size_t len, bool binary = false);
CreateConnectionSul extended_sul_;
struct lws* wsi_;
const int port_;
const std::string addr_;
const std::string path_;
- const Security security_;
+ const ServerConfig::Security security_;
const std::vector<std::pair<std::string, std::string>> headers_;
- std::weak_ptr<WsConnectionObserver> observer_;
+ std::weak_ptr<ServerConnectionObserver> observer_;
// each element contains the data to be sent and whether it's binary or not
std::deque<WsBuffer> write_queue_;
std::mutex write_queue_mutex_;
// The connection object should not outlive the context object. This reference
// guarantees it.
- std::shared_ptr<WsConnectionContextImpl> context_;
+ std::shared_ptr<WsConnectionContext> context_;
};
-class WsConnectionContextImpl
- : public WsConnectionContext,
- public std::enable_shared_from_this<WsConnectionContextImpl> {
+class WsConnectionContext
+ : public std::enable_shared_from_this<WsConnectionContext> {
public:
- WsConnectionContextImpl(struct lws_context* lws_ctx);
- ~WsConnectionContextImpl() override;
+ static std::shared_ptr<WsConnectionContext> Create();
- std::shared_ptr<WsConnection> CreateConnection(
+ WsConnectionContext(struct lws_context* lws_ctx);
+ ~WsConnectionContext();
+
+ std::unique_ptr<ServerConnection> CreateConnection(
int port, const std::string& addr, const std::string& path,
- WsConnection::Security secure,
- std::weak_ptr<WsConnectionObserver> observer,
- const std::vector<std::pair<std::string, std::string>>& headers) override;
+ ServerConfig::Security secure,
+ std::weak_ptr<ServerConnectionObserver> observer,
+ const std::vector<std::pair<std::string, std::string>>& headers);
- void RememberConnection(void*, std::weak_ptr<WsConnectionImpl>);
+ void RememberConnection(void*, std::weak_ptr<WsConnection>);
void ForgetConnection(void*);
- std::shared_ptr<WsConnectionImpl> GetConnection(void*);
+ std::shared_ptr<WsConnection> GetConnection(void*);
struct lws_context* lws_context() {
return lws_context_;
@@ -109,12 +141,148 @@ class WsConnectionContextImpl
private:
void Start();
- std::map<void*, std::weak_ptr<WsConnectionImpl>> weak_by_ptr_;
+ std::map<void*, std::weak_ptr<WsConnection>> weak_by_ptr_;
std::mutex map_mutex_;
struct lws_context* lws_context_;
std::thread message_loop_;
};
+std::unique_ptr<ServerConnection> ServerConnection::Connect(
+ const ServerConfig& conf,
+ std::weak_ptr<ServerConnectionObserver> observer) {
+ std::unique_ptr<ServerConnection> ret;
+ // If the provided address points to an existing UNIX socket in the file
+ // system connect to it, otherwise assume it's a network address and connect
+ // using websockets
+ if (FileIsSocket(conf.addr)) {
+ auto conn = SharedFD::SocketLocalClient(conf.addr, false, SOCK_SEQPACKET);
+ if (!conn->IsOpen()) {
+ LOG(ERROR) << "Failed to connect to unix socket: " << conn->StrError();
+ if (auto o = observer.lock(); o) {
+ o->OnError("Failed to connect to unix socket");
+ }
+ // Safe to return null here since OnOpen wasn't called and therefore the
+ // connection won't be accessed.
+ return nullptr;
+ }
+ auto thread_notifier = SharedFD::Event();
+ if (!thread_notifier->IsOpen()) {
+ LOG(ERROR) << "Failed to create eventfd for background thread";
+ return nullptr;
+ }
+ ret.reset(new UnixServerConnection(conn, observer, thread_notifier));
+ } else {
+ // This can be a local variable since the ws connection will keep a
+ // reference to it.
+ auto ws_context = WsConnectionContext::Create();
+ CHECK(ws_context) << "Failed to create websocket context";
+ ret = ws_context->CreateConnection(conf.port, conf.addr, conf.path,
+ conf.security, observer,
+ conf.http_headers);
+ }
+ ret->Connect();
+ return ret;
+}
+
+void ServerConnection::Reconnect() { Connect(); }
+
+// UnixServerConnection implementation
+
+UnixServerConnection::UnixServerConnection(
+ SharedFD conn, std::weak_ptr<ServerConnectionObserver> observer,
+ SharedFD thread_notifier)
+ : conn_(conn),
+ observer_(observer),
+ thread_notifier_(thread_notifier),
+ thread_([this]() { ReadLoop(); }) {}
+
+UnixServerConnection::~UnixServerConnection() {
+ if (!thread_notifier_->IsOpen()) {
+ // The thread won't be running if this isn't open
+ return;
+ }
+ running_ = false;
+ if (thread_notifier_->EventfdWrite(1) < 0) {
+ LOG(ERROR) << "Failed to notify background thread, this thread may block";
+ }
+ if (thread_.joinable()) {
+ thread_.join();
+ }
+}
+
+bool UnixServerConnection::Send(const Json::Value& msg) {
+ Json::StreamWriterBuilder factory;
+ auto str = Json::writeString(factory, msg);
+ std::lock_guard<std::mutex> lock(write_mtx_);
+ auto res =
+ conn_->Send(reinterpret_cast<const uint8_t*>(str.c_str()), str.size(), 0);
+ if (res < 0) {
+ LOG(ERROR) << "Failed to send data to signaling server: "
+ << conn_->StrError();
+ // Don't call OnError() here, the receiving thread probably did it already
+ // or is about to do it.
+ }
+ // A SOCK_SEQPACKET unix socket will send the entire message or fail, but it
+ // won't send a partial message.
+ return res == str.size();
+}
+
+void UnixServerConnection::Connect() {
+ if (auto observer = observer_.lock(); observer) {
+ observer->OnOpen();
+ }
+}
+
+void UnixServerConnection::ReadLoop() {
+ if (!thread_notifier_->IsOpen()) {
+ LOG(ERROR) << "The UnixServerConnection's background thread is unable to "
+ "receive notifications so it can't run";
+ return;
+ }
+ std::vector<uint8_t> buffer(4096, 0);
+ while (running_) {
+ SharedFDSet rset;
+ rset.Set(thread_notifier_);
+ rset.Set(conn_);
+ auto res = Select(&rset, nullptr, nullptr, nullptr);
+ if (res < 0) {
+ LOG(ERROR) << "Failed to select from background thread";
+ break;
+ }
+ if (rset.IsSet(thread_notifier_)) {
+ eventfd_t val;
+ auto res = thread_notifier_->EventfdRead(&val);
+ if (res < 0) {
+ LOG(ERROR) << "Error reading from event fd: "
+ << thread_notifier_->StrError();
+ break;
+ }
+ }
+ if (rset.IsSet(conn_)) {
+ auto size = conn_->Recv(buffer.data(), 0, MSG_TRUNC | MSG_PEEK);
+ if (size > buffer.size()) {
+ // Enlarge enough to accommodate size bytes and be a multiple of 4096
+ auto new_size = (size + 4095) & ~4095;
+ buffer.resize(new_size);
+ }
+ auto res = conn_->Recv(buffer.data(), buffer.size(), MSG_TRUNC);
+ if (res < 0) {
+ LOG(ERROR) << "Failed to read from server: " << conn_->StrError();
+ if (auto observer = observer_.lock(); observer) {
+ observer->OnError(conn_->StrError());
+ }
+ return;
+ }
+ auto observer = observer_.lock();
+ if (observer) {
+ observer->OnReceive(buffer.data(), res, false);
+ }
+ }
+ }
+}
+
+// WsConnection implementation
+
int LwsCallback(struct lws* wsi, enum lws_callback_reasons reason, void* user,
void* in, size_t len);
void CreateConnectionCallback(lws_sorted_usec_list_t* sul);
@@ -152,23 +320,22 @@ std::shared_ptr<WsConnectionContext> WsConnectionContext::Create() {
if (!lws_ctx) {
return nullptr;
}
- return std::shared_ptr<WsConnectionContext>(
- new WsConnectionContextImpl(lws_ctx));
+ return std::shared_ptr<WsConnectionContext>(new WsConnectionContext(lws_ctx));
}
-WsConnectionContextImpl::WsConnectionContextImpl(struct lws_context* lws_ctx)
+WsConnectionContext::WsConnectionContext(struct lws_context* lws_ctx)
: lws_context_(lws_ctx) {
Start();
}
-WsConnectionContextImpl::~WsConnectionContextImpl() {
+WsConnectionContext::~WsConnectionContext() {
lws_context_destroy(lws_context_);
if (message_loop_.joinable()) {
message_loop_.join();
}
}
-void WsConnectionContextImpl::Start() {
+void WsConnectionContext::Start() {
message_loop_ = std::thread([this]() {
for (;;) {
if (lws_service(lws_context_, 0) < 0) {
@@ -178,18 +345,33 @@ void WsConnectionContextImpl::Start() {
});
}
-std::shared_ptr<WsConnection> WsConnectionContextImpl::CreateConnection(
+// This wrapper is needed because the ServerConnection objects are meant to be
+// referenced by std::unique_ptr but WsConnection needs to be referenced by
+// std::shared_ptr because it's also (weakly) referenced by the websocket
+// thread.
+class WsConnectionWrapper : public ServerConnection {
+ public:
+ WsConnectionWrapper(std::shared_ptr<WsConnection> conn) : conn_(conn) {}
+
+ bool Send(const Json::Value& msg) override { return conn_->Send(msg); }
+
+ private:
+ void Connect() override { return conn_->Connect(); }
+ std::shared_ptr<WsConnection> conn_;
+};
+
+std::unique_ptr<ServerConnection> WsConnectionContext::CreateConnection(
int port, const std::string& addr, const std::string& path,
- WsConnection::Security security,
- std::weak_ptr<WsConnectionObserver> observer,
+ ServerConfig::Security security,
+ std::weak_ptr<ServerConnectionObserver> observer,
const std::vector<std::pair<std::string, std::string>>& headers) {
- return std::shared_ptr<WsConnection>(new WsConnectionImpl(
- port, addr, path, security, headers, observer, shared_from_this()));
+ return std::unique_ptr<ServerConnection>(
+ new WsConnectionWrapper(std::make_shared<WsConnection>(
+ port, addr, path, security, headers, observer, shared_from_this())));
}
-std::shared_ptr<WsConnectionImpl> WsConnectionContextImpl::GetConnection(
- void* raw) {
- std::shared_ptr<WsConnectionImpl> connection;
+std::shared_ptr<WsConnection> WsConnectionContext::GetConnection(void* raw) {
+ std::shared_ptr<WsConnection> connection;
{
std::lock_guard<std::mutex> lock(map_mutex_);
if (weak_by_ptr_.count(raw) == 0) {
@@ -203,24 +385,24 @@ std::shared_ptr<WsConnectionImpl> WsConnectionContextImpl::GetConnection(
return connection;
}
-void WsConnectionContextImpl::RememberConnection(
- void* raw, std::weak_ptr<WsConnectionImpl> conn) {
+void WsConnectionContext::RememberConnection(void* raw,
+ std::weak_ptr<WsConnection> conn) {
std::lock_guard<std::mutex> lock(map_mutex_);
weak_by_ptr_.emplace(
- std::pair<void*, std::weak_ptr<WsConnectionImpl>>(raw, conn));
+ std::pair<void*, std::weak_ptr<WsConnection>>(raw, conn));
}
-void WsConnectionContextImpl::ForgetConnection(void* raw) {
+void WsConnectionContext::ForgetConnection(void* raw) {
std::lock_guard<std::mutex> lock(map_mutex_);
weak_by_ptr_.erase(raw);
}
-WsConnectionImpl::WsConnectionImpl(
+WsConnection::WsConnection(
int port, const std::string& addr, const std::string& path,
- Security security,
+ ServerConfig::Security security,
const std::vector<std::pair<std::string, std::string>>& headers,
- std::weak_ptr<WsConnectionObserver> observer,
- std::shared_ptr<WsConnectionContextImpl> context)
+ std::weak_ptr<ServerConnectionObserver> observer,
+ std::shared_ptr<WsConnectionContext> context)
: port_(port),
addr_(addr),
path_(path),
@@ -229,23 +411,22 @@ WsConnectionImpl::WsConnectionImpl(
observer_(observer),
context_(context) {}
-WsConnectionImpl::~WsConnectionImpl() {
+WsConnection::~WsConnection() {
context_->ForgetConnection(this);
// This will cause the callback to be called which will drop the connection
// after seeing the context doesn't remember this object
lws_callback_on_writable(wsi_);
}
-void WsConnectionImpl::Connect() {
+void WsConnection::Connect() {
memset(&extended_sul_.sul, 0, sizeof(extended_sul_.sul));
extended_sul_.weak_this = weak_from_this();
lws_sul_schedule(context_->lws_context(), 0, &extended_sul_.sul,
CreateConnectionCallback, 1);
}
-void WsConnectionImpl::AddHttpHeaders(unsigned char** p,
- unsigned char* end) const {
- for (const auto& header_entry: headers_) {
+void WsConnection::AddHttpHeaders(unsigned char** p, unsigned char* end) const {
+ for (const auto& header_entry : headers_) {
const auto& name = header_entry.first;
const auto& value = header_entry.second;
auto res = lws_add_http_header_by_name(
@@ -262,33 +443,32 @@ void WsConnectionImpl::AddHttpHeaders(unsigned char** p,
}
}
-void WsConnectionImpl::OnError(const std::string& error) {
+void WsConnection::OnError(const std::string& error) {
auto observer = observer_.lock();
if (observer) {
observer->OnError(error);
}
}
-void WsConnectionImpl::OnReceive(const uint8_t* data, size_t len,
- bool is_binary) {
+void WsConnection::OnReceive(const uint8_t* data, size_t len, bool is_binary) {
auto observer = observer_.lock();
if (observer) {
observer->OnReceive(data, len, is_binary);
}
}
-void WsConnectionImpl::OnOpen() {
+void WsConnection::OnOpen() {
auto observer = observer_.lock();
if (observer) {
observer->OnOpen();
}
}
-void WsConnectionImpl::OnClose() {
+void WsConnection::OnClose() {
auto observer = observer_.lock();
if (observer) {
observer->OnClose();
}
}
-void WsConnectionImpl::OnWriteable() {
+void WsConnection::OnWriteable() {
WsBuffer buffer;
{
std::lock_guard<std::mutex> lock(write_queue_mutex_);
@@ -307,7 +487,13 @@ void WsConnectionImpl::OnWriteable() {
}
}
-bool WsConnectionImpl::Send(const uint8_t* data, size_t len, bool binary) {
+bool WsConnection::Send(const Json::Value& msg) {
+ Json::StreamWriterBuilder factory;
+ auto str = Json::writeString(factory, msg);
+ return Send(reinterpret_cast<const uint8_t*>(str.c_str()), str.size());
+}
+
+bool WsConnection::Send(const uint8_t* data, size_t len, bool binary) {
if (!wsi_) {
LOG(WARNING) << "Send called on an uninitialized connection!!";
return false;
@@ -331,8 +517,8 @@ int LwsCallback(struct lws* wsi, enum lws_callback_reasons reason, void* user,
// when the connection was created. This function object should be used with
// care.
auto with_connection =
- [wsi, user](std::function<void(std::shared_ptr<WsConnectionImpl>)> cb) {
- auto context = reinterpret_cast<WsConnectionContextImpl*>(user);
+ [wsi, user](std::function<void(std::shared_ptr<WsConnection>)> cb) {
+ auto context = reinterpret_cast<WsConnectionContext*>(user);
auto connection = context->GetConnection(wsi);
if (!connection) {
return DROP;
@@ -343,36 +529,35 @@ int LwsCallback(struct lws* wsi, enum lws_callback_reasons reason, void* user,
switch (reason) {
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
- return with_connection(
- [in](std::shared_ptr<WsConnectionImpl> connection) {
- connection->OnError(in ? (char*)in : "(null)");
- });
+ return with_connection([in](std::shared_ptr<WsConnection> connection) {
+ connection->OnError(in ? (char*)in : "(null)");
+ });
case LWS_CALLBACK_CLIENT_RECEIVE:
return with_connection(
- [in, len, wsi](std::shared_ptr<WsConnectionImpl> connection) {
+ [in, len, wsi](std::shared_ptr<WsConnection> connection) {
connection->OnReceive((const uint8_t*)in, len,
lws_frame_is_binary(wsi));
});
case LWS_CALLBACK_CLIENT_ESTABLISHED:
- return with_connection([](std::shared_ptr<WsConnectionImpl> connection) {
+ return with_connection([](std::shared_ptr<WsConnection> connection) {
connection->OnOpen();
});
case LWS_CALLBACK_CLIENT_CLOSED:
- return with_connection([](std::shared_ptr<WsConnectionImpl> connection) {
+ return with_connection([](std::shared_ptr<WsConnection> connection) {
connection->OnClose();
});
case LWS_CALLBACK_CLIENT_WRITEABLE:
- return with_connection([](std::shared_ptr<WsConnectionImpl> connection) {
+ return with_connection([](std::shared_ptr<WsConnection> connection) {
connection->OnWriteable();
});
case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
return with_connection(
- [in, len](std::shared_ptr<WsConnectionImpl> connection) {
+ [in, len](std::shared_ptr<WsConnection> connection) {
auto p = reinterpret_cast<unsigned char**>(in);
auto end = (*p) + len;
connection->AddHttpHeaders(p, end);
@@ -391,8 +576,8 @@ int LwsCallback(struct lws* wsi, enum lws_callback_reasons reason, void* user,
}
void CreateConnectionCallback(lws_sorted_usec_list_t* sul) {
- std::shared_ptr<WsConnectionImpl> connection =
- reinterpret_cast<WsConnectionImpl::CreateConnectionSul*>(sul)
+ std::shared_ptr<WsConnection> connection =
+ reinterpret_cast<WsConnection::CreateConnectionSul*>(sul)
->weak_this.lock();
if (!connection) {
LOG(WARNING) << "The object was already destroyed by the time of the first "
@@ -402,7 +587,7 @@ void CreateConnectionCallback(lws_sorted_usec_list_t* sul) {
connection->ConnectInner();
}
-void WsConnectionImpl::ConnectInner() {
+void WsConnection::ConnectInner() {
struct lws_client_connect_info connect_info;
memset(&connect_info, 0, sizeof(connect_info));
@@ -414,15 +599,15 @@ void WsConnectionImpl::ConnectInner() {
connect_info.host = connect_info.address;
connect_info.origin = connect_info.address;
switch (security_) {
- case Security::kAllowSelfSigned:
+ case ServerConfig::Security::kAllowSelfSigned:
connect_info.ssl_connection = LCCSCF_ALLOW_SELFSIGNED |
LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK |
LCCSCF_USE_SSL;
break;
- case Security::kStrict:
+ case ServerConfig::Security::kStrict:
connect_info.ssl_connection = LCCSCF_USE_SSL;
break;
- case Security::kInsecure:
+ case ServerConfig::Security::kInsecure:
connect_info.ssl_connection = 0;
break;
}
@@ -444,3 +629,6 @@ void WsConnectionImpl::ConnectInner() {
LOG(ERROR) << "Connection failed!";
}
}
+
+} // namespace webrtc_streaming
+} // namespace cuttlefish
diff --git a/host/frontend/webrtc/lib/server_connection.h b/host/frontend/webrtc/lib/server_connection.h
index 1c03265f1..247681ef0 100644
--- a/host/frontend/webrtc/lib/server_connection.h
+++ b/host/frontend/webrtc/lib/server_connection.h
@@ -26,47 +26,65 @@
#include <thread>
#include <vector>
+#include <json/json.h>
#include <libwebsockets.h>
-class WsConnectionObserver {
- public:
- virtual ~WsConnectionObserver() = default;
- virtual void OnOpen() = 0;
- virtual void OnClose() = 0;
- virtual void OnError(const std::string& error) = 0;
- virtual void OnReceive(const uint8_t* msg, size_t length, bool is_binary) = 0;
-};
+namespace cuttlefish {
+namespace webrtc_streaming {
-class WsConnection {
- public:
+struct ServerConfig {
enum class Security {
kInsecure,
kAllowSelfSigned,
kStrict,
};
- virtual ~WsConnection() = default;
-
- virtual void Connect() = 0;
-
- virtual bool Send(const uint8_t* data, size_t len, bool binary = false) = 0;
+ // The ip address or domain name of the operator server.
+ std::string addr;
+ int port;
+ // The path component of the operator server's register url.
+ std::string path;
+ // The security level to use when connecting to the operator server.
+ Security security;
+ // A list of key value pairs to include as HTTP handshake headers when
+ // connecting to the operator.
+ std::vector<std::pair<std::string, std::string>> http_headers;
+};
- protected:
- WsConnection() = default;
+class ServerConnectionObserver {
+ public:
+ virtual ~ServerConnectionObserver() = default;
+ // Called when the connection to the server has been established. This is the
+ // cue to start using Send().
+ virtual void OnOpen() = 0;
+ virtual void OnClose() = 0;
+ // Called when the connection to the server has failed with an unrecoverable
+ // error.
+ virtual void OnError(const std::string& error) = 0;
+ virtual void OnReceive(const uint8_t* msg, size_t length, bool is_binary) = 0;
};
-class WsConnectionContext {
+// Represents a connection to the signaling server. When a connection is created
+// it connects with the server automatically but sends no info.
+class ServerConnection {
public:
- static std::shared_ptr<WsConnectionContext> Create();
+ static std::unique_ptr<ServerConnection> Connect(
+ const ServerConfig& conf,
+ std::weak_ptr<ServerConnectionObserver> observer);
+
+ // Destroying the connection will disconnect from the signaling server and
+ // release any open fds.
+ virtual ~ServerConnection() = default;
- virtual ~WsConnectionContext() = default;
+ // Sends data to the server encoded as JSON.
+ virtual bool Send(const Json::Value&) = 0;
- virtual std::shared_ptr<WsConnection> CreateConnection(
- int port, const std::string& addr, const std::string& path,
- WsConnection::Security secure,
- std::weak_ptr<WsConnectionObserver> observer,
- const std::vector<std::pair<std::string, std::string>>& headers = {}) = 0;
+ // makes re-connect request
+ virtual void Reconnect();
- protected:
- WsConnectionContext() = default;
+ private:
+ virtual void Connect() = 0;
};
+
+} // namespace webrtc_streaming
+} // namespace cuttlefish
diff --git a/host/frontend/webrtc/lib/streamer.cpp b/host/frontend/webrtc/lib/streamer.cpp
index 3d2e6449a..e99f014c6 100644
--- a/host/frontend/webrtc/lib/streamer.cpp
+++ b/host/frontend/webrtc/lib/streamer.cpp
@@ -64,13 +64,8 @@ constexpr auto kCustomControlPanelButtonsField = "custom_control_panel_buttons";
constexpr int kRegistrationRetries = 3;
constexpr int kRetryFirstIntervalMs = 1000;
-
-void SendJson(WsConnection* ws_conn, const Json::Value& data) {
- Json::StreamWriterBuilder factory;
- auto data_str = Json::writeString(factory, data);
- ws_conn->Send(reinterpret_cast<const uint8_t*>(data_str.c_str()),
- data_str.size());
-}
+constexpr int kReconnectRetries = 100;
+constexpr int kReconnectIntervalMs = 1000;
bool ParseMessage(const uint8_t* data, size_t length, Json::Value* msg_out) {
auto str = reinterpret_cast<const char*>(data);
@@ -139,10 +134,14 @@ class AudioDeviceModuleWrapper : public AudioSource {
} // namespace
-class Streamer::Impl : public WsConnectionObserver {
+
+class Streamer::Impl : public ServerConnectionObserver,
+ public std::enable_shared_from_this<ServerConnectionObserver> {
public:
std::shared_ptr<ClientHandler> CreateClientHandler(int client_id);
+ void Register(std::weak_ptr<OperatorObserver> observer);
+
void SendMessageToClient(int client_id, const Json::Value& msg);
void DestroyClientHandler(int client_id);
void SetupCameraForClient(int client_id);
@@ -160,7 +159,7 @@ class Streamer::Impl : public WsConnectionObserver {
// no need for extra synchronization mechanisms (mutex)
StreamerConfig config_;
OperatorServerConfig operator_config_;
- std::shared_ptr<WsConnection> server_connection_;
+ std::unique_ptr<ServerConnection> server_connection_;
std::shared_ptr<ConnectionObserverFactory> connection_observer_factory_;
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface>
peer_connection_factory_;
@@ -312,22 +311,7 @@ void Streamer::Register(std::weak_ptr<OperatorObserver> observer) {
// No need to block the calling thread on this, the observer will be notified
// when the connection is established.
impl_->signal_thread_->PostTask(RTC_FROM_HERE, [this, observer]() {
- impl_->operator_observer_ = observer;
- // This can be a local variable since the connection object will keep a
- // reference to it.
- auto ws_context = WsConnectionContext::Create();
- CHECK(ws_context) << "Failed to create websocket context";
- impl_->server_connection_ = ws_context->CreateConnection(
- impl_->config_.operator_server.port,
- impl_->config_.operator_server.addr,
- impl_->config_.operator_server.path,
- impl_->config_.operator_server.security, impl_,
- impl_->config_.operator_server.http_headers);
-
- CHECK(impl_->server_connection_)
- << "Unable to create websocket connection object";
-
- impl_->server_connection_->Connect();
+ impl_->Register(observer);
});
}
@@ -350,6 +334,21 @@ void Streamer::RecordDisplays(LocalRecorder& recorder) {
}
}
+void Streamer::Impl::Register(std::weak_ptr<OperatorObserver> observer) {
+ operator_observer_ = observer;
+ // When the connection is established the OnOpen function will be called where
+ // the registration will take place
+ if (!server_connection_) {
+ server_connection_ =
+ ServerConnection::Connect(config_.operator_server, weak_from_this());
+ } else {
+ // in case connection attempt is retried, just call Reconnect().
+ // Recreating server_connection_ object will destroy existing WSConnection
+ // object and task re-scheduling will fail
+ server_connection_->Reconnect();
+ }
+}
+
void Streamer::Impl::OnOpen() {
// Called from the websocket thread.
// Connected to operator.
@@ -417,7 +416,7 @@ void Streamer::Impl::OnOpen() {
}
device_info[kCustomControlPanelButtonsField] = custom_control_panel_buttons;
register_obj[cuttlefish::webrtc_signaling::kDeviceInfoField] = device_info;
- SendJson(server_connection_.get(), register_obj);
+ server_connection_->Send(register_obj);
// Do this last as OnRegistered() is user code and may take some time to
// complete (although it shouldn't...)
auto observer = operator_observer_.lock();
@@ -438,16 +437,27 @@ void Streamer::Impl::OnClose() {
observer->OnClose();
}
});
+ LOG(INFO) << "Trying to re-connect to operator..";
+ registration_retries_left_ = kReconnectRetries;
+ retry_interval_ms_ = kReconnectIntervalMs;
+ signal_thread_->PostDelayedTask(
+ RTC_FROM_HERE, [this]() { Register(operator_observer_); },
+ retry_interval_ms_);
}
void Streamer::Impl::OnError(const std::string& error) {
// Called from websocket thread.
if (registration_retries_left_) {
LOG(WARNING) << "Connection to operator failed (" << error << "), "
- << registration_retries_left_ << " retries left";
+ << registration_retries_left_ << " retries left"
+ << " (will retry in " << retry_interval_ms_ / 1000 << "s)";
--registration_retries_left_;
signal_thread_->PostDelayedTask(
- RTC_FROM_HERE, [this]() { server_connection_->Connect(); },
+ RTC_FROM_HERE,
+ [this]() {
+ // Need to reconnect and register again with operator
+ Register(operator_observer_);
+ },
retry_interval_ms_);
retry_interval_ms_ *= 2;
} else {
@@ -653,8 +663,8 @@ void Streamer::Impl::SendMessageToClient(int client_id,
cuttlefish::webrtc_signaling::kForwardType;
wrapper[cuttlefish::webrtc_signaling::kClientIdField] = client_id;
// This is safe to call from the webrtc threads because
- // WsConnection is thread safe
- SendJson(server_connection_.get(), wrapper);
+ // ServerConnection(s) are thread safe
+ server_connection_->Send(wrapper);
}
void Streamer::Impl::DestroyClientHandler(int client_id) {
diff --git a/host/frontend/webrtc/lib/streamer.h b/host/frontend/webrtc/lib/streamer.h
index 355c4dd8e..bc2297e07 100644
--- a/host/frontend/webrtc/lib/streamer.h
+++ b/host/frontend/webrtc/lib/streamer.h
@@ -44,18 +44,7 @@ struct StreamerConfig {
std::string device_id;
// The port on which the client files are being served
int client_files_port;
- struct {
- // The ip address or domain name of the operator server.
- std::string addr;
- int port;
- // The path component of the operator server's register url.
- std::string path;
- // The security level to use when connecting to the operator server.
- WsConnection::Security security;
- // A list of key value pairs to include as HTTP handshake headers when
- // connecting to the operator.
- std::vector<std::pair<std::string, std::string>> http_headers;
- } operator_server;
+ ServerConfig operator_server;
// The port ranges webrtc is allowed to use.
// [0,0] means all ports
std::pair<uint16_t, uint16_t> udp_port_range = {15550, 15558};
diff --git a/host/frontend/webrtc/main.cpp b/host/frontend/webrtc/main.cpp
index 3cfcf081f..b607dca2f 100644
--- a/host/frontend/webrtc/main.cpp
+++ b/host/frontend/webrtc/main.cpp
@@ -70,6 +70,7 @@ using cuttlefish::webrtc_streaming::LocalRecorder;
using cuttlefish::webrtc_streaming::Streamer;
using cuttlefish::webrtc_streaming::StreamerConfig;
using cuttlefish::webrtc_streaming::VideoSink;
+using cuttlefish::webrtc_streaming::ServerConfig;
class CfOperatorObserver
: public cuttlefish::webrtc_streaming::OperatorObserver {
@@ -226,11 +227,11 @@ int main(int argc, char** argv) {
if (cvd_config->sig_server_secure()) {
streamer_config.operator_server.security =
cvd_config->sig_server_strict()
- ? WsConnection::Security::kStrict
- : WsConnection::Security::kAllowSelfSigned;
+ ? ServerConfig::Security::kStrict
+ : ServerConfig::Security::kAllowSelfSigned;
} else {
streamer_config.operator_server.security =
- WsConnection::Security::kInsecure;
+ ServerConfig::Security::kInsecure;
}
if (!cvd_config->sig_server_headers_path().empty()) {
diff --git a/host/libs/config/bootconfig_args.cpp b/host/libs/config/bootconfig_args.cpp
index d34d5ca9f..0c1a02651 100644
--- a/host/libs/config/bootconfig_args.cpp
+++ b/host/libs/config/bootconfig_args.cpp
@@ -78,7 +78,8 @@ std::vector<std::string> BootconfigArgsFromConfig(
auto vmm = vm_manager::GetVmManager(config.vm_manager(), config.target_arch());
bootconfig_args.push_back(
vmm->ConfigureBootDevices(instance.virtual_disk_paths().size()));
- AppendVector(&bootconfig_args, vmm->ConfigureGpuMode(config.gpu_mode()));
+ AppendVector(&bootconfig_args, vmm->ConfigureGraphics(config.gpu_mode(),
+ config.hwcomposer()));
bootconfig_args.push_back(
concat("androidboot.serialno=", instance.serial_number()));
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 47b2bc0ca..0b0022f2f 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -91,6 +91,10 @@ const char* const kGpuModeGuestSwiftshader = "guest_swiftshader";
const char* const kGpuModeDrmVirgl = "drm_virgl";
const char* const kGpuModeGfxStream = "gfxstream";
+const char* const kHwComposerAuto = "auto";
+const char* const kHwComposerDrmMinigbm = "drm_minigbm";
+const char* const kHwComposerRanchu = "ranchu";
+
std::string DefaultEnvironmentPath(const char* environment_key,
const char* default_value,
const char* subpath) {
@@ -122,12 +126,12 @@ bool CuttlefishConfig::SaveFragment(const ConfigFragment& fragment) {
return true;
}
-static constexpr char kAssemblyDir[] = "assembly_dir";
-std::string CuttlefishConfig::assembly_dir() const {
- return (*dictionary_)[kAssemblyDir].asString();
+static constexpr char kRootDir[] = "root_dir";
+std::string CuttlefishConfig::root_dir() const {
+ return (*dictionary_)[kRootDir].asString();
}
-void CuttlefishConfig::set_assembly_dir(const std::string& assembly_dir) {
- (*dictionary_)[kAssemblyDir] = assembly_dir;
+void CuttlefishConfig::set_root_dir(const std::string& root_dir) {
+ (*dictionary_)[kRootDir] = root_dir;
}
static constexpr char kVmManager[] = "vm_manager";
@@ -154,6 +158,22 @@ void CuttlefishConfig::set_gpu_capture_binary(const std::string& name) {
(*dictionary_)[kGpuCaptureBinary] = name;
}
+static constexpr char kHWComposer[] = "hwcomposer";
+std::string CuttlefishConfig::hwcomposer() const {
+ return (*dictionary_)[kHWComposer].asString();
+}
+void CuttlefishConfig::set_hwcomposer(const std::string& name) {
+ (*dictionary_)[kHWComposer] = name;
+}
+
+static constexpr char kEnableGpuUdmabuf[] = "enable_gpu_udmabuf";
+void CuttlefishConfig::set_enable_gpu_udmabuf(const bool enable_gpu_udmabuf) {
+ (*dictionary_)[kEnableGpuUdmabuf] = enable_gpu_udmabuf;
+}
+bool CuttlefishConfig::enable_gpu_udmabuf() const {
+ return (*dictionary_)[kEnableGpuUdmabuf].asBool();
+}
+
static constexpr char kCpus[] = "cpus";
int CuttlefishConfig::cpus() const { return (*dictionary_)[kCpus].asInt(); }
void CuttlefishConfig::set_cpus(int cpus) { (*dictionary_)[kCpus] = cpus; }
@@ -857,6 +877,19 @@ bool CuttlefishConfig::SaveToFile(const std::string& file) const {
return !ofs.fail();
}
+std::string CuttlefishConfig::instances_dir() const {
+ return AbsolutePath(root_dir() + "/instances");
+}
+
+std::string CuttlefishConfig::InstancesPath(
+ const std::string& file_name) const {
+ return AbsolutePath(instances_dir() + "/" + file_name);
+}
+
+std::string CuttlefishConfig::assembly_dir() const {
+ return AbsolutePath(root_dir() + "/assembly");
+}
+
std::string CuttlefishConfig::AssemblyPath(
const std::string& file_name) const {
return AbsolutePath(assembly_dir() + "/" + file_name);
@@ -963,8 +996,7 @@ std::string RandomSerialNumber(const std::string& prefix) {
}
std::string DefaultHostArtifactsPath(const std::string& file_name) {
- return (StringFromEnv("ANDROID_SOONG_HOST_OUT", StringFromEnv("HOME", ".")) +
- "/") +
+ return (StringFromEnv("ANDROID_HOST_OUT", StringFromEnv("HOME", ".")) + "/") +
file_name;
}
@@ -972,9 +1004,7 @@ std::string HostBinaryPath(const std::string& binary_name) {
#ifdef __ANDROID__
return binary_name;
#else
- return (StringFromEnv("ANDROID_HOST_OUT", StringFromEnv("HOME", ".")) +
- "/bin/") +
- binary_name;
+ return DefaultHostArtifactsPath("bin/" + binary_name);
#endif
}
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index f518e475c..fc38be839 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -54,6 +54,7 @@ constexpr char kDisplayPowerModeChangedMessage[] =
"VIRTUAL_DEVICE_DISPLAY_POWER_MODE_CHANGED";
constexpr char kInternalDirName[] = "internal";
constexpr char kSharedDirName[] = "shared";
+constexpr char kLogDirName[] = "logs";
constexpr char kCrosvmVarEmptyDir[] = "/var/empty";
constexpr char kKernelLoadedMessage[] = "] Linux version";
@@ -83,9 +84,13 @@ class CuttlefishConfig {
bool SaveFragment(const ConfigFragment&);
bool LoadFragment(ConfigFragment&) const;
- std::string assembly_dir() const;
- void set_assembly_dir(const std::string& assembly_dir);
+ std::string root_dir() const;
+ void set_root_dir(const std::string& root_dir);
+
+ std::string instances_dir() const;
+ std::string InstancesPath(const std::string&) const;
+ std::string assembly_dir() const;
std::string AssemblyPath(const std::string&) const;
std::string os_composite_disk_path() const;
@@ -99,6 +104,12 @@ class CuttlefishConfig {
std::string gpu_capture_binary() const;
void set_gpu_capture_binary(const std::string&);
+ std::string hwcomposer() const;
+ void set_hwcomposer(const std::string&);
+
+ void set_enable_gpu_udmabuf(const bool enable_gpu_udmabuf);
+ bool enable_gpu_udmabuf() const;
+
int cpus() const;
void set_cpus(int cpus);
@@ -404,6 +415,7 @@ class CuttlefishConfig {
// directory..
std::string PerInstancePath(const char* file_name) const;
std::string PerInstanceInternalPath(const char* file_name) const;
+ std::string PerInstanceLogPath(const std::string& file_name) const;
std::string instance_dir() const;
@@ -458,6 +470,10 @@ class CuttlefishConfig {
// Whether this instance should start the webrtc signaling server
bool start_webrtc_sig_server() const;
+ // Whether to start a reverse proxy to the webrtc signaling server already
+ // running in the host
+ bool start_webrtc_sig_server_proxy() const;
+
// Whether this instance should start the wmediumd process
bool start_wmediumd() const;
@@ -470,6 +486,8 @@ class CuttlefishConfig {
std::string factory_reset_protected_path() const;
std::string persistent_bootconfig_path() const;
+
+ std::string id() const;
};
// A view into an existing CuttlefishConfig object for a particular instance.
@@ -478,8 +496,7 @@ class CuttlefishConfig {
std::string id_;
friend MutableInstanceSpecific CuttlefishConfig::ForInstance(int num);
- MutableInstanceSpecific(CuttlefishConfig* config, const std::string& id)
- : config_(config), id_(id) {}
+ MutableInstanceSpecific(CuttlefishConfig* config, const std::string& id);
Json::Value* Dictionary();
public:
@@ -513,12 +530,12 @@ class CuttlefishConfig {
void set_use_allocd(bool use_allocd);
void set_vsock_guest_cid(int vsock_guest_cid);
void set_uuid(const std::string& uuid);
- void set_instance_dir(const std::string& instance_dir);
// modem simulator related
void set_modem_simulator_ports(const std::string& modem_simulator_ports);
void set_virtual_disk_paths(const std::vector<std::string>& disk_paths);
void set_webrtc_device_id(const std::string& id);
void set_start_webrtc_signaling_server(bool start);
+ void set_start_webrtc_sig_server_proxy(bool start);
void set_start_wmediumd(bool start);
void set_start_ap(bool start);
// Wifi MAC address inside the guest
@@ -581,4 +598,9 @@ extern const char* const kGpuModeAuto;
extern const char* const kGpuModeGuestSwiftshader;
extern const char* const kGpuModeDrmVirgl;
extern const char* const kGpuModeGfxStream;
+
+// HwComposer modes
+extern const char* const kHwComposerAuto;
+extern const char* const kHwComposerDrmMinigbm;
+extern const char* const kHwComposerRanchu;
} // namespace cuttlefish
diff --git a/host/libs/config/cuttlefish_config_instance.cpp b/host/libs/config/cuttlefish_config_instance.cpp
index 29a6145dc..518a6e10b 100644
--- a/host/libs/config/cuttlefish_config_instance.cpp
+++ b/host/libs/config/cuttlefish_config_instance.cpp
@@ -26,8 +26,18 @@ namespace {
const char* kInstances = "instances";
+std::string IdToName(const std::string& id) { return kCvdNamePrefix + id; }
+
} // namespace
+static constexpr char kInstanceDir[] = "instance_dir";
+CuttlefishConfig::MutableInstanceSpecific::MutableInstanceSpecific(
+ CuttlefishConfig* config, const std::string& id)
+ : config_(config), id_(id) {
+ // Legacy for acloud
+ (*Dictionary())[kInstanceDir] = config_->InstancesPath(IdToName(id));
+}
+
Json::Value* CuttlefishConfig::MutableInstanceSpecific::Dictionary() {
return &(*config_->dictionary_)[kInstances][id_];
}
@@ -36,13 +46,8 @@ const Json::Value* CuttlefishConfig::InstanceSpecific::Dictionary() const {
return &(*config_->dictionary_)[kInstances][id_];
}
-static constexpr char kInstanceDir[] = "instance_dir";
std::string CuttlefishConfig::InstanceSpecific::instance_dir() const {
- return (*Dictionary())[kInstanceDir].asString();
-}
-void CuttlefishConfig::MutableInstanceSpecific::set_instance_dir(
- const std::string& instance_dir) {
- (*Dictionary())[kInstanceDir] = instance_dir;
+ return config_->InstancesPath(IdToName(id_));
}
std::string CuttlefishConfig::InstanceSpecific::instance_internal_dir() const {
@@ -140,7 +145,7 @@ std::string CuttlefishConfig::InstanceSpecific::console_path() const {
}
std::string CuttlefishConfig::InstanceSpecific::logcat_path() const {
- return AbsolutePath(PerInstancePath("logcat"));
+ return AbsolutePath(PerInstanceLogPath("logcat"));
}
std::string CuttlefishConfig::InstanceSpecific::launcher_monitor_socket_path()
@@ -158,7 +163,7 @@ void CuttlefishConfig::MutableInstanceSpecific::set_modem_simulator_ports(
}
std::string CuttlefishConfig::InstanceSpecific::launcher_log_path() const {
- return AbsolutePath(PerInstancePath("launcher.log"));
+ return AbsolutePath(PerInstanceLogPath("launcher.log"));
}
std::string CuttlefishConfig::InstanceSpecific::sdcard_path() const {
@@ -428,6 +433,15 @@ bool CuttlefishConfig::InstanceSpecific::start_webrtc_sig_server() const {
return (*Dictionary())[kStartSigServer].asBool();
}
+static constexpr char kStartSigServerProxy[] = "webrtc_start_sig_server_proxy";
+void CuttlefishConfig::MutableInstanceSpecific::
+ set_start_webrtc_sig_server_proxy(bool start) {
+ (*Dictionary())[kStartSigServerProxy] = start;
+}
+bool CuttlefishConfig::InstanceSpecific::start_webrtc_sig_server_proxy() const {
+ return (*Dictionary())[kStartSigServerProxy].asBool();
+}
+
static constexpr char kStartWmediumd[] = "start_wmediumd";
void CuttlefishConfig::MutableInstanceSpecific::set_start_wmediumd(bool start) {
(*Dictionary())[kStartWmediumd] = start;
@@ -495,8 +509,20 @@ std::string CuttlefishConfig::InstanceSpecific::PerInstanceInternalPath(
return PerInstancePath(relative_path.c_str());
}
+std::string CuttlefishConfig::InstanceSpecific::PerInstanceLogPath(
+ const std::string& file_name) const {
+ if (file_name.size() == 0) {
+ // Don't append a / if file_name is empty.
+ return PerInstancePath(kLogDirName);
+ }
+ auto relative_path = (std::string(kLogDirName) + "/") + file_name;
+ return PerInstancePath(relative_path.c_str());
+}
+
std::string CuttlefishConfig::InstanceSpecific::instance_name() const {
- return kCvdNamePrefix + id_;
+ return IdToName(id_);
}
+std::string CuttlefishConfig::InstanceSpecific::id() const { return id_; }
+
} // namespace cuttlefish
diff --git a/host/libs/config/data_image.cpp b/host/libs/config/data_image.cpp
index e3631e3f9..52992e655 100644
--- a/host/libs/config/data_image.cpp
+++ b/host/libs/config/data_image.cpp
@@ -401,98 +401,146 @@ InitializeMiscImageComponent() {
.bind<InitializeMiscImage, InitializeMiscImageImpl>();
}
-bool InitializeEspImage(const std::string& esp_image,
- const std::string& kernel_path,
- const std::string& initramfs_path) {
- bool esp_exists = FileHasContent(esp_image.c_str());
- if (esp_exists) {
- LOG(DEBUG) << "esp partition image: use existing";
- return true;
- }
+struct EspImageTag {};
+struct KernelPathTag {};
+struct InitRamFsTag {};
+struct RootFsTag {};
- LOG(DEBUG) << "esp partition image: creating default";
+class InitializeEspImageImpl : public InitializeEspImage {
+ public:
+ INJECT(InitializeEspImageImpl(ANNOTATED(EspImageTag, std::string) esp_image,
+ ANNOTATED(KernelPathTag, std::string)
+ kernel_path,
+ ANNOTATED(InitRamFsTag, std::string)
+ initramfs_path,
+ ANNOTATED(RootFsTag, std::string) rootfs_path))
+ : esp_image_(esp_image),
+ kernel_path_(kernel_path),
+ initramfs_path_(initramfs_path),
+ rootfs_path_(rootfs_path) {}
- // newfs_msdos won't make a partition smaller than 257 mb
- // this should be enough for anybody..
- auto tmp_esp_image = esp_image + ".tmp";
- if (!NewfsMsdos(tmp_esp_image, 257 /* mb */, 0 /* mb (offset) */)) {
- LOG(ERROR) << "Failed to create filesystem for " << tmp_esp_image;
- return false;
- }
+ // Feature
+ std::string Name() const override { return "InitializeEspImageImpl"; }
+ std::unordered_set<Feature*> Dependencies() const override { return {}; }
+ bool Enabled() const override { return !rootfs_path_.empty(); }
- // For licensing and build reproducibility reasons, pick up the bootloaders
- // from the host Linux distribution (if present) and pack them into the
- // automatically generated ESP. If the user wants their own bootloaders,
- // they can use -esp_image=/path/to/esp.img to override, so we don't need
- // to accommodate customizations of this packing process.
-
- // Currently we only support Debian based distributions, and GRUB is built
- // for those distros to always load grub.cfg from EFI/debian/grub.cfg, and
- // nowhere else. If you want to add support for other distros, make the
- // extra directories below and copy the initial grub.cfg there as well
- auto mmd = HostBinaryPath("mmd");
- auto success =
- execute({mmd, "-i", tmp_esp_image, "EFI", "EFI/BOOT", "EFI/debian"});
- if (success != 0) {
- LOG(ERROR) << "Failed to create directories in " << tmp_esp_image;
- return false;
- }
+ protected:
+ bool Setup() override {
+ bool esp_exists = FileHasContent(esp_image_);
+ if (esp_exists) {
+ LOG(DEBUG) << "esp partition image: use existing";
+ return true;
+ }
+
+ LOG(DEBUG) << "esp partition image: creating default";
- // The grub binaries are small, so just copy all the architecture blobs
- // we can find, which minimizes complexity. If the user removed the grub bin
- // package from their system, the ESP will be empty and Other OS will not be
- // supported
- auto mcopy = HostBinaryPath("mcopy");
- bool copied = false;
- for (auto grub : kGrubBlobTable) {
- if (!FileExists(grub.first)) {
- continue;
+ // newfs_msdos won't make a partition smaller than 257 mb
+ // this should be enough for anybody..
+ auto tmp_esp_image = esp_image_ + ".tmp";
+ if (!NewfsMsdos(tmp_esp_image, 257 /* mb */, 0 /* mb (offset) */)) {
+ LOG(ERROR) << "Failed to create filesystem for " << tmp_esp_image;
+ return false;
}
- success = execute(
- {mcopy, "-o", "-i", tmp_esp_image, "-s", grub.first, "::" + grub.second});
+
+ // For licensing and build reproducibility reasons, pick up the bootloaders
+ // from the host Linux distribution (if present) and pack them into the
+ // automatically generated ESP. If the user wants their own bootloaders,
+ // they can use -esp_image=/path/to/esp.img to override, so we don't need
+ // to accommodate customizations of this packing process.
+
+ // Currently we only support Debian based distributions, and GRUB is built
+ // for those distros to always load grub.cfg from EFI/debian/grub.cfg, and
+ // nowhere else. If you want to add support for other distros, make the
+ // extra directories below and copy the initial grub.cfg there as well
+ auto mmd = HostBinaryPath("mmd");
+ auto success =
+ execute({mmd, "-i", tmp_esp_image, "EFI", "EFI/BOOT", "EFI/debian"});
if (success != 0) {
- LOG(ERROR) << "Failed to copy " << grub.first << " to " << grub.second
- << " in " << tmp_esp_image;
+ LOG(ERROR) << "Failed to create directories in " << tmp_esp_image;
return false;
}
- copied = true;
- }
- if (!copied) {
- LOG(ERROR) << "No GRUB binaries were found on this system; Other OS "
- "support will be broken";
- return false;
- }
+ // The grub binaries are small, so just copy all the architecture blobs
+ // we can find, which minimizes complexity. If the user removed the grub bin
+ // package from their system, the ESP will be empty and Other OS will not be
+ // supported
+ auto mcopy = HostBinaryPath("mcopy");
+ bool copied = false;
+ for (auto grub : kGrubBlobTable) {
+ if (!FileExists(grub.first)) {
+ continue;
+ }
+ success = execute({mcopy, "-o", "-i", tmp_esp_image, "-s", grub.first,
+ "::" + grub.second});
+ if (success != 0) {
+ LOG(ERROR) << "Failed to copy " << grub.first << " to " << grub.second
+ << " in " << tmp_esp_image;
+ return false;
+ }
+ copied = true;
+ }
- auto grub_cfg = DefaultHostArtifactsPath("etc/grub/grub.cfg");
- CHECK(FileExists(grub_cfg)) << "Missing file " << grub_cfg << "!";
- success = execute({mcopy, "-i", tmp_esp_image, "-s", grub_cfg, "::EFI/debian/"});
- if (success != 0) {
- LOG(ERROR) << "Failed to copy " << grub_cfg << " to " << tmp_esp_image;
- return false;
- }
+ if (!copied) {
+ LOG(ERROR) << "No GRUB binaries were found on this system; Other OS "
+ "support will be broken";
+ return false;
+ }
- if (!kernel_path.empty()) {
- success = execute({mcopy, "-i", tmp_esp_image, "-s", kernel_path, "::vmlinuz"});
+ auto grub_cfg = DefaultHostArtifactsPath("etc/grub/grub.cfg");
+ CHECK(FileExists(grub_cfg)) << "Missing file " << grub_cfg << "!";
+ success =
+ execute({mcopy, "-i", tmp_esp_image, "-s", grub_cfg, "::EFI/debian/"});
if (success != 0) {
- LOG(ERROR) << "Failed to copy " << kernel_path << " to " << tmp_esp_image;
+ LOG(ERROR) << "Failed to copy " << grub_cfg << " to " << tmp_esp_image;
return false;
}
- if (!initramfs_path.empty()) {
+ if (!kernel_path_.empty()) {
success = execute(
- {mcopy, "-i", tmp_esp_image, "-s", initramfs_path, "::initrd.img"});
+ {mcopy, "-i", tmp_esp_image, "-s", kernel_path_, "::vmlinuz"});
if (success != 0) {
- LOG(ERROR) << "Failed to copy " << initramfs_path << " to "
+ LOG(ERROR) << "Failed to copy " << kernel_path_ << " to "
<< tmp_esp_image;
return false;
}
+
+ if (!initramfs_path_.empty()) {
+ success = execute({mcopy, "-i", tmp_esp_image, "-s", initramfs_path_,
+ "::initrd.img"});
+ if (success != 0) {
+ LOG(ERROR) << "Failed to copy " << initramfs_path_ << " to "
+ << tmp_esp_image;
+ return false;
+ }
+ }
}
+
+ if (!cuttlefish::RenameFile(tmp_esp_image, esp_image_)) {
+ LOG(ERROR) << "Renaming " << tmp_esp_image << " to " << esp_image_
+ << " failed";
+ return false;
+ }
+ return true;
}
- CHECK(cuttlefish::RenameFile(tmp_esp_image, esp_image))
- << "Renaming " << tmp_esp_image << " to " << esp_image << " failed";
- return true;
+ private:
+ std::string esp_image_;
+ std::string kernel_path_;
+ std::string initramfs_path_;
+ std::string rootfs_path_;
+};
+
+fruit::Component<InitializeEspImage> InitializeEspImageComponent(
+ const std::string* esp_image, const std::string* kernel_path,
+ const std::string* initramfs_path, const std::string* rootfs_path) {
+ return fruit::createComponent()
+ .addMultibinding<Feature, InitializeEspImage>()
+ .bind<InitializeEspImage, InitializeEspImageImpl>()
+ .bindInstance<fruit::Annotated<EspImageTag, std::string>>(*esp_image)
+ .bindInstance<fruit::Annotated<KernelPathTag, std::string>>(*kernel_path)
+ .bindInstance<fruit::Annotated<InitRamFsTag, std::string>>(
+ *initramfs_path)
+ .bindInstance<fruit::Annotated<RootFsTag, std::string>>(*rootfs_path);
}
} // namespace cuttlefish
diff --git a/host/libs/config/data_image.h b/host/libs/config/data_image.h
index 9aa68bd35..c60ec51d4 100644
--- a/host/libs/config/data_image.h
+++ b/host/libs/config/data_image.h
@@ -23,9 +23,12 @@ fruit::Component<fruit::Required<const CuttlefishConfig, DataImagePath>,
InitializeDataImage>
InitializeDataImageComponent();
-bool InitializeEspImage(const std::string& esp_image,
- const std::string& kernel_path,
- const std::string& initramfs_path);
+class InitializeEspImage : public Feature {};
+
+fruit::Component<InitializeEspImage> InitializeEspImageComponent(
+ const std::string* esp_image, const std::string* kernel_path,
+ const std::string* initramfs_path, const std::string* root_fs);
+
bool CreateBlankImage(
const std::string& image, int num_mb, const std::string& image_fmt);
diff --git a/host/libs/config/known_paths.cpp b/host/libs/config/known_paths.cpp
index 9b9108284..5a457e6ad 100644
--- a/host/libs/config/known_paths.cpp
+++ b/host/libs/config/known_paths.cpp
@@ -52,7 +52,9 @@ std::string ModemSimulatorBinary() {
return HostBinaryPath("modem_simulator");
}
-std::string RootCanalBinary() { return HostBinaryPath("root-canal"); }
+std::string RootCanalBinary() {
+ return DefaultHostArtifactsPath("bin/root-canal");
+}
std::string SocketVsockProxyBinary() {
return HostBinaryPath("socket_vsock_proxy");
@@ -75,6 +77,10 @@ std::string WebRtcSigServerBinary() {
return HostBinaryPath("webrtc_operator");
}
+std::string WebRtcSigServerProxyBinary() {
+ return HostBinaryPath("operator_proxy");
+}
+
std::string WmediumdBinary() { return HostBinaryPath("wmediumd"); }
std::string WmediumdGenConfigBinary() {
diff --git a/host/libs/config/known_paths.h b/host/libs/config/known_paths.h
index b32b7dd14..8c39e614d 100644
--- a/host/libs/config/known_paths.h
+++ b/host/libs/config/known_paths.h
@@ -33,6 +33,7 @@ std::string TombstoneReceiverBinary();
std::string VehicleHalGrpcServerBinary();
std::string WebRtcBinary();
std::string WebRtcSigServerBinary();
+std::string WebRtcSigServerProxyBinary();
std::string WmediumdBinary();
std::string WmediumdGenConfigBinary();
diff --git a/host/libs/confui/host_renderer.cc b/host/libs/confui/host_renderer.cc
index a7dac6a9f..8bf218b66 100644
--- a/host/libs/confui/host_renderer.cc
+++ b/host/libs/confui/host_renderer.cc
@@ -16,6 +16,8 @@
#include "host/libs/confui/host_renderer.h"
+#include "host/libs/config/cuttlefish_config.h"
+
namespace cuttlefish {
namespace confui {
static teeui::Color alfaCombineChannel(std::uint32_t shift, double alfa,
@@ -42,6 +44,15 @@ std::unique_ptr<ConfUiRenderer> ConfUiRenderer::GenerateRenderer(
return nullptr;
}
+static int GetDpi(const int display_num = 0) {
+ auto config = CuttlefishConfig::Get();
+ CHECK(config) << "Config is Missing";
+ auto display_configs = config->display_configs();
+ CHECK_GT(display_configs.size(), display_num)
+ << "Invalid display number " << display_num;
+ return display_configs[display_num].dpi;
+}
+
/**
* device configuration
*
@@ -51,6 +62,11 @@ std::unique_ptr<ConfUiRenderer> ConfUiRenderer::GenerateRenderer(
* in general, not necessarily the supposedly guest device (e.g. Auto, phone,
* etc)
*
+ * In general, for a normal PC, roughly ctx_(6.45211, 400.0/412.0) is a good
+ * combination for the default DPI, 320. If we want to see the impact
+ * of the change in the guest DPI, we could adjust the combination above
+ * proportionally
+ *
*/
ConfUiRenderer::ConfUiRenderer(const std::uint32_t display,
const std::string& confirmation_msg,
@@ -63,7 +79,7 @@ ConfUiRenderer::ConfUiRenderer(const std::uint32_t display,
current_width_{ScreenConnectorInfo::ScreenWidth(display_num_)},
is_inverted_(inverted),
is_magnified_(magnified),
- ctx_(6.45211, 400.0 / 412.0),
+ ctx_(6.45211 * GetDpi() / 320.0, 400.0 / 412.0 * GetDpi() / 320.0),
is_setup_well_(false) {
SetDeviceContext(current_width_, current_height_, is_inverted_,
is_magnified_);
diff --git a/host/libs/image_aggregator/image_aggregator.cc b/host/libs/image_aggregator/image_aggregator.cc
index 0868aa155..4f508356d 100644
--- a/host/libs/image_aggregator/image_aggregator.cc
+++ b/host/libs/image_aggregator/image_aggregator.cc
@@ -573,6 +573,12 @@ void CreateQcowOverlay(const std::string& crosvm_path,
crosvm_qcow2_cmd.AddParameter("create_qcow2");
crosvm_qcow2_cmd.AddParameter("--backing_file=", backing_file);
crosvm_qcow2_cmd.AddParameter(output_overlay_path);
+
+ auto devnull = SharedFD::Open("/dev/null", O_RDONLY);
+ CHECK(devnull->IsOpen()) << "Failed to open /dev/null";
+ crosvm_qcow2_cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdOut, devnull);
+ crosvm_qcow2_cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdErr, devnull);
+
int success = crosvm_qcow2_cmd.Start().Wait();
if (success != 0) {
LOG(FATAL) << "Unable to run crosvm create_qcow2. Exited with status " << success;
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
index 8982e5d0c..2b729f507 100644
--- a/host/libs/vm_manager/crosvm_manager.cpp
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -57,8 +57,8 @@ bool CrosvmManager::IsSupported() {
#endif
}
-std::vector<std::string> CrosvmManager::ConfigureGpuMode(
- const std::string& gpu_mode) {
+std::vector<std::string> CrosvmManager::ConfigureGraphics(
+ const std::string& gpu_mode, const std::string& hwcomposer) {
// Override the default HAL search paths in all cases. We do this because
// the HAL search path allows for fallbacks, and fallbacks in conjunction
// with properities lead to non-deterministic behavior while loading the
@@ -67,7 +67,7 @@ std::vector<std::string> CrosvmManager::ConfigureGpuMode(
return {
"androidboot.cpuvulkan.version=" + std::to_string(VK_API_VERSION_1_2),
"androidboot.hardware.gralloc=minigbm",
- "androidboot.hardware.hwcomposer=ranchu",
+ "androidboot.hardware.hwcomposer="+ hwcomposer,
"androidboot.hardware.egl=angle",
"androidboot.hardware.vulkan=pastel",
"androidboot.opengles.version=196609"}; // OpenGL ES 3.1
@@ -84,7 +84,7 @@ std::vector<std::string> CrosvmManager::ConfigureGpuMode(
if (gpu_mode == kGpuModeGfxStream) {
return {"androidboot.cpuvulkan.version=0",
"androidboot.hardware.gralloc=minigbm",
- "androidboot.hardware.hwcomposer=ranchu",
+ "androidboot.hardware.hwcomposer=" + hwcomposer,
"androidboot.hardware.egl=emulation",
"androidboot.hardware.vulkan=ranchu",
"androidboot.hardware.gltransport=virtio-gpu-asg",
@@ -140,12 +140,13 @@ std::vector<Command> CrosvmManager::StartCommands(
auto gpu_capture_enabled = !config.gpu_capture_binary().empty();
auto gpu_mode = config.gpu_mode();
+ auto udmabuf_string = config.enable_gpu_udmabuf() ? "true" : "false";
if (gpu_mode == kGpuModeGuestSwiftshader) {
- crosvm_cmd.Cmd().AddParameter("--gpu=2D");
+ crosvm_cmd.Cmd().AddParameter("--gpu=2D,udmabuf=", udmabuf_string);
} else if (gpu_mode == kGpuModeDrmVirgl || gpu_mode == kGpuModeGfxStream) {
crosvm_cmd.Cmd().AddParameter(
gpu_mode == kGpuModeGfxStream ? "--gpu=gfxstream," : "--gpu=",
- "egl=true,surfaceless=true,glx=false,gles=true");
+ "egl=true,surfaceless=true,glx=false,gles=true,udmabuf=", udmabuf_string);
}
for (const auto& display_config : config.display_configs()) {
@@ -206,7 +207,8 @@ std::vector<Command> CrosvmManager::StartCommands(
#endif
}
- if (FileExists(instance.access_kregistry_path())) {
+ bool is_arm = HostArch() == Arch::Arm || HostArch() == Arch::Arm64;
+ if (!is_arm && FileExists(instance.access_kregistry_path())) {
crosvm_cmd.Cmd().AddParameter("--rw-pmem-device=",
instance.access_kregistry_path());
}
@@ -327,7 +329,7 @@ std::vector<Command> CrosvmManager::StartCommands(
}
// TODO(b/162071003): virtiofs crashes without sandboxing, this should be fixed
- if (config.enable_sandbox()) {
+ if (0 && config.enable_sandbox()) {
// Set up directory shared with virtiofs
crosvm_cmd.Cmd().AddParameter(
"--shared-dir=", instance.PerInstancePath(kSharedDirName),
diff --git a/host/libs/vm_manager/crosvm_manager.h b/host/libs/vm_manager/crosvm_manager.h
index 1e7a0dea4..0c14da84f 100644
--- a/host/libs/vm_manager/crosvm_manager.h
+++ b/host/libs/vm_manager/crosvm_manager.h
@@ -34,7 +34,8 @@ class CrosvmManager : public VmManager {
virtual ~CrosvmManager() = default;
bool IsSupported() override;
- std::vector<std::string> ConfigureGpuMode(const std::string&) override;
+ std::vector<std::string> ConfigureGraphics(const std::string&,
+ const std::string&) override;
std::string ConfigureBootDevices(int num_disks) override;
std::vector<cuttlefish::Command> StartCommands(
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index 00fdf5adc..c8bce99a9 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -123,8 +123,8 @@ bool QemuManager::IsSupported() {
return HostSupportsQemuCli();
}
-std::vector<std::string> QemuManager::ConfigureGpuMode(
- const std::string& gpu_mode) {
+std::vector<std::string> QemuManager::ConfigureGraphics(
+ const std::string& gpu_mode, const std::string& hwcomposer) {
if (gpu_mode == kGpuModeGuestSwiftshader) {
// Override the default HAL search paths in all cases. We do this because
// the HAL search path allows for fallbacks, and fallbacks in conjunction
@@ -133,7 +133,7 @@ std::vector<std::string> QemuManager::ConfigureGpuMode(
return {
"androidboot.cpuvulkan.version=" + std::to_string(VK_API_VERSION_1_1),
"androidboot.hardware.gralloc=minigbm",
- "androidboot.hardware.hwcomposer=ranchu",
+ "androidboot.hardware.hwcomposer=" + hwcomposer,
"androidboot.hardware.egl=angle",
"androidboot.hardware.vulkan=pastel",
};
@@ -501,13 +501,13 @@ std::vector<Command> QemuManager::StartCommands(
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter("virtio-net-pci-non-transitional,netdev=hostnet1,id=net1");
-
+#ifndef ENFORCE_MAC80211_HWSIM
qemu_cmd.AddParameter("-netdev");
qemu_cmd.AddParameter("tap,id=hostnet2,ifname=", instance.wifi_tap_name(),
",script=no,downscript=no", vhost_net);
-
qemu_cmd.AddParameter("-device");
qemu_cmd.AddParameter("virtio-net-pci-non-transitional,netdev=hostnet2,id=net2");
+#endif
auto display_configs = config.display_configs();
CHECK_GE(display_configs.size(), 1);
diff --git a/host/libs/vm_manager/qemu_manager.h b/host/libs/vm_manager/qemu_manager.h
index 663bf474a..21ff3d00e 100644
--- a/host/libs/vm_manager/qemu_manager.h
+++ b/host/libs/vm_manager/qemu_manager.h
@@ -35,7 +35,8 @@ class QemuManager : public VmManager {
virtual ~QemuManager() = default;
bool IsSupported() override;
- std::vector<std::string> ConfigureGpuMode(const std::string&) override;
+ std::vector<std::string> ConfigureGraphics(const std::string&,
+ const std::string&) override;
std::string ConfigureBootDevices(int num_disks) override;
std::vector<cuttlefish::Command> StartCommands(
diff --git a/host/libs/vm_manager/vm_manager.h b/host/libs/vm_manager/vm_manager.h
index bfc8c8b74..f829b6b0f 100644
--- a/host/libs/vm_manager/vm_manager.h
+++ b/host/libs/vm_manager/vm_manager.h
@@ -58,7 +58,8 @@ class VmManager {
virtual ~VmManager() = default;
virtual bool IsSupported() = 0;
- virtual std::vector<std::string> ConfigureGpuMode(const std::string&) = 0;
+ virtual std::vector<std::string> ConfigureGraphics(const std::string&,
+ const std::string&) = 0;
virtual std::string ConfigureBootDevices(int num_disks) = 0;
// Starts the VMM. It will usually build a command and pass it to the
diff --git a/host/libs/websocket/websocket_server.cpp b/host/libs/websocket/websocket_server.cpp
index 22c7caf68..f48209b15 100644
--- a/host/libs/websocket/websocket_server.cpp
+++ b/host/libs/websocket/websocket_server.cpp
@@ -46,9 +46,9 @@ std::string GetPath(struct lws* wsi) {
}
const std::vector<std::pair<std::string, std::string>> kCORSHeaders = {
- {"Access-Control-Allow-Origin", "*"},
- {"Access-Control-Allow-Methods", "POST, GET, OPTIONS"},
- {"Access-Control-Allow-Headers",
+ {"Access-Control-Allow-Origin:", "*"},
+ {"Access-Control-Allow-Methods:", "POST, GET, OPTIONS"},
+ {"Access-Control-Allow-Headers:",
"Content-Type, Access-Control-Allow-Headers, Authorization, "
"X-Requested-With, Accept"}};
@@ -204,7 +204,6 @@ void WebSocketServer::InitializeLwsObjects() {
info.mounts = &static_mount_;
info.protocols = protocols;
info.vhost_name = "localhost";
- info.ws_ping_pong_interval = 10;
info.headers = &headers_;
info.retry_and_idle_policy = &retry_;
diff --git a/host/libs/wmediumd_controller/wmediumd_api_protocol.cpp b/host/libs/wmediumd_controller/wmediumd_api_protocol.cpp
index 9abd86068..b1ffc5eb2 100644
--- a/host/libs/wmediumd_controller/wmediumd_api_protocol.cpp
+++ b/host/libs/wmediumd_controller/wmediumd_api_protocol.cpp
@@ -98,4 +98,10 @@ void WmediumdMessageReloadConfig::SerializeBody(std::string& buf) const {
buf.push_back('\0');
}
+void WmediumdMessageStartPcap::SerializeBody(std::string& buf) const {
+ std::copy(std::begin(pcap_path_), std::end(pcap_path_),
+ std::back_inserter(buf));
+ buf.push_back('\0');
+}
+
} // namespace cuttlefish
diff --git a/host/libs/wmediumd_controller/wmediumd_api_protocol.h b/host/libs/wmediumd_controller/wmediumd_api_protocol.h
index 3a385680c..b6fb90f61 100644
--- a/host/libs/wmediumd_controller/wmediumd_api_protocol.h
+++ b/host/libs/wmediumd_controller/wmediumd_api_protocol.h
@@ -36,6 +36,8 @@ enum class WmediumdMessageType : uint32_t {
kSetSnr = 8,
kReloadConfig = 9,
kReloadCurrentConfig = 10,
+ kStartPcap = 11,
+ kStopPcap = 12,
};
class WmediumdMessage {
@@ -104,4 +106,28 @@ class WmediumdMessageReloadCurrentConfig : public WmediumdMessage {
}
};
+class WmediumdMessageStartPcap : public WmediumdMessage {
+ public:
+ WmediumdMessageStartPcap(const std::string& pcapPath)
+ : pcap_path_(pcapPath) {}
+
+ WmediumdMessageType Type() const override {
+ return WmediumdMessageType::kStartPcap;
+ }
+
+ private:
+ void SerializeBody(std::string& out) const override;
+
+ std::string pcap_path_;
+};
+
+class WmediumdMessageStopPcap : public WmediumdMessage {
+ public:
+ WmediumdMessageStopPcap() = default;
+
+ WmediumdMessageType Type() const override {
+ return WmediumdMessageType::kStopPcap;
+ }
+};
+
} // namespace cuttlefish
diff --git a/host/libs/wmediumd_controller/wmediumd_controller.cpp b/host/libs/wmediumd_controller/wmediumd_controller.cpp
index c5c7ad647..481aa6ffc 100644
--- a/host/libs/wmediumd_controller/wmediumd_controller.cpp
+++ b/host/libs/wmediumd_controller/wmediumd_controller.cpp
@@ -69,6 +69,14 @@ bool WmediumdController::ReloadConfig(const std::string& configPath) {
return SendMessage(WmediumdMessageReloadConfig(configPath));
}
+bool WmediumdController::StartPcap(const std::string& pcapPath) {
+ return SendMessage(WmediumdMessageStartPcap(pcapPath));
+}
+
+bool WmediumdController::StopPcap(void) {
+ return SendMessage(WmediumdMessageStopPcap());
+}
+
bool WmediumdController::SendMessage(const WmediumdMessage& message) {
auto sendResult = SendAll(wmediumd_socket_, message.Serialize());
diff --git a/host/libs/wmediumd_controller/wmediumd_controller.h b/host/libs/wmediumd_controller/wmediumd_controller.h
index a5ef922e3..acd636396 100644
--- a/host/libs/wmediumd_controller/wmediumd_controller.h
+++ b/host/libs/wmediumd_controller/wmediumd_controller.h
@@ -41,6 +41,8 @@ class WmediumdController {
bool SetSnr(const std::string& node1, const std::string& node2, uint8_t snr);
bool ReloadCurrentConfig(void);
bool ReloadConfig(const std::string& configPath);
+ bool StartPcap(const std::string& pcapPath);
+ bool StopPcap(void);
private:
WmediumdController() {}
diff --git a/host_package.mk b/host_package.mk
index bd6b51f42..e1afedf9b 100644
--- a/host_package.mk
+++ b/host_package.mk
@@ -1,6 +1,6 @@
-cvd_host_packages := $(SOONG_HOST_OUT)/cvd-host_package.tar.gz
+cvd_host_packages := $(HOST_OUT)/cvd-host_package.tar.gz
ifeq ($(HOST_CROSS_OS)_$(HOST_CROSS_ARCH),linux_bionic_arm64)
- cvd_host_packages += $(SOONG_OUT_DIR)/host/$(HOST_CROSS_OS)-$(HOST_CROSS_ARCH)/cvd-host_package.tar.gz
+ cvd_host_packages += $(OUT_DIR)/host/$(HOST_CROSS_OS)-$(HOST_CROSS_ARCH)/cvd-host_package.tar.gz
endif
.PHONY: hosttar
diff --git a/required_images b/required_images
index 6f5c1800b..e6f0616b6 100644
--- a/required_images
+++ b/required_images
@@ -1,4 +1,5 @@
boot.img
+init_boot.img
bootloader
super.img
userdata.img
diff --git a/shared/BoardConfig.mk b/shared/BoardConfig.mk
index 6260377de..8fc162942 100644
--- a/shared/BoardConfig.mk
+++ b/shared/BoardConfig.mk
@@ -27,7 +27,7 @@ TARGET_BOOTLOADER_BOARD_NAME := cutf
BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE := $(TARGET_RO_FILE_SYSTEM_TYPE)
-# Boot partition size: 32M
+# Boot partition size: 64M
# This is only used for OTA update packages. The image size on disk
# will not change (as is it not a filesystem.)
BOARD_BOOTIMAGE_PARTITION_SIZE := 67108864
@@ -36,6 +36,8 @@ BOARD_RECOVERYIMAGE_PARTITION_SIZE := 67108864
endif
BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE := 67108864
+BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE := 8388608
+
# Build a separate vendor.img partition
BOARD_USES_VENDORIMAGE := true
BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE := $(TARGET_RO_FILE_SYSTEM_TYPE)
@@ -58,12 +60,12 @@ TARGET_COPY_OUT_ODM := odm
# Build a separate vendor_dlkm partition
BOARD_USES_VENDOR_DLKMIMAGE := true
-BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE := ext4
+BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE := $(TARGET_RO_FILE_SYSTEM_TYPE)
TARGET_COPY_OUT_VENDOR_DLKM := vendor_dlkm
# Build a separate odm_dlkm partition
BOARD_USES_ODM_DLKMIMAGE := true
-BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE := ext4
+BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE := $(TARGET_RO_FILE_SYSTEM_TYPE)
TARGET_COPY_OUT_ODM_DLKM := odm_dlkm
# Enable AVB
@@ -84,6 +86,12 @@ BOARD_AVB_BOOT_ALGORITHM := SHA256_RSA4096
BOARD_AVB_BOOT_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
BOARD_AVB_BOOT_ROLLBACK_INDEX_LOCATION := 2
+# Enable chained vbmeta for init_boot images
+BOARD_AVB_INIT_BOOT_KEY_PATH := external/avb/test/data/testkey_rsa4096.pem
+BOARD_AVB_INIT_BOOT_ALGORITHM := SHA256_RSA4096
+BOARD_AVB_INIT_BOOT_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
+BOARD_AVB_INIT_BOOT_ROLLBACK_INDEX_LOCATION := 3
+
# Using sha256 for dm-verity partitions. b/178983355
# system, system_other, product.
TARGET_AVB_SYSTEM_HASHTREE_ALGORITHM ?= sha256
@@ -229,9 +237,16 @@ BOARD_BOOTCONFIG += kernel.mac80211_hwsim.radios=0
BOARD_BOOTCONFIG += \
kernel.vmw_vsock_virtio_transport_common.virtio_transport_max_vsock_pkt_buf_size=16384
+BOARD_BOOTCONFIG += \
+ androidboot.vendor.apex.com.android.wifi.hal=com.google.cf.wifi_hwsim
+
BOARD_INCLUDE_DTB_IN_BOOTIMG := true
+ifndef BOARD_BOOT_HEADER_VERSION
BOARD_BOOT_HEADER_VERSION := 4
+endif
BOARD_MKBOOTIMG_ARGS += --header_version $(BOARD_BOOT_HEADER_VERSION)
+BOARD_INIT_BOOT_HEADER_VERSION := 4
+BOARD_MKBOOTIMG_INIT_ARGS += --header_version $(BOARD_INIT_BOOT_HEADER_VERSION)
PRODUCT_COPY_FILES += \
device/google/cuttlefish/dtb.img:dtb.img \
device/google/cuttlefish/required_images:required_images \
diff --git a/shared/auto/preinstalled-packages-product-car-cuttlefish.xml b/shared/auto/preinstalled-packages-product-car-cuttlefish.xml
index 49aef5d1f..3c455f205 100644
--- a/shared/auto/preinstalled-packages-product-car-cuttlefish.xml
+++ b/shared/auto/preinstalled-packages-product-car-cuttlefish.xml
@@ -94,8 +94,13 @@
<install-in-user-type package="com.android.cameraextensions">
<install-in user-type="SYSTEM" />
<install-in user-type="FULL" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.car.messenger">
+ <install-in user-type="FULL" />
+ <install-in user-type="SYSTEM" />
</install-in-user-type>
+
<!--
Apps that do need to run on SYSTEM and evaluated by package owner.
Here the apps will have FULL only.
@@ -136,9 +141,6 @@
<install-in-user-type package="com.android.car.radio">
<install-in user-type="FULL" />
</install-in-user-type>
- <install-in-user-type package="com.android.car.messenger">
- <install-in user-type="FULL" />
- </install-in-user-type>
<install-in-user-type package="com.android.car.media.localmediaplayer">
<install-in user-type="FULL" />
</install-in-user-type>
diff --git a/shared/config/fstab-erofs.ext4 b/shared/config/fstab-erofs.ext4
index b296b7a4c..7722fbe10 100644
--- a/shared/config/fstab-erofs.ext4
+++ b/shared/config/fstab-erofs.ext4
@@ -1,4 +1,5 @@
/dev/block/by-name/boot /boot emmc defaults recoveryonly,slotselect,first_stage_mount,avb=boot
+/dev/block/by-name/init_boot /init_boot emmc defaults recoveryonly,slotselect,first_stage_mount,avb=init_boot
/dev/block/by-name/vendor_boot /vendor_boot emmc defaults recoveryonly,slotselect
system /system erofs ro wait,logical,first_stage_mount,slotselect,avb=vbmeta_system,avb_keys=/avb
# Add all non-dynamic partitions except system, after this comment
@@ -10,8 +11,8 @@ odm /odm erofs ro wait,logical,first_stage_mount,slotselect,avb
product /product erofs ro wait,logical,first_stage_mount,slotselect,avb
system_ext /system_ext erofs ro wait,logical,first_stage_mount,slotselect,avb=vbmeta_system
vendor /vendor erofs ro wait,logical,first_stage_mount,slotselect,avb=vbmeta
-vendor_dlkm /vendor_dlkm ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
-odm_dlkm /odm_dlkm ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
+vendor_dlkm /vendor_dlkm erofs noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
+odm_dlkm /odm_dlkm erofs noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
/dev/block/zram0 none swap defaults zramsize=75%
/dev/block/vdc1 /sdcard vfat defaults recoveryonly
/devices/*/block/vdc auto auto defaults voldmanaged=sdcard1:auto,encryptable=userdata
diff --git a/shared/config/fstab-erofs.f2fs b/shared/config/fstab-erofs.f2fs
index 79f0f4ff5..0b28b937a 100644
--- a/shared/config/fstab-erofs.f2fs
+++ b/shared/config/fstab-erofs.f2fs
@@ -1,4 +1,5 @@
/dev/block/by-name/boot /boot emmc defaults recoveryonly,slotselect,first_stage_mount,avb=boot
+/dev/block/by-name/init_boot /init_boot emmc defaults recoveryonly,slotselect,first_stage_mount,avb=init_boot
/dev/block/by-name/vendor_boot /vendor_boot emmc defaults recoveryonly,slotselect
system /system erofs ro wait,logical,first_stage_mount,slotselect,avb=vbmeta_system,avb_keys=/avb
# Add all non-dynamic partitions except system, after this comment
@@ -10,8 +11,8 @@ odm /odm erofs ro wait,logical,first_stage_mount,slotselect,avb
product /product erofs ro wait,logical,first_stage_mount,slotselect,avb
system_ext /system_ext erofs ro wait,logical,first_stage_mount,slotselect,avb=vbmeta_system
vendor /vendor erofs ro wait,logical,first_stage_mount,slotselect,avb=vbmeta
-vendor_dlkm /vendor_dlkm ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
-odm_dlkm /odm_dlkm ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
+vendor_dlkm /vendor_dlkm erofs noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
+odm_dlkm /odm_dlkm erofs noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb
/dev/block/zram0 none swap defaults zramsize=75%
/dev/block/vdc1 /sdcard vfat defaults recoveryonly
/devices/*/block/vdc auto auto defaults voldmanaged=sdcard1:auto,encryptable=userdata
diff --git a/shared/config/fstab.ext4 b/shared/config/fstab.ext4
index e6dd2c07f..7567d1459 100644
--- a/shared/config/fstab.ext4
+++ b/shared/config/fstab.ext4
@@ -1,4 +1,5 @@
/dev/block/by-name/boot /boot emmc defaults recoveryonly,slotselect,first_stage_mount,avb=boot
+/dev/block/by-name/init_boot /init_boot emmc defaults recoveryonly,slotselect,first_stage_mount,avb=init_boot
/dev/block/by-name/vendor_boot /vendor_boot emmc defaults recoveryonly,slotselect
system /system ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb=vbmeta_system,avb_keys=/avb
# Add all non-dynamic partitions except system, after this comment
diff --git a/shared/config/fstab.f2fs b/shared/config/fstab.f2fs
index 301c203be..008ca0048 100644
--- a/shared/config/fstab.f2fs
+++ b/shared/config/fstab.f2fs
@@ -1,4 +1,5 @@
/dev/block/by-name/boot /boot emmc defaults recoveryonly,slotselect,first_stage_mount,avb=boot
+/dev/block/by-name/init_boot /init_boot emmc defaults recoveryonly,slotselect,first_stage_mount,avb=init_boot
/dev/block/by-name/vendor_boot /vendor_boot emmc defaults recoveryonly,slotselect
system /system ext4 noatime,ro,errors=panic wait,logical,first_stage_mount,slotselect,avb=vbmeta_system,avb_keys=/avb
# Add all non-dynamic partitions except system, after this comment
diff --git a/shared/config/init.vendor.rc b/shared/config/init.vendor.rc
index 4876eadae..24b0962b3 100644
--- a/shared/config/init.vendor.rc
+++ b/shared/config/init.vendor.rc
@@ -75,8 +75,11 @@ on late-fs
write /dev/kmsg "GUEST_BUILD_FINGERPRINT: ${ro.build.fingerprint}"
on post-fs-data && property:ro.vendor.wifi_impl=mac8011_hwsim_virtio
+ mkdir /data/vendor/wifi 0770 wifi wifi
+ mkdir /data/vendor/wifi/hostapd 0770 wifi wifi
+ mkdir /data/vendor/wifi/hostapd/sockets 0770 wifi wifi
setprop vold.post_fs_data_done 1
- start wifi-net-sh
+ start init_wifi_sh
on boot
chmod 0660 /dev/cpuctl
@@ -87,13 +90,6 @@ on boot
setprop ro.hardware.audio.primary goldfish
symlink /dev/hvc6 /dev/gnss0
-service wifi-net-sh /vendor/bin/init.wifi.sh
- class late_start
- user root
- group root wakelock wifi
- oneshot
- disabled # Started on post-fs-data
-
on property:sys.boot_completed=1
trigger sys-boot-completed-set
diff --git a/shared/device.mk b/shared/device.mk
index 957711b41..80059c051 100644
--- a/shared/device.mk
+++ b/shared/device.mk
@@ -32,6 +32,9 @@ VENDOR_SECURITY_PATCH = $(PLATFORM_SECURITY_PATCH)
# Set boot SPL
BOOT_SECURITY_PATCH = $(PLATFORM_SECURITY_PATCH)
+PRODUCT_VENDOR_PROPERTIES += \
+ ro.vendor.boot_security_patch=$(BOOT_SECURITY_PATCH)
+
PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for vulkan
PRODUCT_SOONG_NAMESPACES += device/generic/goldfish # for audio and wifi
@@ -39,6 +42,11 @@ PRODUCT_SHIPPING_API_LEVEL := 33
PRODUCT_USE_DYNAMIC_PARTITIONS := true
DISABLE_RILD_OEM_HOOK := true
+# TODO(b/205788876) remove this condition when openwrt has an image for arm.
+ifndef PRODUCT_ENFORCE_MAC80211_HWSIM
+PRODUCT_ENFORCE_MAC80211_HWSIM := true
+endif
+
PRODUCT_SET_DEBUGFS_RESTRICTIONS := true
PRODUCT_SOONG_NAMESPACES += device/generic/goldfish-opengl # for vulkan
@@ -72,6 +80,8 @@ AB_OTA_PARTITIONS += \
# Enable Virtual A/B
$(call inherit-product, $(SRC_TARGET_DIR)/product/virtual_ab_ota/compression_with_xor.mk)
+PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.userspace.snapshots.enabled=true
+
# Enable Scoped Storage related
$(call inherit-product, $(SRC_TARGET_DIR)/product/emulated_storage.mk)
@@ -192,8 +202,7 @@ PRODUCT_PACKAGES += \
PRODUCT_PACKAGES += \
libEGL_angle \
libGLESv1_CM_angle \
- libGLESv2_angle \
- libfeature_support_angle.so
+ libGLESv2_angle
# GL implementation for virgl
PRODUCT_PACKAGES += \
@@ -290,6 +299,7 @@ PRODUCT_COPY_FILES += \
hardware/google/camera/devices/EmulatedCamera/hwl/configs/emu_camera_back.json:$(TARGET_COPY_OUT_VENDOR)/etc/config/emu_camera_back.json \
hardware/google/camera/devices/EmulatedCamera/hwl/configs/emu_camera_front.json:$(TARGET_COPY_OUT_VENDOR)/etc/config/emu_camera_front.json \
hardware/google/camera/devices/EmulatedCamera/hwl/configs/emu_camera_depth.json:$(TARGET_COPY_OUT_VENDOR)/etc/config/emu_camera_depth.json \
+ frameworks/native/data/etc/android.hardware.consumerir.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.consumerir.xml \
device/google/cuttlefish/shared/config/init.vendor.rc:$(TARGET_COPY_OUT_VENDOR)/etc/init/init.cutf_cvm.rc \
device/google/cuttlefish/shared/config/init.product.rc:$(TARGET_COPY_OUT_PRODUCT)/etc/init/init.rc \
device/google/cuttlefish/shared/config/ueventd.rc:$(TARGET_COPY_OUT_VENDOR)/etc/ueventd.rc \
@@ -307,14 +317,6 @@ PRODUCT_COPY_FILES += \
frameworks/av/services/audiopolicy/config/surround_sound_configuration_5_0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/surround_sound_configuration_5_0.xml \
device/google/cuttlefish/shared/config/task_profiles.json:$(TARGET_COPY_OUT_VENDOR)/etc/task_profiles.json \
-# TODO(b/205065320): remove this when wifi vendor apex support mac80211_hwsim
-ifeq ($(PRODUCT_ENFORCE_MAC80211_HWSIM),true)
-PRODUCT_COPY_FILES += \
- frameworks/native/data/etc/android.hardware.wifi.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.wifi.xml \
- frameworks/native/data/etc/android.hardware.wifi.passpoint.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.wifi.passpoint.xml \
-
-endif
-
ifeq ($(LOCAL_PREFER_VENDOR_APEX),true)
PRODUCT_PACKAGES += com.google.cf.input.config
else
@@ -368,6 +370,12 @@ PRODUCT_PACKAGES += \
android.hardware.weaver-service.example
#
+# IR aidl HAL
+#
+PRODUCT_PACKAGES += \
+ android.hardware.ir-service.example
+
+#
# OemLock aidl HAL
#
PRODUCT_PACKAGES += \
@@ -506,7 +514,7 @@ PRODUCT_PACKAGES += $(LOCAL_CONFIRMATIONUI_PRODUCT_PACKAGE)
# Dumpstate HAL
#
ifeq ($(LOCAL_DUMPSTATE_PRODUCT_PACKAGE),)
- LOCAL_DUMPSTATE_PRODUCT_PACKAGE := android.hardware.dumpstate@1.1-service.example
+ LOCAL_DUMPSTATE_PRODUCT_PACKAGE += android.hardware.dumpstate-service.example
endif
PRODUCT_PACKAGES += $(LOCAL_DUMPSTATE_PRODUCT_PACKAGE)
@@ -544,8 +552,9 @@ PRODUCT_PACKAGES += \
# Health
ifeq ($(LOCAL_HEALTH_PRODUCT_PACKAGE),)
LOCAL_HEALTH_PRODUCT_PACKAGE := \
- android.hardware.health@2.1-impl-cuttlefish \
- android.hardware.health@2.1-service
+ android.hardware.health-service.cuttlefish \
+ android.hardware.health-service.cuttlefish_recovery \
+
endif
PRODUCT_PACKAGES += $(LOCAL_HEALTH_PRODUCT_PACKAGE)
@@ -555,7 +564,7 @@ PRODUCT_PACKAGES += \
# Identity Credential
PRODUCT_PACKAGES += \
- android.hardware.identity-service.example
+ android.hardware.identity-service.remote
# Input Classifier HAL
PRODUCT_PACKAGES += \
@@ -633,8 +642,13 @@ PRODUCT_PACKAGES += \
#
# USB
+ifeq ($(LOCAL_PREFER_VENDOR_APEX),true)
+PRODUCT_PACKAGES += \
+ com.android.hardware.usb
+else
PRODUCT_PACKAGES += \
android.hardware.usb@1.0-service
+endif
# Vibrator HAL
ifeq ($(LOCAL_PREFER_VENDOR_APEX),true)
@@ -684,23 +698,35 @@ PRODUCT_PACKAGES += linker.recovery shell_and_utilities_recovery
endif
# wifi
-
-# TODO(b/205065320): remove this when wifi vendor apex support mac80211_hwsim
-LOCAL_USE_WIFI_VENDOR_APEX := false
-ifneq ($(PRODUCT_ENFORCE_MAC80211_HWSIM),true)
ifeq ($(LOCAL_PREFER_VENDOR_APEX),true)
-LOCAL_USE_WIFI_VENDOR_APEX := true
-endif
-endif
-
-ifeq ($(LOCAL_USE_WIFI_VENDOR_APEX),true)
+ifneq ($(PRODUCT_ENFORCE_MAC80211_HWSIM),true)
PRODUCT_PACKAGES += com.google.cf.wifi
+# Demonstrate multi-installed vendor APEXes by installing another wifi HAL vendor APEX
+# which does not include the passpoint feature XML.
+#
+# The default is set in BoardConfig.mk using bootconfig.
+# This can be changed at CVD launch-time using
+# --extra_bootconfig_args "androidboot.vendor.apex.com.android.wifi.hal:=X"
+# or post-launch, at runtime using
+# setprop persist.vendor.apex.com.android.wifi.hal X && reboot
+# where X is the name of the APEX file to use.
+PRODUCT_PACKAGES += com.google.cf.wifi.no-passpoint
+
$(call add_soong_config_namespace, wpa_supplicant)
$(call add_soong_config_var_value, wpa_supplicant, platform_version, $(PLATFORM_VERSION))
$(call add_soong_config_var_value, wpa_supplicant, nl80211_driver, CONFIG_DRIVER_NL80211_QCA)
-# TODO(b/205065320): Convert com.google.cf.wifi to use mac8011_hwsim_virtio
PRODUCT_VENDOR_PROPERTIES += ro.vendor.wifi_impl=virt_wifi
else
+PRODUCT_SOONG_NAMESPACES += device/google/cuttlefish/apex/com.google.cf.wifi_hwsim
+PRODUCT_PACKAGES += com.google.cf.wifi_hwsim
+$(call add_soong_config_namespace, wpa_supplicant)
+$(call add_soong_config_var_value, wpa_supplicant, platform_version, $(PLATFORM_VERSION))
+$(call add_soong_config_var_value, wpa_supplicant, nl80211_driver, CONFIG_DRIVER_NL80211_QCA)
+PRODUCT_VENDOR_PROPERTIES += ro.vendor.wifi_impl=mac8011_hwsim_virtio
+
+$(call soong_config_append,cvdhost,enforce_mac80211_hwsim,true)
+endif
+else
PRODUCT_PACKAGES += \
rename_netiface \
@@ -718,10 +744,8 @@ ifeq ($(PRODUCT_ENFORCE_MAC80211_HWSIM),true)
PRODUCT_PACKAGES += \
mac80211_create_radios \
hostapd \
- android.hardware.wifi@1.0-service
-
-PRODUCT_COPY_FILES += \
- device/google/cuttlefish/guest/services/wifi/init.wifi.sh:$(TARGET_COPY_OUT_VENDOR)/bin/init.wifi.sh \
+ android.hardware.wifi@1.0-service \
+ init.wifi.sh
PRODUCT_VENDOR_PROPERTIES += ro.vendor.wifi_impl=mac8011_hwsim_virtio
diff --git a/shared/permissions/cuttlefish_excluded_hardware.xml b/shared/permissions/cuttlefish_excluded_hardware.xml
index c3d03d597..11e9c429b 100644
--- a/shared/permissions/cuttlefish_excluded_hardware.xml
+++ b/shared/permissions/cuttlefish_excluded_hardware.xml
@@ -16,4 +16,7 @@
<permissions>
<unavailable-feature name="android.software.print" />
<unavailable-feature name="android.software.voice_recognizers" />
+<!-- b/208794808 Exclude this feature to skip the CTS test
+ android.telephony.cts.TelephonyManagerTest#testSimSlotMapping -->
+ <unavailable-feature name="android.hardware.telephony.subscription" />
</permissions>
diff --git a/shared/sepolicy/OWNERS b/shared/sepolicy/OWNERS
index 2975ddf68..9b37b0ee9 100644
--- a/shared/sepolicy/OWNERS
+++ b/shared/sepolicy/OWNERS
@@ -1,12 +1,4 @@
-adamshih@google.com
+include platform/system/sepolicy:/OWNERS
+
adelva@google.com
-alanstokes@google.com
-bowgotsai@google.com
-jbires@google.com
-jeffv@google.com
-jgalenson@google.com
-jiyong@google.com
-nnk@google.com
-smoreland@google.com
-sspatil@google.com
-trong@google.com
+rurumihong@google.com
diff --git a/shared/sepolicy/system_ext/private/platform_app.te b/shared/sepolicy/system_ext/private/platform_app.te
index def6ca2fd..3a789d8ef 100644
--- a/shared/sepolicy/system_ext/private/platform_app.te
+++ b/shared/sepolicy/system_ext/private/platform_app.te
@@ -1 +1,4 @@
get_prop(platform_app, vendor_aware_available_prop)
+
+# allow systemui to set boot animation colors
+set_prop(platform_app, bootanim_system_prop);
diff --git a/shared/sepolicy/system_ext/private/property_contexts b/shared/sepolicy/system_ext/private/property_contexts
index 13c17c3fd..6d6af0741 100644
--- a/shared/sepolicy/system_ext/private/property_contexts
+++ b/shared/sepolicy/system_ext/private/property_contexts
@@ -7,3 +7,9 @@ persist.vendor.radio.videopause.mode u:object_r:vendor_radio_prop:s0
persist.vendor.radio.smlog_switch u:object_r:vendor_radio_prop:s0
ro.vendor.radio.log_loc u:object_r:vendor_radio_prop:s0
ro.vendor.radio.log_prefix u:object_r:vendor_radio_prop:s0
+
+# Boot animation dynamic colors
+persist.bootanim.color1 u:object_r:bootanim_system_prop:s0 exact int
+persist.bootanim.color2 u:object_r:bootanim_system_prop:s0 exact int
+persist.bootanim.color3 u:object_r:bootanim_system_prop:s0 exact int
+persist.bootanim.color4 u:object_r:bootanim_system_prop:s0 exact int
diff --git a/shared/sepolicy/vendor/bug_map b/shared/sepolicy/vendor/bug_map
index bf7f79080..86b097a8b 100644
--- a/shared/sepolicy/vendor/bug_map
+++ b/shared/sepolicy/vendor/bug_map
@@ -1,3 +1,4 @@
+init init capability b/199386018
init system_lib_file dir b/133444385
init system_lib_file file b/133444385
kernel kernel capability b/179966921
diff --git a/shared/sepolicy/vendor/file_contexts b/shared/sepolicy/vendor/file_contexts
index 55b8d964e..4708f1f55 100644
--- a/shared/sepolicy/vendor/file_contexts
+++ b/shared/sepolicy/vendor/file_contexts
@@ -4,6 +4,7 @@
/dev/block/by-name/misc u:object_r:misc_block_device:s0
/dev/block/by-name/boot_[ab] u:object_r:boot_block_device:s0
+/dev/block/by-name/init_boot_[ab] u:object_r:boot_block_device:s0
/dev/block/by-name/vendor_boot_[ab] u:object_r:boot_block_device:s0
/dev/block/by-name/vbmeta_[ab] u:object_r:ab_block_device:s0
/dev/block/by-name/vbmeta_system_[ab] u:object_r:ab_block_device:s0
@@ -72,6 +73,7 @@
/vendor/bin/hw/android\.hardware\.drm@[0-9]+\.[0-9]+-service-lazy\.widevine u:object_r:hal_drm_widevine_exec:s0
/vendor/bin/hw/android\.hardware\.graphics\.allocator@4\.0-service\.minigbm u:object_r:hal_graphics_allocator_default_exec:s0
/vendor/bin/hw/android\.hardware\.gatekeeper@1\.0-service\.software u:object_r:hal_gatekeeper_default_exec:s0
+/vendor/bin/hw/android\.hardware\.health-service\.cuttlefish u:object_r:hal_health_default_exec:s0
/vendor/bin/hw/android\.hardware\.health\.storage-service\.cuttlefish u:object_r:hal_health_storage_default_exec:s0
/vendor/bin/hw/android\.hardware\.lights-service\.example u:object_r:hal_light_default_exec:s0
/vendor/bin/hw/android\.hardware\.neuralnetworks@1\.3-service-sample-.* u:object_r:hal_neuralnetworks_sample_exec:s0
@@ -84,6 +86,7 @@
/vendor/bin/hw/android\.hardware\.sensors@2\.1-service\.mock u:object_r:hal_sensors_default_exec:s0
/vendor/bin/hw/android\.hardware\.input\.classifier@1\.0-service.default u:object_r:hal_input_classifier_default_exec:s0
/vendor/bin/hw/android\.hardware\.thermal@2\.0-service\.mock u:object_r:hal_thermal_default_exec:s0
+/vendor/bin/hw/android\.hardware\.identity-service\.remote u:object_r:hal_identity_remote_exec:s0
/vendor/bin/hw/android\.hardware\.security\.keymint-service\.remote u:object_r:hal_keymint_remote_exec:s0
/vendor/bin/hw/android\.hardware\.keymaster@4\.1-service.remote u:object_r:hal_keymaster_remote_exec:s0
/vendor/bin/hw/android\.hardware\.gatekeeper@1\.0-service.remote u:object_r:hal_gatekeeper_remote_exec:s0
@@ -94,7 +97,7 @@
/vendor/bin/hw/android\.hardware\.authsecret-service.example u:object_r:hal_authsecret_default_exec:s0
/vendor/bin/hw/android\.hardware\.rebootescrow-service\.default u:object_r:hal_rebootescrow_default_exec:s0
/vendor/bin/dlkm_loader u:object_r:dlkm_loader_exec:s0
-/vendor/bin/init\.wifi\.sh u:object_r:init_wifi_sh_exec:s0
+/vendor/bin/init_wifi\.sh u:object_r:init_wifi_sh_exec:s0
/vendor/lib(64)?/libdrm.so u:object_r:same_process_hal_file:s0
/vendor/lib(64)?/libglapi.so u:object_r:same_process_hal_file:s0
diff --git a/shared/sepolicy/vendor/genfs_contexts b/shared/sepolicy/vendor/genfs_contexts
index cf70691fd..a62ae445a 100644
--- a/shared/sepolicy/vendor/genfs_contexts
+++ b/shared/sepolicy/vendor/genfs_contexts
@@ -41,6 +41,8 @@ cf_rtc_wakeup_alarmtimer(/devices/platform/rtc_cmos, 0, 1)
#genfscon sysfs /devices/platform/GFSH0001:00/power_supply u:object_r:sysfs_batteryinfo:s0
#genfscon sysfs /devices/platform/GFSH0001:00/power_supply/ac/wakeup3 u:object_r:sysfs_wakeup:s0
#genfscon sysfs /devices/platform/GFSH0001:00/power_supply/battery/wakeup4 u:object_r:sysfs_wakeup:s0
+genfscon sysfs /devices/virtual/mac80211_hwsim/hwsim0/net u:object_r:sysfs_net:s0
+genfscon sysfs /devices/virtual/mac80211_hwsim/hwsim1/net u:object_r:sysfs_net:s0
# crosvm (arm64)
cf_pci_block_device(/devices/platform/10000.pci, 0x7, 7)
diff --git a/shared/sepolicy/vendor/hal_identity_remote.te b/shared/sepolicy/vendor/hal_identity_remote.te
new file mode 100644
index 000000000..3f892266f
--- /dev/null
+++ b/shared/sepolicy/vendor/hal_identity_remote.te
@@ -0,0 +1,5 @@
+type hal_identity_remote, domain;
+hal_server_domain(hal_identity_remote, hal_identity)
+
+type hal_identity_remote_exec, exec_type, vendor_file_type, file_type;
+init_daemon_domain(hal_identity_remote)
diff --git a/shared/sepolicy/vendor/hal_keymint_remote.te b/shared/sepolicy/vendor/hal_keymint_remote.te
index 578d1ad8a..7d5f6d569 100644
--- a/shared/sepolicy/vendor/hal_keymint_remote.te
+++ b/shared/sepolicy/vendor/hal_keymint_remote.te
@@ -12,3 +12,4 @@ allow hal_keymint_remote kmsg_device:chr_file w_file_perms;
allow hal_keymint_remote kmsg_device:chr_file getattr;
get_prop(hal_keymint_remote, vendor_security_patch_level_prop)
+get_prop(hal_keymint_remote, vendor_boot_security_patch_level_prop)
diff --git a/shared/sepolicy/system_ext/private/mediatranscoding.te b/shared/sepolicy/vendor/mediatranscoding.te
index 47f6d8e76..47f6d8e76 100644
--- a/shared/sepolicy/system_ext/private/mediatranscoding.te
+++ b/shared/sepolicy/vendor/mediatranscoding.te
diff --git a/shared/sepolicy/vendor/property.te b/shared/sepolicy/vendor/property.te
index a727365b2..679941be6 100644
--- a/shared/sepolicy/vendor/property.te
+++ b/shared/sepolicy/vendor/property.te
@@ -1,2 +1,3 @@
vendor_restricted_prop(vendor_cuttlefish_config_server_port_prop)
vendor_internal_prop(vendor_modem_simulator_ports_prop)
+vendor_internal_prop(vendor_boot_security_patch_level_prop)
diff --git a/shared/sepolicy/vendor/property_contexts b/shared/sepolicy/vendor/property_contexts
index 6775db685..9369d444a 100644
--- a/shared/sepolicy/vendor/property_contexts
+++ b/shared/sepolicy/vendor/property_contexts
@@ -11,5 +11,6 @@ ro.boot.modem_simulator_ports u:object_r:vendor_modem_simulator_ports_prop:s0
ro.boot.vsock_touch_port u:object_r:vendor_vsock_touch_port:s0
ro.boot.wifi_mac_prefix u:object_r:vendor_wifi_mac_prefix:s0 exact string
ro.vendor.wifi_impl u:object_r:vendor_wifi_impl:s0 exact string
+ro.vendor.boot_security_patch u:object_r:vendor_boot_security_patch_level_prop:s0
vendor.bt.rootcanal_mac_address u:object_r:vendor_bt_rootcanal_prop:s0
vendor.bt.rootcanal_test_console u:object_r:vendor_bt_rootcanal_prop:s0
diff --git a/shared/sepolicy/vendor/vendor_init.te b/shared/sepolicy/vendor/vendor_init.te
index bcda4a392..5bddb1473 100644
--- a/shared/sepolicy/vendor/vendor_init.te
+++ b/shared/sepolicy/vendor/vendor_init.te
@@ -10,3 +10,5 @@ get_prop(vendor_init, vendor_graphics_config_prop)
vendor_internal_prop(vendor_wifi_impl)
set_prop(vendor_init, vendor_wifi_impl)
+
+set_prop(vendor_init, vendor_boot_security_patch_level_prop)
diff --git a/shared/tv/OWNERS b/shared/tv/OWNERS
new file mode 100644
index 000000000..4df9f2711
--- /dev/null
+++ b/shared/tv/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 760438
+include device/google/atv:/OWNERS
diff --git a/shared/tv/device.mk b/shared/tv/device.mk
index 9b78ee197..f0ae93db5 100644
--- a/shared/tv/device.mk
+++ b/shared/tv/device.mk
@@ -40,6 +40,9 @@ PRODUCT_PROPERTY_OVERRIDES += ro.hdmi.device_type=4
# Tuner HAL
PRODUCT_PACKAGES += android.hardware.tv.tuner@1.1-service
+# Fallback IME and Home apps
+PRODUCT_PACKAGES += LeanbackIME TvSampleLeanbackLauncher TvProvision
+
# Enabling managed profiles
DEVICE_PACKAGE_OVERLAYS += device/google/cuttlefish/shared/tv/overlay
diff --git a/tests/hal/hal_implementation_test.cpp b/tests/hal/hal_implementation_test.cpp
index 1f36b3739..66b8225d6 100644
--- a/tests/hal/hal_implementation_test.cpp
+++ b/tests/hal/hal_implementation_test.cpp
@@ -54,6 +54,7 @@ static const std::set<std::string> kKnownMissingHidl = {
"android.hardware.cas.native@1.0",
"android.hardware.configstore@1.1", // deprecated, see b/149050985, b/149050733
"android.hardware.fastboot@1.1",
+ "android.hardware.dumpstate@1.1", // deprecated, see b/205760700
"android.hardware.gnss.measurement_corrections@1.1", // is sub-interface of gnss
"android.hardware.gnss.visibility_control@1.0",
"android.hardware.graphics.allocator@2.0",
@@ -63,7 +64,8 @@ static const std::set<std::string> kKnownMissingHidl = {
"android.hardware.graphics.mapper@2.1",
"android.hardware.graphics.mapper@3.0",
"android.hardware.health.storage@1.0", // converted to AIDL, see b/177470478
- "android.hardware.ir@1.0",
+ "android.hardware.health@2.1", // converted to AIDL, see b/177269435
+ "android.hardware.ir@1.0", // converted to AIDL, see b/205000342
"android.hardware.keymaster@3.0",
"android.hardware.keymaster@4.1", // Replaced by KeyMint
"android.hardware.light@2.0",
@@ -90,8 +92,6 @@ static const std::set<std::string> kKnownMissingHidl = {
"android.hardware.vibrator@1.3",
"android.hardware.vr@1.0",
"android.hardware.weaver@1.0",
- "android.hardware.wifi@1.5",
- "android.hardware.wifi.hostapd@1.3",
"android.hardware.wifi.offload@1.0",
"android.hidl.base@1.0",
"android.hidl.memory.token@1.0",
@@ -109,6 +109,7 @@ struct VersionedAidlPackage {
static const std::set<VersionedAidlPackage> kKnownMissingAidl = {
// types-only packages, which never expect a default implementation
+ {"android.hardware.audio.common.", 1},
{"android.hardware.biometrics.common.", 1},
{"android.hardware.common.", 1},
{"android.hardware.common.", 2},
@@ -119,6 +120,13 @@ static const std::set<VersionedAidlPackage> kKnownMissingAidl = {
// No implementations on cuttlefish for wifi aidl hal
{"android.hardware.wifi.hostapd.", 1},
+ // No implementations on cuttlefish for omapi aidl hal
+ {"android.se.omapi.", 1},
+
+ // Temporarily treat the dice hal default implementation as missing until it
+ // and its dependencies have landed. b/198197213
+ {"android.hardware.security.dice.", 1},
+
// These KeyMaster types are in an AIDL types-only HAL because they're used
// by the Identity Credential AIDL HAL. Remove this when fully porting
// KeyMaster to AIDL.
@@ -126,6 +134,8 @@ static const std::set<VersionedAidlPackage> kKnownMissingAidl = {
{"android.hardware.keymaster.", 2},
{"android.hardware.keymaster.", 3},
+ {"android.media.audio.common.", 1},
+
// These types are only used in Automotive.
{"android.automotive.computepipe.registry.", 1},
{"android.automotive.computepipe.runner.", 1},
@@ -136,11 +146,8 @@ static const std::set<VersionedAidlPackage> kKnownMissingAidl = {
{"android.hardware.automotive.audiocontrol.", 1},
{"android.hardware.automotive.occupant_awareness.", 1},
- // This version needs to be implemented (b/190505425)
- {"android.system.keystore2.", 2},
-
- // This version needs to be implemented (b/177269435)
- {"android.hardware.health.", 1},
+ // No implementation in AOSP for supplicant aidl hal (b/210166896)
+ {"android.hardware.wifi.supplicant.", 1},
// These versions need to be implemented (b/198331776)
{"android.hardware.radio.", 1},
@@ -153,6 +160,9 @@ static const std::set<VersionedAidlPackage> kKnownMissingAidl = {
// This version needs to be implemented (b/198331886)
{"android.hardware.radio.config.", 1},
+
+ // These versions need to be implemented (b/203490261)
+ {"android.hardware.bluetooth.audio.", 1},
};
static const std::set<VersionedAidlPackage> kComingSoonAidl = {
diff --git a/tools/create_base_image_gce.sh b/tools/create_base_image_gce.sh
index 396f67deb..c87534054 100755
--- a/tools/create_base_image_gce.sh
+++ b/tools/create_base_image_gce.sh
@@ -60,6 +60,10 @@ sudo chroot /mnt/image /usr/bin/apt install -y screen # needed by tradefed
sudo chroot /mnt/image /usr/bin/find /home -ls
+# update QEMU version to most recent backport
+sudo chroot /mnt/image /usr/bin/apt install -y --only-upgrade qemu-system-x86 -t bullseye-backports
+sudo chroot /mnt/image /usr/bin/apt install -y --only-upgrade qemu-system-arm -t bullseye-backports
+
# Install GPU driver dependencies
sudo chroot /mnt/image /usr/bin/apt install -y gcc
sudo chroot /mnt/image /usr/bin/apt install -y linux-source
diff --git a/vsoc_arm64/auto/aosp_cf.mk b/vsoc_arm64/auto/aosp_cf.mk
index 51ae7a3a4..0f8c417ce 100644
--- a/vsoc_arm64/auto/aosp_cf.mk
+++ b/vsoc_arm64/auto/aosp_cf.mk
@@ -26,3 +26,6 @@ PRODUCT_MODEL := Cuttlefish arm64 auto
PRODUCT_VENDOR_PROPERTIES += \
ro.soc.manufacturer=$(PRODUCT_MANUFACTURER) \
ro.soc.model=$(PRODUCT_DEVICE)
+
+# TODO(b/205788876) remove this when openwrt has an image for arm.
+PRODUCT_ENFORCE_MAC80211_HWSIM := false \ No newline at end of file
diff --git a/vsoc_arm64/phone/aosp_cf.mk b/vsoc_arm64/phone/aosp_cf.mk
index b33e52372..86fe29e0e 100644
--- a/vsoc_arm64/phone/aosp_cf.mk
+++ b/vsoc_arm64/phone/aosp_cf.mk
@@ -37,6 +37,8 @@ $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)
# All components inherited here go to vendor image
#
$(call inherit-product, device/google/cuttlefish/shared/phone/device_vendor.mk)
+# TODO(b/205788876) remove this when openwrt has an image for arm.
+PRODUCT_ENFORCE_MAC80211_HWSIM := false
# Nested virtualization support
$(call inherit-product, packages/modules/Virtualization/apex/product_packages.mk)
diff --git a/vsoc_arm64_only/phone/aosp_cf.mk b/vsoc_arm64_only/phone/aosp_cf.mk
index 5bcfc7b19..0da151faa 100644
--- a/vsoc_arm64_only/phone/aosp_cf.mk
+++ b/vsoc_arm64_only/phone/aosp_cf.mk
@@ -39,6 +39,9 @@ $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)
LOCAL_DISABLE_OMX := true
$(call inherit-product, device/google/cuttlefish/shared/phone/device_vendor.mk)
+# TODO(b/205788876) remove this when openwrt has an image for arm.
+PRODUCT_ENFORCE_MAC80211_HWSIM := false
+
# Nested virtualization support
$(call inherit-product, packages/modules/Virtualization/apex/product_packages.mk)
diff --git a/vsoc_arm_only/phone/aosp_cf.mk b/vsoc_arm_only/phone/aosp_cf.mk
index 3bd16a81b..3ba708a82 100644
--- a/vsoc_arm_only/phone/aosp_cf.mk
+++ b/vsoc_arm_only/phone/aosp_cf.mk
@@ -51,6 +51,9 @@ PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST += \
#
$(call inherit-product, device/google/cuttlefish/shared/phone/device_vendor.mk)
+# TODO(b/205788876) remove this when openwrt has an image for arm.
+PRODUCT_ENFORCE_MAC80211_HWSIM := false
+
#
# Special settings for the target
#
diff --git a/vsoc_x86/go_phone/OWNERS b/vsoc_x86/go_phone/OWNERS
new file mode 100644
index 000000000..0c77d0e42
--- /dev/null
+++ b/vsoc_x86/go_phone/OWNERS
@@ -0,0 +1,2 @@
+rajekumar@google.com
+tjoines@google.com
diff --git a/vsoc_x86/go_phone/device.mk b/vsoc_x86/go_phone/device.mk
index 6fe021b7d..394e35992 100644
--- a/vsoc_x86/go_phone/device.mk
+++ b/vsoc_x86/go_phone/device.mk
@@ -22,7 +22,7 @@ PRODUCT_NAME := aosp_cf_x86_go_phone
PRODUCT_DEVICE := vsoc_x86
PRODUCT_MANUFACTURER := Google
PRODUCT_MODEL := Cuttlefish x86 Go phone
-PRODUCT_PACKAGE_OVERLAYS := device/google/cuttlefish/vsoc_x86/phone/overlay
+PRODUCT_PACKAGE_OVERLAYS := device/google/cuttlefish/vsoc_x86/go_phone/overlay
PRODUCT_VENDOR_PROPERTIES += \
ro.soc.manufacturer=$(PRODUCT_MANUFACTURER) \
diff --git a/vsoc_x86/go_phone/overlay/frameworks/base/core/res/res/values/config.xml b/vsoc_x86/go_phone/overlay/frameworks/base/core/res/res/values/config.xml
new file mode 100644
index 000000000..da1dca261
--- /dev/null
+++ b/vsoc_x86/go_phone/overlay/frameworks/base/core/res/res/values/config.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2022, 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- True if the device requires AppWidgetService even if it does not have
+ the PackageManager.FEATURE_APP_WIDGETS feature -->
+ <bool name="config_enableAppWidgetService">true</bool>
+
+ <!-- circle shape icon mask to be used for {@link AdaptiveIconDrawable} -->
+ <string name="config_icon_mask" translatable="false">"M50 0C77.6 0 100 22.4 100 50C100 77.6 77.6 100 50 100C22.4 100 0 77.6 0 50C0 22.4 22.4 0 50 0Z"</string>
+</resources>
diff --git a/vsoc_x86_64/tv/OWNERS b/vsoc_x86_64/tv/OWNERS
new file mode 100644
index 000000000..4df9f2711
--- /dev/null
+++ b/vsoc_x86_64/tv/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 760438
+include device/google/atv:/OWNERS
diff --git a/vsoc_x86_64/tv/device.mk b/vsoc_x86_64/tv/device.mk
index d25210dd7..7ab18cdf2 100644
--- a/vsoc_x86_64/tv/device.mk
+++ b/vsoc_x86_64/tv/device.mk
@@ -15,10 +15,12 @@
#
$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)
+$(call inherit-product, device/google/atv/products/atv_base.mk)
$(call inherit-product, device/google/cuttlefish/shared/tv/device.mk)
$(call inherit-product, device/google/cuttlefish/vsoc_x86_64/kernel.mk)
$(call inherit-product, device/google/cuttlefish/vsoc_x86_64/bootloader.mk)
+PRODUCT_BRAND := generic
PRODUCT_NAME := aosp_cf_x86_64_tv
PRODUCT_DEVICE := vsoc_x86_64
PRODUCT_MANUFACTURER := Google