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