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