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