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