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