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