• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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