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