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