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