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