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