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