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