OSDN Git Service

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