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