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 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 getHalInterfaceVersion()208 HalInterfaceVersion getHalInterfaceVersion() { 209 return native_get_gnss_configuration_version(); 210 } 211 212 interface SetCarrierProperty { set(int value)213 boolean set(int value); 214 } 215 216 /** 217 * Loads the GNSS properties from carrier config file followed by the properties from 218 * gps debug config file. 219 */ reloadGpsProperties()220 void reloadGpsProperties() { 221 if (DEBUG) Log.d(TAG, "Reset GPS properties, previous size = " + mProperties.size()); 222 loadPropertiesFromCarrierConfig(); 223 224 String lpp_prof = SystemProperties.get(LPP_PROFILE); 225 if (!TextUtils.isEmpty(lpp_prof)) { 226 // override default value of this if lpp_prof is not empty 227 mProperties.setProperty(CONFIG_LPP_PROFILE, lpp_prof); 228 } 229 /* 230 * Overlay carrier properties from a debug configuration file. 231 */ 232 loadPropertiesFromGpsDebugConfig(mProperties); 233 234 mEsExtensionSec = getRangeCheckedConfigEsExtensionSec(); 235 236 logConfigurations(); 237 238 final HalInterfaceVersion gnssConfigurationIfaceVersion = getHalInterfaceVersion(); 239 if (gnssConfigurationIfaceVersion != null) { 240 // Set to a range checked value. 241 if (isConfigEsExtensionSecSupported(gnssConfigurationIfaceVersion) 242 && !native_set_es_extension_sec(mEsExtensionSec)) { 243 Log.e(TAG, "Unable to set " + CONFIG_ES_EXTENSION_SEC + ": " + mEsExtensionSec); 244 } 245 246 Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() { 247 { 248 put(CONFIG_SUPL_VER, GnssConfiguration::native_set_supl_version); 249 put(CONFIG_SUPL_MODE, GnssConfiguration::native_set_supl_mode); 250 251 if (isConfigSuplEsSupported(gnssConfigurationIfaceVersion)) { 252 put(CONFIG_SUPL_ES, GnssConfiguration::native_set_supl_es); 253 } 254 255 put(CONFIG_LPP_PROFILE, GnssConfiguration::native_set_lpp_profile); 256 put(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, 257 GnssConfiguration::native_set_gnss_pos_protocol_select); 258 put(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, 259 GnssConfiguration::native_set_emergency_supl_pdn); 260 261 if (isConfigGpsLockSupported(gnssConfigurationIfaceVersion)) { 262 put(CONFIG_GPS_LOCK, GnssConfiguration::native_set_gps_lock); 263 } 264 } 265 }; 266 267 for (Entry<String, SetCarrierProperty> entry : map.entrySet()) { 268 String propertyName = entry.getKey(); 269 String propertyValueString = mProperties.getProperty(propertyName); 270 if (propertyValueString != null) { 271 try { 272 int propertyValueInt = Integer.decode(propertyValueString); 273 boolean result = entry.getValue().set(propertyValueInt); 274 if (!result) { 275 Log.e(TAG, "Unable to set " + propertyName); 276 } 277 } catch (NumberFormatException e) { 278 Log.e(TAG, "Unable to parse propertyName: " + propertyValueString); 279 } 280 } 281 } 282 } else if (DEBUG) { 283 Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not" 284 + " supported"); 285 } 286 } 287 logConfigurations()288 private void logConfigurations() { 289 StatsLog.write(StatsLog.GNSS_CONFIGURATION_REPORTED, 290 getSuplHost(), 291 getSuplPort(0), 292 getC2KHost(), 293 getC2KPort(0), 294 getIntConfig(CONFIG_SUPL_VER, 0), 295 getSuplMode(0), 296 getSuplEs(0) == 1, 297 getIntConfig(CONFIG_LPP_PROFILE, 0), 298 getIntConfig(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT, 0), 299 getIntConfig(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL, 0) == 1, 300 getIntConfig(CONFIG_GPS_LOCK, 0), 301 getEsExtensionSec(), 302 mProperties.getProperty(CONFIG_NFW_PROXY_APPS)); 303 } 304 305 /** 306 * Loads GNSS properties from carrier config file. 307 */ loadPropertiesFromCarrierConfig()308 void loadPropertiesFromCarrierConfig() { 309 CarrierConfigManager configManager = (CarrierConfigManager) 310 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); 311 if (configManager == null) { 312 return; 313 } 314 315 int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId(); 316 PersistableBundle configs = SubscriptionManager.isValidSubscriptionId(ddSubId) 317 ? configManager.getConfigForSubId(ddSubId) : null; 318 if (configs == null) { 319 if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config."); 320 configs = CarrierConfigManager.getDefaultConfig(); 321 } 322 for (String configKey : configs.keySet()) { 323 if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) { 324 String key = configKey 325 .substring(CarrierConfigManager.Gps.KEY_PREFIX.length()) 326 .toUpperCase(); 327 Object value = configs.get(configKey); 328 if (DEBUG) Log.d(TAG, "Gps config: " + key + " = " + value); 329 if (value instanceof String) { 330 // Most GPS properties are of String type; convert so. 331 mProperties.setProperty(key, (String) value); 332 } else if (value != null) { 333 mProperties.setProperty(key, value.toString()); 334 } 335 } 336 } 337 } 338 loadPropertiesFromGpsDebugConfig(Properties properties)339 private void loadPropertiesFromGpsDebugConfig(Properties properties) { 340 try { 341 File file = new File(DEBUG_PROPERTIES_FILE); 342 FileInputStream stream = null; 343 try { 344 stream = new FileInputStream(file); 345 properties.load(stream); 346 } finally { 347 IoUtils.closeQuietly(stream); 348 } 349 } catch (IOException e) { 350 if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE); 351 } 352 } 353 getRangeCheckedConfigEsExtensionSec()354 private int getRangeCheckedConfigEsExtensionSec() { 355 int emergencyExtensionSeconds = getIntConfig(CONFIG_ES_EXTENSION_SEC, 0); 356 if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) { 357 Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds 358 + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS); 359 emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS; 360 } else if (emergencyExtensionSeconds < 0) { 361 Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds 362 + " is negative, reset to zero."); 363 emergencyExtensionSeconds = 0; 364 } 365 return emergencyExtensionSeconds; 366 } 367 getIntConfig(String configParameter, int defaultValue)368 private int getIntConfig(String configParameter, int defaultValue) { 369 String valueString = mProperties.getProperty(configParameter); 370 if (TextUtils.isEmpty(valueString)) { 371 return defaultValue; 372 } 373 try { 374 return Integer.decode(valueString); 375 } catch (NumberFormatException e) { 376 Log.e(TAG, "Unable to parse config parameter " + configParameter + " value: " 377 + valueString + ". Using default value: " + defaultValue); 378 return defaultValue; 379 } 380 } 381 isConfigEsExtensionSecSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)382 private static boolean isConfigEsExtensionSecSupported( 383 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 384 // ES_EXTENSION_SEC is introduced in @2.0::IGnssConfiguration.hal 385 return gnssConfiguartionIfaceVersion.mMajor >= 2; 386 } 387 isConfigSuplEsSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)388 private static boolean isConfigSuplEsSupported( 389 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 390 // SUPL_ES is deprecated in @2.0::IGnssConfiguration.hal 391 return gnssConfiguartionIfaceVersion.mMajor < 2; 392 } 393 isConfigGpsLockSupported( HalInterfaceVersion gnssConfiguartionIfaceVersion)394 private static boolean isConfigGpsLockSupported( 395 HalInterfaceVersion gnssConfiguartionIfaceVersion) { 396 // GPS_LOCK is deprecated in @2.0::IGnssConfiguration.hal 397 return gnssConfiguartionIfaceVersion.mMajor < 2; 398 } 399 native_get_gnss_configuration_version()400 private static native HalInterfaceVersion native_get_gnss_configuration_version(); 401 native_set_supl_version(int version)402 private static native boolean native_set_supl_version(int version); 403 native_set_supl_mode(int mode)404 private static native boolean native_set_supl_mode(int mode); 405 native_set_supl_es(int es)406 private static native boolean native_set_supl_es(int es); 407 native_set_lpp_profile(int lppProfile)408 private static native boolean native_set_lpp_profile(int lppProfile); 409 native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect)410 private static native boolean native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect); 411 native_set_gps_lock(int gpsLock)412 private static native boolean native_set_gps_lock(int gpsLock); 413 native_set_emergency_supl_pdn(int emergencySuplPdn)414 private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn); 415 native_set_satellite_blacklist(int[] constellations, int[] svIds)416 private static native boolean native_set_satellite_blacklist(int[] constellations, int[] svIds); 417 native_set_es_extension_sec(int emergencyExtensionSeconds)418 private static native boolean native_set_es_extension_sec(int emergencyExtensionSeconds); 419 } 420