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