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