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