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