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