GpsLocationProvider.java revision 29c84340a14e70419449628ab193d11b7a609e9a
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.internal.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.ILocationProvider; 30import android.location.Location; 31import android.location.LocationManager; 32import android.location.LocationProvider; 33import android.net.ConnectivityManager; 34import android.net.SntpClient; 35import android.os.Bundle; 36import android.os.IBinder; 37import android.os.RemoteException; 38import android.os.ServiceManager; 39import android.os.SystemClock; 40import android.util.Config; 41import android.util.Log; 42import android.util.SparseIntArray; 43 44import com.android.internal.app.IBatteryStats; 45import com.android.internal.telephony.Phone; 46import com.android.internal.telephony.TelephonyIntents; 47 48import java.io.File; 49import java.io.FileInputStream; 50import java.io.IOException; 51import java.net.InetAddress; 52import java.net.UnknownHostException; 53import java.util.ArrayList; 54import java.util.Properties; 55 56/** 57 * A GPS implementation of LocationProvider used by LocationManager. 58 * 59 * {@hide} 60 */ 61public class GpsLocationProvider extends ILocationProvider.Stub { 62 63 private static final String TAG = "GpsLocationProvider"; 64 65 private static final boolean DEBUG = true; 66 private static final boolean VERBOSE = false; 67 68 /** 69 * Broadcast intent action indicating that the GPS has either been 70 * enabled or disabled. An intent extra provides this state as a boolean, 71 * where {@code true} means enabled. 72 * @see #EXTRA_ENABLED 73 * 74 * {@hide} 75 */ 76 public static final String GPS_ENABLED_CHANGE_ACTION = 77 "android.location.GPS_ENABLED_CHANGE"; 78 79 /** 80 * Broadcast intent action indicating that the GPS has either started or 81 * stopped receiving GPS fixes. An intent extra provides this state as a 82 * boolean, where {@code true} means that the GPS is actively receiving fixes. 83 * @see #EXTRA_ENABLED 84 * 85 * {@hide} 86 */ 87 public static final String GPS_FIX_CHANGE_ACTION = 88 "android.location.GPS_FIX_CHANGE"; 89 90 /** 91 * The lookup key for a boolean that indicates whether GPS is enabled or 92 * disabled. {@code true} means GPS is enabled. Retrieve it with 93 * {@link android.content.Intent#getBooleanExtra(String,boolean)}. 94 * 95 * {@hide} 96 */ 97 public static final String EXTRA_ENABLED = "enabled"; 98 99 // these need to match GpsPositionMode enum in gps.h 100 private static final int GPS_POSITION_MODE_STANDALONE = 0; 101 private static final int GPS_POSITION_MODE_MS_BASED = 1; 102 private static final int GPS_POSITION_MODE_MS_ASSISTED = 2; 103 104 // these need to match GpsStatusValue defines in gps.h 105 private static final int GPS_STATUS_NONE = 0; 106 private static final int GPS_STATUS_SESSION_BEGIN = 1; 107 private static final int GPS_STATUS_SESSION_END = 2; 108 private static final int GPS_STATUS_ENGINE_ON = 3; 109 private static final int GPS_STATUS_ENGINE_OFF = 4; 110 111 // these need to match GpsSuplStatusValue defines in gps.h 112 /** SUPL status event values. */ 113 private static final int GPS_REQUEST_SUPL_DATA_CONN = 1; 114 private static final int GPS_RELEASE_SUPL_DATA_CONN = 2; 115 private static final int GPS_SUPL_DATA_CONNECTED = 3; 116 private static final int GPS_SUPL_DATA_CONN_DONE = 4; 117 private static final int GPS_SUPL_DATA_CONN_FAILED = 5; 118 119 // these need to match GpsLocationFlags enum in gps.h 120 private static final int LOCATION_INVALID = 0; 121 private static final int LOCATION_HAS_LAT_LONG = 1; 122 private static final int LOCATION_HAS_ALTITUDE = 2; 123 private static final int LOCATION_HAS_SPEED = 4; 124 private static final int LOCATION_HAS_BEARING = 8; 125 private static final int LOCATION_HAS_ACCURACY = 16; 126 127// IMPORTANT - the GPS_DELETE_* symbols here must match constants in GpsLocationProvider.java 128 private static final int GPS_DELETE_EPHEMERIS = 0x0001; 129 private static final int GPS_DELETE_ALMANAC = 0x0002; 130 private static final int GPS_DELETE_POSITION = 0x0004; 131 private static final int GPS_DELETE_TIME = 0x0008; 132 private static final int GPS_DELETE_IONO = 0x0010; 133 private static final int GPS_DELETE_UTC = 0x0020; 134 private static final int GPS_DELETE_HEALTH = 0x0040; 135 private static final int GPS_DELETE_SVDIR = 0x0080; 136 private static final int GPS_DELETE_SVSTEER = 0x0100; 137 private static final int GPS_DELETE_SADATA = 0x0200; 138 private static final int GPS_DELETE_RTI = 0x0400; 139 private static final int GPS_DELETE_CELLDB_INFO = 0x8000; 140 private static final int GPS_DELETE_ALL = 0xFFFF; 141 142 // for mSuplDataConnectionState 143 private static final int SUPL_DATA_CONNECTION_CLOSED = 0; 144 private static final int SUPL_DATA_CONNECTION_OPENING = 1; 145 private static final int SUPL_DATA_CONNECTION_OPEN = 2; 146 147 private static final String PROPERTIES_FILE = "/etc/gps.conf"; 148 149 private int mLocationFlags = LOCATION_INVALID; 150 151 // current status 152 private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE; 153 154 // time for last status update 155 private long mStatusUpdateTime = SystemClock.elapsedRealtime(); 156 157 // turn off GPS fix icon if we haven't received a fix in 10 seconds 158 private static final long RECENT_FIX_TIMEOUT = 10 * 1000; 159 160 // number of fixes to receive before disabling GPS 161 private static final int MIN_FIX_COUNT = 10; 162 163 // true if we are enabled 164 private boolean mEnabled; 165 166 // true if we have network connectivity 167 private boolean mNetworkAvailable; 168 169 // true if GPS is navigating 170 private boolean mNavigating; 171 172 // requested frequency of fixes, in seconds 173 private int mFixInterval = 1; 174 175 // number of fixes we have received since we started navigating 176 private int mFixCount; 177 178 private int mPositionMode = GPS_POSITION_MODE_STANDALONE; 179 180 // true if we started navigation 181 private boolean mStarted; 182 183 // for calculating time to first fix 184 private long mFixRequestTime = 0; 185 // time to first fix for most recent session 186 private int mTTFF = 0; 187 // time we received our last fix 188 private long mLastFixTime; 189 190 // properties loaded from PROPERTIES_FILE 191 private Properties mProperties; 192 private String mNtpServer; 193 194 private final Context mContext; 195 private final ILocationManager mLocationManager; 196 private Location mLocation = new Location(LocationManager.GPS_PROVIDER); 197 private Bundle mLocationExtras = new Bundle(); 198 private ArrayList<Listener> mListeners = new ArrayList<Listener>(); 199 private GpsEventThread mEventThread; 200 private GpsNetworkThread mNetworkThread; 201 private Object mNetworkThreadLock = new Object(); 202 203 private String mSuplHost; 204 private int mSuplPort; 205 private boolean mSetSuplServer; 206 private String mSuplApn; 207 private int mSuplDataConnectionState; 208 private final ConnectivityManager mConnMgr; 209 210 // Alarms 211 private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP"; 212 private final AlarmManager mAlarmManager; 213 private final PendingIntent mWakeupIntent; 214 215 private final IBatteryStats mBatteryStats; 216 private final SparseIntArray mClientUids = new SparseIntArray(); 217 218 // how often to request NTP time, in milliseconds 219 // current setting 4 hours 220 private static final long NTP_INTERVAL = 4*60*60*1000; 221 // how long to wait if we have a network error in NTP or XTRA downloading 222 // current setting - 5 minutes 223 private static final long RETRY_INTERVAL = 5*60*1000; 224 225 private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() { 226 public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException { 227 if (listener == null) { 228 throw new NullPointerException("listener is null in addGpsStatusListener"); 229 } 230 231 synchronized(mListeners) { 232 IBinder binder = listener.asBinder(); 233 int size = mListeners.size(); 234 for (int i = 0; i < size; i++) { 235 Listener test = mListeners.get(i); 236 if (binder.equals(test.mListener.asBinder())) { 237 // listener already added 238 return; 239 } 240 } 241 242 Listener l = new Listener(listener); 243 binder.linkToDeath(l, 0); 244 mListeners.add(l); 245 } 246 } 247 248 public void removeGpsStatusListener(IGpsStatusListener listener) { 249 if (listener == null) { 250 throw new NullPointerException("listener is null in addGpsStatusListener"); 251 } 252 253 synchronized(mListeners) { 254 IBinder binder = listener.asBinder(); 255 Listener l = null; 256 int size = mListeners.size(); 257 for (int i = 0; i < size && l == null; i++) { 258 Listener test = mListeners.get(i); 259 if (binder.equals(test.mListener.asBinder())) { 260 l = test; 261 } 262 } 263 264 if (l != null) { 265 mListeners.remove(l); 266 binder.unlinkToDeath(l, 0); 267 } 268 } 269 } 270 }; 271 272 public IGpsStatusProvider getGpsStatusProvider() { 273 return mGpsStatusProvider; 274 } 275 276 private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() { 277 @Override public void onReceive(Context context, Intent intent) { 278 String action = intent.getAction(); 279 280 if (action.equals(ALARM_WAKEUP)) { 281 if (DEBUG) Log.d(TAG, "ALARM_WAKEUP"); 282 startNavigating(); 283 } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) { 284 String state = intent.getStringExtra(Phone.STATE_KEY); 285 String apnName = intent.getStringExtra(Phone.DATA_APN_KEY); 286 String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY); 287 288 if (Config.LOGD) { 289 Log.d(TAG, "state: " + state + " apnName: " + apnName + " reason: " + reason); 290 } 291 if ("CONNECTED".equals(state) && apnName != null && apnName.length() > 0) { 292 mSuplApn = apnName; 293 if (mSuplDataConnectionState == SUPL_DATA_CONNECTION_OPENING) { 294 native_supl_data_conn_open(mSuplApn); 295 mSuplDataConnectionState = SUPL_DATA_CONNECTION_OPEN; 296 } 297 } 298 } 299 } 300 }; 301 302 public static boolean isSupported() { 303 return native_is_supported(); 304 } 305 306 public GpsLocationProvider(Context context, ILocationManager locationManager) { 307 mContext = context; 308 mLocationManager = locationManager; 309 310 mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); 311 mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0); 312 313 IntentFilter intentFilter = new IntentFilter(); 314 intentFilter.addAction(ALARM_WAKEUP); 315 intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); 316 context.registerReceiver(mBroadcastReciever, intentFilter); 317 318 mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); 319 320 // Battery statistics service to be notified when GPS turns on or off 321 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo")); 322 323 mProperties = new Properties(); 324 try { 325 File file = new File(PROPERTIES_FILE); 326 FileInputStream stream = new FileInputStream(file); 327 mProperties.load(stream); 328 stream.close(); 329 mNtpServer = mProperties.getProperty("NTP_SERVER", null); 330 mSuplHost = mProperties.getProperty("SUPL_HOST"); 331 String suplPortString = mProperties.getProperty("SUPL_PORT"); 332 if (mSuplHost != null && suplPortString != null) { 333 try { 334 mSuplPort = Integer.parseInt(suplPortString); 335 mSetSuplServer = true; 336 } catch (NumberFormatException e) { 337 Log.e(TAG, "unable to parse SUPL_PORT: " + suplPortString); 338 } 339 } 340 } catch (IOException e) { 341 Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE); 342 } 343 } 344 345 /** 346 * Returns true if the provider requires access to a 347 * data network (e.g., the Internet), false otherwise. 348 */ 349 public boolean requiresNetwork() { 350 // We want updateNetworkState() to get called when the network state changes 351 // for XTRA and NTP time injection support. 352 return (mNtpServer != null || native_supports_xtra() || mSuplHost != null); 353 } 354 355 public void updateNetworkState(int state) { 356 mNetworkAvailable = (state == LocationProvider.AVAILABLE); 357 358 if (Config.LOGD) { 359 Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable")); 360 } 361 362 if (mNetworkAvailable && mNetworkThread != null && mEnabled) { 363 // signal the network thread when the network becomes available 364 mNetworkThread.signal(); 365 } 366 } 367 368 /** 369 * Returns true if the provider requires access to a 370 * satellite-based positioning system (e.g., GPS), false 371 * otherwise. 372 */ 373 public boolean requiresSatellite() { 374 return true; 375 } 376 377 /** 378 * Returns true if the provider requires access to an appropriate 379 * cellular network (e.g., to make use of cell tower IDs), false 380 * otherwise. 381 */ 382 public boolean requiresCell() { 383 return false; 384 } 385 386 /** 387 * Returns true if the use of this provider may result in a 388 * monetary charge to the user, false if use is free. It is up to 389 * each provider to give accurate information. 390 */ 391 public boolean hasMonetaryCost() { 392 return false; 393 } 394 395 /** 396 * Returns true if the provider is able to provide altitude 397 * information, false otherwise. A provider that reports altitude 398 * under most circumstances but may occassionally not report it 399 * should return true. 400 */ 401 public boolean supportsAltitude() { 402 return true; 403 } 404 405 /** 406 * Returns true if the provider is able to provide speed 407 * information, false otherwise. A provider that reports speed 408 * under most circumstances but may occassionally not report it 409 * should return true. 410 */ 411 public boolean supportsSpeed() { 412 return true; 413 } 414 415 /** 416 * Returns true if the provider is able to provide bearing 417 * information, false otherwise. A provider that reports bearing 418 * under most circumstances but may occassionally not report it 419 * should return true. 420 */ 421 public boolean supportsBearing() { 422 return true; 423 } 424 425 /** 426 * Returns the power requirement for this provider. 427 * 428 * @return the power requirement for this provider, as one of the 429 * constants Criteria.POWER_REQUIREMENT_*. 430 */ 431 public int getPowerRequirement() { 432 return Criteria.POWER_HIGH; 433 } 434 435 /** 436 * Returns the horizontal accuracy of this provider 437 * 438 * @return the accuracy of location from this provider, as one 439 * of the constants Criteria.ACCURACY_*. 440 */ 441 public int getAccuracy() { 442 return Criteria.ACCURACY_FINE; 443 } 444 445 /** 446 * Enables this provider. When enabled, calls to getStatus() 447 * must be handled. Hardware may be started up 448 * when the provider is enabled. 449 */ 450 public synchronized void enable() { 451 if (Config.LOGD) Log.d(TAG, "enable"); 452 if (mEnabled) return; 453 mEnabled = native_init(); 454 455 if (mEnabled) { 456 // run event listener thread while we are enabled 457 mEventThread = new GpsEventThread(); 458 mEventThread.start(); 459 460 if (requiresNetwork()) { 461 // run network thread for NTP and XTRA support 462 if (mNetworkThread == null) { 463 mNetworkThread = new GpsNetworkThread(); 464 mNetworkThread.start(); 465 } else { 466 mNetworkThread.signal(); 467 } 468 } 469 } else { 470 Log.w(TAG, "Failed to enable location provider"); 471 } 472 } 473 474 /** 475 * Disables this provider. When disabled, calls to getStatus() 476 * need not be handled. Hardware may be shut 477 * down while the provider is disabled. 478 */ 479 public synchronized void disable() { 480 if (Config.LOGD) Log.d(TAG, "disable"); 481 if (!mEnabled) return; 482 483 mEnabled = false; 484 stopNavigating(); 485 native_disable(); 486 487 // make sure our event thread exits 488 if (mEventThread != null) { 489 try { 490 mEventThread.join(); 491 } catch (InterruptedException e) { 492 Log.w(TAG, "InterruptedException when joining mEventThread"); 493 } 494 mEventThread = null; 495 } 496 497 if (mNetworkThread != null) { 498 mNetworkThread.setDone(); 499 mNetworkThread = null; 500 } 501 502 // The GpsEventThread does not wait for the GPS to shutdown 503 // so we need to report the GPS_STATUS_ENGINE_OFF event here 504 if (mNavigating) { 505 reportStatus(GPS_STATUS_ENGINE_OFF); 506 } 507 508 native_cleanup(); 509 } 510 511 public boolean isEnabled() { 512 return mEnabled; 513 } 514 515 public int getStatus(Bundle extras) { 516 if (extras != null) { 517 extras.putInt("satellites", mSvCount); 518 } 519 return mStatus; 520 } 521 522 private void updateStatus(int status, int svCount) { 523 if (status != mStatus || svCount != mSvCount) { 524 mStatus = status; 525 mSvCount = svCount; 526 mLocationExtras.putInt("satellites", svCount); 527 mStatusUpdateTime = SystemClock.elapsedRealtime(); 528 } 529 } 530 531 public long getStatusUpdateTime() { 532 return mStatusUpdateTime; 533 } 534 535 public void enableLocationTracking(boolean enable) { 536 if (enable) { 537 mTTFF = 0; 538 mLastFixTime = 0; 539 startNavigating(); 540 } else { 541 mAlarmManager.cancel(mWakeupIntent); 542 stopNavigating(); 543 } 544 } 545 546 public void setMinTime(long minTime) { 547 if (Config.LOGD) Log.d(TAG, "setMinTime " + minTime); 548 549 if (minTime >= 0) { 550 int interval = (int)(minTime/1000); 551 if (interval < 1) { 552 interval = 1; 553 } 554 mFixInterval = interval; 555 native_set_fix_frequency(mFixInterval); 556 } 557 } 558 559 private final class Listener implements IBinder.DeathRecipient { 560 final IGpsStatusListener mListener; 561 562 int mSensors = 0; 563 564 Listener(IGpsStatusListener listener) { 565 mListener = listener; 566 } 567 568 public void binderDied() { 569 if (Config.LOGD) Log.d(TAG, "GPS status listener died"); 570 571 synchronized(mListeners) { 572 mListeners.remove(this); 573 } 574 } 575 } 576 577 public void wakeLockAcquired() { 578 } 579 580 public void wakeLockReleased() { 581 } 582 583 public void addListener(int uid) { 584 mClientUids.put(uid, 0); 585 if (mNavigating) { 586 try { 587 mBatteryStats.noteStartGps(uid); 588 } catch (RemoteException e) { 589 Log.w(TAG, "RemoteException in addListener"); 590 } 591 } 592 } 593 594 public void removeListener(int uid) { 595 mClientUids.delete(uid); 596 if (mNavigating) { 597 try { 598 mBatteryStats.noteStopGps(uid); 599 } catch (RemoteException e) { 600 Log.w(TAG, "RemoteException in removeListener"); 601 } 602 } 603 } 604 605 public boolean sendExtraCommand(String command, Bundle extras) { 606 607 if ("delete_aiding_data".equals(command)) { 608 return deleteAidingData(extras); 609 } 610 611 Log.w(TAG, "sendExtraCommand: unknown command " + command); 612 return false; 613 } 614 615 private boolean deleteAidingData(Bundle extras) { 616 int flags; 617 618 if (extras == null) { 619 flags = GPS_DELETE_ALL; 620 } else { 621 flags = 0; 622 if (extras.getBoolean("ephemeris")) flags |= GPS_DELETE_EPHEMERIS; 623 if (extras.getBoolean("almanac")) flags |= GPS_DELETE_ALMANAC; 624 if (extras.getBoolean("position")) flags |= GPS_DELETE_POSITION; 625 if (extras.getBoolean("time")) flags |= GPS_DELETE_TIME; 626 if (extras.getBoolean("iono")) flags |= GPS_DELETE_IONO; 627 if (extras.getBoolean("utc")) flags |= GPS_DELETE_UTC; 628 if (extras.getBoolean("health")) flags |= GPS_DELETE_HEALTH; 629 if (extras.getBoolean("svdir")) flags |= GPS_DELETE_SVDIR; 630 if (extras.getBoolean("svsteer")) flags |= GPS_DELETE_SVSTEER; 631 if (extras.getBoolean("sadata")) flags |= GPS_DELETE_SADATA; 632 if (extras.getBoolean("rti")) flags |= GPS_DELETE_RTI; 633 if (extras.getBoolean("celldb-info")) flags |= GPS_DELETE_CELLDB_INFO; 634 if (extras.getBoolean("all")) flags |= GPS_DELETE_ALL; 635 } 636 637 if (flags != 0) { 638 native_delete_aiding_data(flags); 639 return true; 640 } 641 642 return false; 643 } 644 645 public void startNavigating() { 646 if (!mStarted) { 647 if (DEBUG) Log.d(TAG, "startNavigating"); 648 mStarted = true; 649 if (!native_start(mPositionMode, false, mFixInterval)) { 650 mStarted = false; 651 Log.e(TAG, "native_start failed in startNavigating()"); 652 } 653 654 // reset SV count to zero 655 updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0); 656 mFixCount = 0; 657 mFixRequestTime = System.currentTimeMillis(); 658 } 659 } 660 661 public void stopNavigating() { 662 if (DEBUG) Log.d(TAG, "stopNavigating"); 663 if (mStarted) { 664 mStarted = false; 665 native_stop(); 666 mTTFF = 0; 667 mLastFixTime = 0; 668 mLocationFlags = LOCATION_INVALID; 669 670 // reset SV count to zero 671 updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, 0); 672 } 673 } 674 675 /** 676 * called from native code to update our position. 677 */ 678 private void reportLocation(int flags, double latitude, double longitude, double altitude, 679 float speed, float bearing, float accuracy, long timestamp) { 680 if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude + 681 " timestamp: " + timestamp); 682 683 mLastFixTime = System.currentTimeMillis(); 684 // report time to first fix 685 if (mTTFF == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) { 686 mTTFF = (int)(mLastFixTime - mFixRequestTime); 687 if (Config.LOGD) Log.d(TAG, "TTFF: " + mTTFF); 688 689 // notify status listeners 690 synchronized(mListeners) { 691 int size = mListeners.size(); 692 for (int i = 0; i < size; i++) { 693 Listener listener = mListeners.get(i); 694 try { 695 listener.mListener.onFirstFix(mTTFF); 696 } catch (RemoteException e) { 697 Log.w(TAG, "RemoteException in stopNavigating"); 698 mListeners.remove(listener); 699 // adjust for size of list changing 700 size--; 701 } 702 } 703 } 704 } 705 706 synchronized (mLocation) { 707 mLocationFlags = flags; 708 if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) { 709 mLocation.setLatitude(latitude); 710 mLocation.setLongitude(longitude); 711 mLocation.setTime(timestamp); 712 } 713 if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) { 714 mLocation.setAltitude(altitude); 715 } else { 716 mLocation.removeAltitude(); 717 } 718 if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) { 719 mLocation.setSpeed(speed); 720 } else { 721 mLocation.removeSpeed(); 722 } 723 if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) { 724 mLocation.setBearing(bearing); 725 } else { 726 mLocation.removeBearing(); 727 } 728 if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) { 729 mLocation.setAccuracy(accuracy); 730 } else { 731 mLocation.removeAccuracy(); 732 } 733 734 try { 735 mLocationManager.reportLocation(mLocation); 736 } catch (RemoteException e) { 737 Log.e(TAG, "RemoteException calling reportLocation"); 738 } 739 } 740 741 if (mStarted && mStatus != LocationProvider.AVAILABLE) { 742 // send an intent to notify that the GPS is receiving fixes. 743 Intent intent = new Intent(GPS_FIX_CHANGE_ACTION); 744 intent.putExtra(EXTRA_ENABLED, true); 745 mContext.sendBroadcast(intent); 746 updateStatus(LocationProvider.AVAILABLE, mSvCount); 747 } 748 749 if (mFixCount++ >= MIN_FIX_COUNT && mFixInterval > 1) { 750 if (DEBUG) Log.d(TAG, "exceeded MIN_FIX_COUNT"); 751 stopNavigating(); 752 mFixCount = 0; 753 mAlarmManager.cancel(mWakeupIntent); 754 long now = SystemClock.elapsedRealtime(); 755 mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 756 SystemClock.elapsedRealtime() + mFixInterval * 1000, mWakeupIntent); 757 } 758 } 759 760 /** 761 * called from native code to update our status 762 */ 763 private void reportStatus(int status) { 764 if (VERBOSE) Log.v(TAG, "reportStatus status: " + status); 765 766 boolean wasNavigating = mNavigating; 767 mNavigating = (status == GPS_STATUS_SESSION_BEGIN); 768 769 if (wasNavigating != mNavigating) { 770 synchronized(mListeners) { 771 int size = mListeners.size(); 772 for (int i = 0; i < size; i++) { 773 Listener listener = mListeners.get(i); 774 try { 775 if (mNavigating) { 776 listener.mListener.onGpsStarted(); 777 } else { 778 listener.mListener.onGpsStopped(); 779 } 780 } catch (RemoteException e) { 781 Log.w(TAG, "RemoteException in reportStatus"); 782 mListeners.remove(listener); 783 // adjust for size of list changing 784 size--; 785 } 786 } 787 } 788 789 try { 790 // update battery stats 791 for (int i=mClientUids.size() - 1; i >= 0; i--) { 792 int uid = mClientUids.keyAt(i); 793 if (mNavigating) { 794 mBatteryStats.noteStartGps(uid); 795 } else { 796 mBatteryStats.noteStopGps(uid); 797 } 798 } 799 } catch (RemoteException e) { 800 Log.w(TAG, "RemoteException in reportStatus"); 801 } 802 803 // send an intent to notify that the GPS has been enabled or disabled. 804 Intent intent = new Intent(GPS_ENABLED_CHANGE_ACTION); 805 intent.putExtra(EXTRA_ENABLED, mNavigating); 806 mContext.sendBroadcast(intent); 807 } 808 } 809 810 /** 811 * called from native code to update SV info 812 */ 813 private void reportSvStatus() { 814 815 int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks); 816 817 synchronized(mListeners) { 818 int size = mListeners.size(); 819 for (int i = 0; i < size; i++) { 820 Listener listener = mListeners.get(i); 821 try { 822 listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs, 823 mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK], 824 mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]); 825 } catch (RemoteException e) { 826 Log.w(TAG, "RemoteException in reportSvInfo"); 827 mListeners.remove(listener); 828 // adjust for size of list changing 829 size--; 830 } 831 } 832 } 833 834 if (VERBOSE) { 835 Log.v(TAG, "SV count: " + svCount + 836 " ephemerisMask: " + Integer.toHexString(mSvMasks[EPHEMERIS_MASK]) + 837 " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK])); 838 for (int i = 0; i < svCount; i++) { 839 Log.v(TAG, "sv: " + mSvs[i] + 840 " snr: " + (float)mSnrs[i]/10 + 841 " elev: " + mSvElevations[i] + 842 " azimuth: " + mSvAzimuths[i] + 843 ((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " E") + 844 ((mSvMasks[ALMANAC_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " A") + 845 ((mSvMasks[USED_FOR_FIX_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "" : "U")); 846 } 847 } 848 849 updateStatus(mStatus, svCount); 850 851 if (mNavigating && mStatus == LocationProvider.AVAILABLE && mLastFixTime > 0 && 852 System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) { 853 // send an intent to notify that the GPS is no longer receiving fixes. 854 Intent intent = new Intent(GPS_FIX_CHANGE_ACTION); 855 intent.putExtra(EXTRA_ENABLED, false); 856 mContext.sendBroadcast(intent); 857 updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE, mSvCount); 858 } 859 } 860 861 /** 862 * called from native code to update SUPL status 863 */ 864 private void reportSuplStatus(int status) { 865 switch (status) { 866 case GPS_REQUEST_SUPL_DATA_CONN: 867 int result = mConnMgr.startUsingNetworkFeature( 868 ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL); 869 if (result == Phone.APN_ALREADY_ACTIVE) { 870 native_supl_data_conn_open(mSuplApn); 871 mSuplDataConnectionState = SUPL_DATA_CONNECTION_OPEN; 872 } else if (result == Phone.APN_REQUEST_STARTED) { 873 mSuplDataConnectionState = SUPL_DATA_CONNECTION_OPENING; 874 } else { 875 native_supl_data_conn_failed(); 876 } 877 break; 878 case GPS_RELEASE_SUPL_DATA_CONN: 879 if (mSuplDataConnectionState != SUPL_DATA_CONNECTION_CLOSED) { 880 mConnMgr.stopUsingNetworkFeature( 881 ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL); 882 native_supl_data_conn_closed(); 883 mSuplDataConnectionState = SUPL_DATA_CONNECTION_CLOSED; 884 } 885 break; 886 case GPS_SUPL_DATA_CONNECTED: 887 // Log.d(TAG, "GPS_SUPL_DATA_CONNECTED"); 888 break; 889 case GPS_SUPL_DATA_CONN_DONE: 890 // Log.d(TAG, "GPS_SUPL_DATA_CONN_DONE"); 891 break; 892 case GPS_SUPL_DATA_CONN_FAILED: 893 // Log.d(TAG, "GPS_SUPL_DATA_CONN_FAILED"); 894 break; 895 } 896 } 897 898 private void xtraDownloadRequest() { 899 if (Config.LOGD) Log.d(TAG, "xtraDownloadRequest"); 900 if (mNetworkThread != null) { 901 mNetworkThread.xtraDownloadRequest(); 902 } 903 } 904 905 private class GpsEventThread extends Thread { 906 907 public GpsEventThread() { 908 super("GpsEventThread"); 909 } 910 911 public void run() { 912 if (Config.LOGD) Log.d(TAG, "GpsEventThread starting"); 913 // Exit as soon as disable() is called instead of waiting for the GPS to stop. 914 while (mEnabled) { 915 // this will wait for an event from the GPS, 916 // which will be reported via reportLocation or reportStatus 917 native_wait_for_event(); 918 } 919 if (Config.LOGD) Log.d(TAG, "GpsEventThread exiting"); 920 } 921 } 922 923 private class GpsNetworkThread extends Thread { 924 925 private long mNextNtpTime = 0; 926 private long mNextXtraTime = 0; 927 private boolean mXtraDownloadRequested = false; 928 private boolean mDone = false; 929 930 public GpsNetworkThread() { 931 super("GpsNetworkThread"); 932 } 933 934 public void run() { 935 synchronized (mNetworkThreadLock) { 936 if (!mDone) { 937 runLocked(); 938 } 939 } 940 } 941 942 public void runLocked() { 943 if (Config.LOGD) Log.d(TAG, "NetworkThread starting"); 944 945 SntpClient client = new SntpClient(); 946 GpsXtraDownloader xtraDownloader = null; 947 948 if (native_supports_xtra()) { 949 xtraDownloader = new GpsXtraDownloader(mContext, mProperties); 950 } 951 952 // thread exits after disable() is called 953 while (!mDone) { 954 long waitTime = getWaitTime(); 955 do { 956 synchronized (this) { 957 try { 958 if (!mNetworkAvailable) { 959 if (Config.LOGD) Log.d(TAG, 960 "NetworkThread wait for network"); 961 wait(); 962 } else if (waitTime > 0) { 963 if (Config.LOGD) { 964 Log.d(TAG, "NetworkThread wait for " + 965 waitTime + "ms"); 966 } 967 wait(waitTime); 968 } 969 } catch (InterruptedException e) { 970 if (Config.LOGD) { 971 Log.d(TAG, "InterruptedException in GpsNetworkThread"); 972 } 973 } 974 } 975 waitTime = getWaitTime(); 976 } while (!mDone && ((!mXtraDownloadRequested && !mSetSuplServer && waitTime > 0) 977 || !mNetworkAvailable)); 978 if (Config.LOGD) Log.d(TAG, "NetworkThread out of wake loop"); 979 980 if (!mDone) { 981 if (mNtpServer != null && 982 mNextNtpTime <= System.currentTimeMillis()) { 983 if (Config.LOGD) { 984 Log.d(TAG, "Requesting time from NTP server " + mNtpServer); 985 } 986 if (client.requestTime(mNtpServer, 10000)) { 987 long time = client.getNtpTime(); 988 long timeReference = client.getNtpTimeReference(); 989 int certainty = (int)(client.getRoundTripTime()/2); 990 991 if (Config.LOGD) Log.d(TAG, "calling native_inject_time: " + 992 time + " reference: " + timeReference 993 + " certainty: " + certainty); 994 995 native_inject_time(time, timeReference, certainty); 996 mNextNtpTime = System.currentTimeMillis() + NTP_INTERVAL; 997 } else { 998 if (Config.LOGD) Log.d(TAG, "requestTime failed"); 999 mNextNtpTime = System.currentTimeMillis() + RETRY_INTERVAL; 1000 } 1001 } 1002 1003 // Set the SUPL server address if we have not yet 1004 if (mSetSuplServer) { 1005 try { 1006 InetAddress inetAddress = InetAddress.getByName(mSuplHost); 1007 if (inetAddress != null) { 1008 byte[] addrBytes = inetAddress.getAddress(); 1009 long addr = 0; 1010 for (int i = 0; i < addrBytes.length; i++) { 1011 int temp = addrBytes[i]; 1012 // signed -> unsigned 1013 if (temp < 0) temp = 256 + temp; 1014 addr = addr * 256 + temp; 1015 } 1016 // use MS-Based position mode if SUPL support is enabled 1017 mPositionMode = GPS_POSITION_MODE_MS_BASED; 1018 native_set_supl_server((int)addr, mSuplPort); 1019 mSetSuplServer = false; 1020 } 1021 } catch (UnknownHostException e) { 1022 Log.e(TAG, "unknown host for SUPL server " + mSuplHost); 1023 } 1024 } 1025 1026 if ((mXtraDownloadRequested || 1027 (mNextXtraTime > 0 && mNextXtraTime <= System.currentTimeMillis())) 1028 && xtraDownloader != null) { 1029 byte[] data = xtraDownloader.downloadXtraData(); 1030 if (data != null) { 1031 if (Config.LOGD) { 1032 Log.d(TAG, "calling native_inject_xtra_data"); 1033 } 1034 native_inject_xtra_data(data, data.length); 1035 mNextXtraTime = 0; 1036 mXtraDownloadRequested = false; 1037 } else { 1038 mNextXtraTime = System.currentTimeMillis() + RETRY_INTERVAL; 1039 } 1040 } 1041 } 1042 } 1043 if (Config.LOGD) Log.d(TAG, "NetworkThread exiting"); 1044 } 1045 1046 synchronized void xtraDownloadRequest() { 1047 mXtraDownloadRequested = true; 1048 notify(); 1049 } 1050 1051 synchronized void signal() { 1052 notify(); 1053 } 1054 1055 synchronized void setDone() { 1056 if (Config.LOGD) Log.d(TAG, "stopping NetworkThread"); 1057 mDone = true; 1058 notify(); 1059 } 1060 1061 private long getWaitTime() { 1062 long now = System.currentTimeMillis(); 1063 long waitTime = Long.MAX_VALUE; 1064 if (mNtpServer != null) { 1065 waitTime = mNextNtpTime - now; 1066 } 1067 if (mNextXtraTime != 0) { 1068 long xtraWaitTime = mNextXtraTime - now; 1069 if (xtraWaitTime < waitTime) { 1070 waitTime = xtraWaitTime; 1071 } 1072 } 1073 if (waitTime < 0) { 1074 waitTime = 0; 1075 } 1076 return waitTime; 1077 } 1078 } 1079 1080 // for GPS SV statistics 1081 private static final int MAX_SVS = 32; 1082 private static final int EPHEMERIS_MASK = 0; 1083 private static final int ALMANAC_MASK = 1; 1084 private static final int USED_FOR_FIX_MASK = 2; 1085 1086 // preallocated arrays, to avoid memory allocation in reportStatus() 1087 private int mSvs[] = new int[MAX_SVS]; 1088 private float mSnrs[] = new float[MAX_SVS]; 1089 private float mSvElevations[] = new float[MAX_SVS]; 1090 private float mSvAzimuths[] = new float[MAX_SVS]; 1091 private int mSvMasks[] = new int[3]; 1092 private int mSvCount; 1093 1094 static { class_init_native(); } 1095 private static native void class_init_native(); 1096 private static native boolean native_is_supported(); 1097 1098 private native boolean native_init(); 1099 private native void native_disable(); 1100 private native void native_cleanup(); 1101 private native boolean native_start(int positionMode, boolean singleFix, int fixInterval); 1102 private native boolean native_stop(); 1103 private native void native_set_fix_frequency(int fixFrequency); 1104 private native void native_delete_aiding_data(int flags); 1105 private native void native_wait_for_event(); 1106 // returns number of SVs 1107 // mask[0] is ephemeris mask and mask[1] is almanac mask 1108 private native int native_read_sv_status(int[] svs, float[] snrs, 1109 float[] elevations, float[] azimuths, int[] masks); 1110 1111 // XTRA Support 1112 private native void native_inject_time(long time, long timeReference, int uncertainty); 1113 private native boolean native_supports_xtra(); 1114 private native void native_inject_xtra_data(byte[] data, int length); 1115 1116 // SUPL Support 1117 private native void native_supl_data_conn_open(String apn); 1118 private native void native_supl_data_conn_closed(); 1119 private native void native_supl_data_conn_failed(); 1120 private native void native_set_supl_server(int addr, int port); 1121} 1122