GpsLocationProvider.java revision 931a37f992c738ebc5d3c82e424d94591798a4d7
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                Log.v(TAG, "Received SUPL IP addr[]: " + ipaddr);
1376                // Set mAGpsDataConnectionState before calling startUsingNetworkFeature
1377                //  to avoid a race condition with handleUpdateNetworkState()
1378                mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
1379                int result = mConnMgr.startUsingNetworkFeature(
1380                        ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
1381                if (ipaddr != null) {
1382                    try {
1383                        mAGpsDataConnectionIpAddr = InetAddress.getByAddress(ipaddr);
1384                        Log.v(TAG, "IP address converted to: " + mAGpsDataConnectionIpAddr);
1385                    } catch (UnknownHostException e) {
1386                        Log.e(TAG, "Bad IP Address: " + ipaddr, e);
1387                        mAGpsDataConnectionIpAddr = null;
1388                    }
1389                }
1390
1391                if (result == PhoneConstants.APN_ALREADY_ACTIVE) {
1392                    if (DEBUG) Log.d(TAG, "PhoneConstants.APN_ALREADY_ACTIVE");
1393                    if (mAGpsApn != null) {
1394                        setRouting();
1395                        native_agps_data_conn_open(mAGpsApn, mApnIpType);
1396                        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
1397                    } else {
1398                        Log.e(TAG, "mAGpsApn not set when receiving PhoneConstants.APN_ALREADY_ACTIVE");
1399                        mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
1400                        native_agps_data_conn_failed();
1401                    }
1402                } else if (result == PhoneConstants.APN_REQUEST_STARTED) {
1403                    if (DEBUG) Log.d(TAG, "PhoneConstants.APN_REQUEST_STARTED");
1404                    // Nothing to do here
1405                } else {
1406                    if (DEBUG) Log.d(TAG, "startUsingNetworkFeature failed, value is " +
1407                                     result);
1408                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
1409                    native_agps_data_conn_failed();
1410                }
1411                break;
1412            case GPS_RELEASE_AGPS_DATA_CONN:
1413                if (DEBUG) Log.d(TAG, "GPS_RELEASE_AGPS_DATA_CONN");
1414                if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
1415                    mConnMgr.stopUsingNetworkFeature(
1416                            ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
1417                    native_agps_data_conn_closed();
1418                    mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
1419                    mAGpsDataConnectionIpAddr = null;
1420                }
1421                break;
1422            case GPS_AGPS_DATA_CONNECTED:
1423                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
1424                break;
1425            case GPS_AGPS_DATA_CONN_DONE:
1426                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_DONE");
1427                break;
1428            case GPS_AGPS_DATA_CONN_FAILED:
1429                if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
1430                break;
1431            default:
1432                Log.d(TAG, "Received Unknown AGPS status: " + status);
1433        }
1434    }
1435
1436    /**
1437     * called from native code to report NMEA data received
1438     */
1439    private void reportNmea(long timestamp) {
1440        int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
1441        String nmea = new String(mNmeaBuffer, 0 /* offset */, length);
1442        mListenerHelper.onNmeaReceived(timestamp, nmea);
1443    }
1444
1445    /**
1446     * called from native code - Gps measurements callback
1447     */
1448    private void reportMeasurementData(GpsMeasurementsEvent event) {
1449        mGpsMeasurementsProvider.onMeasurementsAvailable(event);
1450    }
1451
1452    /**
1453     * called from native code - GPS navigation message callback
1454     */
1455    private void reportNavigationMessage(GpsNavigationMessageEvent event) {
1456        mGpsNavigationMessageProvider.onNavigationMessageAvailable(event);
1457    }
1458
1459    /**
1460     * called from native code to inform us what the GPS engine capabilities are
1461     */
1462    private void setEngineCapabilities(int capabilities) {
1463        mEngineCapabilities = capabilities;
1464
1465        if (!hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME) && !mPeriodicTimeInjection) {
1466            mPeriodicTimeInjection = true;
1467            requestUtcTime();
1468        }
1469    }
1470
1471    /**
1472     * called from native code to request XTRA data
1473     */
1474    private void xtraDownloadRequest() {
1475        if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
1476        sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
1477    }
1478
1479    /**
1480     * Helper method to construct a location object.
1481     */
1482    private Location buildLocation(
1483            int flags,
1484            double latitude,
1485            double longitude,
1486            double altitude,
1487            float speed,
1488            float bearing,
1489            float accuracy,
1490            long timestamp) {
1491        Location location = new Location(LocationManager.GPS_PROVIDER);
1492        if((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
1493            location.setLatitude(latitude);
1494            location.setLongitude(longitude);
1495            location.setTime(timestamp);
1496            location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
1497        }
1498        if((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
1499            location.setAltitude(altitude);
1500        }
1501        if((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
1502            location.setSpeed(speed);
1503        }
1504        if((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
1505            location.setBearing(bearing);
1506        }
1507        if((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
1508            location.setAccuracy(accuracy);
1509        }
1510        return location;
1511    }
1512
1513    /**
1514     * Converts the GPS HAL status to the internal Geofence Hardware status.
1515     */
1516    private int getGeofenceStatus(int status) {
1517        switch(status) {
1518            case GPS_GEOFENCE_OPERATION_SUCCESS:
1519                return GeofenceHardware.GEOFENCE_SUCCESS;
1520            case GPS_GEOFENCE_ERROR_GENERIC:
1521                return GeofenceHardware.GEOFENCE_FAILURE;
1522            case GPS_GEOFENCE_ERROR_ID_EXISTS:
1523                return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
1524            case GPS_GEOFENCE_ERROR_INVALID_TRANSITION:
1525                return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
1526            case GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES:
1527                return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
1528            case GPS_GEOFENCE_ERROR_ID_UNKNOWN:
1529                return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
1530            default:
1531                return -1;
1532        }
1533    }
1534
1535    /**
1536     * Called from native to report GPS Geofence transition
1537     * All geofence callbacks are called on the same thread
1538     */
1539    private void reportGeofenceTransition(int geofenceId, int flags, double latitude,
1540            double longitude, double altitude, float speed, float bearing, float accuracy,
1541            long timestamp, int transition, long transitionTimestamp) {
1542        if (mGeofenceHardwareImpl == null) {
1543            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1544        }
1545        Location location = buildLocation(
1546                flags,
1547                latitude,
1548                longitude,
1549                altitude,
1550                speed,
1551                bearing,
1552                accuracy,
1553                timestamp);
1554        mGeofenceHardwareImpl.reportGeofenceTransition(
1555                geofenceId,
1556                location,
1557                transition,
1558                transitionTimestamp,
1559                GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
1560                FusedBatchOptions.SourceTechnologies.GNSS);
1561    }
1562
1563    /**
1564     * called from native code to report GPS status change.
1565     */
1566    private void reportGeofenceStatus(int status, int flags, double latitude,
1567            double longitude, double altitude, float speed, float bearing, float accuracy,
1568            long timestamp) {
1569        if (mGeofenceHardwareImpl == null) {
1570            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1571        }
1572        Location location = buildLocation(
1573                flags,
1574                latitude,
1575                longitude,
1576                altitude,
1577                speed,
1578                bearing,
1579                accuracy,
1580                timestamp);
1581        int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
1582        if(status == GPS_GEOFENCE_AVAILABLE) {
1583            monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
1584        }
1585        mGeofenceHardwareImpl.reportGeofenceMonitorStatus(
1586                GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
1587                monitorStatus,
1588                location,
1589                FusedBatchOptions.SourceTechnologies.GNSS);
1590    }
1591
1592    /**
1593     * called from native code - Geofence Add callback
1594     */
1595    private void reportGeofenceAddStatus(int geofenceId, int status) {
1596        if (mGeofenceHardwareImpl == null) {
1597            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1598        }
1599        mGeofenceHardwareImpl.reportGeofenceAddStatus(geofenceId, getGeofenceStatus(status));
1600    }
1601
1602    /**
1603     * called from native code - Geofence Remove callback
1604     */
1605    private void reportGeofenceRemoveStatus(int geofenceId, int status) {
1606        if (mGeofenceHardwareImpl == null) {
1607            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1608        }
1609        mGeofenceHardwareImpl.reportGeofenceRemoveStatus(geofenceId, getGeofenceStatus(status));
1610    }
1611
1612    /**
1613     * called from native code - Geofence Pause callback
1614     */
1615    private void reportGeofencePauseStatus(int geofenceId, int status) {
1616        if (mGeofenceHardwareImpl == null) {
1617            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1618        }
1619        mGeofenceHardwareImpl.reportGeofencePauseStatus(geofenceId, getGeofenceStatus(status));
1620    }
1621
1622    /**
1623     * called from native code - Geofence Resume callback
1624     */
1625    private void reportGeofenceResumeStatus(int geofenceId, int status) {
1626        if (mGeofenceHardwareImpl == null) {
1627            mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
1628        }
1629        mGeofenceHardwareImpl.reportGeofenceResumeStatus(geofenceId, getGeofenceStatus(status));
1630    }
1631
1632    //=============================================================
1633    // NI Client support
1634    //=============================================================
1635    private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
1636        // Sends a response for an NI reqeust to HAL.
1637        @Override
1638        public boolean sendNiResponse(int notificationId, int userResponse)
1639        {
1640            // TODO Add Permission check
1641
1642            if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
1643                    ", response: " + userResponse);
1644            native_send_ni_response(notificationId, userResponse);
1645            return true;
1646        }
1647    };
1648
1649    public INetInitiatedListener getNetInitiatedListener() {
1650        return mNetInitiatedListener;
1651    }
1652
1653    // Called by JNI function to report an NI request.
1654    public void reportNiNotification(
1655            int notificationId,
1656            int niType,
1657            int notifyFlags,
1658            int timeout,
1659            int defaultResponse,
1660            String requestorId,
1661            String text,
1662            int requestorIdEncoding,
1663            int textEncoding,
1664            String extras  // Encoded extra data
1665        )
1666    {
1667        Log.i(TAG, "reportNiNotification: entered");
1668        Log.i(TAG, "notificationId: " + notificationId +
1669                ", niType: " + niType +
1670                ", notifyFlags: " + notifyFlags +
1671                ", timeout: " + timeout +
1672                ", defaultResponse: " + defaultResponse);
1673
1674        Log.i(TAG, "requestorId: " + requestorId +
1675                ", text: " + text +
1676                ", requestorIdEncoding: " + requestorIdEncoding +
1677                ", textEncoding: " + textEncoding);
1678
1679        GpsNiNotification notification = new GpsNiNotification();
1680
1681        notification.notificationId = notificationId;
1682        notification.niType = niType;
1683        notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
1684        notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
1685        notification.privacyOverride = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
1686        notification.timeout = timeout;
1687        notification.defaultResponse = defaultResponse;
1688        notification.requestorId = requestorId;
1689        notification.text = text;
1690        notification.requestorIdEncoding = requestorIdEncoding;
1691        notification.textEncoding = textEncoding;
1692
1693        // Process extras, assuming the format is
1694        // one of more lines of "key = value"
1695        Bundle bundle = new Bundle();
1696
1697        if (extras == null) extras = "";
1698        Properties extraProp = new Properties();
1699
1700        try {
1701            extraProp.load(new StringReader(extras));
1702        }
1703        catch (IOException e)
1704        {
1705            Log.e(TAG, "reportNiNotification cannot parse extras data: " + extras);
1706        }
1707
1708        for (Entry<Object, Object> ent : extraProp.entrySet())
1709        {
1710            bundle.putString((String) ent.getKey(), (String) ent.getValue());
1711        }
1712
1713        notification.extras = bundle;
1714
1715        mNIHandler.handleNiNotification(notification);
1716    }
1717
1718    /**
1719     * Called from native code to request set id info.
1720     * We should be careful about receiving null string from the TelephonyManager,
1721     * because sending null String to JNI function would cause a crash.
1722     */
1723
1724    private void requestSetID(int flags) {
1725        TelephonyManager phone = (TelephonyManager)
1726                mContext.getSystemService(Context.TELEPHONY_SERVICE);
1727        int    type = AGPS_SETID_TYPE_NONE;
1728        String data = "";
1729
1730        if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
1731            String data_temp = phone.getSubscriberId();
1732            if (data_temp == null) {
1733                // This means the framework does not have the SIM card ready.
1734            } else {
1735                // This means the framework has the SIM card.
1736                data = data_temp;
1737                type = AGPS_SETID_TYPE_IMSI;
1738            }
1739        }
1740        else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
1741            String data_temp = phone.getLine1Number();
1742            if (data_temp == null) {
1743                // This means the framework does not have the SIM card ready.
1744            } else {
1745                // This means the framework has the SIM card.
1746                data = data_temp;
1747                type = AGPS_SETID_TYPE_MSISDN;
1748            }
1749        }
1750        native_agps_set_id(type, data);
1751    }
1752
1753    /**
1754     * Called from native code to request utc time info
1755     */
1756
1757    private void requestUtcTime() {
1758        sendMessage(INJECT_NTP_TIME, 0, null);
1759    }
1760
1761    /**
1762     * Called from native code to request reference location info
1763     */
1764
1765    private void requestRefLocation(int flags) {
1766        TelephonyManager phone = (TelephonyManager)
1767                mContext.getSystemService(Context.TELEPHONY_SERVICE);
1768        final int phoneType = phone.getPhoneType();
1769        if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
1770            GsmCellLocation gsm_cell = (GsmCellLocation) phone.getCellLocation();
1771            if ((gsm_cell != null) && (phone.getNetworkOperator() != null)
1772                    && (phone.getNetworkOperator().length() > 3)) {
1773                int type;
1774                int mcc = Integer.parseInt(phone.getNetworkOperator().substring(0,3));
1775                int mnc = Integer.parseInt(phone.getNetworkOperator().substring(3));
1776                int networkType = phone.getNetworkType();
1777                if (networkType == TelephonyManager.NETWORK_TYPE_UMTS
1778                    || networkType == TelephonyManager.NETWORK_TYPE_HSDPA
1779                    || networkType == TelephonyManager.NETWORK_TYPE_HSUPA
1780                    || networkType == TelephonyManager.NETWORK_TYPE_HSPA
1781                    || networkType == TelephonyManager.NETWORK_TYPE_HSPAP) {
1782                    type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
1783                } else {
1784                    type = AGPS_REF_LOCATION_TYPE_GSM_CELLID;
1785                }
1786                native_agps_set_ref_location_cellid(type, mcc, mnc,
1787                        gsm_cell.getLac(), gsm_cell.getCid());
1788            } else {
1789                Log.e(TAG,"Error getting cell location info.");
1790            }
1791        } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
1792            Log.e(TAG, "CDMA not supported.");
1793        }
1794    }
1795
1796    private void sendMessage(int message, int arg, Object obj) {
1797        // hold a wake lock until this message is delivered
1798        // note that this assumes the message will not be removed from the queue before
1799        // it is handled (otherwise the wake lock would be leaked).
1800        mWakeLock.acquire();
1801        mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
1802    }
1803
1804    private final class ProviderHandler extends Handler {
1805        public ProviderHandler(Looper looper) {
1806            super(looper, null, true /*async*/);
1807        }
1808
1809        @Override
1810        public void handleMessage(Message msg) {
1811            int message = msg.what;
1812            switch (message) {
1813                case ENABLE:
1814                    if (msg.arg1 == 1) {
1815                        handleEnable();
1816                    } else {
1817                        handleDisable();
1818                    }
1819                    break;
1820                case SET_REQUEST:
1821                    GpsRequest gpsRequest = (GpsRequest) msg.obj;
1822                    handleSetRequest(gpsRequest.request, gpsRequest.source);
1823                    break;
1824                case UPDATE_NETWORK_STATE:
1825                    handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
1826                    break;
1827                case INJECT_NTP_TIME:
1828                    handleInjectNtpTime();
1829                    break;
1830                case DOWNLOAD_XTRA_DATA:
1831                    if (mSupportsXtra) {
1832                        handleDownloadXtraData();
1833                    }
1834                    break;
1835                case INJECT_NTP_TIME_FINISHED:
1836                    mInjectNtpTimePending = STATE_IDLE;
1837                    break;
1838                case DOWNLOAD_XTRA_DATA_FINISHED:
1839                    mDownloadXtraDataPending = STATE_IDLE;
1840                    break;
1841                case UPDATE_LOCATION:
1842                    handleUpdateLocation((Location)msg.obj);
1843                    break;
1844            }
1845            if (msg.arg2 == 1) {
1846                // wakelock was taken for this message, release it
1847                mWakeLock.release();
1848            }
1849        }
1850    };
1851
1852    private final class NetworkLocationListener implements LocationListener {
1853        @Override
1854        public void onLocationChanged(Location location) {
1855            // this callback happens on mHandler looper
1856            if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
1857                handleUpdateLocation(location);
1858            }
1859        }
1860        @Override
1861        public void onStatusChanged(String provider, int status, Bundle extras) { }
1862        @Override
1863        public void onProviderEnabled(String provider) { }
1864        @Override
1865        public void onProviderDisabled(String provider) { }
1866    }
1867
1868    private String getSelectedApn() {
1869        Uri uri = Uri.parse("content://telephony/carriers/preferapn");
1870        Cursor cursor = null;
1871        try {
1872            cursor = mContext.getContentResolver().query(
1873                    uri,
1874                    new String[] { "apn" },
1875                    null /* selection */,
1876                    null /* selectionArgs */,
1877                    Carriers.DEFAULT_SORT_ORDER);
1878            if (cursor != null && cursor.moveToFirst()) {
1879                return cursor.getString(0);
1880            } else {
1881                Log.e(TAG, "No APN found to select.");
1882            }
1883        } catch (Exception e) {
1884            Log.e(TAG, "Error encountered on selecting the APN.", e);
1885        } finally {
1886            if (cursor != null) {
1887                cursor.close();
1888            }
1889        }
1890
1891        return null;
1892    }
1893
1894    private int getApnIpType(String apn) {
1895        if (apn == null) {
1896            return APN_INVALID;
1897        }
1898
1899        // look for cached data to use
1900        if (apn.equals(mAGpsApn) && mApnIpType != APN_INVALID) {
1901            return mApnIpType;
1902        }
1903
1904        String selection = String.format("current = 1 and apn = '%s' and carrier_enabled = 1", apn);
1905        Cursor cursor = null;
1906        try {
1907            cursor = mContext.getContentResolver().query(
1908                    Carriers.CONTENT_URI,
1909                    new String[] { Carriers.PROTOCOL },
1910                    selection,
1911                    null,
1912                    Carriers.DEFAULT_SORT_ORDER);
1913
1914            if (null != cursor && cursor.moveToFirst()) {
1915                return translateToApnIpType(cursor.getString(0), apn);
1916            } else {
1917                Log.e(TAG, "No entry found in query for APN: " + apn);
1918            }
1919        } catch (Exception e) {
1920            Log.e(TAG, "Error encountered on APN query for: " + apn, e);
1921        } finally {
1922            if (cursor != null) {
1923                cursor.close();
1924            }
1925        }
1926
1927        return APN_INVALID;
1928    }
1929
1930    private int translateToApnIpType(String ipProtocol, String apn) {
1931        if ("IP".equals(ipProtocol)) {
1932            return APN_IPV4;
1933        }
1934        if ("IPV6".equals(ipProtocol)) {
1935            return APN_IPV6;
1936        }
1937        if ("IPV4V6".equals(ipProtocol)) {
1938            return APN_IPV4V6;
1939        }
1940
1941        // we hit the default case so the ipProtocol is not recognized
1942        String message = String.format("Unknown IP Protocol: %s, for APN: %s", ipProtocol, apn);
1943        Log.e(TAG, message);
1944        return APN_INVALID;
1945    }
1946
1947    private void setRouting() {
1948        if (mAGpsDataConnectionIpAddr == null) {
1949            return;
1950        }
1951
1952        boolean result = mConnMgr.requestRouteToHostAddress(
1953                ConnectivityManager.TYPE_MOBILE_SUPL,
1954                mAGpsDataConnectionIpAddr);
1955
1956        if (!result) {
1957            Log.e(TAG, "Error requesting route to host: " + mAGpsDataConnectionIpAddr);
1958        } else if (DEBUG) {
1959            Log.d(TAG, "Successfully requested route to host: " + mAGpsDataConnectionIpAddr);
1960        }
1961    }
1962
1963    @Override
1964    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1965        StringBuilder s = new StringBuilder();
1966        s.append("  mFixInterval=").append(mFixInterval).append("\n");
1967        s.append("  mDisableGps (battery saver mode)=").append(mDisableGps).append("\n");
1968        s.append("  mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" (");
1969        if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED ");
1970        if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB ");
1971        if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA ");
1972        if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT ");
1973        if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME ");
1974        s.append(")\n");
1975
1976        s.append(native_get_internal_state());
1977        pw.append(s);
1978    }
1979
1980    // for GPS SV statistics
1981    private static final int MAX_SVS = 32;
1982    private static final int EPHEMERIS_MASK = 0;
1983    private static final int ALMANAC_MASK = 1;
1984    private static final int USED_FOR_FIX_MASK = 2;
1985
1986    // preallocated arrays, to avoid memory allocation in reportStatus()
1987    private int mSvs[] = new int[MAX_SVS];
1988    private float mSnrs[] = new float[MAX_SVS];
1989    private float mSvElevations[] = new float[MAX_SVS];
1990    private float mSvAzimuths[] = new float[MAX_SVS];
1991    private int mSvMasks[] = new int[3];
1992    private int mSvCount;
1993    // preallocated to avoid memory allocation in reportNmea()
1994    private byte[] mNmeaBuffer = new byte[120];
1995
1996    static { class_init_native(); }
1997    private static native void class_init_native();
1998    private static native boolean native_is_supported();
1999
2000    private native boolean native_init();
2001    private native void native_cleanup();
2002    private native boolean native_set_position_mode(int mode, int recurrence, int min_interval,
2003            int preferred_accuracy, int preferred_time);
2004    private native boolean native_start();
2005    private native boolean native_stop();
2006    private native void native_delete_aiding_data(int flags);
2007    // returns number of SVs
2008    // mask[0] is ephemeris mask and mask[1] is almanac mask
2009    private native int native_read_sv_status(int[] svs, float[] snrs,
2010            float[] elevations, float[] azimuths, int[] masks);
2011    private native int native_read_nmea(byte[] buffer, int bufferSize);
2012    private native void native_inject_location(double latitude, double longitude, float accuracy);
2013
2014    // XTRA Support
2015    private native void native_inject_time(long time, long timeReference, int uncertainty);
2016    private native boolean native_supports_xtra();
2017    private native void native_inject_xtra_data(byte[] data, int length);
2018
2019    // DEBUG Support
2020    private native String native_get_internal_state();
2021
2022    // AGPS Support
2023    private native void native_agps_data_conn_open(String apn, int apnIpType);
2024    private native void native_agps_data_conn_closed();
2025    private native void native_agps_data_conn_failed();
2026    private native void native_agps_ni_message(byte [] msg, int length);
2027    private native void native_set_agps_server(int type, String hostname, int port);
2028
2029    // Network-initiated (NI) Support
2030    private native void native_send_ni_response(int notificationId, int userResponse);
2031
2032    // AGPS ril suport
2033    private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
2034            int lac, int cid);
2035    private native void native_agps_set_id(int type, String setid);
2036
2037    private native void native_update_network_state(boolean connected, int type,
2038            boolean roaming, boolean available, String extraInfo, String defaultAPN);
2039
2040    // Hardware Geofence support.
2041    private static native boolean native_is_geofence_supported();
2042    private static native boolean native_add_geofence(int geofenceId, double latitude,
2043            double longitude, double radius, int lastTransition,int monitorTransitions,
2044            int notificationResponsivenes, int unknownTimer);
2045    private static native boolean native_remove_geofence(int geofenceId);
2046    private static native boolean native_resume_geofence(int geofenceId, int transitions);
2047    private static native boolean native_pause_geofence(int geofenceId);
2048
2049    // Gps Hal measurements support.
2050    private static native boolean native_is_measurement_supported();
2051    private native boolean native_start_measurement_collection();
2052    private native boolean native_stop_measurement_collection();
2053
2054    // Gps Navigation message support.
2055    private static native boolean native_is_navigation_message_supported();
2056    private native boolean native_start_navigation_message_collection();
2057    private native boolean native_stop_navigation_message_collection();
2058}
2059
2060