Home | History | Annotate | Download | only in location
      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 
     17 package com.android.server.location;
     18 
     19 import android.app.AlarmManager;
     20 import android.app.PendingIntent;
     21 import android.content.BroadcastReceiver;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.IntentFilter;
     25 import android.database.Cursor;
     26 import android.location.Criteria;
     27 import android.location.IGpsStatusListener;
     28 import android.location.IGpsStatusProvider;
     29 import android.location.ILocationManager;
     30 import android.location.INetInitiatedListener;
     31 import android.location.Location;
     32 import android.location.LocationManager;
     33 import android.location.LocationProvider;
     34 import android.net.ConnectivityManager;
     35 import android.net.NetworkInfo;
     36 import android.net.Uri;
     37 import android.os.Binder;
     38 import android.os.Bundle;
     39 import android.os.Handler;
     40 import android.os.IBinder;
     41 import android.os.Looper;
     42 import android.os.Message;
     43 import android.os.PowerManager;
     44 import android.os.Process;
     45 import android.os.RemoteException;
     46 import android.os.ServiceManager;
     47 import android.os.SystemClock;
     48 import android.os.WorkSource;
     49 import android.provider.Settings;
     50 import android.provider.Telephony.Carriers;
     51 import android.provider.Telephony.Sms.Intents;
     52 import android.telephony.SmsMessage;
     53 import android.telephony.TelephonyManager;
     54 import android.telephony.gsm.GsmCellLocation;
     55 import android.util.Log;
     56 import android.util.NtpTrustedTime;
     57 import android.util.SparseIntArray;
     58 
     59 import com.android.internal.app.IBatteryStats;
     60 import com.android.internal.location.GpsNetInitiatedHandler;
     61 import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
     62 import com.android.internal.telephony.Phone;
     63 
     64 import java.io.File;
     65 import java.io.FileInputStream;
     66 import java.io.IOException;
     67 import java.io.StringReader;
     68 import java.util.ArrayList;
     69 import java.util.Date;
     70 import java.util.Map.Entry;
     71 import java.util.Properties;
     72 import java.util.concurrent.CountDownLatch;
     73 
     74 /**
     75  * A GPS implementation of LocationProvider used by LocationManager.
     76  *
     77  * {@hide}
     78  */
     79 public class GpsLocationProvider implements LocationProviderInterface {
     80 
     81     private static final String TAG = "GpsLocationProvider";
     82 
     83     private static final boolean DEBUG = false;
     84     private static final boolean VERBOSE = false;
     85 
     86     // these need to match GpsPositionMode enum in gps.h
     87     private static final int GPS_POSITION_MODE_STANDALONE = 0;
     88     private static final int GPS_POSITION_MODE_MS_BASED = 1;
     89     private static final int GPS_POSITION_MODE_MS_ASSISTED = 2;
     90 
     91     // these need to match GpsPositionRecurrence enum in gps.h
     92     private static final int GPS_POSITION_RECURRENCE_PERIODIC = 0;
     93     private static final int GPS_POSITION_RECURRENCE_SINGLE = 1;
     94 
     95     // these need to match GpsStatusValue defines in gps.h
     96     private static final int GPS_STATUS_NONE = 0;
     97     private static final int GPS_STATUS_SESSION_BEGIN = 1;
     98     private static final int GPS_STATUS_SESSION_END = 2;
     99     private static final int GPS_STATUS_ENGINE_ON = 3;
    100     private static final int GPS_STATUS_ENGINE_OFF = 4;
    101 
    102     // these need to match GpsApgsStatusValue defines in gps.h
    103     /** AGPS status event values. */
    104     private static final int GPS_REQUEST_AGPS_DATA_CONN = 1;
    105     private static final int GPS_RELEASE_AGPS_DATA_CONN = 2;
    106     private static final int GPS_AGPS_DATA_CONNECTED = 3;
    107     private static final int GPS_AGPS_DATA_CONN_DONE = 4;
    108     private static final int GPS_AGPS_DATA_CONN_FAILED = 5;
    109 
    110     // these need to match GpsLocationFlags enum in gps.h
    111     private static final int LOCATION_INVALID = 0;
    112     private static final int LOCATION_HAS_LAT_LONG = 1;
    113     private static final int LOCATION_HAS_ALTITUDE = 2;
    114     private static final int LOCATION_HAS_SPEED = 4;
    115     private static final int LOCATION_HAS_BEARING = 8;
    116     private static final int LOCATION_HAS_ACCURACY = 16;
    117 
    118 // IMPORTANT - the GPS_DELETE_* symbols here must match constants in gps.h
    119     private static final int GPS_DELETE_EPHEMERIS = 0x0001;
    120     private static final int GPS_DELETE_ALMANAC = 0x0002;
    121     private static final int GPS_DELETE_POSITION = 0x0004;
    122     private static final int GPS_DELETE_TIME = 0x0008;
    123     private static final int GPS_DELETE_IONO = 0x0010;
    124     private static final int GPS_DELETE_UTC = 0x0020;
    125     private static final int GPS_DELETE_HEALTH = 0x0040;
    126     private static final int GPS_DELETE_SVDIR = 0x0080;
    127     private static final int GPS_DELETE_SVSTEER = 0x0100;
    128     private static final int GPS_DELETE_SADATA = 0x0200;
    129     private static final int GPS_DELETE_RTI = 0x0400;
    130     private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
    131     private static final int GPS_DELETE_ALL = 0xFFFF;
    132 
    133     // The GPS_CAPABILITY_* flags must match the values in gps.h
    134     private static final int GPS_CAPABILITY_SCHEDULING = 0x0000001;
    135     private static final int GPS_CAPABILITY_MSB = 0x0000002;
    136     private static final int GPS_CAPABILITY_MSA = 0x0000004;
    137     private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
    138     private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010;
    139 
    140     // these need to match AGpsType enum in gps.h
    141     private static final int AGPS_TYPE_SUPL = 1;
    142     private static final int AGPS_TYPE_C2K = 2;
    143 
    144     // for mAGpsDataConnectionState
    145     private static final int AGPS_DATA_CONNECTION_CLOSED = 0;
    146     private static final int AGPS_DATA_CONNECTION_OPENING = 1;
    147     private static final int AGPS_DATA_CONNECTION_OPEN = 2;
    148 
    149     // Handler messages
    150     private static final int CHECK_LOCATION = 1;
    151     private static final int ENABLE = 2;
    152     private static final int ENABLE_TRACKING = 3;
    153     private static final int UPDATE_NETWORK_STATE = 4;
    154     private static final int INJECT_NTP_TIME = 5;
    155     private static final int DOWNLOAD_XTRA_DATA = 6;
    156     private static final int UPDATE_LOCATION = 7;
    157     private static final int ADD_LISTENER = 8;
    158     private static final int REMOVE_LISTENER = 9;
    159     private static final int REQUEST_SINGLE_SHOT = 10;
    160 
    161     // Request setid
    162     private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
    163     private static final int AGPS_RIL_REQUEST_SETID_MSISDN = 2;
    164 
    165     // Request ref location
    166     private static final int AGPS_RIL_REQUEST_REFLOC_CELLID = 1;
    167     private static final int AGPS_RIL_REQUEST_REFLOC_MAC = 2;
    168 
    169     // ref. location info
    170     private static final int AGPS_REF_LOCATION_TYPE_GSM_CELLID = 1;
    171     private static final int AGPS_REF_LOCATION_TYPE_UMTS_CELLID = 2;
    172     private static final int AGPS_REG_LOCATION_TYPE_MAC        = 3;
    173 
    174     // set id info
    175     private static final int AGPS_SETID_TYPE_NONE = 0;
    176     private static final int AGPS_SETID_TYPE_IMSI = 1;
    177     private static final int AGPS_SETID_TYPE_MSISDN = 2;
    178 
    179     private static final String PROPERTIES_FILE = "/etc/gps.conf";
    180 
    181     private int mLocationFlags = LOCATION_INVALID;
    182 
    183     // current status
    184     private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE;
    185 
    186     // time for last status update
    187     private long mStatusUpdateTime = SystemClock.elapsedRealtime();
    188 
    189     // turn off GPS fix icon if we haven't received a fix in 10 seconds
    190     private static final long RECENT_FIX_TIMEOUT = 10 * 1000;
    191 
    192     // stop trying if we do not receive a fix within 60 seconds
    193     private static final int NO_FIX_TIMEOUT = 60 * 1000;
    194 
    195     // true if we are enabled
    196     private volatile boolean mEnabled;
    197 
    198     // true if we have network connectivity
    199     private boolean mNetworkAvailable;
    200 
    201     // flags to trigger NTP or XTRA data download when network becomes available
    202     // initialized to true so we do NTP and XTRA when the network comes up after booting
    203     private boolean mInjectNtpTimePending = true;
    204     private boolean mDownloadXtraDataPending = true;
    205 
    206     // set to true if the GPS engine does not do on-demand NTP time requests
    207     private boolean mPeriodicTimeInjection;
    208 
    209     // true if GPS is navigating
    210     private boolean mNavigating;
    211 
    212     // true if GPS engine is on
    213     private boolean mEngineOn;
    214 
    215     // requested frequency of fixes, in milliseconds
    216     private int mFixInterval = 1000;
    217 
    218     // true if we started navigation
    219     private boolean mStarted;
    220 
    221     // true if single shot request is in progress
    222     private boolean mSingleShot;
    223 
    224     // capabilities of the GPS engine
    225     private int mEngineCapabilities;
    226 
    227     // true if XTRA is supported
    228     private boolean mSupportsXtra;
    229 
    230     // for calculating time to first fix
    231     private long mFixRequestTime = 0;
    232     // time to first fix for most recent session
    233     private int mTTFF = 0;
    234     // time we received our last fix
    235     private long mLastFixTime;
    236 
    237     private int mPositionMode;
    238 
    239     // properties loaded from PROPERTIES_FILE
    240     private Properties mProperties;
    241     private String mSuplServerHost;
    242     private int mSuplServerPort;
    243     private String mC2KServerHost;
    244     private int mC2KServerPort;
    245 
    246     private final Context mContext;
    247     private final NtpTrustedTime mNtpTime;
    248     private final ILocationManager mLocationManager;
    249     private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
    250     private Bundle mLocationExtras = new Bundle();
    251     private ArrayList<Listener> mListeners = new ArrayList<Listener>();
    252 
    253     // GpsLocationProvider's handler thread
    254     private final Thread mThread;
    255     // Handler for processing events in mThread.
    256     private Handler mHandler;
    257     // Used to signal when our main thread has initialized everything
    258     private final CountDownLatch mInitializedLatch = new CountDownLatch(1);
    259 
    260     private String mAGpsApn;
    261     private int mAGpsDataConnectionState;
    262     private int mAGpsDataConnectionIpAddr;
    263     private final ConnectivityManager mConnMgr;
    264     private final GpsNetInitiatedHandler mNIHandler;
    265 
    266     // Wakelocks
    267     private final static String WAKELOCK_KEY = "GpsLocationProvider";
    268     private final PowerManager.WakeLock mWakeLock;
    269     // bitfield of pending messages to our Handler
    270     // used only for messages that cannot have multiple instances queued
    271     private int mPendingMessageBits;
    272     // separate counter for ADD_LISTENER and REMOVE_LISTENER messages,
    273     // which might have multiple instances queued
    274     private int mPendingListenerMessages;
    275 
    276     // Alarms
    277     private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
    278     private final static String ALARM_TIMEOUT = "com.android.internal.location.ALARM_TIMEOUT";
    279     private final AlarmManager mAlarmManager;
    280     private final PendingIntent mWakeupIntent;
    281     private final PendingIntent mTimeoutIntent;
    282 
    283     private final IBatteryStats mBatteryStats;
    284     private final SparseIntArray mClientUids = new SparseIntArray();
    285 
    286     // how often to request NTP time, in milliseconds
    287     // current setting 24 hours
    288     private static final long NTP_INTERVAL = 24*60*60*1000;
    289     // how long to wait if we have a network error in NTP or XTRA downloading
    290     // current setting - 5 minutes
    291     private static final long RETRY_INTERVAL = 5*60*1000;
    292 
    293     private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
    294         public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
    295             if (listener == null) {
    296                 throw new NullPointerException("listener is null in addGpsStatusListener");
    297             }
    298 
    299             synchronized(mListeners) {
    300                 IBinder binder = listener.asBinder();
    301                 int size = mListeners.size();
    302                 for (int i = 0; i < size; i++) {
    303                     Listener test = mListeners.get(i);
    304                     if (binder.equals(test.mListener.asBinder())) {
    305                         // listener already added
    306                         return;
    307                     }
    308                 }
    309 
    310                 Listener l = new Listener(listener);
    311                 binder.linkToDeath(l, 0);
    312                 mListeners.add(l);
    313             }
    314         }
    315 
    316         public void removeGpsStatusListener(IGpsStatusListener listener) {
    317             if (listener == null) {
    318                 throw new NullPointerException("listener is null in addGpsStatusListener");
    319             }
    320 
    321             synchronized(mListeners) {
    322                 IBinder binder = listener.asBinder();
    323                 Listener l = null;
    324                 int size = mListeners.size();
    325                 for (int i = 0; i < size && l == null; i++) {
    326                     Listener test = mListeners.get(i);
    327                     if (binder.equals(test.mListener.asBinder())) {
    328                         l = test;
    329                     }
    330                 }
    331 
    332                 if (l != null) {
    333                     mListeners.remove(l);
    334                     binder.unlinkToDeath(l, 0);
    335                 }
    336             }
    337         }
    338     };
    339 
    340     public IGpsStatusProvider getGpsStatusProvider() {
    341         return mGpsStatusProvider;
    342     }
    343 
    344     private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
    345         @Override public void onReceive(Context context, Intent intent) {
    346             String action = intent.getAction();
    347 
    348             if (action.equals(ALARM_WAKEUP)) {
    349                 if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
    350                 startNavigating(false);
    351             } else if (action.equals(ALARM_TIMEOUT)) {
    352                 if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
    353                 hibernate();
    354             } else if (action.equals(Intents.DATA_SMS_RECEIVED_ACTION)) {
    355                 checkSmsSuplInit(intent);
    356             } else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) {
    357                 checkWapSuplInit(intent);
    358              }
    359         }
    360     };
    361 
    362     private void checkSmsSuplInit(Intent intent) {
    363         SmsMessage[] messages = Intents.getMessagesFromIntent(intent);
    364         for (int i=0; i <messages.length; i++) {
    365             byte[] supl_init = messages[i].getUserData();
    366             native_agps_ni_message(supl_init,supl_init.length);
    367         }
    368     }
    369 
    370     private void checkWapSuplInit(Intent intent) {
    371         byte[] supl_init = (byte[]) intent.getExtra("data");
    372         native_agps_ni_message(supl_init,supl_init.length);
    373     }
    374 
    375     public static boolean isSupported() {
    376         return native_is_supported();
    377     }
    378 
    379     public GpsLocationProvider(Context context, ILocationManager locationManager) {
    380         mContext = context;
    381         mNtpTime = NtpTrustedTime.getInstance(context);
    382         mLocationManager = locationManager;
    383         mNIHandler = new GpsNetInitiatedHandler(context);
    384 
    385         mLocation.setExtras(mLocationExtras);
    386 
    387         // Create a wake lock
    388         PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
    389         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
    390         mWakeLock.setReferenceCounted(false);
    391 
    392         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
    393         mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
    394         mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
    395 
    396         IntentFilter intentFilter = new IntentFilter();
    397         intentFilter.addAction(Intents.DATA_SMS_RECEIVED_ACTION);
    398         intentFilter.addDataScheme("sms");
    399         intentFilter.addDataAuthority("localhost","7275");
    400         context.registerReceiver(mBroadcastReciever, intentFilter);
    401 
    402         intentFilter = new IntentFilter();
    403         intentFilter.addAction(Intents.WAP_PUSH_RECEIVED_ACTION);
    404         try {
    405             intentFilter.addDataType("application/vnd.omaloc-supl-init");
    406         } catch (IntentFilter.MalformedMimeTypeException e) {
    407             Log.w(TAG, "Malformed SUPL init mime type");
    408         }
    409         context.registerReceiver(mBroadcastReciever, intentFilter);
    410 
    411         mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
    412 
    413         // Battery statistics service to be notified when GPS turns on or off
    414         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
    415 
    416         mProperties = new Properties();
    417         try {
    418             File file = new File(PROPERTIES_FILE);
    419             FileInputStream stream = new FileInputStream(file);
    420             mProperties.load(stream);
    421             stream.close();
    422 
    423             mSuplServerHost = mProperties.getProperty("SUPL_HOST");
    424             String portString = mProperties.getProperty("SUPL_PORT");
    425             if (mSuplServerHost != null && portString != null) {
    426                 try {
    427                     mSuplServerPort = Integer.parseInt(portString);
    428                 } catch (NumberFormatException e) {
    429                     Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
    430                 }
    431             }
    432 
    433             mC2KServerHost = mProperties.getProperty("C2K_HOST");
    434             portString = mProperties.getProperty("C2K_PORT");
    435             if (mC2KServerHost != null && portString != null) {
    436                 try {
    437                     mC2KServerPort = Integer.parseInt(portString);
    438                 } catch (NumberFormatException e) {
    439                     Log.e(TAG, "unable to parse C2K_PORT: " + portString);
    440                 }
    441             }
    442         } catch (IOException e) {
    443             Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
    444         }
    445 
    446         // wait until we are fully initialized before returning
    447         mThread = new GpsLocationProviderThread();
    448         mThread.start();
    449         while (true) {
    450             try {
    451                 mInitializedLatch.await();
    452                 break;
    453             } catch (InterruptedException e) {
    454                 Thread.currentThread().interrupt();
    455             }
    456         }
    457     }
    458 
    459     private void initialize() {
    460         // register our receiver on our thread rather than the main thread
    461         IntentFilter intentFilter = new IntentFilter();
    462         intentFilter.addAction(ALARM_WAKEUP);
    463         intentFilter.addAction(ALARM_TIMEOUT);
    464         mContext.registerReceiver(mBroadcastReciever, intentFilter);
    465     }
    466 
    467     /**
    468      * Returns the name of this provider.
    469      */
    470     public String getName() {
    471         return LocationManager.GPS_PROVIDER;
    472     }
    473 
    474     /**
    475      * Returns true if the provider requires access to a
    476      * data network (e.g., the Internet), false otherwise.
    477      */
    478     public boolean requiresNetwork() {
    479         return true;
    480     }
    481 
    482     public void updateNetworkState(int state, NetworkInfo info) {
    483         sendMessage(UPDATE_NETWORK_STATE, state, info);
    484     }
    485 
    486     private void handleUpdateNetworkState(int state, NetworkInfo info) {
    487         mNetworkAvailable = (state == LocationProvider.AVAILABLE);
    488 
    489         if (DEBUG) {
    490             Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable")
    491                 + " info: " + info);
    492         }
    493 
    494         if (info != null) {
    495             boolean dataEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
    496                                                          Settings.Secure.MOBILE_DATA, 1) == 1;
    497             boolean networkAvailable = info.isAvailable() && dataEnabled;
    498             String defaultApn = getSelectedApn();
    499             if (defaultApn == null) {
    500                 defaultApn = "dummy-apn";
    501             }
    502 
    503             native_update_network_state(info.isConnected(), info.getType(),
    504                                         info.isRoaming(), networkAvailable,
    505                                         info.getExtraInfo(), defaultApn);
    506         }
    507 
    508         if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
    509                 && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
    510             String apnName = info.getExtraInfo();
    511             if (mNetworkAvailable) {
    512                 if (apnName == null) {
    513                     /* Assign a dummy value in the case of C2K as otherwise we will have a runtime
    514                     exception in the following call to native_agps_data_conn_open*/
    515                     apnName = "dummy-apn";
    516                 }
    517                 mAGpsApn = apnName;
    518                 if (DEBUG) Log.d(TAG, "mAGpsDataConnectionIpAddr " + mAGpsDataConnectionIpAddr);
    519                 if (mAGpsDataConnectionIpAddr != 0xffffffff) {
    520                     boolean route_result;
    521                     if (DEBUG) Log.d(TAG, "call requestRouteToHost");
    522                     route_result = mConnMgr.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_SUPL,
    523                         mAGpsDataConnectionIpAddr);
    524                     if (route_result == false) Log.d(TAG, "call requestRouteToHost failed");
    525                 }
    526                 if (DEBUG) Log.d(TAG, "call native_agps_data_conn_open");
    527                 native_agps_data_conn_open(apnName);
    528                 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
    529             } else {
    530                 if (DEBUG) Log.d(TAG, "call native_agps_data_conn_failed");
    531                 mAGpsApn = null;
    532                 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
    533                 native_agps_data_conn_failed();
    534             }
    535         }
    536 
    537         if (mNetworkAvailable) {
    538             if (mInjectNtpTimePending) {
    539                 sendMessage(INJECT_NTP_TIME, 0, null);
    540             }
    541             if (mDownloadXtraDataPending) {
    542                 sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
    543             }
    544         }
    545     }
    546 
    547     private void handleInjectNtpTime() {
    548         if (!mNetworkAvailable) {
    549             // try again when network is up
    550             mInjectNtpTimePending = true;
    551             return;
    552         }
    553         mInjectNtpTimePending = false;
    554 
    555         long delay;
    556 
    557         // GPS requires fresh NTP time
    558         if (mNtpTime.forceRefresh()) {
    559             long time = mNtpTime.getCachedNtpTime();
    560             long timeReference = mNtpTime.getCachedNtpTimeReference();
    561             long certainty = mNtpTime.getCacheCertainty();
    562             long now = System.currentTimeMillis();
    563 
    564             Log.d(TAG, "NTP server returned: "
    565                     + time + " (" + new Date(time)
    566                     + ") reference: " + timeReference
    567                     + " certainty: " + certainty
    568                     + " system time offset: " + (time - now));
    569 
    570             native_inject_time(time, timeReference, (int) certainty);
    571             delay = NTP_INTERVAL;
    572         } else {
    573             if (DEBUG) Log.d(TAG, "requestTime failed");
    574             delay = RETRY_INTERVAL;
    575         }
    576 
    577         if (mPeriodicTimeInjection) {
    578             // send delayed message for next NTP injection
    579             // since this is delayed and not urgent we do not hold a wake lock here
    580             mHandler.removeMessages(INJECT_NTP_TIME);
    581             mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay);
    582         }
    583     }
    584 
    585     private void handleDownloadXtraData() {
    586         if (!mNetworkAvailable) {
    587             // try again when network is up
    588             mDownloadXtraDataPending = true;
    589             return;
    590         }
    591         mDownloadXtraDataPending = false;
    592 
    593 
    594         GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mContext, mProperties);
    595         byte[] data = xtraDownloader.downloadXtraData();
    596         if (data != null) {
    597             if (DEBUG) {
    598                 Log.d(TAG, "calling native_inject_xtra_data");
    599             }
    600             native_inject_xtra_data(data, data.length);
    601         } else {
    602             // try again later
    603             // since this is delayed and not urgent we do not hold a wake lock here
    604             mHandler.removeMessages(DOWNLOAD_XTRA_DATA);
    605             mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA), RETRY_INTERVAL);
    606         }
    607     }
    608 
    609     /**
    610      * This is called to inform us when another location provider returns a location.
    611      * Someday we might use this for network location injection to aid the GPS
    612      */
    613     public void updateLocation(Location location) {
    614         sendMessage(UPDATE_LOCATION, 0, location);
    615     }
    616 
    617     private void handleUpdateLocation(Location location) {
    618         if (location.hasAccuracy()) {
    619             native_inject_location(location.getLatitude(), location.getLongitude(),
    620                     location.getAccuracy());
    621         }
    622     }
    623 
    624     /**
    625      * Returns true if the provider requires access to a
    626      * satellite-based positioning system (e.g., GPS), false
    627      * otherwise.
    628      */
    629     public boolean requiresSatellite() {
    630         return true;
    631     }
    632 
    633     /**
    634      * Returns true if the provider requires access to an appropriate
    635      * cellular network (e.g., to make use of cell tower IDs), false
    636      * otherwise.
    637      */
    638     public boolean requiresCell() {
    639         return false;
    640     }
    641 
    642     /**
    643      * Returns true if the use of this provider may result in a
    644      * monetary charge to the user, false if use is free.  It is up to
    645      * each provider to give accurate information.
    646      */
    647     public boolean hasMonetaryCost() {
    648         return false;
    649     }
    650 
    651     /**
    652      * Returns true if the provider is able to provide altitude
    653      * information, false otherwise.  A provider that reports altitude
    654      * under most circumstances but may occassionally not report it
    655      * should return true.
    656      */
    657     public boolean supportsAltitude() {
    658         return true;
    659     }
    660 
    661     /**
    662      * Returns true if the provider is able to provide speed
    663      * information, false otherwise.  A provider that reports speed
    664      * under most circumstances but may occassionally not report it
    665      * should return true.
    666      */
    667     public boolean supportsSpeed() {
    668         return true;
    669     }
    670 
    671     /**
    672      * Returns true if the provider is able to provide bearing
    673      * information, false otherwise.  A provider that reports bearing
    674      * under most circumstances but may occassionally not report it
    675      * should return true.
    676      */
    677     public boolean supportsBearing() {
    678         return true;
    679     }
    680 
    681     /**
    682      * Returns the power requirement for this provider.
    683      *
    684      * @return the power requirement for this provider, as one of the
    685      * constants Criteria.POWER_REQUIREMENT_*.
    686      */
    687     public int getPowerRequirement() {
    688         return Criteria.POWER_HIGH;
    689     }
    690 
    691     /**
    692      * Returns true if this provider meets the given criteria,
    693      * false otherwise.
    694      */
    695     public boolean meetsCriteria(Criteria criteria) {
    696         return (criteria.getPowerRequirement() != Criteria.POWER_LOW);
    697     }
    698 
    699     /**
    700      * Returns the horizontal accuracy of this provider
    701      *
    702      * @return the accuracy of location from this provider, as one
    703      * of the constants Criteria.ACCURACY_*.
    704      */
    705     public int getAccuracy() {
    706         return Criteria.ACCURACY_FINE;
    707     }
    708 
    709     /**
    710      * Enables this provider.  When enabled, calls to getStatus()
    711      * must be handled.  Hardware may be started up
    712      * when the provider is enabled.
    713      */
    714     public void enable() {
    715         synchronized (mHandler) {
    716             sendMessage(ENABLE, 1, null);
    717         }
    718     }
    719 
    720     private void handleEnable() {
    721         if (DEBUG) Log.d(TAG, "handleEnable");
    722         if (mEnabled) return;
    723         mEnabled = native_init();
    724 
    725         if (mEnabled) {
    726             mSupportsXtra = native_supports_xtra();
    727             if (mSuplServerHost != null) {
    728                 native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
    729             }
    730             if (mC2KServerHost != null) {
    731                 native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
    732             }
    733         } else {
    734             Log.w(TAG, "Failed to enable location provider");
    735         }
    736     }
    737 
    738     /**
    739      * Disables this provider.  When disabled, calls to getStatus()
    740      * need not be handled.  Hardware may be shut
    741      * down while the provider is disabled.
    742      */
    743     public void disable() {
    744         synchronized (mHandler) {
    745             sendMessage(ENABLE, 0, null);
    746         }
    747     }
    748 
    749     private void handleDisable() {
    750         if (DEBUG) Log.d(TAG, "handleDisable");
    751         if (!mEnabled) return;
    752 
    753         mEnabled = false;
    754         stopNavigating();
    755 
    756         // do this before releasing wakelock
    757         native_cleanup();
    758     }
    759 
    760     public boolean isEnabled() {
    761         return mEnabled;
    762     }
    763 
    764     public int getStatus(Bundle extras) {
    765         if (extras != null) {
    766             extras.putInt("satellites", mSvCount);
    767         }
    768         return mStatus;
    769     }
    770 
    771     private void updateStatus(int status, int svCount) {
    772         if (status != mStatus || svCount != mSvCount) {
    773             mStatus = status;
    774             mSvCount = svCount;
    775             mLocationExtras.putInt("satellites", svCount);
    776             mStatusUpdateTime = SystemClock.elapsedRealtime();
    777         }
    778     }
    779 
    780     public long getStatusUpdateTime() {
    781         return mStatusUpdateTime;
    782     }
    783 
    784     public void enableLocationTracking(boolean enable) {
    785         // FIXME - should set a flag here to avoid race conditions with single shot request
    786         synchronized (mHandler) {
    787             sendMessage(ENABLE_TRACKING, (enable ? 1 : 0), null);
    788         }
    789     }
    790 
    791     private void handleEnableLocationTracking(boolean enable) {
    792         if (enable) {
    793             mTTFF = 0;
    794             mLastFixTime = 0;
    795             startNavigating(false);
    796         } else {
    797             if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
    798                 mAlarmManager.cancel(mWakeupIntent);
    799                 mAlarmManager.cancel(mTimeoutIntent);
    800             }
    801             stopNavigating();
    802         }
    803     }
    804 
    805     public boolean requestSingleShotFix() {
    806         if (mStarted) {
    807             // cannot do single shot if already navigating
    808             return false;
    809         }
    810         synchronized (mHandler) {
    811             mHandler.removeMessages(REQUEST_SINGLE_SHOT);
    812             Message m = Message.obtain(mHandler, REQUEST_SINGLE_SHOT);
    813             mHandler.sendMessage(m);
    814         }
    815         return true;
    816     }
    817 
    818     private void handleRequestSingleShot() {
    819         mTTFF = 0;
    820         mLastFixTime = 0;
    821         startNavigating(true);
    822     }
    823 
    824     public void setMinTime(long minTime, WorkSource ws) {
    825         if (DEBUG) Log.d(TAG, "setMinTime " + minTime);
    826 
    827         if (minTime >= 0) {
    828             mFixInterval = (int)minTime;
    829 
    830             if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
    831                 if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
    832                         mFixInterval, 0, 0)) {
    833                     Log.e(TAG, "set_position_mode failed in setMinTime()");
    834                 }
    835             }
    836         }
    837     }
    838 
    839     public String getInternalState() {
    840         return native_get_internal_state();
    841     }
    842 
    843     private final class Listener implements IBinder.DeathRecipient {
    844         final IGpsStatusListener mListener;
    845 
    846         int mSensors = 0;
    847 
    848         Listener(IGpsStatusListener listener) {
    849             mListener = listener;
    850         }
    851 
    852         public void binderDied() {
    853             if (DEBUG) Log.d(TAG, "GPS status listener died");
    854 
    855             synchronized(mListeners) {
    856                 mListeners.remove(this);
    857             }
    858             if (mListener != null) {
    859                 mListener.asBinder().unlinkToDeath(this, 0);
    860             }
    861         }
    862     }
    863 
    864     public void addListener(int uid) {
    865         synchronized (mWakeLock) {
    866             mPendingListenerMessages++;
    867             mWakeLock.acquire();
    868             Message m = Message.obtain(mHandler, ADD_LISTENER);
    869             m.arg1 = uid;
    870             mHandler.sendMessage(m);
    871         }
    872     }
    873 
    874     private void handleAddListener(int uid) {
    875         synchronized(mListeners) {
    876             if (mClientUids.indexOfKey(uid) >= 0) {
    877                 // Shouldn't be here -- already have this uid.
    878                 Log.w(TAG, "Duplicate add listener for uid " + uid);
    879                 return;
    880             }
    881             mClientUids.put(uid, 0);
    882             if (mNavigating) {
    883                 try {
    884                     mBatteryStats.noteStartGps(uid);
    885                 } catch (RemoteException e) {
    886                     Log.w(TAG, "RemoteException in addListener");
    887                 }
    888             }
    889         }
    890     }
    891 
    892     public void removeListener(int uid) {
    893         synchronized (mWakeLock) {
    894             mPendingListenerMessages++;
    895             mWakeLock.acquire();
    896             Message m = Message.obtain(mHandler, REMOVE_LISTENER);
    897             m.arg1 = uid;
    898             mHandler.sendMessage(m);
    899         }
    900     }
    901 
    902     private void handleRemoveListener(int uid) {
    903         synchronized(mListeners) {
    904             if (mClientUids.indexOfKey(uid) < 0) {
    905                 // Shouldn't be here -- don't have this uid.
    906                 Log.w(TAG, "Unneeded remove listener for uid " + uid);
    907                 return;
    908             }
    909             mClientUids.delete(uid);
    910             if (mNavigating) {
    911                 try {
    912                     mBatteryStats.noteStopGps(uid);
    913                 } catch (RemoteException e) {
    914                     Log.w(TAG, "RemoteException in removeListener");
    915                 }
    916             }
    917         }
    918     }
    919 
    920     public boolean sendExtraCommand(String command, Bundle extras) {
    921 
    922         long identity = Binder.clearCallingIdentity();
    923         boolean result = false;
    924 
    925         if ("delete_aiding_data".equals(command)) {
    926             result = deleteAidingData(extras);
    927         } else if ("force_time_injection".equals(command)) {
    928             sendMessage(INJECT_NTP_TIME, 0, null);
    929             result = true;
    930         } else if ("force_xtra_injection".equals(command)) {
    931             if (mSupportsXtra) {
    932                 xtraDownloadRequest();
    933                 result = true;
    934             }
    935         } else {
    936             Log.w(TAG, "sendExtraCommand: unknown command " + command);
    937         }
    938 
    939         Binder.restoreCallingIdentity(identity);
    940         return result;
    941     }
    942 
    943     private boolean deleteAidingData(Bundle extras) {
    944         int flags;
    945 
    946         if (extras == null) {
    947             flags = GPS_DELETE_ALL;
    948         } else {
    949             flags = 0;
    950             if (extras.getBoolean("ephemeris")) flags |= GPS_DELETE_EPHEMERIS;
    951             if (extras.getBoolean("almanac")) flags |= GPS_DELETE_ALMANAC;
    952             if (extras.getBoolean("position")) flags |= GPS_DELETE_POSITION;
    953             if (extras.getBoolean("time")) flags |= GPS_DELETE_TIME;
    954             if (extras.getBoolean("iono")) flags |= GPS_DELETE_IONO;
    955             if (extras.getBoolean("utc")) flags |= GPS_DELETE_UTC;
    956             if (extras.getBoolean("health")) flags |= GPS_DELETE_HEALTH;
    957             if (extras.getBoolean("svdir")) flags |= GPS_DELETE_SVDIR;
    958             if (extras.getBoolean("svsteer")) flags |= GPS_DELETE_SVSTEER;
    959             if (extras.getBoolean("sadata")) flags |= GPS_DELETE_SADATA;
    960             if (extras.getBoolean("rti")) flags |= GPS_DELETE_RTI;
    961             if (extras.getBoolean("celldb-info")) flags |= GPS_DELETE_CELLDB_INFO;
    962             if (extras.getBoolean("all")) flags |= GPS_DELETE_ALL;
    963         }
    964 
    965         if (flags != 0) {
    966             native_delete_aiding_data(flags);
    967             return true;
    968         }
    969 
    970         return false;
    971     }
    972 
    973     private void startNavigating(boolean singleShot) {
    974         if (!mStarted) {
    975             if (DEBUG) Log.d(TAG, "startNavigating");
    976             mStarted = true;
    977             mSingleShot = singleShot;
    978             mPositionMode = GPS_POSITION_MODE_STANDALONE;
    979 
    980              if (Settings.Secure.getInt(mContext.getContentResolver(),
    981                     Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) {
    982                 if (singleShot && hasCapability(GPS_CAPABILITY_MSA)) {
    983                     mPositionMode = GPS_POSITION_MODE_MS_ASSISTED;
    984                 } else if (hasCapability(GPS_CAPABILITY_MSB)) {
    985                     mPositionMode = GPS_POSITION_MODE_MS_BASED;
    986                 }
    987             }
    988 
    989             int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
    990             if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
    991                     interval, 0, 0)) {
    992                 mStarted = false;
    993                 Log.e(TAG, "set_position_mode failed in startNavigating()");
    994                 return;
    995             }
    996             if (!native_start()) {
    997                 mStarted = false;
    998                 Log.e(TAG, "native_start failed in startNavigating()");
    999                 return;
   1000             }
   1001 
   1002             // reset SV count to zero
   1003             updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
   1004             mFixRequestTime = System.currentTimeMillis();
   1005             if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
   1006                 // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
   1007                 // and our fix interval is not short
   1008                 if (mFixInterval >= NO_FIX_TIMEOUT) {
   1009                     mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
   1010                             SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
   1011                 }
   1012             }
   1013         }
   1014     }
   1015 
   1016     private void stopNavigating() {
   1017         if (DEBUG) Log.d(TAG, "stopNavigating");
   1018         if (mStarted) {
   1019             mStarted = false;
   1020             mSingleShot = false;
   1021             native_stop();
   1022             mTTFF = 0;
   1023             mLastFixTime = 0;
   1024             mLocationFlags = LOCATION_INVALID;
   1025 
   1026             // reset SV count to zero
   1027             updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0);
   1028         }
   1029     }
   1030 
   1031     private void hibernate() {
   1032         // stop GPS until our next fix interval arrives
   1033         stopNavigating();
   1034         mAlarmManager.cancel(mTimeoutIntent);
   1035         mAlarmManager.cancel(mWakeupIntent);
   1036         long now = SystemClock.elapsedRealtime();
   1037         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
   1038                 SystemClock.elapsedRealtime() + mFixInterval, mWakeupIntent);
   1039     }
   1040 
   1041     private boolean hasCapability(int capability) {
   1042         return ((mEngineCapabilities & capability) != 0);
   1043     }
   1044 
   1045     /**
   1046      * called from native code to update our position.
   1047      */
   1048     private void reportLocation(int flags, double latitude, double longitude, double altitude,
   1049             float speed, float bearing, float accuracy, long timestamp) {
   1050         if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +
   1051                 " timestamp: " + timestamp);
   1052 
   1053         synchronized (mLocation) {
   1054             mLocationFlags = flags;
   1055             if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
   1056                 mLocation.setLatitude(latitude);
   1057                 mLocation.setLongitude(longitude);
   1058                 mLocation.setTime(timestamp);
   1059             }
   1060             if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
   1061                 mLocation.setAltitude(altitude);
   1062             } else {
   1063                 mLocation.removeAltitude();
   1064             }
   1065             if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
   1066                 mLocation.setSpeed(speed);
   1067             } else {
   1068                 mLocation.removeSpeed();
   1069             }
   1070             if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
   1071                 mLocation.setBearing(bearing);
   1072             } else {
   1073                 mLocation.removeBearing();
   1074             }
   1075             if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
   1076                 mLocation.setAccuracy(accuracy);
   1077             } else {
   1078                 mLocation.removeAccuracy();
   1079             }
   1080 
   1081             try {
   1082                 mLocationManager.reportLocation(mLocation, false);
   1083             } catch (RemoteException e) {
   1084                 Log.e(TAG, "RemoteException calling reportLocation");
   1085             }
   1086         }
   1087 
   1088         mLastFixTime = System.currentTimeMillis();
   1089         // report time to first fix
   1090         if (mTTFF == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
   1091             mTTFF = (int)(mLastFixTime - mFixRequestTime);
   1092             if (DEBUG) Log.d(TAG, "TTFF: " + mTTFF);
   1093 
   1094             // notify status listeners
   1095             synchronized(mListeners) {
   1096                 int size = mListeners.size();
   1097                 for (int i = 0; i < size; i++) {
   1098                     Listener listener = mListeners.get(i);
   1099                     try {
   1100                         listener.mListener.onFirstFix(mTTFF);
   1101                     } catch (RemoteException e) {
   1102                         Log.w(TAG, "RemoteException in stopNavigating");
   1103                         mListeners.remove(listener);
   1104                         // adjust for size of list changing
   1105                         size--;
   1106                     }
   1107                 }
   1108             }
   1109         }
   1110 
   1111         if (mSingleShot) {
   1112             stopNavigating();
   1113         }
   1114         if (mStarted && mStatus != LocationProvider.AVAILABLE) {
   1115             // we want to time out if we do not receive a fix
   1116             // within the time out and we are requesting infrequent fixes
   1117             if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
   1118                 mAlarmManager.cancel(mTimeoutIntent);
   1119             }
   1120 
   1121             // send an intent to notify that the GPS is receiving fixes.
   1122             Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
   1123             intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, true);
   1124             mContext.sendBroadcast(intent);
   1125             updateStatus(LocationProvider.AVAILABLE, mSvCount);
   1126         }
   1127 
   1128        if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted && mFixInterval > 1000) {
   1129             if (DEBUG) Log.d(TAG, "got fix, hibernating");
   1130             hibernate();
   1131         }
   1132    }
   1133 
   1134     /**
   1135      * called from native code to update our status
   1136      */
   1137     private void reportStatus(int status) {
   1138         if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
   1139 
   1140         synchronized(mListeners) {
   1141             boolean wasNavigating = mNavigating;
   1142 
   1143             switch (status) {
   1144                 case GPS_STATUS_SESSION_BEGIN:
   1145                     mNavigating = true;
   1146                     mEngineOn = true;
   1147                     break;
   1148                 case GPS_STATUS_SESSION_END:
   1149                     mNavigating = false;
   1150                     break;
   1151                 case GPS_STATUS_ENGINE_ON:
   1152                     mEngineOn = true;
   1153                     break;
   1154                 case GPS_STATUS_ENGINE_OFF:
   1155                     mEngineOn = false;
   1156                     mNavigating = false;
   1157                     break;
   1158             }
   1159 
   1160             if (wasNavigating != mNavigating) {
   1161                 int size = mListeners.size();
   1162                 for (int i = 0; i < size; i++) {
   1163                     Listener listener = mListeners.get(i);
   1164                     try {
   1165                         if (mNavigating) {
   1166                             listener.mListener.onGpsStarted();
   1167                         } else {
   1168                             listener.mListener.onGpsStopped();
   1169                         }
   1170                     } catch (RemoteException e) {
   1171                         Log.w(TAG, "RemoteException in reportStatus");
   1172                         mListeners.remove(listener);
   1173                         // adjust for size of list changing
   1174                         size--;
   1175                     }
   1176                 }
   1177 
   1178                 try {
   1179                     // update battery stats
   1180                     for (int i=mClientUids.size() - 1; i >= 0; i--) {
   1181                         int uid = mClientUids.keyAt(i);
   1182                         if (mNavigating) {
   1183                             mBatteryStats.noteStartGps(uid);
   1184                         } else {
   1185                             mBatteryStats.noteStopGps(uid);
   1186                         }
   1187                     }
   1188                 } catch (RemoteException e) {
   1189                     Log.w(TAG, "RemoteException in reportStatus");
   1190                 }
   1191 
   1192                 // send an intent to notify that the GPS has been enabled or disabled.
   1193                 Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
   1194                 intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
   1195                 mContext.sendBroadcast(intent);
   1196             }
   1197         }
   1198     }
   1199 
   1200     /**
   1201      * called from native code to update SV info
   1202      */
   1203     private void reportSvStatus() {
   1204 
   1205         int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
   1206 
   1207         synchronized(mListeners) {
   1208             int size = mListeners.size();
   1209             for (int i = 0; i < size; i++) {
   1210                 Listener listener = mListeners.get(i);
   1211                 try {
   1212                     listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
   1213                             mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
   1214                             mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
   1215                 } catch (RemoteException e) {
   1216                     Log.w(TAG, "RemoteException in reportSvInfo");
   1217                     mListeners.remove(listener);
   1218                     // adjust for size of list changing
   1219                     size--;
   1220                 }
   1221             }
   1222         }
   1223 
   1224         if (VERBOSE) {
   1225             Log.v(TAG, "SV count: " + svCount +
   1226                     " ephemerisMask: " + Integer.toHexString(mSvMasks[EPHEMERIS_MASK]) +
   1227                     " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
   1228             for (int i = 0; i < svCount; i++) {
   1229                 Log.v(TAG, "sv: " + mSvs[i] +
   1230                         " snr: " + (float)mSnrs[i]/10 +
   1231                         " elev: " + mSvElevations[i] +
   1232                         " azimuth: " + mSvAzimuths[i] +
   1233                         ((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " E") +
   1234                         ((mSvMasks[ALMANAC_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "  " : " A") +
   1235                         ((mSvMasks[USED_FOR_FIX_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "" : "U"));
   1236             }
   1237         }
   1238 
   1239         // return number of sets used in fix instead of total
   1240         updateStatus(mStatus, Integer.bitCount(mSvMasks[USED_FOR_FIX_MASK]));
   1241 
   1242         if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 &&
   1243             System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
   1244             // send an intent to notify that the GPS is no longer receiving fixes.
   1245             Intent intent = new Intent(LocationManager.GPS_FIX_CHANGE_ACTION);
   1246             intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, false);
   1247             mContext.sendBroadcast(intent);
   1248             updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount);
   1249         }
   1250     }
   1251 
   1252     /**
   1253      * called from native code to update AGPS status
   1254      */
   1255     private void reportAGpsStatus(int type, int status, int ipaddr) {
   1256         switch (status) {
   1257             case GPS_REQUEST_AGPS_DATA_CONN:
   1258                 if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
   1259                 // Set mAGpsDataConnectionState before calling startUsingNetworkFeature
   1260                 //  to avoid a race condition with handleUpdateNetworkState()
   1261                 mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
   1262                 int result = mConnMgr.startUsingNetworkFeature(
   1263                         ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
   1264                 mAGpsDataConnectionIpAddr = ipaddr;
   1265                 if (result == Phone.APN_ALREADY_ACTIVE) {
   1266                     if (DEBUG) Log.d(TAG, "Phone.APN_ALREADY_ACTIVE");
   1267                     if (mAGpsApn != null) {
   1268                         Log.d(TAG, "mAGpsDataConnectionIpAddr " + mAGpsDataConnectionIpAddr);
   1269                         if (mAGpsDataConnectionIpAddr != 0xffffffff) {
   1270                             boolean route_result;
   1271                             if (DEBUG) Log.d(TAG, "call requestRouteToHost");
   1272                             route_result = mConnMgr.requestRouteToHost(
   1273                                 ConnectivityManager.TYPE_MOBILE_SUPL,
   1274                                 mAGpsDataConnectionIpAddr);
   1275                             if (route_result == false) Log.d(TAG, "call requestRouteToHost failed");
   1276                         }
   1277                         native_agps_data_conn_open(mAGpsApn);
   1278                         mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
   1279                     } else {
   1280                         Log.e(TAG, "mAGpsApn not set when receiving Phone.APN_ALREADY_ACTIVE");
   1281                         mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
   1282                         native_agps_data_conn_failed();
   1283                     }
   1284                 } else if (result == Phone.APN_REQUEST_STARTED) {
   1285                     if (DEBUG) Log.d(TAG, "Phone.APN_REQUEST_STARTED");
   1286                     // Nothing to do here
   1287                 } else {
   1288                     if (DEBUG) Log.d(TAG, "startUsingNetworkFeature failed");
   1289                     mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
   1290                     native_agps_data_conn_failed();
   1291                 }
   1292                 break;
   1293             case GPS_RELEASE_AGPS_DATA_CONN:
   1294                 if (DEBUG) Log.d(TAG, "GPS_RELEASE_AGPS_DATA_CONN");
   1295                 if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
   1296                     mConnMgr.stopUsingNetworkFeature(
   1297                             ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
   1298                     native_agps_data_conn_closed();
   1299                     mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
   1300                 }
   1301                 break;
   1302             case GPS_AGPS_DATA_CONNECTED:
   1303                 if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
   1304                 break;
   1305             case GPS_AGPS_DATA_CONN_DONE:
   1306                 if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_DONE");
   1307                 break;
   1308             case GPS_AGPS_DATA_CONN_FAILED:
   1309                 if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
   1310                 break;
   1311         }
   1312     }
   1313 
   1314     /**
   1315      * called from native code to report NMEA data received
   1316      */
   1317     private void reportNmea(long timestamp) {
   1318         synchronized(mListeners) {
   1319             int size = mListeners.size();
   1320             if (size > 0) {
   1321                 // don't bother creating the String if we have no listeners
   1322                 int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
   1323                 String nmea = new String(mNmeaBuffer, 0, length);
   1324 
   1325                 for (int i = 0; i < size; i++) {
   1326                     Listener listener = mListeners.get(i);
   1327                     try {
   1328                         listener.mListener.onNmeaReceived(timestamp, nmea);
   1329                     } catch (RemoteException e) {
   1330                         Log.w(TAG, "RemoteException in reportNmea");
   1331                         mListeners.remove(listener);
   1332                         // adjust for size of list changing
   1333                         size--;
   1334                     }
   1335                 }
   1336             }
   1337         }
   1338     }
   1339 
   1340     /**
   1341      * called from native code to inform us what the GPS engine capabilities are
   1342      */
   1343     private void setEngineCapabilities(int capabilities) {
   1344         mEngineCapabilities = capabilities;
   1345 
   1346         if (!hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME) && !mPeriodicTimeInjection) {
   1347             mPeriodicTimeInjection = true;
   1348             requestUtcTime();
   1349         }
   1350     }
   1351 
   1352     /**
   1353      * called from native code to request XTRA data
   1354      */
   1355     private void xtraDownloadRequest() {
   1356         if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
   1357         sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
   1358     }
   1359 
   1360     //=============================================================
   1361     // NI Client support
   1362     //=============================================================
   1363     private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
   1364         // Sends a response for an NI reqeust to HAL.
   1365         public boolean sendNiResponse(int notificationId, int userResponse)
   1366         {
   1367             // TODO Add Permission check
   1368 
   1369             StringBuilder extrasBuf = new StringBuilder();
   1370 
   1371             if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
   1372                     ", response: " + userResponse);
   1373             native_send_ni_response(notificationId, userResponse);
   1374             return true;
   1375         }
   1376     };
   1377 
   1378     public INetInitiatedListener getNetInitiatedListener() {
   1379         return mNetInitiatedListener;
   1380     }
   1381 
   1382     // Called by JNI function to report an NI request.
   1383     public void reportNiNotification(
   1384             int notificationId,
   1385             int niType,
   1386             int notifyFlags,
   1387             int timeout,
   1388             int defaultResponse,
   1389             String requestorId,
   1390             String text,
   1391             int requestorIdEncoding,
   1392             int textEncoding,
   1393             String extras  // Encoded extra data
   1394         )
   1395     {
   1396         Log.i(TAG, "reportNiNotification: entered");
   1397         Log.i(TAG, "notificationId: " + notificationId +
   1398                 ", niType: " + niType +
   1399                 ", notifyFlags: " + notifyFlags +
   1400                 ", timeout: " + timeout +
   1401                 ", defaultResponse: " + defaultResponse);
   1402 
   1403         Log.i(TAG, "requestorId: " + requestorId +
   1404                 ", text: " + text +
   1405                 ", requestorIdEncoding: " + requestorIdEncoding +
   1406                 ", textEncoding: " + textEncoding);
   1407 
   1408         GpsNiNotification notification = new GpsNiNotification();
   1409 
   1410         notification.notificationId = notificationId;
   1411         notification.niType = niType;
   1412         notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
   1413         notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
   1414         notification.privacyOverride = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
   1415         notification.timeout = timeout;
   1416         notification.defaultResponse = defaultResponse;
   1417         notification.requestorId = requestorId;
   1418         notification.text = text;
   1419         notification.requestorIdEncoding = requestorIdEncoding;
   1420         notification.textEncoding = textEncoding;
   1421 
   1422         // Process extras, assuming the format is
   1423         // one of more lines of "key = value"
   1424         Bundle bundle = new Bundle();
   1425 
   1426         if (extras == null) extras = "";
   1427         Properties extraProp = new Properties();
   1428 
   1429         try {
   1430             extraProp.load(new StringReader(extras));
   1431         }
   1432         catch (IOException e)
   1433         {
   1434             Log.e(TAG, "reportNiNotification cannot parse extras data: " + extras);
   1435         }
   1436 
   1437         for (Entry<Object, Object> ent : extraProp.entrySet())
   1438         {
   1439             bundle.putString((String) ent.getKey(), (String) ent.getValue());
   1440         }
   1441 
   1442         notification.extras = bundle;
   1443 
   1444         mNIHandler.handleNiNotification(notification);
   1445     }
   1446 
   1447     /**
   1448      * Called from native code to request set id info.
   1449      * We should be careful about receiving null string from the TelephonyManager,
   1450      * because sending null String to JNI function would cause a crash.
   1451      */
   1452 
   1453     private void requestSetID(int flags) {
   1454         TelephonyManager phone = (TelephonyManager)
   1455                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
   1456         int    type = AGPS_SETID_TYPE_NONE;
   1457         String data = "";
   1458 
   1459         if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
   1460             String data_temp = phone.getSubscriberId();
   1461             if (data_temp == null) {
   1462                 // This means the framework does not have the SIM card ready.
   1463             } else {
   1464                 // This means the framework has the SIM card.
   1465                 data = data_temp;
   1466                 type = AGPS_SETID_TYPE_IMSI;
   1467             }
   1468         }
   1469         else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
   1470             String data_temp = phone.getLine1Number();
   1471             if (data_temp == null) {
   1472                 // This means the framework does not have the SIM card ready.
   1473             } else {
   1474                 // This means the framework has the SIM card.
   1475                 data = data_temp;
   1476                 type = AGPS_SETID_TYPE_MSISDN;
   1477             }
   1478         }
   1479         native_agps_set_id(type, data);
   1480     }
   1481 
   1482     /**
   1483      * Called from native code to request utc time info
   1484      */
   1485 
   1486     private void requestUtcTime() {
   1487         sendMessage(INJECT_NTP_TIME, 0, null);
   1488     }
   1489 
   1490     /**
   1491      * Called from native code to request reference location info
   1492      */
   1493 
   1494     private void requestRefLocation(int flags) {
   1495         TelephonyManager phone = (TelephonyManager)
   1496                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
   1497         if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
   1498             GsmCellLocation gsm_cell = (GsmCellLocation) phone.getCellLocation();
   1499             if ((gsm_cell != null) && (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) &&
   1500                     (phone.getNetworkOperator() != null) &&
   1501                         (phone.getNetworkOperator().length() > 3)) {
   1502                 int type;
   1503                 int mcc = Integer.parseInt(phone.getNetworkOperator().substring(0,3));
   1504                 int mnc = Integer.parseInt(phone.getNetworkOperator().substring(3));
   1505                 int networkType = phone.getNetworkType();
   1506                 if (networkType == TelephonyManager.NETWORK_TYPE_UMTS
   1507                     || networkType == TelephonyManager.NETWORK_TYPE_HSDPA
   1508                     || networkType == TelephonyManager.NETWORK_TYPE_HSUPA
   1509                     || networkType == TelephonyManager.NETWORK_TYPE_HSPA) {
   1510                     type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
   1511                 } else {
   1512                     type = AGPS_REF_LOCATION_TYPE_GSM_CELLID;
   1513                 }
   1514                 native_agps_set_ref_location_cellid(type, mcc, mnc,
   1515                         gsm_cell.getLac(), gsm_cell.getCid());
   1516             } else {
   1517                 Log.e(TAG,"Error getting cell location info.");
   1518             }
   1519         }
   1520         else {
   1521             Log.e(TAG,"CDMA not supported.");
   1522         }
   1523     }
   1524 
   1525     private void sendMessage(int message, int arg, Object obj) {
   1526         // hold a wake lock while messages are pending
   1527         synchronized (mWakeLock) {
   1528             mPendingMessageBits |= (1 << message);
   1529             mWakeLock.acquire();
   1530             mHandler.removeMessages(message);
   1531             Message m = Message.obtain(mHandler, message);
   1532             m.arg1 = arg;
   1533             m.obj = obj;
   1534             mHandler.sendMessage(m);
   1535         }
   1536     }
   1537 
   1538     private final class ProviderHandler extends Handler {
   1539         @Override
   1540         public void handleMessage(Message msg) {
   1541             int message = msg.what;
   1542             switch (message) {
   1543                 case ENABLE:
   1544                     if (msg.arg1 == 1) {
   1545                         handleEnable();
   1546                     } else {
   1547                         handleDisable();
   1548                     }
   1549                     break;
   1550                 case ENABLE_TRACKING:
   1551                     handleEnableLocationTracking(msg.arg1 == 1);
   1552                     break;
   1553                 case REQUEST_SINGLE_SHOT:
   1554                     handleRequestSingleShot();
   1555                     break;
   1556                 case UPDATE_NETWORK_STATE:
   1557                     handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
   1558                     break;
   1559                 case INJECT_NTP_TIME:
   1560                     handleInjectNtpTime();
   1561                     break;
   1562                 case DOWNLOAD_XTRA_DATA:
   1563                     if (mSupportsXtra) {
   1564                         handleDownloadXtraData();
   1565                     }
   1566                     break;
   1567                 case UPDATE_LOCATION:
   1568                     handleUpdateLocation((Location)msg.obj);
   1569                     break;
   1570                 case ADD_LISTENER:
   1571                     handleAddListener(msg.arg1);
   1572                     break;
   1573                 case REMOVE_LISTENER:
   1574                     handleRemoveListener(msg.arg1);
   1575                     break;
   1576             }
   1577             // release wake lock if no messages are pending
   1578             synchronized (mWakeLock) {
   1579                 mPendingMessageBits &= ~(1 << message);
   1580                 if (message == ADD_LISTENER || message == REMOVE_LISTENER) {
   1581                     mPendingListenerMessages--;
   1582                 }
   1583                 if (mPendingMessageBits == 0 && mPendingListenerMessages == 0) {
   1584                     mWakeLock.release();
   1585                 }
   1586             }
   1587         }
   1588     };
   1589 
   1590     private final class GpsLocationProviderThread extends Thread {
   1591 
   1592         public GpsLocationProviderThread() {
   1593             super("GpsLocationProvider");
   1594         }
   1595 
   1596         public void run() {
   1597             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
   1598             initialize();
   1599             Looper.prepare();
   1600             mHandler = new ProviderHandler();
   1601             // signal when we are initialized and ready to go
   1602             mInitializedLatch.countDown();
   1603             Looper.loop();
   1604         }
   1605     }
   1606 
   1607     private String getSelectedApn() {
   1608         Uri uri = Uri.parse("content://telephony/carriers/preferapn");
   1609         String apn = null;
   1610 
   1611         Cursor cursor = mContext.getContentResolver().query(uri, new String[] {"apn"},
   1612                 null, null, Carriers.DEFAULT_SORT_ORDER);
   1613 
   1614         if (null != cursor) {
   1615             try {
   1616                 if (cursor.moveToFirst()) {
   1617                     apn = cursor.getString(0);
   1618                 }
   1619             } finally {
   1620                 cursor.close();
   1621             }
   1622         }
   1623         return apn;
   1624     }
   1625 
   1626     // for GPS SV statistics
   1627     private static final int MAX_SVS = 32;
   1628     private static final int EPHEMERIS_MASK = 0;
   1629     private static final int ALMANAC_MASK = 1;
   1630     private static final int USED_FOR_FIX_MASK = 2;
   1631 
   1632     // preallocated arrays, to avoid memory allocation in reportStatus()
   1633     private int mSvs[] = new int[MAX_SVS];
   1634     private float mSnrs[] = new float[MAX_SVS];
   1635     private float mSvElevations[] = new float[MAX_SVS];
   1636     private float mSvAzimuths[] = new float[MAX_SVS];
   1637     private int mSvMasks[] = new int[3];
   1638     private int mSvCount;
   1639     // preallocated to avoid memory allocation in reportNmea()
   1640     private byte[] mNmeaBuffer = new byte[120];
   1641 
   1642     static { class_init_native(); }
   1643     private static native void class_init_native();
   1644     private static native boolean native_is_supported();
   1645 
   1646     private native boolean native_init();
   1647     private native void native_cleanup();
   1648     private native boolean native_set_position_mode(int mode, int recurrence, int min_interval,
   1649             int preferred_accuracy, int preferred_time);
   1650     private native boolean native_start();
   1651     private native boolean native_stop();
   1652     private native void native_delete_aiding_data(int flags);
   1653     // returns number of SVs
   1654     // mask[0] is ephemeris mask and mask[1] is almanac mask
   1655     private native int native_read_sv_status(int[] svs, float[] snrs,
   1656             float[] elevations, float[] azimuths, int[] masks);
   1657     private native int native_read_nmea(byte[] buffer, int bufferSize);
   1658     private native void native_inject_location(double latitude, double longitude, float accuracy);
   1659 
   1660     // XTRA Support
   1661     private native void native_inject_time(long time, long timeReference, int uncertainty);
   1662     private native boolean native_supports_xtra();
   1663     private native void native_inject_xtra_data(byte[] data, int length);
   1664 
   1665     // DEBUG Support
   1666     private native String native_get_internal_state();
   1667 
   1668     // AGPS Support
   1669     private native void native_agps_data_conn_open(String apn);
   1670     private native void native_agps_data_conn_closed();
   1671     private native void native_agps_data_conn_failed();
   1672     private native void native_agps_ni_message(byte [] msg, int length);
   1673     private native void native_set_agps_server(int type, String hostname, int port);
   1674 
   1675     // Network-initiated (NI) Support
   1676     private native void native_send_ni_response(int notificationId, int userResponse);
   1677 
   1678     // AGPS ril suport
   1679     private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
   1680             int lac, int cid);
   1681     private native void native_agps_set_id(int type, String setid);
   1682 
   1683     private native void native_update_network_state(boolean connected, int type,
   1684             boolean roaming, boolean available, String extraInfo, String defaultAPN);
   1685 }
   1686