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