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