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