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