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