/****************************************************************************** @file: loc_eng_ni.cpp @brief: module for network initiated interactions DESCRIPTION LOC_API network initiated operation support INITIALIZATION AND SEQUENCING REQUIREMENTS ----------------------------------------------------------------------------- Copyright (c) 2009 QUALCOMM Incorporated. All Rights Reserved. QUALCOMM Proprietary and Confidential. ----------------------------------------------------------------------------- ******************************************************************************/ /*===================================================================== EDIT HISTORY FOR MODULE This section contains comments describing changes made to the module. Notice that changes are listed in reverse chronological order. when who what, where, why -------- --- ------------------------------------------------------- 07/30/09 dx Initial version $Id: ======================================================================*/ #define LOG_NDDEBUG 0 #define LOG_NIDEBUG 0 #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include <time.h> #include <rpc/rpc.h> #include <loc_api_rpc_glue.h> #include <loc_eng.h> #include <loc_eng_ni.h> #define LOG_TAG "lib_locapi" #include <utils/Log.h> // comment this out to enable logging // #undef ALOGD // #define ALOGD(...) {} /*============================================================================= * * DATA DECLARATION * *============================================================================*/ const GpsNiInterface sLocEngNiInterface = { sizeof(GpsNiInterface), loc_eng_ni_init, loc_eng_ni_respond, }; boolean loc_eng_ni_data_init = FALSE; loc_eng_ni_data_s_type loc_eng_ni_data; extern loc_eng_data_s_type loc_eng_data; /*============================================================================= * * FUNCTION DECLARATIONS * *============================================================================*/ /*=========================================================================== FUNCTION respond_from_enum DESCRIPTION Returns the name of the response RETURN VALUE response name string ===========================================================================*/ static const char* respond_from_enum(rpc_loc_ni_user_resp_e_type resp) { switch (resp) { case RPC_LOC_NI_LCS_NOTIFY_VERIFY_ACCEPT: return "accept"; case RPC_LOC_NI_LCS_NOTIFY_VERIFY_DENY: return "deny"; case RPC_LOC_NI_LCS_NOTIFY_VERIFY_NORESP: return "no response"; default: return NULL; } } /*=========================================================================== FUNCTION loc_ni_respond DESCRIPTION Displays the NI request and awaits user input. If a previous request is in session, the new one is handled using sys.ni_default_response (if exists); otherwise, it is denied. DEPENDENCY Do not lock the data by mutex loc_ni_lock RETURN VALUE none ===========================================================================*/ static void loc_ni_respond(rpc_loc_ni_user_resp_e_type resp, const rpc_loc_ni_event_s_type *request_pass_back ) { ALOGD("Sending NI response: %s\n", respond_from_enum(resp)); rpc_loc_ioctl_data_u_type data; rpc_loc_ioctl_callback_s_type callback_payload; memcpy(&data.rpc_loc_ioctl_data_u_type_u.user_verify_resp.ni_event_pass_back, request_pass_back, sizeof (rpc_loc_ni_event_s_type)); data.rpc_loc_ioctl_data_u_type_u.user_verify_resp.user_resp = resp; loc_eng_ioctl( loc_eng_data.client_handle, RPC_LOC_IOCTL_INFORM_NI_USER_RESPONSE, &data, LOC_IOCTL_DEFAULT_TIMEOUT, &callback_payload ); } /*=========================================================================== FUNCTION loc_ni_fill_notif_verify_type DESCRIPTION Fills need_notify, need_verify, etc. RETURN VALUE none ===========================================================================*/ static boolean loc_ni_fill_notif_verify_type(GpsNiNotification *notif, rpc_loc_ni_notify_verify_e_type notif_priv) { notif->notify_flags = 0; notif->default_response = GPS_NI_RESPONSE_NORESP; switch (notif_priv) { case RPC_LOC_NI_USER_NO_NOTIFY_NO_VERIFY: notif->notify_flags = 0; break; case RPC_LOC_NI_USER_NOTIFY_ONLY: notif->notify_flags = GPS_NI_NEED_NOTIFY; break; case RPC_LOC_NI_USER_NOTIFY_VERIFY_ALLOW_NO_RESP: notif->notify_flags = GPS_NI_NEED_NOTIFY | GPS_NI_NEED_VERIFY; notif->default_response = GPS_NI_RESPONSE_ACCEPT; break; case RPC_LOC_NI_USER_NOTIFY_VERIFY_NOT_ALLOW_NO_RESP: notif->notify_flags = GPS_NI_NEED_NOTIFY | GPS_NI_NEED_VERIFY; notif->default_response = GPS_NI_RESPONSE_DENY; break; case RPC_LOC_NI_USER_PRIVACY_OVERRIDE: notif->notify_flags = GPS_NI_PRIVACY_OVERRIDE; break; default: return FALSE; } return TRUE; } /*=========================================================================== FUNCTION hexcode DESCRIPTION Converts a binary array into a Hex string. E.g., 1F 00 3F --> "1F003F" RETURN VALUE bytes encoded ===========================================================================*/ static int hexcode(char *hexstring, int string_size, const char *data, int data_size) { int i; for (i = 0; i < data_size; i++) { char ch = data[i]; if (i*2 + 3 <= string_size) { snprintf(&hexstring[i*2], 3, "%02X", ch); } else { break; } } return i; } static GpsNiEncodingType convert_encoding_type(int loc_encoding) { GpsNiEncodingType enc = GPS_ENC_UNKNOWN; switch (loc_encoding) { case RPC_LOC_NI_SUPL_UTF8: enc = GPS_ENC_SUPL_UTF8; break; case RPC_LOC_NI_SUPL_UCS2: enc = GPS_ENC_SUPL_UCS2; break; case RPC_LOC_NI_SUPL_GSM_DEFAULT: enc = GPS_ENC_SUPL_GSM_DEFAULT; break; default: break; } return enc; } /*=========================================================================== FUNCTION loc_ni_request_handler DESCRIPTION Displays the NI request and awaits user input. If a previous request is in session, it is ignored. RETURN VALUE none ===========================================================================*/ static void loc_ni_request_handler(const char *msg, const rpc_loc_ni_event_s_type *ni_req) { GpsNiNotification notif; notif.size = sizeof(notif); strlcpy(notif.text, "[text]", sizeof notif.text); // defaults strlcpy(notif.requestor_id, "[requestor id]", sizeof notif.requestor_id); /* If busy, use default or deny */ if (loc_eng_ni_data.notif_in_progress) { #if 0 /* Cannot be here because the current thread is in RPC client */ /* XXX Consider adding an event queue to process overlapped NI requests */ loc_ni_user_resp_e_type response = sys.ni_default_resp == 1 /* accept */ ? LOC_NI_LCS_NOTIFY_VERIFY_ACCEPT : LOC_NI_LCS_NOTIFY_VERIFY_DENY; loc_ni_respond(response, ni_req); */ #endif ALOGW("loc_ni_request_handler, notification in progress, new NI request ignored, type: %d", ni_req->event); } else { /* Print notification */ ALOGD("NI Notification: %s, event: %d", msg, ni_req->event); pthread_mutex_lock(&loc_eng_ni_data.loc_ni_lock); /* Save request */ memcpy(&loc_eng_ni_data.loc_ni_request, ni_req, sizeof loc_eng_ni_data.loc_ni_request); /* Set up NI response waiting */ loc_eng_ni_data.notif_in_progress = TRUE; loc_eng_ni_data.current_notif_id = abs(rand()); /* Fill in notification */ notif.notification_id = loc_eng_ni_data.current_notif_id; const rpc_loc_ni_vx_notify_verify_req_s_type *vx_req; const rpc_loc_ni_supl_notify_verify_req_s_type *supl_req; const rpc_loc_ni_umts_cp_notify_verify_req_s_type *umts_cp_req; switch (ni_req->event) { case RPC_LOC_NI_EVENT_VX_NOTIFY_VERIFY_REQ: vx_req = &ni_req->payload.rpc_loc_ni_event_payload_u_type_u.vx_req; notif.ni_type = GPS_NI_TYPE_VOICE; notif.timeout = LOC_NI_NO_RESPONSE_TIME; // vx_req->user_resp_timer_val; memset(notif.extras, 0, sizeof notif.extras); memset(notif.text, 0, sizeof notif.text); memset(notif.requestor_id, 0, sizeof notif.requestor_id); // Requestor ID hexcode(notif.requestor_id, sizeof notif.requestor_id, vx_req->requester_id.requester_id, vx_req->requester_id.requester_id_length); notif.text_encoding = 0; // No text and no encoding notif.requestor_id_encoding = convert_encoding_type(vx_req->encoding_scheme); // Set default_response & notify_flags loc_ni_fill_notif_verify_type(¬if, vx_req->notification_priv_type); break; case RPC_LOC_NI_EVENT_UMTS_CP_NOTIFY_VERIFY_REQ: umts_cp_req = &ni_req->payload.rpc_loc_ni_event_payload_u_type_u.umts_cp_req; notif.ni_type = GPS_NI_TYPE_UMTS_CTRL_PLANE; notif.timeout = LOC_NI_NO_RESPONSE_TIME; // umts_cp_req->user_response_timer; memset(notif.extras, 0, sizeof notif.extras); memset(notif.text, 0, sizeof notif.text); memset(notif.requestor_id, 0, sizeof notif.requestor_id); // Stores notification text hexcode(notif.text, sizeof notif.text, #if (AMSS_VERSION==3200) umts_cp_req->notification_text.notification_text_val, #else umts_cp_req->notification_text, #endif umts_cp_req->notification_length); // Stores requestor ID hexcode(notif.requestor_id, sizeof notif.requestor_id, #if (AMSS_VERSION==3200) umts_cp_req->requestor_id.requestor_id_string.requestor_id_string_val, #else umts_cp_req->requestor_id.requestor_id_string, #endif umts_cp_req->requestor_id.string_len); notif.text_encoding = convert_encoding_type(umts_cp_req->datacoding_scheme); notif.requestor_id_encoding = convert_encoding_type(umts_cp_req->datacoding_scheme); // Set default_response & notify_flags loc_ni_fill_notif_verify_type(¬if, umts_cp_req->notification_priv_type); break; case RPC_LOC_NI_EVENT_SUPL_NOTIFY_VERIFY_REQ: supl_req = &ni_req->payload.rpc_loc_ni_event_payload_u_type_u.supl_req; notif.ni_type = GPS_NI_TYPE_UMTS_SUPL; notif.timeout = LOC_NI_NO_RESPONSE_TIME; // supl_req->user_response_timer; memset(notif.extras, 0, sizeof notif.extras); memset(notif.text, 0, sizeof notif.text); memset(notif.requestor_id, 0, sizeof notif.requestor_id); // Client name if (supl_req->flags & RPC_LOC_NI_CLIENT_NAME_PRESENT) { hexcode(notif.text, sizeof notif.text, #if (AMSS_VERSION==3200) supl_req->client_name.client_name_string.client_name_string_val, /* buffer */ #else supl_req->client_name.client_name_string, /* buffer */ #endif supl_req->client_name.string_len /* length */ ); ALOGD("SUPL NI: client_name: %s len=%d", notif.text, supl_req->client_name.string_len); } else { ALOGD("SUPL NI: client_name not present."); } // Requestor ID if (supl_req->flags & RPC_LOC_NI_REQUESTOR_ID_PRESENT) { hexcode(notif.requestor_id, sizeof notif.requestor_id, #if (AMSS_VERSION==3200) supl_req->requestor_id.requestor_id_string.requestor_id_string_val, /* buffer */ #else supl_req->requestor_id.requestor_id_string, /* buffer */ #endif supl_req->requestor_id.string_len /* length */ ); ALOGD("SUPL NI: requestor_id: %s len=%d", notif.requestor_id, supl_req->requestor_id.string_len); } else { ALOGD("SUPL NI: requestor_id not present."); } // Encoding type if (supl_req->flags & RPC_LOC_NI_ENCODING_TYPE_PRESENT) { notif.text_encoding = convert_encoding_type(supl_req->datacoding_scheme); notif.requestor_id_encoding = convert_encoding_type(supl_req->datacoding_scheme); } else { notif.text_encoding = notif.requestor_id_encoding = GPS_ENC_UNKNOWN; } // Set default_response & notify_flags loc_ni_fill_notif_verify_type(¬if, ni_req->payload.rpc_loc_ni_event_payload_u_type_u.supl_req.notification_priv_type); break; default: ALOGE("loc_ni_request_handler, unknown request event: %d", ni_req->event); return; } /* Log requestor ID and text for debugging */ ALOGI("Notification: notif_type: %d, timeout: %d, default_resp: %d", notif.ni_type, notif.timeout, notif.default_response); ALOGI(" requestor_id: %s (encoding: %d)", notif.requestor_id, notif.requestor_id_encoding); ALOGI(" text: %s text (encoding: %d)", notif.text, notif.text_encoding); /* For robustness, always sets a timeout to clear up the notification status, even though * the OEM layer in java does not do so. **/ loc_eng_ni_data.response_time_left = 5 + (notif.timeout != 0 ? notif.timeout : LOC_NI_NO_RESPONSE_TIME); ALOGI("Automatically sends 'no response' in %d seconds (to clear status)\n", loc_eng_ni_data.response_time_left); pthread_mutex_unlock(&loc_eng_ni_data.loc_ni_lock); /* Notify callback */ if (loc_eng_data.ni_notify_cb != NULL) { loc_eng_data.ni_notify_cb(¬if); } } } /*=========================================================================== FUNCTION loc_ni_process_user_response DESCRIPTION Handles user input from the UI RETURN VALUE error code (0 for successful, -1 for error) ===========================================================================*/ int loc_ni_process_user_response(GpsUserResponseType userResponse) { ALOGD("NI response from UI: %d", userResponse); rpc_loc_ni_user_resp_e_type resp; switch (userResponse) { case GPS_NI_RESPONSE_ACCEPT: resp = RPC_LOC_NI_LCS_NOTIFY_VERIFY_ACCEPT; break; case GPS_NI_RESPONSE_DENY: resp = RPC_LOC_NI_LCS_NOTIFY_VERIFY_DENY; break; case GPS_NI_RESPONSE_NORESP: resp = RPC_LOC_NI_LCS_NOTIFY_VERIFY_NORESP; break; default: return -1; } loc_ni_respond(resp, &loc_eng_ni_data.loc_ni_request); /* Make the NI respond */ pthread_mutex_lock(&loc_eng_ni_data.loc_ni_lock); loc_eng_ni_data.notif_in_progress = FALSE; loc_eng_ni_data.response_time_left = 0; loc_eng_ni_data.current_notif_id = -1; pthread_mutex_unlock(&loc_eng_ni_data.loc_ni_lock); return 0; } /*=========================================================================== FUNCTION loc_eng_ni_callback DESCRIPTION Loc API callback handler RETURN VALUE error code (0 for success) ===========================================================================*/ int loc_eng_ni_callback ( rpc_loc_event_mask_type loc_event, /* event mask */ const rpc_loc_event_payload_u_type* loc_event_payload /* payload */ ) { int rc = 0; const rpc_loc_ni_event_s_type *ni_req = &loc_event_payload->rpc_loc_event_payload_u_type_u.ni_request; if (loc_event == RPC_LOC_EVENT_NI_NOTIFY_VERIFY_REQUEST) { switch (ni_req->event) { case RPC_LOC_NI_EVENT_VX_NOTIFY_VERIFY_REQ: ALOGI("VX Notification"); loc_ni_request_handler("VX Notify", ni_req); break; case RPC_LOC_NI_EVENT_UMTS_CP_NOTIFY_VERIFY_REQ: ALOGI("UMTS CP Notification\n"); loc_ni_request_handler("UMTS CP Notify", ni_req); break; case RPC_LOC_NI_EVENT_SUPL_NOTIFY_VERIFY_REQ: ALOGI("SUPL Notification\n"); loc_ni_request_handler("SUPL Notify", ni_req); break; default: ALOGE("Unknown NI event: %x\n", (int) ni_req->event); break; } } return rc; } /*=========================================================================== FUNCTION loc_ni_thread_proc ===========================================================================*/ static void loc_ni_thread_proc(void *unused) { ALOGI("Starting Loc NI thread...\n"); while (1) { /* wakes up every second to check timed out requests */ sleep(1); pthread_mutex_lock(&loc_eng_ni_data.loc_ni_lock); if (loc_eng_ni_data.notif_in_progress && loc_eng_ni_data.response_time_left > 0) { loc_eng_ni_data.response_time_left--; if (loc_eng_ni_data.response_time_left <= 0) { loc_ni_respond(RPC_LOC_NI_LCS_NOTIFY_VERIFY_NORESP, &loc_eng_ni_data.loc_ni_request); loc_eng_ni_data.notif_in_progress = FALSE; } } pthread_mutex_unlock(&loc_eng_ni_data.loc_ni_lock); } /* while (1) */ } /*=========================================================================== FUNCTION loc_eng_ni_init DESCRIPTION This function initializes the NI interface DEPENDENCIES NONE RETURN VALUE None SIDE EFFECTS N/A ===========================================================================*/ void loc_eng_ni_init(GpsNiCallbacks *callbacks) { ALOGD("loc_eng_ni_init: entered."); if (!loc_eng_ni_data_init) { pthread_mutex_init(&loc_eng_ni_data.loc_ni_lock, NULL); callbacks->create_thread_cb("loc_api_ni", loc_ni_thread_proc, NULL); loc_eng_ni_data_init = TRUE; } loc_eng_ni_data.notif_in_progress = FALSE; loc_eng_ni_data.current_notif_id = -1; loc_eng_ni_data.response_time_left = 0; srand(time(NULL)); loc_eng_data.ni_notify_cb = callbacks->notify_cb; } /*=========================================================================== FUNCTION loc_eng_ni_respond DESCRIPTION This function sends an NI respond to the modem processor DEPENDENCIES NONE RETURN VALUE None SIDE EFFECTS N/A ===========================================================================*/ void loc_eng_ni_respond(int notif_id, GpsUserResponseType user_response) { if (notif_id == loc_eng_ni_data.current_notif_id && loc_eng_ni_data.notif_in_progress) { ALOGI("loc_eng_ni_respond: send user response %d for notif %d", user_response, notif_id); loc_ni_process_user_response(user_response); } else { ALOGE("loc_eng_ni_respond: notif_id %d mismatch or notification not in progress, response: %d", notif_id, user_response); } }