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