1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.location.gnss; 18 19 import android.content.Context; 20 import android.location.flags.Flags; 21 import android.os.PersistableBundle; 22 import android.os.SystemProperties; 23 import android.telephony.CarrierConfigManager; 24 import android.telephony.SubscriptionManager; 25 import android.telephony.TelephonyManager; 26 import android.text.TextUtils; 27 import android.util.Log; 28 29 import com.android.internal.util.FrameworkStatsLog; 30 31 import libcore.io.IoUtils; 32 33 import java.io.File; 34 import java.io.FileInputStream; 35 import java.io.IOException; 36 import java.util.Arrays; 37 import java.util.Collections; 38 import java.util.HashMap; 39 import java.util.List; 40 import java.util.Locale; 41 import java.util.Map; 42 import java.util.Map.Entry; 43 import java.util.Properties; 44 45 /** 46 * A utility class to hold GNSS configuration properties. 47 * 48 * The trigger to load/reload the configuration parameters should be managed by the class 49 * that owns an instance of this class. 50 * 51 * Instances of this class are not thread-safe and should either be used from a single thread 52 * or with external synchronization when used by multiple threads. 53 */ 54 public class GnssConfiguration { 55 private static final String TAG = "GnssConfiguration"; 56 57 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 58 59 private static final String DEBUG_PROPERTIES_SYSTEM_FILE = "/etc/gps_debug.conf"; 60 61 private static final String DEBUG_PROPERTIES_VENDOR_FILE = "/vendor/etc/gps_debug.conf"; 62 63 // config.xml properties 64 private static final String CONFIG_SUPL_HOST = "SUPL_HOST"; 65 private static final String CONFIG_SUPL_PORT = "SUPL_PORT"; 66 private static final String CONFIG_C2K_HOST = "C2K_HOST"; 67 private static final String CONFIG_C2K_PORT = "C2K_PORT"; 68 private static final String CONFIG_SUPL_VER = "SUPL_VER"; 69 private static final String CONFIG_SUPL_MODE = "SUPL_MODE"; 70 private static final String CONFIG_SUPL_ES = "SUPL_ES"; 71 private static final String CONFIG_LPP_PROFILE = "LPP_PROFILE"; 72 private static final String CONFIG_A_GLONASS_POS_PROTOCOL_SELECT = 73 "A_GLONASS_POS_PROTOCOL_SELECT"; 74 private static final String CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL = 75 "USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL"; 76 private static final String CONFIG_GPS_LOCK = "GPS_LOCK"; 77 private static final String CONFIG_ES_EXTENSION_SEC = "ES_EXTENSION_SEC"; 78 static final String CONFIG_NFW_PROXY_APPS = "NFW_PROXY_APPS"; 79 private static final String CONFIG_ENABLE_PSDS_PERIODIC_DOWNLOAD = 80 "ENABLE_PSDS_PERIODIC_DOWNLOAD"; 81 private static final String CONFIG_ENABLE_ACTIVE_SIM_EMERGENCY_SUPL = 82 "ENABLE_ACTIVE_SIM_EMERGENCY_SUPL"; 83 private static final String CONFIG_ENABLE_NI_SUPL_MESSAGE_INJECTION_BOOL = 84 "ENABLE_NI_SUPL_MESSAGE_INJECTION_BOOL"; 85 static final String CONFIG_LONGTERM_PSDS_SERVER_1 = "LONGTERM_PSDS_SERVER_1"; 86 static final String CONFIG_LONGTERM_PSDS_SERVER_2 = "LONGTERM_PSDS_SERVER_2"; 87 static final String CONFIG_LONGTERM_PSDS_SERVER_3 = "LONGTERM_PSDS_SERVER_3"; 88 static final String CONFIG_NORMAL_PSDS_SERVER = "NORMAL_PSDS_SERVER"; 89 static final String CONFIG_REALTIME_PSDS_SERVER = "REALTIME_PSDS_SERVER"; 90 // Limit on NI emergency mode time extension after emergency sessions ends 91 private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300; // 5 minute maximum 92 93 // Persist property for LPP_PROFILE 94 static final String LPP_PROFILE = "persist.sys.gps.lpp"; 95 96 // Represents an HAL interface version. Instances of this class are created in the JNI layer 97 // and returned through native methods. 98 static class HalInterfaceVersion { 99 // mMajor being this value denotes AIDL HAL. In this case, mMinor denotes the AIDL version. 100 static final int AIDL_INTERFACE = 3; 101 final int mMajor; 102 final int mMinor; 103 HalInterfaceVersion(int major, int minor)104 HalInterfaceVersion(int major, int minor) { 105 mMajor = major; 106 mMinor = minor; 107 } 108 } 109 110 /** 111 * Properties loaded from PROPERTIES_FILE. 112 */ 113 private final Properties mProperties; 114 115 private int mEsExtensionSec = 0; 116 117 private final Context mContext; 118 GnssConfiguration(Context context)119 public GnssConfiguration(Context context) { 120 mContext = context; 121 mProperties = new Properties(); 122 } 123 124 /** 125 * Returns the full set of properties loaded. 126 */ getProperties()127 Properties getProperties() { 128 return mProperties; 129 } 130 131 /** 132 * Returns the value of config parameter ES_EXTENSION_SEC. The value is range checked 133 * and constrained to min/max limits. 134 */ getEsExtensionSec()135 public int getEsExtensionSec() { 136 return mEsExtensionSec; 137 } 138 139 /** 140 * Returns the value of config parameter SUPL_HOST or {@code null} if no value is 141 * provided. 142 */ getSuplHost()143 String getSuplHost() { 144 return mProperties.getProperty(CONFIG_SUPL_HOST); 145 } 146 147 /** 148 * Returns the value of config parameter SUPL_PORT or {@code defaultPort} if no value is 149 * provided or if there is an error parsing the configured value. 150 */ getSuplPort(int defaultPort)151 int getSuplPort(int defaultPort) { 152 return getIntConfig(CONFIG_SUPL_PORT, defaultPort); 153 } 154 155 /** 156 * Returns the value of config parameter C2K_HOST or {@code null} if no value is 157 * provided. 158 */ getC2KHost()159 String getC2KHost() { 160 return mProperties.getProperty(CONFIG_C2K_HOST); 161 } 162 163 /** 164 * Returns the value of config parameter C2K_PORT or {@code defaultPort} if no value is 165 * provided or if there is an error parsing the configured value. 166 */ getC2KPort(int defaultPort)167 int getC2KPort(int defaultPort) { 168 return getIntConfig(CONFIG_C2K_PORT, defaultPort); 169 } 170 171 /** 172 * Returns the value of config parameter SUPL_MODE or {@code defaultMode} if no value is 173 * provided or if there is an error parsing the configured value. 174 */ getSuplMode(int defaultMode)175 int getSuplMode(int defaultMode) { 176 return getIntConfig(CONFIG_SUPL_MODE, defaultMode); 177 } 178 179 /** 180 * Returns the value of config parameter SUPL_ES or {@code defaultSuplEs} if no value is 181 * provided or if there is an error parsing the configured value. 182 */ getSuplEs(int defaultSuplEs)183 public int getSuplEs(int defaultSuplEs) { 184 return getIntConfig(CONFIG_SUPL_ES, defaultSuplEs); 185 } 186 187 /** 188 * Returns the value of config parameter LPP_PROFILE or {@code null} if no value is 189 * provided. 190 */ getLppProfile()191 String getLppProfile() { 192 return mProperties.getProperty(CONFIG_LPP_PROFILE); 193 } 194 195 /** 196 * Returns the list of proxy apps from the value of config parameter NFW_PROXY_APPS. 197 */ getProxyApps()198 List<String> getProxyApps() { 199 // Space separated list of Android proxy app package names. 200 String proxyAppsStr = mProperties.getProperty(CONFIG_NFW_PROXY_APPS); 201 if (TextUtils.isEmpty(proxyAppsStr)) { 202 return Collections.emptyList(); 203 } 204 205 String[] proxyAppsArray = proxyAppsStr.trim().split("\\s+"); 206 if (proxyAppsArray.length == 0) { 207 return Collections.emptyList(); 208 } 209 210 return Arrays.asList(proxyAppsArray); 211 } 212 213 /** 214 * Returns true if PSDS periodic download is enabled, false otherwise. 215 */ isPsdsPeriodicDownloadEnabled()216 boolean isPsdsPeriodicDownloadEnabled() { 217 return getBooleanConfig(CONFIG_ENABLE_PSDS_PERIODIC_DOWNLOAD, false); 218 } 219 220 /** 221 * Returns true if during an emergency call, the GnssConfiguration of the activeSubId will be 222 * injected for the emergency SUPL flow; Returns false otherwise. Default false if not set. 223 */ isActiveSimEmergencySuplEnabled()224 boolean isActiveSimEmergencySuplEnabled() { 225 return getBooleanConfig(CONFIG_ENABLE_ACTIVE_SIM_EMERGENCY_SUPL, false); 226 } 227 228 /** 229 * Returns true if NI SUPL message injection is enabled; Returns false otherwise. 230 * Default false if not set. 231 */ isNiSuplMessageInjectionEnabled()232 boolean isNiSuplMessageInjectionEnabled() { 233 return getBooleanConfig(CONFIG_ENABLE_NI_SUPL_MESSAGE_INJECTION_BOOL, 234 false); 235 } 236 237 /** 238 * Returns true if a long-term PSDS server is configured. 239 */ isLongTermPsdsServerConfigured()240 boolean isLongTermPsdsServerConfigured() { 241 return (mProperties.getProperty(CONFIG_LONGTERM_PSDS_SERVER_1) != null 242 || mProperties.getProperty(CONFIG_LONGTERM_PSDS_SERVER_2) != null 243 || mProperties.getProperty(CONFIG_LONGTERM_PSDS_SERVER_3) != null); 244 } 245 246 /** 247 * Updates the GNSS HAL satellite denylist. 248 */ setSatelliteBlocklist(int[] constellations, int[] svids)249 void setSatelliteBlocklist(int[] constellations, int[] svids) { 250 native_set_satellite_blocklist(constellations, svids); 251 } 252 getHalInterfaceVersion()253 HalInterfaceVersion getHalInterfaceVersion() { 254 return native_get_gnss_configuration_version(); 255 } 256 257 interface SetCarrierProperty { set(int value)258 boolean set(int value); 259 } 260 261 /** 262 * Loads the GNSS properties from carrier config file followed by the properties from 263 * gps debug config file, and injects the GNSS properties into the HAL. 264 */ reloadGpsProperties()265 void reloadGpsProperties() { 266 reloadGpsProperties(/* inEmergency= */ false, /* activeSubId= */ -1); 267 } 268 269 /** 270 * Loads the GNSS properties from carrier config file followed by the properties from 271 * gps debug config file, and injects the GNSS properties into the HAL. 272 */ reloadGpsProperties(boolean inEmergency, int activeSubId)273 void reloadGpsProperties(boolean inEmergency, int activeSubId) { 274 if (DEBUG) { 275 Log.d(TAG, 276 "Reset GPS properties, previous size = " + mProperties.size() + ", inEmergency:" 277 + inEmergency + ", activeSubId=" + activeSubId); 278 } 279 loadPropertiesFromCarrierConfig(inEmergency, activeSubId); 280 281 if (Flags.gnssConfigurationFromResource()) { 282 // Overlay carrier properties from resources. 283 loadPropertiesFromResource(mContext, mProperties); 284 } 285 286 if (isSimAbsent(mContext)) { 287 // Use the default SIM's LPP profile when SIM is absent. 288 String lpp_prof = SystemProperties.get(LPP_PROFILE); 289 if (!TextUtils.isEmpty(lpp_prof)) { 290 // override default value of this if lpp_prof is not empty 291 mProperties.setProperty(CONFIG_LPP_PROFILE, lpp_prof); 292 } 293 } 294 295 /* 296 * Overlay carrier properties from a debug configuration file. 297 */ 298 loadPropertiesFromGpsDebugConfig(mProperties, DEBUG_PROPERTIES_VENDOR_FILE); 299 loadPropertiesFromGpsDebugConfig(mProperties, DEBUG_PROPERTIES_SYSTEM_FILE); 300 mEsExtensionSec = getRangeCheckedConfigEsExtensionSec(); 301 302 logConfigurations(); 303 304 final HalInterfaceVersion gnssConfigurationIfaceVersion = getHalInterfaceVersion(); 305 if (gnssConfigurationIfaceVersion != null) { 306 // Set to a range checked value. 307 if (isConfigEsExtensionSecSupported(gnssConfigurationIfaceVersion) 308 && !native_set_es_extension_sec(mEsExtensionSec)) { 309 Log.e(TAG, "Unable to set " + CONFIG_ES_EXTENSION_SEC + ": " + mEsExtensionSec); 310 } 311 312 Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>(); 313 314 map.put(CONFIG_SUPL_VER, GnssConfiguration::native_set_supl_version); 315 map.put(CONFIG_SUPL_MODE, GnssConfiguration::native_set_supl_mode); 316 317 if (isConfigSuplEsSupported(gnssConfigurationIfaceVersion)) { 318 map.put(CONFIG_SUPL_ES, GnssConfiguration::native_set_supl_es); 319 } 320 321 map.put(CONFIG_LPP_PROFILE, GnssConfiguration::native_set_lpp_profile); 322 map.put(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, 323 GnssConfiguration::native_set_gnss_pos_protocol_select); 324 map.put(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, 325 GnssConfiguration::native_set_emergency_supl_pdn); 326 327 if (isConfigGpsLockSupported(gnssConfigurationIfaceVersion)) { 328 map.put(CONFIG_GPS_LOCK, GnssConfiguration::native_set_gps_lock); 329 } 330 331 for (Entry<String, SetCarrierProperty> entry : map.entrySet()) { 332 String propertyName = entry.getKey(); 333 String propertyValueString = mProperties.getProperty(propertyName); 334 if (propertyValueString != null) { 335 try { 336 int propertyValueInt = Integer.decode(propertyValueString); 337 boolean result = entry.getValue().set(propertyValueInt); 338 if (!result) { 339 Log.e(TAG, "Unable to set " + propertyName); 340 } 341 } catch (NumberFormatException e) { 342 Log.e(TAG, "Unable to parse propertyName: " + propertyValueString); 343 } 344 } 345 } 346 } else if (DEBUG) { 347 Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not" 348 + " supported"); 349 } 350 } 351 logConfigurations()352 private void logConfigurations() { 353 FrameworkStatsLog.write(FrameworkStatsLog.GNSS_CONFIGURATION_REPORTED, 354 getSuplHost(), 355 getSuplPort(0), 356 getC2KHost(), 357 getC2KPort(0), 358 getIntConfig(CONFIG_SUPL_VER, 0), 359 getSuplMode(0), 360 getSuplEs(0) == 1, 361 getIntConfig(CONFIG_LPP_PROFILE, 0), 362 getIntConfig(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, 0), 363 getIntConfig(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, 0) == 1, 364 getIntConfig(CONFIG_GPS_LOCK, 0), 365 getEsExtensionSec(), 366 mProperties.getProperty(CONFIG_NFW_PROXY_APPS)); 367 } 368 369 /** 370 * Loads GNSS properties from carrier config file. 371 */ loadPropertiesFromCarrierConfig(boolean inEmergency, int activeSubId)372 void loadPropertiesFromCarrierConfig(boolean inEmergency, int activeSubId) { 373 CarrierConfigManager configManager = (CarrierConfigManager) 374 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); 375 if (configManager == null) { 376 return; 377 } 378 379 int subId = SubscriptionManager.getDefaultDataSubscriptionId(); 380 if (inEmergency && activeSubId >= 0) { 381 subId = activeSubId; 382 } 383 PersistableBundle configs = SubscriptionManager.isValidSubscriptionId(subId) 384 ? configManager.getConfigForSubId(subId) : configManager.getConfig(); 385 if (configs == null) { 386 if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config."); 387 configs = CarrierConfigManager.getDefaultConfig(); 388 } 389 for (String configKey : configs.keySet()) { 390 if (configKey != null && configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) { 391 String key = configKey 392 .substring(CarrierConfigManager.Gps.KEY_PREFIX.length()) 393 .toUpperCase(Locale.ROOT); 394 Object value = configs.get(configKey); 395 if (DEBUG) Log.d(TAG, "Gps config: " + key + " = " + value); 396 if (value instanceof String) { 397 // Most GPS properties are of String type; convert so. 398 mProperties.setProperty(key, (String) value); 399 } else if (value != null) { 400 mProperties.setProperty(key, value.toString()); 401 } 402 } 403 } 404 } 405 loadPropertiesFromGpsDebugConfig(Properties properties, String filePath)406 private void loadPropertiesFromGpsDebugConfig(Properties properties, String filePath) { 407 try { 408 File file = new File(filePath); 409 FileInputStream stream = null; 410 try { 411 stream = new FileInputStream(file); 412 properties.load(stream); 413 } finally { 414 IoUtils.closeQuietly(stream); 415 } 416 } catch (IOException e) { 417 if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + filePath); 418 } 419 } 420 loadPropertiesFromResource(Context context, Properties properties)421 private void loadPropertiesFromResource(Context context, 422 Properties properties) { 423 String[] configValues = context.getResources().getStringArray( 424 com.android.internal.R.array.config_gnssParameters); 425 for (String item : configValues) { 426 if (DEBUG) Log.d(TAG, "GnssParamsResource: " + item); 427 // We need to support "KEY =", but not "=VALUE". 428 int index = item.indexOf("="); 429 if (index > 0 && index + 1 < item.length()) { 430 String key = item.substring(0, index); 431 String value = item.substring(index + 1); 432 properties.setProperty(key.trim().toUpperCase(Locale.ROOT), value); 433 } else { 434 Log.w(TAG, "malformed contents: " + item); 435 } 436 } 437 } 438 getRangeCheckedConfigEsExtensionSec()439 private int getRangeCheckedConfigEsExtensionSec() { 440 int emergencyExtensionSeconds = getIntConfig(CONFIG_ES_EXTENSION_SEC, 0); 441 if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) { 442 Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds 443 + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS); 444 emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS; 445 } else if (emergencyExtensionSeconds < 0) { 446 Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds 447 + " is negative, reset to zero."); 448 emergencyExtensionSeconds = 0; 449 } 450 return emergencyExtensionSeconds; 451 } 452 getIntConfig(String configParameter, int defaultValue)453 private int getIntConfig(String configParameter, int defaultValue) { 454 String valueString = mProperties.getProperty(configParameter); 455 if (TextUtils.isEmpty(valueString)) { 456 return defaultValue; 457 } 458 try { 459 return Integer.decode(valueString); 460 } catch (NumberFormatException e) { 461 Log.e(TAG, "Unable to parse config parameter " + configParameter + " value: " 462 + valueString + ". Using default value: " + defaultValue); 463 return defaultValue; 464 } 465 } 466 getBooleanConfig(String configParameter, boolean defaultValue)467 private boolean getBooleanConfig(String configParameter, boolean defaultValue) { 468 String valueString = mProperties.getProperty(configParameter); 469 if (TextUtils.isEmpty(valueString)) { 470 return defaultValue; 471 } 472 return Boolean.parseBoolean(valueString); 473 } 474 isConfigEsExtensionSecSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)475 private static boolean isConfigEsExtensionSecSupported( 476 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 477 // ES_EXTENSION_SEC is introduced in @2.0::IGnssConfiguration.hal 478 return gnssConfiguartionIfaceVersion.mMajor >= 2; 479 } 480 isConfigSuplEsSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)481 private static boolean isConfigSuplEsSupported( 482 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 483 // SUPL_ES is deprecated in @2.0::IGnssConfiguration.hal 484 return gnssConfiguartionIfaceVersion.mMajor < 2; 485 } 486 isConfigGpsLockSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)487 private static boolean isConfigGpsLockSupported( 488 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 489 // GPS_LOCK is deprecated in @2.0::IGnssConfiguration.hal 490 return gnssConfiguartionIfaceVersion.mMajor < 2; 491 } 492 isSimAbsent(Context context)493 private static boolean isSimAbsent(Context context) { 494 TelephonyManager phone = (TelephonyManager) context.getSystemService( 495 Context.TELEPHONY_SERVICE); 496 return phone.getSimState() == TelephonyManager.SIM_STATE_ABSENT; 497 } 498 native_get_gnss_configuration_version()499 private static native HalInterfaceVersion native_get_gnss_configuration_version(); 500 native_set_supl_version(int version)501 private static native boolean native_set_supl_version(int version); 502 native_set_supl_mode(int mode)503 private static native boolean native_set_supl_mode(int mode); 504 native_set_supl_es(int es)505 private static native boolean native_set_supl_es(int es); 506 native_set_lpp_profile(int lppProfile)507 private static native boolean native_set_lpp_profile(int lppProfile); 508 native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect)509 private static native boolean native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect); 510 native_set_gps_lock(int gpsLock)511 private static native boolean native_set_gps_lock(int gpsLock); 512 native_set_emergency_supl_pdn(int emergencySuplPdn)513 private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn); 514 native_set_satellite_blocklist(int[] constellations, int[] svIds)515 private static native boolean native_set_satellite_blocklist(int[] constellations, int[] svIds); 516 native_set_es_extension_sec(int emergencyExtensionSeconds)517 private static native boolean native_set_es_extension_sec(int emergencyExtensionSeconds); 518 } 519