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