diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java index 1435016fc55a6..b8abd98456e40 100644 --- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java +++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java @@ -76,6 +76,8 @@ public class GnssConfiguration { "ENABLE_PSDS_PERIODIC_DOWNLOAD"; private static final String CONFIG_ENABLE_ACTIVE_SIM_EMERGENCY_SUPL = "ENABLE_ACTIVE_SIM_EMERGENCY_SUPL"; + private static final String CONFIG_ENABLE_NI_SUPL_MESSAGE_INJECTION = + "ENABLE_NI_SUPL_MESSAGE_INJECTION"; static final String CONFIG_LONGTERM_PSDS_SERVER_1 = "LONGTERM_PSDS_SERVER_1"; static final String CONFIG_LONGTERM_PSDS_SERVER_2 = "LONGTERM_PSDS_SERVER_2"; static final String CONFIG_LONGTERM_PSDS_SERVER_3 = "LONGTERM_PSDS_SERVER_3"; @@ -217,6 +219,14 @@ public class GnssConfiguration { return getBooleanConfig(CONFIG_ENABLE_ACTIVE_SIM_EMERGENCY_SUPL, false); } + /** + * Returns true if NI SUPL message injection is enabled; Returns false otherwise. + * Default false if not set. + */ + boolean isNiSuplMessageInjectionEnabled() { + return getBooleanConfig(CONFIG_ENABLE_NI_SUPL_MESSAGE_INJECTION, false); + } + /** * Returns true if a long-term PSDS server is configured. */ diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index 6f637b83a6948..6f6b1c910ff09 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -84,6 +84,7 @@ import android.os.UserHandle; import android.os.WorkSource; import android.os.WorkSource.WorkChain; import android.provider.Settings; +import android.provider.Telephony.Sms.Intents; import android.telephony.CarrierConfigManager; import android.telephony.CellIdentity; import android.telephony.CellIdentityGsm; @@ -95,6 +96,7 @@ import android.telephony.CellInfoGsm; import android.telephony.CellInfoLte; import android.telephony.CellInfoNr; import android.telephony.CellInfoWcdma; +import android.telephony.SmsMessage; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -107,6 +109,7 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.location.GpsNetInitiatedHandler; import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification; import com.android.internal.util.FrameworkStatsLog; +import com.android.internal.util.HexDump; import com.android.server.FgThread; import com.android.server.location.gnss.GnssSatelliteBlocklistHelper.GnssSatelliteBlocklistCallback; import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback; @@ -523,23 +526,31 @@ public class GnssLocationProvider extends AbstractLocationProvider implements IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); intentFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED); - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (DEBUG) Log.d(TAG, "receive broadcast intent, action: " + action); - if (action == null) { - return; - } + mContext.registerReceiver(mIntentReceiver, intentFilter, null, mHandler); - switch (action) { - case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED: - case TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED: - subscriptionOrCarrierConfigChanged(); - break; - } + if (mNetworkConnectivityHandler.isNativeAgpsRilSupported() + && mGnssConfiguration.isNiSuplMessageInjectionEnabled()) { + // Listen to WAP PUSH NI SUPL message. + // See User Plane Location Protocol Candidate Version 3.0, + // OMA-TS-ULP-V3_0-20110920-C, Section 8.3 OMA Push. + intentFilter = new IntentFilter(); + intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION); + try { + intentFilter.addDataType("application/vnd.omaloc-supl-init"); + } catch (IntentFilter.MalformedMimeTypeException e) { + Log.w(TAG, "Malformed SUPL init mime type"); } - }, intentFilter, null, mHandler); + mContext.registerReceiver(mIntentReceiver, intentFilter, null, mHandler); + + // Listen to MT SMS NI SUPL message. + // See User Plane Location Protocol Candidate Version 3.0, + // OMA-TS-ULP-V3_0-20110920-C, Section 8.4 MT SMS. + intentFilter = new IntentFilter(); + intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION); + intentFilter.addDataScheme("sms"); + intentFilter.addDataAuthority("localhost", "7275"); + mContext.registerReceiver(mIntentReceiver, intentFilter, null, mHandler); + } mNetworkConnectivityHandler.registerNetworkCallbacks(); @@ -560,6 +571,80 @@ public class GnssLocationProvider extends AbstractLocationProvider implements updateEnabled(); } + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (DEBUG) Log.d(TAG, "receive broadcast intent, action: " + action); + if (action == null) { + return; + } + + switch (action) { + case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED: + case TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED: + subscriptionOrCarrierConfigChanged(); + break; + case Intents.WAP_PUSH_RECEIVED_ACTION: + case Intents.DATA_SMS_RECEIVED_ACTION: + injectSuplInit(intent); + break; + } + } + }; + + private void injectSuplInit(Intent intent) { + if (!isNfwLocationAccessAllowed()) { + Log.w(TAG, "Reject SUPL INIT as no NFW location access"); + return; + } + + int slotIndex = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, + SubscriptionManager.INVALID_SIM_SLOT_INDEX); + if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { + Log.e(TAG, "Invalid slot index"); + return; + } + + byte[] suplInit = null; + String action = intent.getAction(); + if (action.equals(Intents.DATA_SMS_RECEIVED_ACTION)) { + SmsMessage[] messages = Intents.getMessagesFromIntent(intent); + if (messages == null) { + Log.e(TAG, "Message does not exist in the intent"); + return; + } + for (SmsMessage message : messages) { + suplInit = message.getUserData(); + injectSuplInit(suplInit, slotIndex); + } + } else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) { + suplInit = intent.getByteArrayExtra("data"); + injectSuplInit(suplInit, slotIndex); + } + } + + private void injectSuplInit(byte[] suplInit, int slotIndex) { + if (suplInit != null) { + if (DEBUG) { + Log.d(TAG, "suplInit = " + + HexDump.toHexString(suplInit) + " slotIndex = " + slotIndex); + } + mGnssNative.injectNiSuplMessageData(suplInit, suplInit.length , slotIndex); + } + } + + private boolean isNfwLocationAccessAllowed() { + if (mGnssNative.isInEmergencySession()) { + return true; + } + if (mGnssVisibilityControl != null + && mGnssVisibilityControl.hasLocationPermissionEnabledProxyApps()) { + return true; + } + return false; + } + /** * Implements {@link InjectNtpTimeCallback#injectTime} */ diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java index 02bdfd5bcb564..a7fffe2ddf29a 100644 --- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java +++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java @@ -762,6 +762,10 @@ class GnssNetworkConnectivityHandler { return APN_INVALID; } + protected boolean isNativeAgpsRilSupported() { + return native_is_agps_ril_supported(); + } + // AGPS support private native void native_agps_data_conn_open(long networkHandle, String apn, int apnIpType); diff --git a/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java index 631dbbf0f1fd9..4e5e5f8e30f46 100644 --- a/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java +++ b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java @@ -437,6 +437,10 @@ class GnssVisibilityControl { return locationPermissionEnabledProxyApps; } + public boolean hasLocationPermissionEnabledProxyApps() { + return getLocationPermissionEnabledProxyApps().length > 0; + } + private void handleNfwNotification(NfwNotification nfwNotification) { if (DEBUG) Log.d(TAG, "Non-framework location access notification: " + nfwNotification); diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java index 2d015a5d58c77..edb2e5bf3f97d 100644 --- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java +++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java @@ -989,6 +989,14 @@ public class GnssNative { mGnssHal.injectPsdsData(data, length, psdsType); } + /** + * Injects NI SUPL message data into the GNSS HAL. + */ + public void injectNiSuplMessageData(byte[] data, int length, int slotIndex) { + Preconditions.checkState(mRegistered); + mGnssHal.injectNiSuplMessageData(data, length, slotIndex); + } + @NativeEntryPoint void reportGnssServiceDied() { // Not necessary to clear (and restore) binder identity since it runs on another thread. @@ -1278,7 +1286,7 @@ public class GnssNative { } @NativeEntryPoint - boolean isInEmergencySession() { + public boolean isInEmergencySession() { return Binder.withCleanCallingIdentity( () -> mEmergencyHelper.isInEmergency( TimeUnit.SECONDS.toMillis(mConfiguration.getEsExtensionSec()))); @@ -1507,6 +1515,10 @@ public class GnssNative { protected void injectPsdsData(byte[] data, int length, int psdsType) { native_inject_psds_data(data, length, psdsType); } + + protected void injectNiSuplMessageData(byte[] data, int length, int slotIndex) { + native_inject_ni_supl_message_data(data, length, slotIndex); + } } // basic APIs @@ -1650,6 +1662,9 @@ public class GnssNative { private static native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc, int lac, long cid, int tac, int pcid, int arfcn); + private static native void native_inject_ni_supl_message_data(byte[] data, int length, + int slotIndex); + // PSDS APIs private static native boolean native_supports_psds(); diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 9fa23c277d463..e1de05cf6c7cf 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -482,6 +482,17 @@ static void android_location_gnss_hal_GnssNative_agps_set_id(JNIEnv* env, jclass agnssRilIface->setSetId(type, setid_string); } +static void android_location_gnss_hal_GnssNative_inject_ni_supl_message_data(JNIEnv* env, jclass, + jbyteArray data, + jint length, + jint slotIndex) { + if (agnssRilIface == nullptr) { + ALOGE("%s: IAGnssRil interface not available.", __func__); + return; + } + agnssRilIface->injectNiSuplMessageData(data, length, slotIndex); +} + static jint android_location_gnss_hal_GnssNative_read_nmea(JNIEnv* env, jclass, jbyteArray nmeaArray, jint buffer_size) { return gnssHal->readNmea(nmeaArray, buffer_size); @@ -974,6 +985,8 @@ static const JNINativeMethod sLocationProviderMethods[] = { android_location_gnss_hal_GnssNative_agps_set_reference_location_cellid)}, {"native_set_agps_server", "(ILjava/lang/String;I)V", reinterpret_cast(android_location_gnss_hal_GnssNative_set_agps_server)}, + {"native_inject_ni_supl_message_data", "([BII)V", + reinterpret_cast(android_location_gnss_hal_GnssNative_inject_ni_supl_message_data)}, {"native_send_ni_response", "(II)V", reinterpret_cast(android_location_gnss_hal_GnssNative_send_ni_response)}, {"native_get_internal_state", "()Ljava/lang/String;", diff --git a/services/core/jni/gnss/AGnssRil.cpp b/services/core/jni/gnss/AGnssRil.cpp index 34e4976dcca00..c7a1af77389da 100644 --- a/services/core/jni/gnss/AGnssRil.cpp +++ b/services/core/jni/gnss/AGnssRil.cpp @@ -84,8 +84,19 @@ jboolean AGnssRil::updateNetworkState(jboolean connected, jint type, jboolean ro networkAttributes.capabilities = static_cast(capabilities), networkAttributes.apn = jniApn.c_str(); - auto result = mIAGnssRil->updateNetworkState(networkAttributes); - return checkAidlStatus(result, "IAGnssRilAidl updateNetworkState() failed."); + auto status = mIAGnssRil->updateNetworkState(networkAttributes); + return checkAidlStatus(status, "IAGnssRilAidl updateNetworkState() failed."); +} + +jboolean AGnssRil::injectNiSuplMessageData(const jbyteArray& msgData, jint length, jint slotIndex) { + JNIEnv* env = getJniEnv(); + jbyte* bytes = reinterpret_cast(env->GetPrimitiveArrayCritical(msgData, 0)); + auto status = mIAGnssRil->injectNiSuplMessageData(std::vector((const uint8_t*)bytes, + (const uint8_t*)bytes + + length), + static_cast(slotIndex)); + env->ReleasePrimitiveArrayCritical(msgData, bytes, JNI_ABORT); + return checkAidlStatus(status, "IAGnssRil injectNiSuplMessageData() failed."); } // Implementation of AGnssRil_V1_0 @@ -151,6 +162,11 @@ jboolean AGnssRil_V1_0::updateNetworkState(jboolean connected, jint type, jboole return checkHidlReturn(result, "IAGnssRil_V1_0 updateNetworkState() failed."); } +jboolean AGnssRil_V1_0::injectNiSuplMessageData(const jbyteArray&, jint, jint) { + ALOGI("IAGnssRil_V1_0 interface does not support injectNiSuplMessageData."); + return JNI_FALSE; +} + // Implementation of AGnssRil_V2_0 AGnssRil_V2_0::AGnssRil_V2_0(const sp& iAGnssRil) diff --git a/services/core/jni/gnss/AGnssRil.h b/services/core/jni/gnss/AGnssRil.h index ce14a77d56c47..b7e02825252c5 100644 --- a/services/core/jni/gnss/AGnssRil.h +++ b/services/core/jni/gnss/AGnssRil.h @@ -43,6 +43,8 @@ public: virtual jboolean updateNetworkState(jboolean connected, jint type, jboolean roaming, jboolean available, const jstring& apn, jlong networkHandle, jshort capabilities) = 0; + virtual jboolean injectNiSuplMessageData(const jbyteArray& msgData, jint length, + jint slotIndex) = 0; }; class AGnssRil : public AGnssRilInterface { @@ -55,6 +57,8 @@ public: jboolean updateNetworkState(jboolean connected, jint type, jboolean roaming, jboolean available, const jstring& apn, jlong networkHandle, jshort capabilities) override; + jboolean injectNiSuplMessageData(const jbyteArray& msgData, jint length, + jint slotIndex) override; private: const sp mIAGnssRil; @@ -70,6 +74,7 @@ public: jboolean updateNetworkState(jboolean connected, jint type, jboolean roaming, jboolean available, const jstring& apn, jlong networkHandle, jshort capabilities) override; + jboolean injectNiSuplMessageData(const jbyteArray&, jint, jint) override; private: const sp mAGnssRil_V1_0;