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