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