diff --git a/radio/1.6/IRadio.hal b/radio/1.6/IRadio.hal index a398e7d07f..4e9dcdb792 100644 --- a/radio/1.6/IRadio.hal +++ b/radio/1.6/IRadio.hal @@ -18,9 +18,12 @@ package android.hardware.radio@1.6; import @1.0::CdmaSmsMessage; +import @1.0::Dial; import @1.0::GsmSmsMessage; import @1.1::CardPowerState; import @1.2::DataRequestReason; +import @1.4::EmergencyCallRouting; +import @1.4::EmergencyServiceCategory; import @1.4::RadioAccessFamily; import @1.5::IRadio; import @1.5::AccessNetwork; @@ -374,6 +377,64 @@ interface IRadio extends @1.5::IRadio { DataThrottlingAction dataThrottlingAction, int64_t completionDurationMillis); + /** + * Initiate emergency voice call, with zero or more emergency service category(s), zero or + * more emergency Uniform Resource Names (URN), and routing information for handling the call. + * Android uses this request to make its emergency call instead of using @1.0::IRadio.dial + * if the 'address' in the 'dialInfo' field is identified as an emergency number by Android. + * + * In multi-sim scenario, if the emergency number is from a specific subscription, this radio + * request can still be sent out on the other subscription as long as routing is set to + * @1.4::EmergencyNumberRouting#EMERGENCY. This radio request will not be sent on an inactive + * (PIN/PUK locked) subscription unless both subscriptions are PIN/PUK locked. In this case, + * the request will be sent on the primary subscription. + * + * Some countries or carriers require some emergency numbers that must be handled with normal + * call routing if possible or emergency routing. 1) if the 'routing' field is specified as + * @1.4::EmergencyNumberRouting#NORMAL, the implementation must try the full radio service to + * use normal call routing to handle the call; if service cannot support normal routing, the + * implementation must use emergency routing to handle the call. 2) if 'routing' is specified + * as @1.4::EmergencyNumberRouting#EMERGENCY, the implementation must use emergency routing to + * handle the call. 3) if 'routing' is specified as @1.4::EmergencyNumberRouting#UNKNOWN, + * Android does not know how to handle the call. + * + * If the dialed emergency number does not have a specified emergency service category, the + * 'categories' field is set to @1.4::EmergencyServiceCategory#UNSPECIFIED; if the dialed + * emergency number does not have specified emergency Uniform Resource Names, the 'urns' field + * is set to an empty list. If the underlying technology used to request emergency services + * does not support the emergency service category or emergency uniform resource names, the + * field 'categories' or 'urns' may be ignored. + * + * In the scenarios that the 'address' in the 'dialInfo' field has other functions besides the + * emergency number function, if the 'hasKnownUserIntentEmergency' field is true, the user's + * intent for this dial request is emergency call, and the modem must treat this as an actual + * emergency dial; if the 'hasKnownUserIntentEmergency' field is false, Android does not know + * user's intent for this call. + * + * If 'isTesting' is true, this request is for testing purpose, and must not be sent to a real + * emergency service; otherwise it's for a real emergency call request. + * + * Reference: 3gpp 22.101, Section 10 - Emergency Calls; + * 3gpp 23.167, Section 6 - Functional description; + * 3gpp 24.503, Section 5.1.6.8.1 - General; + * RFC 5031 + * + * @param serial Serial number of request. + * @param dialInfo the same @1.0::Dial information used by @1.0::IRadio.dial. + * @param categories bitfield<@1.4::EmergencyServiceCategory> the Emergency Service Category(s) + * of the call. + * @param urns the emergency Uniform Resource Names (URN) + * @param routing @1.4::EmergencyCallRouting the emergency call routing information. + * @param hasKnownUserIntentEmergency Flag indicating if user's intent for the emergency call + * is known. + * @param isTesting Flag indicating if this request is for testing purpose. + * + * Response function is IRadioResponse.emergencyDialResponse() + */ + oneway emergencyDial_1_6(int32_t serial, Dial dialInfo, + bitfield categories, vec urns, + EmergencyCallRouting routing, bool hasKnownUserIntentEmergency, bool isTesting); + /** * Get which bands the modem's background scan is acting on. * diff --git a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp index 8b872921ec..44900b8d49 100644 --- a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp +++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp @@ -411,6 +411,167 @@ TEST_P(RadioHidlTest_v1_6, setSimCardPower_1_6) { } } +/* + * Test IRadio.emergencyDial() for the response returned. + */ +TEST_P(RadioHidlTest_v1_6, emergencyDial_1_6) { + if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) { + ALOGI("Skipping emergencyDial because voice call is not supported in device"); + return; + } else { + ALOGI("Running emergencyDial because voice call is supported in device"); + } + + serial = GetRandomSerialNumber(); + + ::android::hardware::radio::V1_0::Dial dialInfo; + dialInfo.address = hidl_string("911"); + int categories = static_cast( + ::android::hardware::radio::V1_4::EmergencyServiceCategory::UNSPECIFIED); + std::vector urns = {""}; + ::android::hardware::radio::V1_4::EmergencyCallRouting routing = + ::android::hardware::radio::V1_4::EmergencyCallRouting::UNKNOWN; + + Return res = + radio_v1_6->emergencyDial_1_6(serial, dialInfo, categories, urns, routing, true, true); + ASSERT_OK(res); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo_v1_0.type); + EXPECT_EQ(serial, radioRsp_v1_6->rspInfo_v1_0.serial); + + ALOGI("emergencyDial, rspInfo_v1_0.error = %s\n", + toString(radioRsp_v1_6->rspInfo_v1_0.error).c_str()); + + ::android::hardware::radio::V1_0::RadioError rspEmergencyDial = + radioRsp_v1_6->rspInfo_v1_0.error; + // In DSDS or TSTS, we only check the result if the current slot is IN_SERVICE + // or Emergency_Only. + if (isDsDsEnabled() || isTsTsEnabled()) { + serial = GetRandomSerialNumber(); + radio_v1_6->getVoiceRegistrationState(serial); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + if (isVoiceEmergencyOnly(radioRsp_v1_6->voiceRegResp.regState) || + isVoiceInService(radioRsp_v1_6->voiceRegResp.regState)) { + EXPECT_EQ(::android::hardware::radio::V1_0::RadioError::NONE, rspEmergencyDial); + } + } else { + EXPECT_EQ(::android::hardware::radio::V1_0::RadioError::NONE, rspEmergencyDial); + } + + // Give some time for modem to establish the emergency call channel. + sleep(MODEM_EMERGENCY_CALL_ESTABLISH_TIME); + + // Disconnect all the potential established calls to prevent them affecting other tests. + clearPotentialEstablishedCalls(); +} + +/* + * Test IRadio.emergencyDial() with specified service and its response returned. + */ +TEST_P(RadioHidlTest_v1_6, emergencyDial_1_6_withServices) { + if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) { + ALOGI("Skipping emergencyDial because voice call is not supported in device"); + return; + } else { + ALOGI("Running emergencyDial because voice call is supported in device"); + } + + serial = GetRandomSerialNumber(); + + ::android::hardware::radio::V1_0::Dial dialInfo; + dialInfo.address = hidl_string("911"); + int categories = + static_cast(::android::hardware::radio::V1_4::EmergencyServiceCategory::AMBULANCE); + std::vector urns = {"urn:service:sos.ambulance"}; + ::android::hardware::radio::V1_4::EmergencyCallRouting routing = + ::android::hardware::radio::V1_4::EmergencyCallRouting::UNKNOWN; + + Return res = + radio_v1_6->emergencyDial_1_6(serial, dialInfo, categories, urns, routing, true, true); + ASSERT_OK(res); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo_v1_0.type); + EXPECT_EQ(serial, radioRsp_v1_6->rspInfo_v1_0.serial); + + ALOGI("emergencyDial_withServices, rspInfo_v1_0.error = %s\n", + toString(radioRsp_v1_6->rspInfo_v1_0.error).c_str()); + ::android::hardware::radio::V1_0::RadioError rspEmergencyDial = + radioRsp_v1_6->rspInfo_v1_0.error; + + // In DSDS or TSTS, we only check the result if the current slot is IN_SERVICE + // or Emergency_Only. + if (isDsDsEnabled() || isTsTsEnabled()) { + serial = GetRandomSerialNumber(); + radio_v1_6->getVoiceRegistrationState(serial); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + if (isVoiceEmergencyOnly(radioRsp_v1_6->voiceRegResp.regState) || + isVoiceInService(radioRsp_v1_6->voiceRegResp.regState)) { + EXPECT_EQ(::android::hardware::radio::V1_0::RadioError::NONE, rspEmergencyDial); + } + } else { + EXPECT_EQ(::android::hardware::radio::V1_0::RadioError::NONE, rspEmergencyDial); + } + // Give some time for modem to establish the emergency call channel. + sleep(MODEM_EMERGENCY_CALL_ESTABLISH_TIME); + + // Disconnect all the potential established calls to prevent them affecting other tests. + clearPotentialEstablishedCalls(); +} + +/* + * Test IRadio.emergencyDial() with known emergency call routing and its response returned. + */ +TEST_P(RadioHidlTest_v1_6, emergencyDial_1_6_withEmergencyRouting) { + if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) { + ALOGI("Skipping emergencyDial because voice call is not supported in device"); + return; + } else { + ALOGI("Running emergencyDial because voice call is supported in device"); + } + + serial = GetRandomSerialNumber(); + + ::android::hardware::radio::V1_0::Dial dialInfo; + dialInfo.address = hidl_string("911"); + int categories = static_cast( + ::android::hardware::radio::V1_4::EmergencyServiceCategory::UNSPECIFIED); + std::vector urns = {""}; + ::android::hardware::radio::V1_4::EmergencyCallRouting routing = + ::android::hardware::radio::V1_4::EmergencyCallRouting::EMERGENCY; + + Return res = + radio_v1_6->emergencyDial_1_6(serial, dialInfo, categories, urns, routing, true, true); + ASSERT_OK(res); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo_v1_0.type); + EXPECT_EQ(serial, radioRsp_v1_6->rspInfo_v1_0.serial); + + ALOGI("emergencyDial_withEmergencyRouting, rspInfo_v1_0.error = %s\n", + toString(radioRsp_v1_6->rspInfo_v1_0.error).c_str()); + ::android::hardware::radio::V1_0::RadioError rspEmergencyDial = + radioRsp_v1_6->rspInfo_v1_0.error; + + // In DSDS or TSTS, we only check the result if the current slot is IN_SERVICE + // or Emergency_Only. + if (isDsDsEnabled() || isTsTsEnabled()) { + serial = GetRandomSerialNumber(); + radio_v1_6->getVoiceRegistrationState(serial); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + if (isVoiceEmergencyOnly(radioRsp_v1_6->voiceRegResp.regState) || + isVoiceInService(radioRsp_v1_6->voiceRegResp.regState)) { + EXPECT_EQ(::android::hardware::radio::V1_0::RadioError::NONE, rspEmergencyDial); + } + } else { + EXPECT_EQ(::android::hardware::radio::V1_0::RadioError::NONE, rspEmergencyDial); + } + + // Give some time for modem to establish the emergency call channel. + sleep(MODEM_EMERGENCY_CALL_ESTABLISH_TIME); + + // Disconnect all the potential established calls to prevent them affecting other tests. + clearPotentialEstablishedCalls(); +} + /* * Test IRadio.getCurrentCalls_1_6() for the response returned. */ diff --git a/radio/1.6/vts/functional/radio_hidl_hal_test.cpp b/radio/1.6/vts/functional/radio_hidl_hal_test.cpp index 79c3cde745..59f768201d 100644 --- a/radio/1.6/vts/functional/radio_hidl_hal_test.cpp +++ b/radio/1.6/vts/functional/radio_hidl_hal_test.cpp @@ -74,6 +74,29 @@ std::cv_status RadioHidlTest_v1_6::wait() { return status; } +void RadioHidlTest_v1_6::clearPotentialEstablishedCalls() { + // Get the current call Id to hangup the established emergency call. + serial = GetRandomSerialNumber(); + radio_v1_6->getCurrentCalls_1_6(serial); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + + // Hang up to disconnect the established call channels. + for (const ::android::hardware::radio::V1_6::Call& call : radioRsp_v1_6->currentCalls) { + serial = GetRandomSerialNumber(); + radio_v1_6->hangup(serial, call.base.base.index); + ALOGI("Hang up to disconnect the established call channel: %d", call.base.base.index); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + // Give some time for modem to disconnect the established call channel. + sleep(MODEM_EMERGENCY_CALL_DISCONNECT_TIME); + } + + // Verify there are no more current calls. + serial = GetRandomSerialNumber(); + radio_v1_6->getCurrentCalls_1_6(serial); + EXPECT_EQ(std::cv_status::no_timeout, wait()); + EXPECT_EQ(0, radioRsp_v1_6->currentCalls.size()); +} + void RadioHidlTest_v1_6::updateSimCardStatus() { serial = GetRandomSerialNumber(); radio_v1_6->getIccCardStatus(serial); diff --git a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h index fbcd7a9339..e41b807484 100644 --- a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h +++ b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h @@ -70,7 +70,8 @@ class RadioResponse_v1_6 : public ::android::hardware::radio::V1_6::IRadioRespon ::android::hardware::radio::V1_6::RadioResponseInfo rspInfo; // Call - hidl_vec<::android::hardware::radio::V1_2::Call> currentCalls; + hidl_vec<::android::hardware::radio::V1_6::Call> currentCalls; + ::android::hardware::radio::V1_2::VoiceRegStateResult voiceRegResp; // Sms SendSmsResult sendSmsResult; diff --git a/radio/1.6/vts/functional/radio_response.cpp b/radio/1.6/vts/functional/radio_response.cpp index 7c5cf6df86..ba84fd45b3 100644 --- a/radio/1.6/vts/functional/radio_response.cpp +++ b/radio/1.6/vts/functional/radio_response.cpp @@ -816,8 +816,11 @@ Return RadioResponse_v1_6::getCellInfoListResponse_1_2( } Return RadioResponse_v1_6::getVoiceRegistrationStateResponse_1_2( - const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, - const ::android::hardware::radio::V1_2::VoiceRegStateResult& /*voiceRegResponse*/) { + const ::android::hardware::radio::V1_0::RadioResponseInfo& info, + const ::android::hardware::radio::V1_2::VoiceRegStateResult& voiceRegResponse) { + rspInfo_v1_0 = info; + voiceRegResp = voiceRegResponse; + parent_v1_6.notify(info.serial); return Void(); } @@ -1210,8 +1213,9 @@ Return RadioResponse_v1_6::getDataRegistrationStateResponse_1_6( Return RadioResponse_v1_6::getCurrentCallsResponse_1_6( const ::android::hardware::radio::V1_6::RadioResponseInfo& info, - const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::Call>& /*calls*/) { + const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::Call>& calls) { rspInfo = info; + currentCalls = calls; parent_v1_6.notify(info.serial); return Void(); }