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