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