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