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