diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-01-27 20:59:05 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-01-27 20:59:05 +0000 |
commit | e486d95b5a3af9a452f7d51fe18582a95f9c3b2d (patch) | |
tree | 18d7ad26d8cc41d78fec4fa01a9f4d6f1b480dbf | |
parent | f9e2ee5b70ab7288ea973dfac11f779cc8b20e21 (diff) | |
parent | 814e7b36d26b26af07c00173e89026e3df68171c (diff) | |
download | cuttlefish-emu-31-stable-release.tar.gz |
Snap for 8074862 from 814e7b36d26b26af07c00173e89026e3df68171c to emu-31-stable-releaseemu-31-stable-release
Change-Id: Id6da07d27a0cd04c7ccf18c089a60ad86a2b6a24
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) @@ -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 |