GpsLocationProvider.java revision 462bcd8194cfbcf5ecbed893b19f1a21f721e1ed
1/*
2 * Copyright (C) 2008 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
17package com.android.server.location;
18
19import com.android.internal.app.IAppOpsService;
20import com.android.internal.app.IBatteryStats;
21import com.android.internal.location.GpsNetInitiatedHandler;
22import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
23import com.android.internal.location.ProviderProperties;
24import com.android.internal.location.ProviderRequest;
25import com.android.internal.R;
26import com.android.internal.telephony.Phone;
27import com.android.internal.telephony.PhoneConstants;
28
29import android.app.AlarmManager;
30import android.app.AppOpsManager;
31import android.app.PendingIntent;
32import android.content.BroadcastReceiver;
33import android.content.Context;
34import android.content.Intent;
35import android.content.IntentFilter;
36import android.database.Cursor;
37import android.hardware.location.GeofenceHardware;
38import android.hardware.location.GeofenceHardwareImpl;
39import android.location.Criteria;
40import android.location.FusedBatchOptions;
41import android.location.GpsMeasurementsEvent;
42import android.location.GpsNavigationMessageEvent;
43import android.location.IGpsGeofenceHardware;
44import android.location.IGpsStatusListener;
45import android.location.IGpsStatusProvider;
46import android.location.ILocationManager;
47import android.location.INetInitiatedListener;
48import android.location.Location;
49import android.location.LocationListener;
50import android.location.LocationManager;
51import android.location.LocationProvider;
52import android.location.LocationRequest;
53import android.net.ConnectivityManager;
54import android.net.NetworkInfo;
55import android.net.Uri;
56import android.os.AsyncTask;
57import android.os.BatteryStats;
58import android.os.Binder;
59import android.os.Bundle;
60import android.os.Handler;
61import android.os.Looper;
62import android.os.Message;
63import android.os.PowerManager;
64import android.os.RemoteException;
65import android.os.ServiceManager;
66import android.os.SystemClock;
67import android.os.SystemProperties;
68import android.os.UserHandle;
69import android.os.WorkSource;
70import android.provider.Settings;
71import android.provider.Telephony.Carriers;
72import android.provider.Telephony.Sms.Intents;
73import android.telephony.SmsMessage;
74import android.telephony.TelephonyManager;
75import android.telephony.gsm.GsmCellLocation;
76import android.text.TextUtils;
77import android.util.Log;
78import android.util.NtpTrustedTime;
79
80import java.io.ByteArrayOutputStream;
81import java.io.File;
82import java.io.FileDescriptor;
83import java.io.FileInputStream;
84import java.io.IOException;
85import java.io.PrintWriter;
86import java.io.StringReader;
87import java.net.InetAddress;
88import java.net.UnknownHostException;
89import java.util.Date;
90import java.util.Map.Entry;
91import java.util.Properties;
92
93import libcore.io.IoUtils;
94
95/**
96 * A GPS implementation of LocationProvider used by LocationManager.
97 *
98 * {@hide}
99 */
100public class GpsLocationProvider implements LocationProviderInterface {
101
102    private static final String TAG = "GpsLocationProvider";
103
104    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
105    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
106
107    private static final ProviderProperties PROPERTIES = new ProviderProperties(
108            true, true, false, false, true, true, true,
109            Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
110
111    // these need to match GpsPositionMode enum in gps.h
112    private static final int GPS_POSITION_MODE_STANDALONE = 0;
113    private static final int GPS_POSITION_MODE_MS_BASED = 1;
114    private static final int GPS_POSITION_MODE_MS_ASSISTED = 2;
115
116    // these need to match GpsPositionRecurrence enum in gps.h
117    private static final int GPS_POSITION_RECURRENCE_PERIODIC = 0;
118    private static final int GPS_POSITION_RECURRENCE_SINGLE = 1;
119
120    // these need to match GpsStatusValue defines in gps.h
121    private static final int GPS_STATUS_NONE = 0;
122    private static final int GPS_STATUS_SESSION_BEGIN = 1;
123    private static final int GPS_STATUS_SESSION_END = 2;
124    private static final int GPS_STATUS_ENGINE_ON = 3;
125    private static final int GPS_STATUS_ENGINE_OFF = 4;
126
127    // these need to match GpsApgsStatusValue defines in gps.h
128    /** AGPS status event values. */
129    private static final int GPS_REQUEST_AGPS_DATA_CONN = 1;
130    private static final int GPS_RELEASE_AGPS_DATA_CONN = 2;
131    private static final int GPS_AGPS_DATA_CONNECTED = 3;
132    private static final int GPS_AGPS_DATA_CONN_DONE = 4;
133    private static final int GPS_AGPS_DATA_CONN_FAILED = 5;
134
135    // these need to match GpsLocationFlags enum in gps.h
136    private static final int LOCATION_INVALID = 0;
137    private static final int LOCATION_HAS_LAT_LONG = 1;
138    private static final int LOCATION_HAS_ALTITUDE = 2;
139    private static final int LOCATION_HAS_SPEED = 4;
140    private static final int LOCATION_HAS_BEARING = 8;
141    private static final int LOCATION_HAS_ACCURACY = 16;
142
143    // IMPORTANT - the GPS_DELETE_* symbols here must match constants in gps.h
144    private static final int GPS_DELETE_EPHEMERIS = 0x0001;
145    private static final int GPS_DELETE_ALMANAC = 0x0002;
146    private static final int GPS_DELETE_POSITION = 0x0004;
147    private static final int GPS_DELETE_TIME = 0x0008;
148    private static final int GPS_DELETE_IONO = 0x0010;
149    private static final int GPS_DELETE_UTC = 0x0020;
150    private static final int GPS_DELETE_HEALTH = 0x0040;
151    private static final int GPS_DELETE_SVDIR = 0x0080;
152    private static final int GPS_DELETE_SVSTEER = 0x0100;
153    private static final int GPS_DELETE_SADATA = 0x0200;
154    private static final int GPS_DELETE_RTI = 0x0400;
155    private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
156    private static final int GPS_DELETE_ALL = 0xFFFF;
157
158    // The GPS_CAPABILITY_* flags must match the values in gps.h
159    private static final int GPS_CAPABILITY_SCHEDULING = 0x0000001;
160    private static final int GPS_CAPABILITY_MSB = 0x0000002;
161    private static final int GPS_CAPABILITY_MSA = 0x0000004;
162    private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
163    private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010;
164
165    // The AGPS SUPL mode
166    private static final int AGPS_SUPL_MODE_MSA = 0x02;
167    private static final int AGPS_SUPL_MODE_MSB = 0x01;
168
169    // these need to match AGpsType enum in gps.h
170    private static final int AGPS_TYPE_SUPL = 1;
171    private static final int AGPS_TYPE_C2K = 2;
172
173    // these must match the definitions in gps.h
174    private static final int APN_INVALID = 0;
175    private static final int APN_IPV4 = 1;
176    private static final int APN_IPV6 = 2;
177    private static final int APN_IPV4V6 = 3;
178
179    // for mAGpsDataConnectionState
180    private static final int AGPS_DATA_CONNECTION_CLOSED = 0;
181    private static final int AGPS_DATA_CONNECTION_OPENING = 1;
182    private static final int AGPS_DATA_CONNECTION_OPEN = 2;
183
184    // Handler messages
185    private static final int CHECK_LOCATION = 1;
186    private static final int ENABLE = 2;
187    private static final int SET_REQUEST = 3;
188    private static final int UPDATE_NETWORK_STATE = 4;
189    private static final int INJECT_NTP_TIME = 5;
190    private static final int DOWNLOAD_XTRA_DATA = 6;
191    private static final int UPDATE_LOCATION = 7;
192    private static final int ADD_LISTENER = 8;
193    private static final int REMOVE_LISTENER = 9;
194    private static final int INJECT_NTP_TIME_FINISHED = 10;
195    private static final int DOWNLOAD_XTRA_DATA_FINISHED = 11;
196
197    // Request setid
198    private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
199    private static final int AGPS_RIL_REQUEST_SETID_MSISDN = 2;
200
201    // Request ref location
202    private static final int AGPS_RIL_REQUEST_REFLOC_CELLID = 1;
203    private static final int AGPS_RIL_REQUEST_REFLOC_MAC = 2;
204
205    // ref. location info
206    private static final int AGPS_REF_LOCATION_TYPE_GSM_CELLID = 1;
207    private static final int AGPS_REF_LOCATION_TYPE_UMTS_CELLID = 2;
208    private static final int AGPS_REG_LOCATION_TYPE_MAC        = 3;
209
210    // set id info
211    private static final int AGPS_SETID_TYPE_NONE = 0;
212    private static final int AGPS_SETID_TYPE_IMSI = 1;
213    private static final int AGPS_SETID_TYPE_MSISDN = 2;
214
215    private static final String PROPERTIES_FILE_PREFIX = "/etc/gps";
216    private static final String PROPERTIES_FILE_SUFFIX = ".conf";
217    private static final String DEFAULT_PROPERTIES_FILE = PROPERTIES_FILE_PREFIX + PROPERTIES_FILE_SUFFIX;
218
219    private static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
220    private static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
221
222    // GPS Geofence errors. Should match gps.h constants.
223    private static final int GPS_GEOFENCE_OPERATION_SUCCESS = 0;
224    private static final int GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 100;
225    private static final int GPS_GEOFENCE_ERROR_ID_EXISTS  = -101;
226    private static final int GPS_GEOFENCE_ERROR_ID_UNKNOWN = -102;
227    private static final int GPS_GEOFENCE_ERROR_INVALID_TRANSITION = -103;
228    private static final int GPS_GEOFENCE_ERROR_GENERIC = -149;
229
230    // Value of batterySaverGpsMode such that GPS isn't affected by battery saver mode.
231    private static final int BATTERY_SAVER_MODE_NO_CHANGE = 0;
232    // Value of batterySaverGpsMode such that GPS is disabled when battery saver mode
233    // is enabled and the screen is off.
234    private static final int BATTERY_SAVER_MODE_DISABLED_WHEN_SCREEN_OFF = 1;
235    // Secure setting for GPS behavior when battery saver mode is on.
236    private static final String BATTERY_SAVER_GPS_MODE = "batterySaverGpsMode";
237
238    /** simpler wrapper for ProviderRequest + Worksource */
239    private static class GpsRequest {
240        public ProviderRequest request;
241        public WorkSource source;
242        public GpsRequest(ProviderRequest request, WorkSource source) {
243            this.request = request;
244            this.source = source;
245        }
246    }
247
248    private Object mLock = new Object();
249
250    private int mLocationFlags = LOCATION_INVALID;
251
252    // current status
253    private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE;
254
255    // time for last status update
256    private long mStatusUpdateTime = SystemClock.elapsedRealtime();
257
258    // turn off GPS fix icon if we haven't received a fix in 10 seconds
259    private static final long RECENT_FIX_TIMEOUT = 10 * 1000;
260
261    // stop trying if we do not receive a fix within 60 seconds
262    private static final int NO_FIX_TIMEOUT = 60 * 1000;
263
264    // if the fix interval is below this we leave GPS on,
265    // if above then we cycle the GPS driver.
266    // Typical hot TTTF is ~5 seconds, so 10 seconds seems sane.
267    private static final int GPS_POLLING_THRESHOLD_INTERVAL = 10 * 1000;
268
269    // how often to request NTP time, in milliseconds
270    // current setting 24 hours
271    private static final long NTP_INTERVAL = 24*60*60*1000;
272    // how long to wait if we have a network error in NTP or XTRA downloading
273    // current setting - 5 minutes
274    private static final long RETRY_INTERVAL = 5*60*1000;
275
276    // true if we are enabled, protected by this
277    private boolean mEnabled;
278
279    // true if we have network connectivity
280    private boolean mNetworkAvailable;
281
282    // states for injecting ntp and downloading xtra data
283    private static final int STATE_PENDING_NETWORK = 0;
284    private static final int STATE_DOWNLOADING = 1;
285    private static final int STATE_IDLE = 2;
286
287    // flags to trigger NTP or XTRA data download when network becomes available
288    // initialized to true so we do NTP and XTRA when the network comes up after booting
289    private int mInjectNtpTimePending = STATE_PENDING_NETWORK;
290    private int mDownloadXtraDataPending = STATE_PENDING_NETWORK;
291
292    // set to true if the GPS engine does not do on-demand NTP time requests
293    private boolean mPeriodicTimeInjection;
294
295    // true if GPS is navigating
296    private boolean mNavigating;
297
298    // true if GPS engine is on
299    private boolean mEngineOn;
300
301    // requested frequency of fixes, in milliseconds
302    private int mFixInterval = 1000;
303
304    // true if we started navigation
305    private boolean mStarted;
306
307    // true if single shot request is in progress
308    private boolean mSingleShot;
309
310    // capabilities of the GPS engine
311    private int mEngineCapabilities;
312
313    // true if XTRA is supported
314    private boolean mSupportsXtra;
315
316    // for calculating time to first fix
317    private long mFixRequestTime = 0;
318    // time to first fix for most recent session
319    private int mTimeToFirstFix = 0;
320    // time we received our last fix
321    private long mLastFixTime;
322
323    private int mPositionMode;
324
325    // Current request from underlying location clients.
326    private ProviderRequest mProviderRequest = null;
327    // Current list of underlying location clients.
328    private WorkSource mWorkSource = null;
329    // True if gps should be disabled (used to support battery saver mode in settings).
330    private boolean mDisableGps = false;
331
332    // properties loaded from PROPERTIES_FILE
333    private Properties mProperties;
334    private String mSuplServerHost;
335    private int mSuplServerPort;
336    private String mC2KServerHost;
337    private int mC2KServerPort;
338    private boolean mSuplEsEnabled = false;
339
340    private final Context mContext;
341    private final NtpTrustedTime mNtpTime;
342    private final ILocationManager mILocationManager;
343    private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
344    private Bundle mLocationExtras = new Bundle();
345    private GpsStatusListenerHelper mListenerHelper = new GpsStatusListenerHelper() {
346        @Override
347        protected boolean isSupported() {
348            return GpsLocationProvider.isSupported();
349        }
350
351        @Override
352        protected boolean registerWithService() {
353            return true;
354        }
355
356        @Override
357        protected void unregisterFromService() {}
358    };
359
360    // Handler for processing events
361    private Handler mHandler;
362
363    private String mAGpsApn;
364    private int mApnIpType;
365    private int mAGpsDataConnectionState;
366    private InetAddress mAGpsDataConnectionIpAddr;
367    private final ConnectivityManager mConnMgr;
368    private final GpsNetInitiatedHandler mNIHandler;
369
370    // Wakelocks
371    private final static String WAKELOCK_KEY = "GpsLocationProvider";
372    private final PowerManager.WakeLock mWakeLock;
373
374    // Alarms
375    private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
376    private final static String ALARM_TIMEOUT = "com.android.internal.location.ALARM_TIMEOUT";
377
378    // SIM/Carrier info.
379    private final static String SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
380
381    private final PowerManager mPowerManager;
382    private final AlarmManager mAlarmManager;
383    private final PendingIntent mWakeupIntent;
384    private final PendingIntent mTimeoutIntent;
385
386    private final IAppOpsService mAppOpsService;
387    private final IBatteryStats mBatteryStats;
388
389    // only modified on handler thread
390    private WorkSource mClientSource = new WorkSource();
391
392    private GeofenceHardwareImpl mGeofenceHardwareImpl;
393
394    private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
395        @Override
396        public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
397            mListenerHelper.addListener(listener);
398        }
399
400        @Override
401        public void removeGpsStatusListener(IGpsStatusListener listener) {
402            mListenerHelper.removeListener(listener);
403        }
404    };
405
406    private final GpsMeasurementsProvider mGpsMeasurementsProvider = new GpsMeasurementsProvider() {
407        @Override
408        public boolean isSupported() {
409            return native_is_measurement_supported();
410        }
411
412        @Override
413        protected boolean registerWithService() {
414            return native_start_measurement_collection();
415        }
416
417        @Override
418        protected void unregisterFromService() {
419            native_stop_measurement_collection();
420        }
421    };
422
423    private final GpsNavigationMessageProvider mGpsNavigationMessageProvider =
424            new GpsNavigationMessageProvider() {
425        @Override
426        protected boolean isSupported() {
427            return native_is_navigation_message_supported();
428        }
429
430        @Override
431        protected boolean registerWithService() {
432            return native_start_navigation_message_collection();
433        }
434
435        @Override
436        protected void unregisterFromService() {
437            native_stop_navigation_message_collection();
438        }
439    };
440
441    public IGpsStatusProvider getGpsStatusProvider() {
442        return mGpsStatusProvider;
443    }
444
445    public IGpsGeofenceHardware getGpsGeofenceProxy() {
446        return mGpsGeofenceBinder;
447    }
448
449    public GpsMeasurementsProvider getGpsMeasurementsProvider() {
450        return mGpsMeasurementsProvider;
451    }
452
453    public GpsNavigationMessageProvider getGpsNavigationMessageProvider() {
454        return mGpsNavigationMessageProvider;
455    }
456
457    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
458        @Override public void onReceive(Context context, Intent intent) {
459            String action = intent.getAction();
460
461            if (DEBUG) Log.d(TAG, "receive broadcast intent, action: " + action);
462            if (action.equals(ALARM_WAKEUP)) {
463                startNavigating(false);
464            } else if (action.equals(ALARM_TIMEOUT)) {
465                hibernate();
466            } else if (action.equals(Intents.DATA_SMS_RECEIVED_ACTION)) {
467                checkSmsSuplInit(intent);
468            } else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) {
469                checkWapSuplInit(intent);
470            } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
471                int networkState;
472                if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)) {
473                    networkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
474                } else {
475                    networkState = LocationProvider.AVAILABLE;
476                }
477
478                // retrieve NetworkInfo result for this UID
479                NetworkInfo info =
480                        intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
481                ConnectivityManager connManager = (ConnectivityManager)
482                        mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
483                info = connManager.getNetworkInfo(info.getType());
484
485                updateNetworkState(networkState, info);
486            } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)
487                    || Intent.ACTION_SCREEN_OFF.equals(action)
488                    || Intent.ACTION_SCREEN_ON.equals(action)) {
489                updateLowPowerMode();
490            } else if (action.equals(SIM_STATE_CHANGED)) {
491                TelephonyManager phone = (TelephonyManager)
492                        mContext.getSystemService(Context.TELEPHONY_SERVICE);
493                String mccMnc = phone.getSimOperator();
494                if (!TextUtils.isEmpty(mccMnc)) {
495                    Log.d(TAG, "SIM MCC/MNC is available: " + mccMnc);
496                    synchronized (mLock) {
497                        reloadGpsProperties(context, mProperties);
498                        mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
499                    }
500                }
501            }
502        }
503    };
504
505    private void checkSmsSuplInit(Intent intent) {
506        SmsMessage[] messages = Intents.getMessagesFromIntent(intent);
507        for (int i=0; i <messages.length; i++) {
508            byte[] supl_init = messages[i].getUserData();
509            native_agps_ni_message(supl_init,supl_init.length);
510        }
511    }
512
513    private void checkWapSuplInit(Intent intent) {
514        byte[] supl_init = (byte[]) intent.getExtra("data");
515        native_agps_ni_message(supl_init,supl_init.length);
516    }
517
518    private void updateLowPowerMode() {
519        final boolean disableGps;
520        switch (Settings.Secure.getInt(mContext.getContentResolver(), BATTERY_SAVER_GPS_MODE,
521                BATTERY_SAVER_MODE_DISABLED_WHEN_SCREEN_OFF)) {
522            case BATTERY_SAVER_MODE_DISABLED_WHEN_SCREEN_OFF:
523                disableGps = mPowerManager.isPowerSaveMode() && !mPowerManager.isInteractive();
524                break;
525            default:
526                disableGps = false;
527        }
528        if (disableGps != mDisableGps) {
529            mDisableGps = disableGps;
530            updateRequirements();
531        }
532    }
533
534    public static boolean isSupported() {
535        return native_is_supported();
536    }
537
538    private void reloadGpsProperties(Context context, Properties properties) {
539        Log.d(TAG, "Reset GPS properties, previous size = " + properties.size());
540        loadPropertiesFromResource(context, properties);
541        boolean isPropertiesLoadedFromFile = false;
542        final String gpsHardware = SystemProperties.get("ro.hardware.gps");
543        if (!TextUtils.isEmpty(gpsHardware)) {
544            final String propFilename =
545                    PROPERTIES_FILE_PREFIX + "." + gpsHardware + PROPERTIES_FILE_SUFFIX;
546            isPropertiesLoadedFromFile =
547                    loadPropertiesFromFile(propFilename, properties);
548        }
549        if (!isPropertiesLoadedFromFile) {
550            loadPropertiesFromFile(DEFAULT_PROPERTIES_FILE, properties);
551        }
552        Log.d(TAG, "GPS properties reloaded, size = " + properties.size());
553
554        // TODO: we should get rid of C2K specific setting.
555        setSuplHostPort(properties.getProperty("SUPL_HOST"),
556                        properties.getProperty("SUPL_PORT"));
557        mC2KServerHost = properties.getProperty("C2K_HOST");
558        String portString = properties.getProperty("C2K_PORT");
559        if (mC2KServerHost != null && portString != null) {
560            try {
561                mC2KServerPort = Integer.parseInt(portString);
562            } catch (NumberFormatException e) {
563                Log.e(TAG, "unable to parse C2K_PORT: " + portString);
564            }
565        }
566
567        try {
568            // Convert properties to string contents and send it to HAL.
569            ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
570            properties.store(baos, null);
571            native_configuration_update(baos.toString());
572            Log.d(TAG, "final config = " + baos.toString());
573        } catch (IOException ex) {
574            Log.w(TAG, "failed to dump properties contents");
575        }
576
577        // SUPL_ES configuration.
578        String suplESProperty = mProperties.getProperty("SUPL_ES");
579        if (suplESProperty != null) {
580            try {
581                mSuplEsEnabled = (Integer.parseInt(suplESProperty) == 1);
582            } catch (NumberFormatException e) {
583                Log.e(TAG, "unable to parse SUPL_ES: " + suplESProperty);
584            }
585        }
586    }
587
588    private void loadPropertiesFromResource(Context context,
589                                            Properties properties) {
590        String[] configValues = context.getResources().getStringArray(
591                com.android.internal.R.array.config_gpsParameters);
592        for (String item : configValues) {
593            Log.d(TAG, "GpsParamsResource: " + item);
594            // We need to support "KEY =", but not "=VALUE".
595            String[] split = item.split("=");
596            if (split.length == 2) {
597                properties.setProperty(split[0].trim().toUpperCase(), split[1]);
598            } else {
599                Log.w(TAG, "malformed contents: " + item);
600            }
601        }
602    }
603
604    private boolean loadPropertiesFromFile(String filename,
605                                           Properties properties) {
606        try {
607            File file = new File(filename);
608            FileInputStream stream = null;
609            try {
610                stream = new FileInputStream(file);
611                properties.load(stream);
612            } finally {
613                IoUtils.closeQuietly(stream);
614            }
615
616        } catch (IOException e) {
617            Log.w(TAG, "Could not open GPS configuration file " + filename);
618            return false;
619        }
620        return true;
621    }
622
623    public GpsLocationProvider(Context context, ILocationManager ilocationManager,
624            Looper looper) {
625        mContext = context;
626        mNtpTime = NtpTrustedTime.getInstance(context);
627        mILocationManager = ilocationManager;
628
629        mLocation.setExtras(mLocationExtras);
630
631        // Create a wake lock
632        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
633        mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
634        mWakeLock.setReferenceCounted(true);
635
636        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
637        mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
638        mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
639
640        mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
641
642        // App ops service to keep track of who is accessing the GPS
643        mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(
644                Context.APP_OPS_SERVICE));
645
646        // Battery statistics service to be notified when GPS turns on or off
647        mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
648                BatteryStats.SERVICE_NAME));
649
650        // Load GPS configuration.
651        mProperties = new Properties();
652        reloadGpsProperties(mContext, mProperties);
653
654        // Create a GPS net-initiated handler.
655        mNIHandler = new GpsNetInitiatedHandler(context,
656                                                mNetInitiatedListener,
657                                                mSuplEsEnabled);
658
659        // construct handler, listen for events
660        mHandler = new ProviderHandler(looper);
661        listenForBroadcasts();
662
663        // also listen for PASSIVE_PROVIDER updates
664        mHandler.post(new Runnable() {
665            @Override
666            public void run() {
667                LocationManager locManager =
668                        (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
669                final long minTime = 0;
670                final float minDistance = 0;
671                final boolean oneShot = false;
672                LocationRequest request = LocationRequest.createFromDeprecatedProvider(
673                        LocationManager.PASSIVE_PROVIDER,
674                        minTime,
675                        minDistance,
676                        oneShot);
677                // Don't keep track of this request since it's done on behalf of other clients
678                // (which are kept track of separately).
679                request.setHideFromAppOps(true);
680                locManager.requestLocationUpdates(
681                        request,
682                        new NetworkLocationListener(),
683                        mHandler.getLooper());
684            }
685        });
686    }
687
688    private void listenForBroadcasts() {
689        IntentFilter intentFilter = new IntentFilter();
690        intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
691        intentFilter.addDataScheme("sms");
692        intentFilter.addDataAuthority("localhost","7275");
693        mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, mHandler);
694
695        intentFilter = new IntentFilter();
696        intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
697        try {
698            intentFilter.addDataType("application/vnd.omaloc-supl-init");
699        } catch (IntentFilter.MalformedMimeTypeException e) {
700            Log.w(TAG, "Malformed SUPL init mime type");
701        }
702        mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, mHandler);
703
704        intentFilter = new IntentFilter();
705        intentFilter.addAction(ALARM_WAKEUP);
706        intentFilter.addAction(ALARM_TIMEOUT);
707        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
708        intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
709        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
710        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
711        intentFilter.addAction(SIM_STATE_CHANGED);
712        mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, mHandler);
713    }
714
715    /**
716     * Returns the name of this provider.
717     */
718    @Override
719    public String getName() {
720        return LocationManager.GPS_PROVIDER;
721    }
722
723    @Override
724    public ProviderProperties getProperties() {
725        return PROPERTIES;
726    }
727
728    public void updateNetworkState(int state, NetworkInfo info) {
729        sendMessage(UPDATE_NETWORK_STATE, state, info);
730    }
731
732    private void handleUpdateNetworkState(int state, NetworkInfo info) {
733        mNetworkAvailable = (state == LocationProvider.AVAILABLE);
734
735        if (DEBUG) {
736            Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable")
737                + " info: " + info);
738        }
739
740        if (info != null) {
741            boolean dataEnabled = Settings.Global.getInt(mContext.getContentResolver(),
742                                                         Settings.Global.MOBILE_DATA, 1) == 1;
743            boolean networkAvailable = info.isAvailable() && dataEnabled;
744            String defaultApn = getSelectedApn();
745            if (defaultApn == null) {
746                defaultApn = "dummy-apn";
747            }
748
749            native_update_network_state(info.isConnected(), info.getType(),
750                                        info.isRoaming(), networkAvailable,
751                                        info.getExtraInfo(), defaultApn);
752        }
753
754        if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
755                && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
756            if (mNetworkAvailable) {
757                String apnName = info.getExtraInfo();
758                if (apnName == null) {
759                    /* Assign a dummy value in the case of C2K as otherwise we will have a runtime
760                    exception in the following call to native_agps_data_conn_open*/
761                    apnName = "dummy-apn";
762                }
763                mAGpsApn = apnName;
764                mApnIpType = getApnIpType(apnName);
765                setRouting();
766                if (DEBUG) {
767                    String message = String.format(
768                            "native_agps_data_conn_open: mAgpsApn=%s, mApnIpType=%s",
769                            mAGpsApn, mApnIpType);
770                    Log.d(TAG, message);
771                }
772                native_agps_data_conn_open(mAGpsApn, mApnIpType);
773                mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
774            } else {
775                Log.e(TAG, "call native_agps_data_conn_failed, info: " + info);
776                mAGpsApn = null;
777                mApnIpType = APN_INVALID;
778                mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
779                native_agps_data_conn_failed();
780            }
781        }
782
783        if (mNetworkAvailable) {
784            if (mInjectNtpTimePending == STATE_PENDING_NETWORK) {
785                sendMessage(INJECT_NTP_TIME, 0, null);
786            }
787            if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
788                sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
789            }
790        }
791    }
792
793    private void handleInjectNtpTime() {
794        if (mInjectNtpTimePending == STATE_DOWNLOADING) {
795            // already downloading data
796            return;
797        }
798        if (!mNetworkAvailable) {
799            // try again when network is up
800            mInjectNtpTimePending = STATE_PENDING_NETWORK;
801            return;
802        }
803        mInjectNtpTimePending = STATE_DOWNLOADING;
804
805        // hold wake lock while task runs
806        mWakeLock.acquire();
807        AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
808            @Override
809            public void run() {
810                long delay;
811
812                // force refresh NTP cache when outdated
813                if (mNtpTime.getCacheAge() >= NTP_INTERVAL) {
814                    mNtpTime.forceRefresh();
815                }
816
817                // only update when NTP time is fresh
818                if (mNtpTime.getCacheAge() < NTP_INTERVAL) {
819                    long time = mNtpTime.getCachedNtpTime();
820                    long timeReference = mNtpTime.getCachedNtpTimeReference();
821                    long certainty = mNtpTime.getCacheCertainty();
822                    long now = System.currentTimeMillis();
823
824                    Log.d(TAG, "NTP server returned: "
825                            + time + " (" + new Date(time)
826                            + ") reference: " + timeReference
827                            + " certainty: " + certainty
828                            + " system time offset: " + (time - now));
829
830                    native_inject_time(time, timeReference, (int) certainty);
831                    delay = NTP_INTERVAL;
832                } else {
833                    if (DEBUG) Log.d(TAG, "requestTime failed");
834                    delay = RETRY_INTERVAL;
835                }
836
837                sendMessage(INJECT_NTP_TIME_FINISHED, 0, null);
838
839                if (mPeriodicTimeInjection) {
840                    // send delayed message for next NTP injection
841                    // since this is delayed and not urgent we do not hold a wake lock here
842                    mHandler.sendEmptyMessageDelayed(INJECT_NTP_TIME, delay);
843                }
844
845                // release wake lock held by task
846                mWakeLock.release();
847            }
848        });
849    }
850
851    private void handleDownloadXtraData() {
852        if (mDownloadXtraDataPending == STATE_DOWNLOADING) {
853            // already downloading data
854            return;
855        }
856        if (!mNetworkAvailable) {
857            // try again when network is up
858            mDownloadXtraDataPending = STATE_PENDING_NETWORK;
859            return;
860        }
861        mDownloadXtraDataPending = STATE_DOWNLOADING;
862
863        // hold wake lock while task runs
864        mWakeLock.acquire();
865        AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
866            @Override
867            public void run() {
868                GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mContext, mProperties);
869                byte[] data = xtraDownloader.downloadXtraData();
870                if (data != null) {
871                    if (DEBUG) {
872                        Log.d(TAG, "calling native_inject_xtra_data");
873                    }
874                    native_inject_xtra_data(data, data.length);
875                }
876
877                sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
878
879                if (data == null) {
880                    // try again later
881                    // since this is delayed and not urgent we do not hold a wake lock here
882                    mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA, RETRY_INTERVAL);
883                }
884
885                // release wake lock held by task
886                mWakeLock.release();
887            }
888        });
889    }
890
891    private void handleUpdateLocation(Location location) {
892        if (location.hasAccuracy()) {
893            native_inject_location(location.getLatitude(), location.getLongitude(),
894                    location.getAccuracy());
895        }
896    }
897
898    /**
899     * Enables this provider.  When enabled, calls to getStatus()
900     * must be handled.  Hardware may be started up
901     * when the provider is enabled.
902     */
903    @Override
904    public void enable() {
905        synchronized (mLock) {
906            if (mEnabled) return;
907            mEnabled = true;
908        }
909
910        sendMessage(ENABLE, 1, null);
911    }
912
913    private void setSuplHostPort(String hostString, String portString) {
914        if (hostString != null) {
915            mSuplServerHost = hostString;
916        }
917        if (portString != null) {
918            try {
919                mSuplServerPort = Integer.parseInt(portString);
920            } catch (NumberFormatException e) {
921                Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
922            }
923        }
924    }
925
926    /**
927     * Checks what SUPL mode to use, according to the AGPS mode as well as the
928     * allowed mode from properties.
929     *
930     * @param properties GPS properties
931     * @param agpsEnabled whether AGPS is enabled by settings value
932     * @param singleShot whether "singleshot" is needed
933     * @return SUPL mode (MSA vs MSB vs STANDALONE)
934     */
935    private int getSuplMode(Properties properties, boolean agpsEnabled, boolean singleShot) {
936        if (agpsEnabled) {
937            String modeString = properties.getProperty("SUPL_MODE");
938            int suplMode = 0;
939            if (!TextUtils.isEmpty(modeString)) {
940                try {
941                    suplMode = Integer.parseInt(modeString);
942                } catch (NumberFormatException e) {
943                    Log.e(TAG, "unable to parse SUPL_MODE: " + modeString);
944                    return GPS_POSITION_MODE_STANDALONE;
945                }
946            }
947            if (singleShot
948                    && hasCapability(GPS_CAPABILITY_MSA)
949                    && (suplMode & AGPS_SUPL_MODE_MSA) != 0) {
950                return GPS_POSITION_MODE_MS_ASSISTED;
951            } else if (hasCapability(GPS_CAPABILITY_MSB)
952                    && (suplMode & AGPS_SUPL_MODE_MSB) != 0) {
953                return GPS_POSITION_MODE_MS_BASED;
954            }
955        }
956        return GPS_POSITION_MODE_STANDALONE;
957    }
958
959    private void handleEnable() {
960        if (DEBUG) Log.d(TAG, "handleEnable");
961
962        boolean enabled = native_init();
963
964        if (enabled) {
965            mSupportsXtra = native_supports_xtra();
966            if (mSuplServerHost != null) {
967                native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
968            }
969            if (mC2KServerHost != null) {
970                native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
971            }
972        } else {
973            synchronized (mLock) {
974                mEnabled = false;
975            }
976            Log.w(TAG, "Failed to enable location provider");
977        }
978    }
979
980    /**
981     * Disables this provider.  When disabled, calls to getStatus()
982     * need not be handled.  Hardware may be shut
983     * down while the provider is disabled.
984     */
985    @Override
986    public void disable() {
987        synchronized (mLock) {
988            if (!mEnabled) return;
989            mEnabled = false;
990        }
991
992        sendMessage(ENABLE, 0, null);
993    }
994
995    private void handleDisable() {
996        if (DEBUG) Log.d(TAG, "handleDisable");
997
998        updateClientUids(new WorkSource());
999        stopNavigating();
1000        mAlarmManager.cancel(mWakeupIntent);
1001        mAlarmManager.cancel(mTimeoutIntent);
1002
1003        // do this before releasing wakelock
1004        native_cleanup();
1005    }
1006
1007    @Override
1008    public boolean isEnabled() {
1009        synchronized (mLock) {
1010            return mEnabled;
1011        }
1012    }
1013
1014    @Override
1015    public int getStatus(Bundle extras) {
1016        if (extras != null) {
1017            extras.putInt("satellites", mSvCount);
1018        }
1019        return mStatus;
1020    }
1021
1022    private void updateStatus(int status, int svCount) {
1023        if (status != mStatus || svCount != mSvCount) {
1024            mStatus = status;
1025            mSvCount = svCount;
1026            mLocationExtras.putInt("satellites", svCount);
1027            mStatusUpdateTime = SystemClock.elapsedRealtime();
1028        }
1029    }
1030
1031    @Override
1032    public long getStatusUpdateTime() {
1033        return mStatusUpdateTime;
1034    }
1035
1036    @Override
1037    public void setRequest(ProviderRequest request, WorkSource source) {
1038        sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
1039    }
1040
1041    private void handleSetRequest(ProviderRequest request, WorkSource source) {
1042        mProviderRequest = request;
1043        mWorkSource = source;
1044        updateRequirements();
1045    }
1046
1047    // Called when the requirements for GPS may have changed
1048    private void updateRequirements() {
1049        if (mProviderRequest == null || mWorkSource == null) {
1050            return;
1051        }
1052
1053        boolean singleShot = false;
1054
1055        // see if the request is for a single update
1056        if (mProviderRequest.locationRequests != null
1057                && mProviderRequest.locationRequests.size() > 0) {
1058            // if any request has zero or more than one updates
1059            // requested, then this is not single-shot mode
1060            singleShot = true;
1061
1062            for (LocationRequest lr : mProviderRequest.locationRequests) {
1063                if (lr.getNumUpdates() != 1) {
1064                    singleShot = false;
1065                }
1066            }
1067        }
1068
1069        if (DEBUG) Log.d(TAG, "setRequest " + mProviderRequest);
1070        if (mProviderRequest.reportLocation && !mDisableGps) {
1071            // update client uids
1072            updateClientUids(mWorkSource);
1073
1074            mFixInterval = (int) mProviderRequest.interval;
1075
1076            // check for overflow
1077            if (mFixInterval != mProviderRequest.interval) {
1078                Log.w(TAG, "interval overflow: " + mProviderRequest.interval);
1079                mFixInterval = Integer.MAX_VALUE;
1080            }
1081
1082            // apply request to GPS engine
1083            if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
1084                // change period
1085                if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
1086                        mFixInterval, 0, 0)) {
1087                    Log.e(TAG, "set_position_mode failed in setMinTime()");
1088                }
1089            } else if (!mStarted) {
1090                // start GPS
1091                startNavigating(singleShot);
1092            }
1093        } else {
1094            updateClientUids(new WorkSource());
1095
1096            stopNavigating();
1097            mAlarmManager.cancel(mWakeupIntent);
1098            mAlarmManager.cancel(mTimeoutIntent);
1099        }
1100    }
1101
1102    private void updateClientUids(WorkSource source) {
1103        // Update work source.
1104        WorkSource[] changes = mClientSource.setReturningDiffs(source);
1105        if (changes == null) {
1106            return;
1107        }
1108        WorkSource newWork = changes[0];
1109        WorkSource goneWork = changes[1];
1110
1111        // Update sources that were not previously tracked.
1112        if (newWork != null) {
1113            int lastuid = -1;
1114            for (int i=0; i<newWork.size(); i++) {
1115                try {
1116                    int uid = newWork.get(i);
1117                    mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
1118                            AppOpsManager.OP_GPS, uid, newWork.getName(i));
1119                    if (uid != lastuid) {
1120                        lastuid = uid;
1121                        mBatteryStats.noteStartGps(uid);
1122                    }
1123                } catch (RemoteException e) {
1124                    Log.w(TAG, "RemoteException", e);
1125                }
1126            }
1127        }
1128
1129        // Update sources that are no longer tracked.
1130        if (goneWork != null) {
1131            int lastuid = -1;
1132            for (int i=0; i<goneWork.size(); i++) {
1133                try {
1134                    int uid = goneWork.get(i);
1135                    mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
1136                            AppOpsManager.OP_GPS, uid, goneWork.getName(i));
1137                    if (uid != lastuid) {
1138                        lastuid = uid;
1139                        mBatteryStats.noteStopGps(uid);
1140                    }
1141                } catch (RemoteException e) {
1142                    Log.w(TAG, "RemoteException", e);
1143                }
1144            }
1145        }
1146    }
1147
1148    @Override
1149    public boolean sendExtraCommand(String command, Bundle extras) {
1150
1151        long identity = Binder.clearCallingIdentity();
1152        boolean result = false;
1153
1154        if ("delete_aiding_data".equals(command)) {
1155            result = deleteAidingData(extras);
1156        } else if ("force_time_injection".equals(command)) {
1157            sendMessage(INJECT_NTP_TIME, 0, null);
1158            result = true;
1159        } else if ("force_xtra_injection".equals(command)) {
1160            if (mSupportsXtra) {
1161                xtraDownloadRequest();
1162                result = true;
1163            }
1164        } else {
1165            Log.w(TAG, "sendExtraCommand: unknown command " + command);
1166        }
1167
1168        Binder.restoreCallingIdentity(identity);
1169        return result;
1170    }
1171
1172    private IGpsGeofenceHardware mGpsGeofenceBinder = new IGpsGeofenceHardware.Stub() {
1173        public boolean isHardwareGeofenceSupported() {
1174            return native_is_geofence_supported();
1175        }
1176
1177        public boolean addCircularHardwareGeofence(int geofenceId, double latitude,
1178                double longitude, double radius, int lastTransition, int monitorTransitions,
1179                int notificationResponsiveness, int unknownTimer) {
1180            return native_add_geofence(geofenceId, latitude, longitude, radius,
1181                    lastTransition, monitorTransitions, notificationResponsiveness, unknownTimer);
1182        }
1183
1184        public boolean removeHardwareGeofence(int geofenceId) {
1185            return native_remove_geofence(geofenceId);
1186        }
1187
1188        public boolean pauseHardwareGeofence(int geofenceId) {
1189            return native_pause_geofence(geofenceId);
1190        }
1191
1192        public boolean resumeHardwareGeofence(int geofenceId, int monitorTransition) {
1193            return native_resume_geofence(geofenceId, monitorTransition);
1194        }
1195    };
1196
1197    private boolean deleteAidingData(Bundle extras) {
1198        int flags;
1199
1200        if (extras == null) {
1201            flags = GPS_DELETE_ALL;
1202        } else {
1203            flags = 0;
1204            if (extras.getBoolean("ephemeris")) flags |= GPS_DELETE_EPHEMERIS;
1205            if (extras.getBoolean("almanac")) flags |= GPS_DELETE_ALMANAC;
1206            if (extras.getBoolean("position")) flags |= GPS_DELETE_POSITION;
1207            if (extras.getBoolean("time")) flags |= GPS_DELETE_TIME;
1208            if (extras.getBoolean("iono")) flags |= GPS_DELETE_IONO;
1209            if (extras.getBoolean("utc")) flags |= GPS_DELETE_UTC;
1210            if (extras.getBoolean("health")) flags |= GPS_DELETE_HEALTH;
1211            if (extras.getBoolean("svdir")) flags |= GPS_DELETE_SVDIR;
1212            if (extras.getBoolean("svsteer")) flags |= GPS_DELETE_SVSTEER;
1213            if (extras.getBoolean("sadata")) flags |= GPS_DELETE_SADATA;
1214            if (extras.getBoolean("rti")) flags |= GPS_DELETE_RTI;
1215            if (extras.getBoolean("celldb-info")) flags |= GPS_DELETE_CELLDB_INFO;
1216            if (extras.getBoolean("all")) flags |= GPS_DELETE_ALL;
1217        }
1218
1219        if (flags != 0) {
1220            native_delete_aiding_data(flags);
1221            return true;
1222        }
1223
1224        return false;
1225    }
1226
1227    private void startNavigating(boolean singleShot) {
1228        if (!mStarted) {
1229            if (DEBUG) Log.d(TAG, "startNavigating, singleShot is " + singleShot);
1230            mTimeToFirstFix = 0;
1231            mLastFixTime = 0;
1232            mStarted = true;
1233            mSingleShot = singleShot;
1234            mPositionMode = GPS_POSITION_MODE_STANDALONE;
1235
1236            boolean agpsEnabled =
1237                    (Settings.Global.getInt(mContext.getContentResolver(),
1238                                            Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0);
1239            mPositionMode = getSuplMode(mProperties, agpsEnabled, singleShot);
1240
1241            if (DEBUG) {
1242                String mode;
1243
1244                switch(mPositionMode) {
1245                    case GPS_POSITION_MODE_STANDALONE:
1246                        mode = "standalone";
1247                        break;
1248                    case GPS_POSITION_MODE_MS_ASSISTED:
1249                        mode = "MS_ASSISTED";
1250                        break;
1251                    case GPS_POSITION_MODE_MS_BASED:
1252                        mode = "MS_BASED";
1253                        break;
1254                    default:
1255                        mode = "unknown";
1256                        break;
1257                }
1258                Log.d(TAG, "setting position_mode to " + mode);
1259            }
1260
1261            int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
1262            if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
1263                    interval, 0, 0)) {
1264                mStarted = false;
1265                Log.e(TAG, "set_position_mode failed in startNavigating()");
1266                return;
1267            }
1268            if (!native_start()) {
1269                mStarted = false;
1270                Log.e(TAG, "native_start failed in startNavigating()");
1271                return;
1272            }
1273
1274            // reset SV count to zero
1275            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
1276            mFixRequestTime = System.currentTimeMillis();
1277            if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
1278                // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
1279                // and our fix interval is not short
1280                if (mFixInterval >= NO_FIX_TIMEOUT) {
1281                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
1282                            SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
1283                }
1284            }
1285        }
1286    }
1287
1288    private void stopNavigating() {
1289        if (DEBUG) Log.d(TAG, "stopNavigating");
1290        if (mStarted) {
1291            mStarted = false;
1292            mSingleShot = false;
1293            native_stop();
1294            mTimeToFirstFix = 0;
1295            mLastFixTime = 0;
1296            mLocationFlags = LOCATION_INVALID;
1297
1298            // reset SV count to zero
1299            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
1300        }
1301    }
1302
1303    private void hibernate() {
1304        // stop GPS until our next fix interval arrives
1305        stopNavigating();
1306        mAlarmManager.cancel(mTimeoutIntent);
1307        mAlarmManager.cancel(mWakeupIntent);
1308        long now = SystemClock.elapsedRealtime();
1309        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, now + mFixInterval, mWakeupIntent);
1310    }
1311
1312    private boolean hasCapability(int capability) {
1313        return ((mEngineCapabilities & capability) != 0);
1314    }
1315
1316
1317    /**
1318     * called from native code to update our position.
1319     */
1320    private void reportLocation(int flags, double latitude, double longitude, double altitude,
1321            float speed, float bearing, float accuracy, long timestamp) {
1322        if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +
1323                " timestamp: " + timestamp);
1324
1325        synchronized (mLocation) {
1326            mLocationFlags = flags;
1327            if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
1328                mLocation.setLatitude(latitude);
1329                mLocation.setLongitude(longitude);
1330                mLocation.setTime(timestamp);
1331                // It would be nice to push the elapsed real-time timestamp
1332                // further down the stack, but this is still useful
1333                mLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
1334            }
1335            if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
1336                mLocation.setAltitude(altitude);
1337            } else {
1338                mLocation.removeAltitude();
1339            }
1340            if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
1341                mLocation.setSpeed(speed);
1342            } else {
1343                mLocation.removeSpeed();
1344            }
1345            if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
1346                mLocation.setBearing(bearing);
1347            } else {
1348                mLocation.removeBearing();
1349            }
1350            if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
1351                mLocation.setAccuracy(accuracy);
1352            } else {
1353                mLocation.removeAccuracy();
1354            }
1355            mLocation.setExtras(mLocationExtras);
1356
1357            try {
1358                mILocationManager.reportLocation(mLocation, false);
1359            } catch (RemoteException e) {
1360                Log.e(TAG, "RemoteException calling reportLocation");
1361            }
1362        }
1363
1364        mLastFixTime = System.currentTimeMillis();
1365        // report time to first fix
1366        if (mTimeToFirstFix == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
1367            mTimeToFirstFix = (int)(mLastFixTime - mFixRequestTime);
1368            if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
1369
1370            // notify status listeners
1371            mListenerHelper.onFirstFix(mTimeToFirstFix);
1372        }
1373
1374        if (mSingleShot) {
1375            stopNavigating();
1376        }
1377
1378        if (mStarted && mStatus != LocationProvider.AVAILABLE) {
1379            // we want to time out if we do not receive a fix
1380            // within the time out and we are requesting infrequent fixes
1381            if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
1382                mAlarmManager.cancel(mTimeoutIntent);
1383            }
1384
1385            // send an intent to notify that the GPS is receiving fixes.
1386            Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
1387            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
1388            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1389            updateStatus(LocationProvider.AVAILABLE, mSvCount);
1390        }
1391
1392       if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted &&
1393               mFixInterval > GPS_POLLING_THRESHOLD_INTERVAL) {
1394            if (DEBUG) Log.d(TAG, "got fix, hibernating");
1395            hibernate();
1396        }
1397   }
1398
1399    /**
1400     * called from native code to update our status
1401     */
1402    private void reportStatus(int status) {
1403        if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
1404
1405        boolean wasNavigating = mNavigating;
1406        switch (status) {
1407            case GPS_STATUS_SESSION_BEGIN:
1408                mNavigating = true;
1409                mEngineOn = true;
1410                break;
1411            case GPS_STATUS_SESSION_END:
1412                mNavigating = false;
1413                break;
1414            case GPS_STATUS_ENGINE_ON:
1415                mEngineOn = true;
1416                break;
1417            case GPS_STATUS_ENGINE_OFF:
1418                mEngineOn = false;
1419                mNavigating = false;
1420                break;
1421        }
1422
1423        if (wasNavigating != mNavigating) {
1424            mListenerHelper.onStatusChanged(mNavigating);
1425
1426            // send an intent to notify that the GPS has been enabled or disabled
1427            Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
1428            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
1429            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1430        }
1431    }
1432
1433    /**
1434     * called from native code to update SV info
1435     */
1436    private void reportSvStatus() {
1437        int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
1438        mListenerHelper.onSvStatusChanged(
1439                svCount,
1440                mSvs,
1441                mSnrs,
1442                mSvElevations,
1443                mSvAzimuths,
1444                mSvMasks[EPHEMERIS_MASK],
1445                mSvMasks[ALMANAC_MASK],
1446                mSvMasks[USED_FOR_FIX_MASK]);
1447
1448        if (VERBOSE) {
1449            Log.v(TAG, "SV count: " + svCount +
1450                    " ephemerisMask: " + Integer.toHexString(mSvMasks[EPHEMERIS_MASK]) +
1451                    " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
1452            for (int i = 0; i < svCount; i++) {
1453                Log.v(TAG, "sv: " + mSvs[i] +
1454                        " snr: " + mSnrs[i]/10 +
1455                        " elev: " + mSvElevations[i] +
1456                        " azimuth: " + mSvAzimuths[i] +
1457                        ((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " E") +
1458                        ((mSvMasks[ALMANAC_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " A") +
1459                        ((mSvMasks[USED_FOR_FIX_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "" : "U"));
1460            }
1461        }
1462
1463        // return number of sets used in fix instead of total
1464        updateStatus(mStatus, Integer.bitCount(mSvMasks[USED_FOR_FIX_MASK]));
1465
1466        if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
1467            System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
1468            // send an intent to notify that the GPS is no longer receiving fixes.
1469            Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
1470            intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
1471            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1472            updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
1473        }
1474    }
1475
1476    /**
1477     * called from native code to update AGPS status
1478     */
1479    private void reportAGpsStatus(int type, int status, byte[] ipaddr) {
1480        switch (status) {
1481            case GPS_REQUEST_AGPS_DATA_CONN:
1482                if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
1483                Log.v(TAG, "Received SUPL IP addr[]: " + ipaddr);
1484                // Set mAGpsDataConnectionState before calling startUsingNetworkFeature
1485                //  to avoid a race condition with handleUpdateNetworkState()
1486                mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
1487                int result = mConnMgr.startUsingNetworkFeature(
1488                        ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
1489                if (ipaddr != null) {
1490                    try {
1491                        mAGpsDataConnectionIpAddr = InetAddress.getByAddress(ipaddr);
1492                        Log.v(TAG, "IP address converted to: " + mAGpsDataConnectionIpAddr);
1493                    } catch (UnknownHostException e) {
1494                        Log.e(TAG, "Bad IP Address: " + ipaddr, e);
1495                        mAGpsDataConnectionIpAddr = null;
1496                    }
1497                }
1498
1499                if (result == PhoneConstants.APN_ALREADY_ACTIVE) {
1500                    if (DEBUG) Log.d(TAG, "PhoneConstants.APN_ALREADY_ACTIVE");
1501                    if (mAGpsApn != null) {
1502                        setRouting();
1503                        native_agps_data_conn_open(mAGpsApn, mApnIpType);
1504                        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
1505                    } else {
1506                        Log.e(TAG, "mAGpsApn not set when receiving PhoneConstants.APN_ALREADY_ACTIVE");
1507                        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
1508                        native_agps_data_conn_failed();
1509                    }
1510                } else if (result == PhoneConstants.APN_REQUEST_STARTED) {
1511                    if (DEBUG) Log.d(TAG, "PhoneConstants.APN_REQUEST_STARTED");
1512                    // Nothing to do here
1513                } else {
1514                    if (DEBUG) Log.d(TAG, "startUsingNetworkFeature failed, value is " +
1515                                     result);
1516                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
1517                    native_agps_data_conn_failed();
1518                }
1519                break;
1520            case GPS_RELEASE_AGPS_DATA_CONN:
1521                if (DEBUG) Log.d(TAG, "GPS_RELEASE_AGPS_DATA_CONN");
1522                if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
1523                    mConnMgr.stopUsingNetworkFeature(
1524                            ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
1525                    native_agps_data_conn_closed();
1526                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
1527                    mAGpsDataConnectionIpAddr = null;
1528                }
1529                break;
1530            case GPS_AGPS_DATA_CONNECTED:
1531                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
1532                break;
1533            case GPS_AGPS_DATA_CONN_DONE:
1534                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_DONE");
1535                break;
1536            case GPS_AGPS_DATA_CONN_FAILED:
1537                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
1538                break;
1539            default:
1540                Log.d(TAG, "Received Unknown AGPS status: " + status);
1541        }
1542    }
1543
1544    /**
1545     * called from native code to report NMEA data received
1546     */
1547    private void reportNmea(long timestamp) {
1548        int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
1549        String nmea = new String(mNmeaBuffer, 0 /* offset */, length);
1550        mListenerHelper.onNmeaReceived(timestamp, nmea);
1551    }
1552
1553    /**
1554     * called from native code - Gps measurements callback
1555     */
1556    private void reportMeasurementData(GpsMeasurementsEvent event) {
1557        mGpsMeasurementsProvider.onMeasurementsAvailable(event);
1558    }
1559
1560    /**
1561     * called from native code - GPS navigation message callback
1562     */
1563    private void reportNavigationMessage(GpsNavigationMessageEvent event) {
1564        mGpsNavigationMessageProvider.onNavigationMessageAvailable(event);
1565    }
1566
1567    /**
1568     * called from native code to inform us what the GPS engine capabilities are
1569     */
1570    private void setEngineCapabilities(int capabilities) {
1571        mEngineCapabilities = capabilities;
1572
1573        if (!hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME) && !mPeriodicTimeInjection) {
1574            mPeriodicTimeInjection = true;
1575            requestUtcTime();
1576        }
1577    }
1578
1579    /**
1580     * called from native code to request XTRA data
1581     */
1582    private void xtraDownloadRequest() {
1583        if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
1584        sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
1585    }
1586
1587    /**
1588     * Helper method to construct a location object.
1589     */
1590    private Location buildLocation(
1591            int flags,
1592            double latitude,
1593            double longitude,
1594            double altitude,
1595            float speed,
1596            float bearing,
1597            float accuracy,
1598            long timestamp) {
1599        Location location = new Location(LocationManager.GPS_PROVIDER);
1600        if((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
1601            location.setLatitude(latitude);
1602            location.setLongitude(longitude);
1603            location.setTime(timestamp);
1604            location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
1605        }
1606        if((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
1607            location.setAltitude(altitude);
1608        }
1609        if((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
1610            location.setSpeed(speed);
1611        }
1612        if((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
1613            location.setBearing(bearing);
1614        }
1615        if((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
1616            location.setAccuracy(accuracy);
1617        }
1618        return location;
1619    }
1620
1621    /**
1622     * Converts the GPS HAL status to the internal Geofence Hardware status.
1623     */
1624    private int getGeofenceStatus(int status) {
1625        switch(status) {
1626            case GPS_GEOFENCE_OPERATION_SUCCESS:
1627                return GeofenceHardware.GEOFENCE_SUCCESS;
1628            case GPS_GEOFENCE_ERROR_GENERIC:
1629                return GeofenceHardware.GEOFENCE_FAILURE;
1630            case GPS_GEOFENCE_ERROR_ID_EXISTS:
1631                return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
1632            case GPS_GEOFENCE_ERROR_INVALID_TRANSITION:
1633                return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
1634            case GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES:
1635                return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
1636            case GPS_GEOFENCE_ERROR_ID_UNKNOWN:
1637                return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
1638            default:
1639                return -1;
1640        }
1641    }
1642
1643    /**
1644     * Called from native to report GPS Geofence transition
1645     * All geofence callbacks are called on the same thread
1646     */
1647    private void reportGeofenceTransition(int geofenceId, int flags, double latitude,
1648            double longitude, double altitude, float speed, float bearing, float accuracy,
1649            long timestamp, int transition, long transitionTimestamp) {
1650        if (mGeofenceHardwareImpl == null) {
1651            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1652        }
1653        Location location = buildLocation(
1654                flags,
1655                latitude,
1656                longitude,
1657                altitude,
1658                speed,
1659                bearing,
1660                accuracy,
1661                timestamp);
1662        mGeofenceHardwareImpl.reportGeofenceTransition(
1663                geofenceId,
1664                location,
1665                transition,
1666                transitionTimestamp,
1667                GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
1668                FusedBatchOptions.SourceTechnologies.GNSS);
1669    }
1670
1671    /**
1672     * called from native code to report GPS status change.
1673     */
1674    private void reportGeofenceStatus(int status, int flags, double latitude,
1675            double longitude, double altitude, float speed, float bearing, float accuracy,
1676            long timestamp) {
1677        if (mGeofenceHardwareImpl == null) {
1678            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1679        }
1680        Location location = buildLocation(
1681                flags,
1682                latitude,
1683                longitude,
1684                altitude,
1685                speed,
1686                bearing,
1687                accuracy,
1688                timestamp);
1689        int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
1690        if(status == GPS_GEOFENCE_AVAILABLE) {
1691            monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
1692        }
1693        mGeofenceHardwareImpl.reportGeofenceMonitorStatus(
1694                GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
1695                monitorStatus,
1696                location,
1697                FusedBatchOptions.SourceTechnologies.GNSS);
1698    }
1699
1700    /**
1701     * called from native code - Geofence Add callback
1702     */
1703    private void reportGeofenceAddStatus(int geofenceId, int status) {
1704        if (mGeofenceHardwareImpl == null) {
1705            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1706        }
1707        mGeofenceHardwareImpl.reportGeofenceAddStatus(geofenceId, getGeofenceStatus(status));
1708    }
1709
1710    /**
1711     * called from native code - Geofence Remove callback
1712     */
1713    private void reportGeofenceRemoveStatus(int geofenceId, int status) {
1714        if (mGeofenceHardwareImpl == null) {
1715            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1716        }
1717        mGeofenceHardwareImpl.reportGeofenceRemoveStatus(geofenceId, getGeofenceStatus(status));
1718    }
1719
1720    /**
1721     * called from native code - Geofence Pause callback
1722     */
1723    private void reportGeofencePauseStatus(int geofenceId, int status) {
1724        if (mGeofenceHardwareImpl == null) {
1725            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1726        }
1727        mGeofenceHardwareImpl.reportGeofencePauseStatus(geofenceId, getGeofenceStatus(status));
1728    }
1729
1730    /**
1731     * called from native code - Geofence Resume callback
1732     */
1733    private void reportGeofenceResumeStatus(int geofenceId, int status) {
1734        if (mGeofenceHardwareImpl == null) {
1735            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1736        }
1737        mGeofenceHardwareImpl.reportGeofenceResumeStatus(geofenceId, getGeofenceStatus(status));
1738    }
1739
1740    //=============================================================
1741    // NI Client support
1742    //=============================================================
1743    private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
1744        // Sends a response for an NI reqeust to HAL.
1745        @Override
1746        public boolean sendNiResponse(int notificationId, int userResponse)
1747        {
1748            // TODO Add Permission check
1749
1750            if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
1751                    ", response: " + userResponse);
1752            native_send_ni_response(notificationId, userResponse);
1753            return true;
1754        }
1755    };
1756
1757    public INetInitiatedListener getNetInitiatedListener() {
1758        return mNetInitiatedListener;
1759    }
1760
1761    // Called by JNI function to report an NI request.
1762    public void reportNiNotification(
1763            int notificationId,
1764            int niType,
1765            int notifyFlags,
1766            int timeout,
1767            int defaultResponse,
1768            String requestorId,
1769            String text,
1770            int requestorIdEncoding,
1771            int textEncoding,
1772            String extras  // Encoded extra data
1773        )
1774    {
1775        Log.i(TAG, "reportNiNotification: entered");
1776        Log.i(TAG, "notificationId: " + notificationId +
1777                ", niType: " + niType +
1778                ", notifyFlags: " + notifyFlags +
1779                ", timeout: " + timeout +
1780                ", defaultResponse: " + defaultResponse);
1781
1782        Log.i(TAG, "requestorId: " + requestorId +
1783                ", text: " + text +
1784                ", requestorIdEncoding: " + requestorIdEncoding +
1785                ", textEncoding: " + textEncoding);
1786
1787        GpsNiNotification notification = new GpsNiNotification();
1788
1789        notification.notificationId = notificationId;
1790        notification.niType = niType;
1791        notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
1792        notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
1793        notification.privacyOverride = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
1794        notification.timeout = timeout;
1795        notification.defaultResponse = defaultResponse;
1796        notification.requestorId = requestorId;
1797        notification.text = text;
1798        notification.requestorIdEncoding = requestorIdEncoding;
1799        notification.textEncoding = textEncoding;
1800
1801        // Process extras, assuming the format is
1802        // one of more lines of "key = value"
1803        Bundle bundle = new Bundle();
1804
1805        if (extras == null) extras = "";
1806        Properties extraProp = new Properties();
1807
1808        try {
1809            extraProp.load(new StringReader(extras));
1810        }
1811        catch (IOException e)
1812        {
1813            Log.e(TAG, "reportNiNotification cannot parse extras data: " + extras);
1814        }
1815
1816        for (Entry<Object, Object> ent : extraProp.entrySet())
1817        {
1818            bundle.putString((String) ent.getKey(), (String) ent.getValue());
1819        }
1820
1821        notification.extras = bundle;
1822
1823        mNIHandler.handleNiNotification(notification);
1824    }
1825
1826    /**
1827     * Called from native code to request set id info.
1828     * We should be careful about receiving null string from the TelephonyManager,
1829     * because sending null String to JNI function would cause a crash.
1830     */
1831
1832    private void requestSetID(int flags) {
1833        TelephonyManager phone = (TelephonyManager)
1834                mContext.getSystemService(Context.TELEPHONY_SERVICE);
1835        int    type = AGPS_SETID_TYPE_NONE;
1836        String data = "";
1837
1838        if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
1839            String data_temp = phone.getSubscriberId();
1840            if (data_temp == null) {
1841                // This means the framework does not have the SIM card ready.
1842            } else {
1843                // This means the framework has the SIM card.
1844                data = data_temp;
1845                type = AGPS_SETID_TYPE_IMSI;
1846            }
1847        }
1848        else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
1849            String data_temp = phone.getLine1Number();
1850            if (data_temp == null) {
1851                // This means the framework does not have the SIM card ready.
1852            } else {
1853                // This means the framework has the SIM card.
1854                data = data_temp;
1855                type = AGPS_SETID_TYPE_MSISDN;
1856            }
1857        }
1858        native_agps_set_id(type, data);
1859    }
1860
1861    /**
1862     * Called from native code to request utc time info
1863     */
1864
1865    private void requestUtcTime() {
1866        sendMessage(INJECT_NTP_TIME, 0, null);
1867    }
1868
1869    /**
1870     * Called from native code to request reference location info
1871     */
1872
1873    private void requestRefLocation(int flags) {
1874        TelephonyManager phone = (TelephonyManager)
1875                mContext.getSystemService(Context.TELEPHONY_SERVICE);
1876        final int phoneType = phone.getPhoneType();
1877        if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
1878            GsmCellLocation gsm_cell = (GsmCellLocation) phone.getCellLocation();
1879            if ((gsm_cell != null) && (phone.getNetworkOperator() != null)
1880                    && (phone.getNetworkOperator().length() > 3)) {
1881                int type;
1882                int mcc = Integer.parseInt(phone.getNetworkOperator().substring(0,3));
1883                int mnc = Integer.parseInt(phone.getNetworkOperator().substring(3));
1884                int networkType = phone.getNetworkType();
1885                if (networkType == TelephonyManager.NETWORK_TYPE_UMTS
1886                    || networkType == TelephonyManager.NETWORK_TYPE_HSDPA
1887                    || networkType == TelephonyManager.NETWORK_TYPE_HSUPA
1888                    || networkType == TelephonyManager.NETWORK_TYPE_HSPA
1889                    || networkType == TelephonyManager.NETWORK_TYPE_HSPAP) {
1890                    type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
1891                } else {
1892                    type = AGPS_REF_LOCATION_TYPE_GSM_CELLID;
1893                }
1894                native_agps_set_ref_location_cellid(type, mcc, mnc,
1895                        gsm_cell.getLac(), gsm_cell.getCid());
1896            } else {
1897                Log.e(TAG,"Error getting cell location info.");
1898            }
1899        } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
1900            Log.e(TAG, "CDMA not supported.");
1901        }
1902    }
1903
1904    private void sendMessage(int message, int arg, Object obj) {
1905        // hold a wake lock until this message is delivered
1906        // note that this assumes the message will not be removed from the queue before
1907        // it is handled (otherwise the wake lock would be leaked).
1908        mWakeLock.acquire();
1909        mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
1910    }
1911
1912    private final class ProviderHandler extends Handler {
1913        public ProviderHandler(Looper looper) {
1914            super(looper, null, true /*async*/);
1915        }
1916
1917        @Override
1918        public void handleMessage(Message msg) {
1919            int message = msg.what;
1920            switch (message) {
1921                case ENABLE:
1922                    if (msg.arg1 == 1) {
1923                        handleEnable();
1924                    } else {
1925                        handleDisable();
1926                    }
1927                    break;
1928                case SET_REQUEST:
1929                    GpsRequest gpsRequest = (GpsRequest) msg.obj;
1930                    handleSetRequest(gpsRequest.request, gpsRequest.source);
1931                    break;
1932                case UPDATE_NETWORK_STATE:
1933                    handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
1934                    break;
1935                case INJECT_NTP_TIME:
1936                    handleInjectNtpTime();
1937                    break;
1938                case DOWNLOAD_XTRA_DATA:
1939                    if (mSupportsXtra) {
1940                        handleDownloadXtraData();
1941                    }
1942                    break;
1943                case INJECT_NTP_TIME_FINISHED:
1944                    mInjectNtpTimePending = STATE_IDLE;
1945                    break;
1946                case DOWNLOAD_XTRA_DATA_FINISHED:
1947                    mDownloadXtraDataPending = STATE_IDLE;
1948                    break;
1949                case UPDATE_LOCATION:
1950                    handleUpdateLocation((Location)msg.obj);
1951                    break;
1952            }
1953            if (msg.arg2 == 1) {
1954                // wakelock was taken for this message, release it
1955                mWakeLock.release();
1956            }
1957        }
1958    };
1959
1960    private final class NetworkLocationListener implements LocationListener {
1961        @Override
1962        public void onLocationChanged(Location location) {
1963            // this callback happens on mHandler looper
1964            if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
1965                handleUpdateLocation(location);
1966            }
1967        }
1968        @Override
1969        public void onStatusChanged(String provider, int status, Bundle extras) { }
1970        @Override
1971        public void onProviderEnabled(String provider) { }
1972        @Override
1973        public void onProviderDisabled(String provider) { }
1974    }
1975
1976    private String getSelectedApn() {
1977        Uri uri = Uri.parse("content://telephony/carriers/preferapn");
1978        Cursor cursor = null;
1979        try {
1980            cursor = mContext.getContentResolver().query(
1981                    uri,
1982                    new String[] { "apn" },
1983                    null /* selection */,
1984                    null /* selectionArgs */,
1985                    Carriers.DEFAULT_SORT_ORDER);
1986            if (cursor != null && cursor.moveToFirst()) {
1987                return cursor.getString(0);
1988            } else {
1989                Log.e(TAG, "No APN found to select.");
1990            }
1991        } catch (Exception e) {
1992            Log.e(TAG, "Error encountered on selecting the APN.", e);
1993        } finally {
1994            if (cursor != null) {
1995                cursor.close();
1996            }
1997        }
1998
1999        return null;
2000    }
2001
2002    private int getApnIpType(String apn) {
2003        if (apn == null) {
2004            return APN_INVALID;
2005        }
2006
2007        // look for cached data to use
2008        if (apn.equals(mAGpsApn) && mApnIpType != APN_INVALID) {
2009            return mApnIpType;
2010        }
2011
2012        String selection = String.format("current = 1 and apn = '%s' and carrier_enabled = 1", apn);
2013        Cursor cursor = null;
2014        try {
2015            cursor = mContext.getContentResolver().query(
2016                    Carriers.CONTENT_URI,
2017                    new String[] { Carriers.PROTOCOL },
2018                    selection,
2019                    null,
2020                    Carriers.DEFAULT_SORT_ORDER);
2021
2022            if (null != cursor && cursor.moveToFirst()) {
2023                return translateToApnIpType(cursor.getString(0), apn);
2024            } else {
2025                Log.e(TAG, "No entry found in query for APN: " + apn);
2026            }
2027        } catch (Exception e) {
2028            Log.e(TAG, "Error encountered on APN query for: " + apn, e);
2029        } finally {
2030            if (cursor != null) {
2031                cursor.close();
2032            }
2033        }
2034
2035        return APN_INVALID;
2036    }
2037
2038    private int translateToApnIpType(String ipProtocol, String apn) {
2039        if ("IP".equals(ipProtocol)) {
2040            return APN_IPV4;
2041        }
2042        if ("IPV6".equals(ipProtocol)) {
2043            return APN_IPV6;
2044        }
2045        if ("IPV4V6".equals(ipProtocol)) {
2046            return APN_IPV4V6;
2047        }
2048
2049        // we hit the default case so the ipProtocol is not recognized
2050        String message = String.format("Unknown IP Protocol: %s, for APN: %s", ipProtocol, apn);
2051        Log.e(TAG, message);
2052        return APN_INVALID;
2053    }
2054
2055    private void setRouting() {
2056        if (mAGpsDataConnectionIpAddr == null) {
2057            return;
2058        }
2059
2060        boolean result = mConnMgr.requestRouteToHostAddress(
2061                ConnectivityManager.TYPE_MOBILE_SUPL,
2062                mAGpsDataConnectionIpAddr);
2063
2064        if (!result) {
2065            Log.e(TAG, "Error requesting route to host: " + mAGpsDataConnectionIpAddr);
2066        } else if (DEBUG) {
2067            Log.d(TAG, "Successfully requested route to host: " + mAGpsDataConnectionIpAddr);
2068        }
2069    }
2070
2071    @Override
2072    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2073        StringBuilder s = new StringBuilder();
2074        s.append("  mFixInterval=").append(mFixInterval).append("\n");
2075        s.append("  mDisableGps (battery saver mode)=").append(mDisableGps).append("\n");
2076        s.append("  mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" (");
2077        if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED ");
2078        if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB ");
2079        if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA ");
2080        if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT ");
2081        if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME ");
2082        s.append(")\n");
2083
2084        s.append(native_get_internal_state());
2085        pw.append(s);
2086    }
2087
2088    // for GPS SV statistics
2089    private static final int MAX_SVS = 32;
2090    private static final int EPHEMERIS_MASK = 0;
2091    private static final int ALMANAC_MASK = 1;
2092    private static final int USED_FOR_FIX_MASK = 2;
2093
2094    // preallocated arrays, to avoid memory allocation in reportStatus()
2095    private int mSvs[] = new int[MAX_SVS];
2096    private float mSnrs[] = new float[MAX_SVS];
2097    private float mSvElevations[] = new float[MAX_SVS];
2098    private float mSvAzimuths[] = new float[MAX_SVS];
2099    private int mSvMasks[] = new int[3];
2100    private int mSvCount;
2101    // preallocated to avoid memory allocation in reportNmea()
2102    private byte[] mNmeaBuffer = new byte[120];
2103
2104    static { class_init_native(); }
2105    private static native void class_init_native();
2106    private static native boolean native_is_supported();
2107
2108    private native boolean native_init();
2109    private native void native_cleanup();
2110    private native boolean native_set_position_mode(int mode, int recurrence, int min_interval,
2111            int preferred_accuracy, int preferred_time);
2112    private native boolean native_start();
2113    private native boolean native_stop();
2114    private native void native_delete_aiding_data(int flags);
2115    // returns number of SVs
2116    // mask[0] is ephemeris mask and mask[1] is almanac mask
2117    private native int native_read_sv_status(int[] svs, float[] snrs,
2118            float[] elevations, float[] azimuths, int[] masks);
2119    private native int native_read_nmea(byte[] buffer, int bufferSize);
2120    private native void native_inject_location(double latitude, double longitude, float accuracy);
2121
2122    // XTRA Support
2123    private native void native_inject_time(long time, long timeReference, int uncertainty);
2124    private native boolean native_supports_xtra();
2125    private native void native_inject_xtra_data(byte[] data, int length);
2126
2127    // DEBUG Support
2128    private native String native_get_internal_state();
2129
2130    // AGPS Support
2131    private native void native_agps_data_conn_open(String apn, int apnIpType);
2132    private native void native_agps_data_conn_closed();
2133    private native void native_agps_data_conn_failed();
2134    private native void native_agps_ni_message(byte [] msg, int length);
2135    private native void native_set_agps_server(int type, String hostname, int port);
2136
2137    // Network-initiated (NI) Support
2138    private native void native_send_ni_response(int notificationId, int userResponse);
2139
2140    // AGPS ril suport
2141    private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
2142            int lac, int cid);
2143    private native void native_agps_set_id(int type, String setid);
2144
2145    private native void native_update_network_state(boolean connected, int type,
2146            boolean roaming, boolean available, String extraInfo, String defaultAPN);
2147
2148    // Hardware Geofence support.
2149    private static native boolean native_is_geofence_supported();
2150    private static native boolean native_add_geofence(int geofenceId, double latitude,
2151            double longitude, double radius, int lastTransition,int monitorTransitions,
2152            int notificationResponsivenes, int unknownTimer);
2153    private static native boolean native_remove_geofence(int geofenceId);
2154    private static native boolean native_resume_geofence(int geofenceId, int transitions);
2155    private static native boolean native_pause_geofence(int geofenceId);
2156
2157    // Gps Hal measurements support.
2158    private static native boolean native_is_measurement_supported();
2159    private native boolean native_start_measurement_collection();
2160    private native boolean native_stop_measurement_collection();
2161
2162    // Gps Navigation message support.
2163    private static native boolean native_is_navigation_message_supported();
2164    private native boolean native_start_navigation_message_collection();
2165    private native boolean native_stop_navigation_message_collection();
2166
2167    // GNSS Configuration
2168    private static native void native_configuration_update(String configData);
2169}
2170
2171