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 = 84 "ENABLE_NI_SUPL_MESSAGE_INJECTION"; 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, false); 234 } 235 236 /** 237 * Returns true if a long-term PSDS server is configured. 238 */ isLongTermPsdsServerConfigured()239 boolean isLongTermPsdsServerConfigured() { 240 return (mProperties.getProperty(CONFIG_LONGTERM_PSDS_SERVER_1) != null 241 || mProperties.getProperty(CONFIG_LONGTERM_PSDS_SERVER_2) != null 242 || mProperties.getProperty(CONFIG_LONGTERM_PSDS_SERVER_3) != null); 243 } 244 245 /** 246 * Updates the GNSS HAL satellite denylist. 247 */ setSatelliteBlocklist(int[] constellations, int[] svids)248 void setSatelliteBlocklist(int[] constellations, int[] svids) { 249 native_set_satellite_blocklist(constellations, svids); 250 } 251 getHalInterfaceVersion()252 HalInterfaceVersion getHalInterfaceVersion() { 253 return native_get_gnss_configuration_version(); 254 } 255 256 interface SetCarrierProperty { set(int value)257 boolean set(int value); 258 } 259 260 /** 261 * Loads the GNSS properties from carrier config file followed by the properties from 262 * gps debug config file, and injects the GNSS properties into the HAL. 263 */ reloadGpsProperties()264 void reloadGpsProperties() { 265 reloadGpsProperties(/* inEmergency= */ false, /* activeSubId= */ -1); 266 } 267 268 /** 269 * Loads the GNSS properties from carrier config file followed by the properties from 270 * gps debug config file, and injects the GNSS properties into the HAL. 271 */ reloadGpsProperties(boolean inEmergency, int activeSubId)272 void reloadGpsProperties(boolean inEmergency, int activeSubId) { 273 if (DEBUG) { 274 Log.d(TAG, 275 "Reset GPS properties, previous size = " + mProperties.size() + ", inEmergency:" 276 + inEmergency + ", activeSubId=" + activeSubId); 277 } 278 loadPropertiesFromCarrierConfig(inEmergency, activeSubId); 279 280 if (Flags.gnssConfigurationFromResource()) { 281 // Overlay carrier properties from resources. 282 loadPropertiesFromResource(mContext, mProperties); 283 } 284 285 if (isSimAbsent(mContext)) { 286 // Use the default SIM's LPP profile when SIM is absent. 287 String lpp_prof = SystemProperties.get(LPP_PROFILE); 288 if (!TextUtils.isEmpty(lpp_prof)) { 289 // override default value of this if lpp_prof is not empty 290 mProperties.setProperty(CONFIG_LPP_PROFILE, lpp_prof); 291 } 292 } 293 294 /* 295 * Overlay carrier properties from a debug configuration file. 296 */ 297 loadPropertiesFromGpsDebugConfig(mProperties, DEBUG_PROPERTIES_VENDOR_FILE); 298 loadPropertiesFromGpsDebugConfig(mProperties, DEBUG_PROPERTIES_SYSTEM_FILE); 299 mEsExtensionSec = getRangeCheckedConfigEsExtensionSec(); 300 301 logConfigurations(); 302 303 final HalInterfaceVersion gnssConfigurationIfaceVersion = getHalInterfaceVersion(); 304 if (gnssConfigurationIfaceVersion != null) { 305 // Set to a range checked value. 306 if (isConfigEsExtensionSecSupported(gnssConfigurationIfaceVersion) 307 && !native_set_es_extension_sec(mEsExtensionSec)) { 308 Log.e(TAG, "Unable to set " + CONFIG_ES_EXTENSION_SEC + ": " + mEsExtensionSec); 309 } 310 311 Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>(); 312 313 map.put(CONFIG_SUPL_VER, GnssConfiguration::native_set_supl_version); 314 map.put(CONFIG_SUPL_MODE, GnssConfiguration::native_set_supl_mode); 315 316 if (isConfigSuplEsSupported(gnssConfigurationIfaceVersion)) { 317 map.put(CONFIG_SUPL_ES, GnssConfiguration::native_set_supl_es); 318 } 319 320 map.put(CONFIG_LPP_PROFILE, GnssConfiguration::native_set_lpp_profile); 321 map.put(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, 322 GnssConfiguration::native_set_gnss_pos_protocol_select); 323 map.put(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, 324 GnssConfiguration::native_set_emergency_supl_pdn); 325 326 if (isConfigGpsLockSupported(gnssConfigurationIfaceVersion)) { 327 map.put(CONFIG_GPS_LOCK, GnssConfiguration::native_set_gps_lock); 328 } 329 330 for (Entry<String, SetCarrierProperty> entry : map.entrySet()) { 331 String propertyName = entry.getKey(); 332 String propertyValueString = mProperties.getProperty(propertyName); 333 if (propertyValueString != null) { 334 try { 335 int propertyValueInt = Integer.decode(propertyValueString); 336 boolean result = entry.getValue().set(propertyValueInt); 337 if (!result) { 338 Log.e(TAG, "Unable to set " + propertyName); 339 } 340 } catch (NumberFormatException e) { 341 Log.e(TAG, "Unable to parse propertyName: " + propertyValueString); 342 } 343 } 344 } 345 } else if (DEBUG) { 346 Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not" 347 + " supported"); 348 } 349 } 350 logConfigurations()351 private void logConfigurations() { 352 FrameworkStatsLog.write(FrameworkStatsLog.GNSS_CONFIGURATION_REPORTED, 353 getSuplHost(), 354 getSuplPort(0), 355 getC2KHost(), 356 getC2KPort(0), 357 getIntConfig(CONFIG_SUPL_VER, 0), 358 getSuplMode(0), 359 getSuplEs(0) == 1, 360 getIntConfig(CONFIG_LPP_PROFILE, 0), 361 getIntConfig(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, 0), 362 getIntConfig(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, 0) == 1, 363 getIntConfig(CONFIG_GPS_LOCK, 0), 364 getEsExtensionSec(), 365 mProperties.getProperty(CONFIG_NFW_PROXY_APPS)); 366 } 367 368 /** 369 * Loads GNSS properties from carrier config file. 370 */ loadPropertiesFromCarrierConfig(boolean inEmergency, int activeSubId)371 void loadPropertiesFromCarrierConfig(boolean inEmergency, int activeSubId) { 372 CarrierConfigManager configManager = (CarrierConfigManager) 373 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); 374 if (configManager == null) { 375 return; 376 } 377 378 int subId = SubscriptionManager.getDefaultDataSubscriptionId(); 379 if (inEmergency && activeSubId >= 0) { 380 subId = activeSubId; 381 } 382 PersistableBundle configs = SubscriptionManager.isValidSubscriptionId(subId) 383 ? configManager.getConfigForSubId(subId) : configManager.getConfig(); 384 if (configs == null) { 385 if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config."); 386 configs = CarrierConfigManager.getDefaultConfig(); 387 } 388 for (String configKey : configs.keySet()) { 389 if (configKey != null && configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) { 390 String key = configKey 391 .substring(CarrierConfigManager.Gps.KEY_PREFIX.length()) 392 .toUpperCase(Locale.ROOT); 393 Object value = configs.get(configKey); 394 if (DEBUG) Log.d(TAG, "Gps config: " + key + " = " + value); 395 if (value instanceof String) { 396 // Most GPS properties are of String type; convert so. 397 mProperties.setProperty(key, (String) value); 398 } else if (value != null) { 399 mProperties.setProperty(key, value.toString()); 400 } 401 } 402 } 403 } 404 loadPropertiesFromGpsDebugConfig(Properties properties, String filePath)405 private void loadPropertiesFromGpsDebugConfig(Properties properties, String filePath) { 406 try { 407 File file = new File(filePath); 408 FileInputStream stream = null; 409 try { 410 stream = new FileInputStream(file); 411 properties.load(stream); 412 } finally { 413 IoUtils.closeQuietly(stream); 414 } 415 } catch (IOException e) { 416 if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + filePath); 417 } 418 } 419 loadPropertiesFromResource(Context context, Properties properties)420 private void loadPropertiesFromResource(Context context, 421 Properties properties) { 422 String[] configValues = context.getResources().getStringArray( 423 com.android.internal.R.array.config_gnssParameters); 424 for (String item : configValues) { 425 if (DEBUG) Log.d(TAG, "GnssParamsResource: " + item); 426 // We need to support "KEY =", but not "=VALUE". 427 int index = item.indexOf("="); 428 if (index > 0 && index + 1 < item.length()) { 429 String key = item.substring(0, index); 430 String value = item.substring(index + 1); 431 properties.setProperty(key.trim().toUpperCase(Locale.ROOT), value); 432 } else { 433 Log.w(TAG, "malformed contents: " + item); 434 } 435 } 436 } 437 getRangeCheckedConfigEsExtensionSec()438 private int getRangeCheckedConfigEsExtensionSec() { 439 int emergencyExtensionSeconds = getIntConfig(CONFIG_ES_EXTENSION_SEC, 0); 440 if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) { 441 Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds 442 + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS); 443 emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS; 444 } else if (emergencyExtensionSeconds < 0) { 445 Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds 446 + " is negative, reset to zero."); 447 emergencyExtensionSeconds = 0; 448 } 449 return emergencyExtensionSeconds; 450 } 451 getIntConfig(String configParameter, int defaultValue)452 private int getIntConfig(String configParameter, int defaultValue) { 453 String valueString = mProperties.getProperty(configParameter); 454 if (TextUtils.isEmpty(valueString)) { 455 return defaultValue; 456 } 457 try { 458 return Integer.decode(valueString); 459 } catch (NumberFormatException e) { 460 Log.e(TAG, "Unable to parse config parameter " + configParameter + " value: " 461 + valueString + ". Using default value: " + defaultValue); 462 return defaultValue; 463 } 464 } 465 getBooleanConfig(String configParameter, boolean defaultValue)466 private boolean getBooleanConfig(String configParameter, boolean defaultValue) { 467 String valueString = mProperties.getProperty(configParameter); 468 if (TextUtils.isEmpty(valueString)) { 469 return defaultValue; 470 } 471 return Boolean.parseBoolean(valueString); 472 } 473 isConfigEsExtensionSecSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)474 private static boolean isConfigEsExtensionSecSupported( 475 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 476 // ES_EXTENSION_SEC is introduced in @2.0::IGnssConfiguration.hal 477 return gnssConfiguartionIfaceVersion.mMajor >= 2; 478 } 479 isConfigSuplEsSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)480 private static boolean isConfigSuplEsSupported( 481 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 482 // SUPL_ES is deprecated in @2.0::IGnssConfiguration.hal 483 return gnssConfiguartionIfaceVersion.mMajor < 2; 484 } 485 isConfigGpsLockSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)486 private static boolean isConfigGpsLockSupported( 487 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 488 // GPS_LOCK is deprecated in @2.0::IGnssConfiguration.hal 489 return gnssConfiguartionIfaceVersion.mMajor < 2; 490 } 491 isSimAbsent(Context context)492 private static boolean isSimAbsent(Context context) { 493 TelephonyManager phone = (TelephonyManager) context.getSystemService( 494 Context.TELEPHONY_SERVICE); 495 return phone.getSimState() == TelephonyManager.SIM_STATE_ABSENT; 496 } 497 native_get_gnss_configuration_version()498 private static native HalInterfaceVersion native_get_gnss_configuration_version(); 499 native_set_supl_version(int version)500 private static native boolean native_set_supl_version(int version); 501 native_set_supl_mode(int mode)502 private static native boolean native_set_supl_mode(int mode); 503 native_set_supl_es(int es)504 private static native boolean native_set_supl_es(int es); 505 native_set_lpp_profile(int lppProfile)506 private static native boolean native_set_lpp_profile(int lppProfile); 507 native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect)508 private static native boolean native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect); 509 native_set_gps_lock(int gpsLock)510 private static native boolean native_set_gps_lock(int gpsLock); 511 native_set_emergency_supl_pdn(int emergencySuplPdn)512 private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn); 513 native_set_satellite_blocklist(int[] constellations, int[] svIds)514 private static native boolean native_set_satellite_blocklist(int[] constellations, int[] svIds); 515 native_set_es_extension_sec(int emergencyExtensionSeconds)516 private static native boolean native_set_es_extension_sec(int emergencyExtensionSeconds); 517 } 518