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