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