diff options
Diffstat (limited to 'location/java')
18 files changed, 1424 insertions, 872 deletions
diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java index 2ce1273..c325b1b 100644 --- a/location/java/android/location/Geocoder.java +++ b/location/java/android/location/Geocoder.java @@ -45,10 +45,7 @@ import java.util.List; public final class Geocoder { private static final String TAG = "Geocoder"; - private String mLanguage; - private String mCountry; - private String mVariant; - private String mAppName; + private GeocoderParams mParams; private ILocationManager mService; /** @@ -64,11 +61,7 @@ public final class Geocoder { if (locale == null) { throw new NullPointerException("locale == null"); } - mLanguage = locale.getLanguage(); - mCountry = locale.getCountry(); - mVariant = locale.getVariant(); - mAppName = context.getPackageName(); - + mParams = new GeocoderParams(context, locale); IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE); mService = ILocationManager.Stub.asInterface(b); } @@ -119,7 +112,7 @@ public final class Geocoder { try { List<Address> results = new ArrayList<Address>(); String ex = mService.getFromLocation(latitude, longitude, maxResults, - mLanguage, mCountry, mVariant, mAppName, results); + mParams, results); if (ex != null) { throw new IOException(ex); } else { @@ -161,7 +154,7 @@ public final class Geocoder { try { List<Address> results = new ArrayList<Address>(); String ex = mService.getFromLocationName(locationName, - 0, 0, 0, 0, maxResults, mLanguage, mCountry, mVariant, mAppName, results); + 0, 0, 0, 0, maxResults, mParams, results); if (ex != null) { throw new IOException(ex); } else { @@ -234,7 +227,7 @@ public final class Geocoder { ArrayList<Address> result = new ArrayList<Address>(); String ex = mService.getFromLocationName(locationName, lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude, - maxResults, mLanguage, mCountry, mVariant, mAppName, result); + maxResults, mParams, result); if (ex != null) { throw new IOException(ex); } else { diff --git a/location/java/android/location/GeocoderParams.aidl b/location/java/android/location/GeocoderParams.aidl new file mode 100644 index 0000000..2484e20 --- /dev/null +++ b/location/java/android/location/GeocoderParams.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2010, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +parcelable GeocoderParams; diff --git a/location/java/android/location/GeocoderParams.java b/location/java/android/location/GeocoderParams.java new file mode 100644 index 0000000..174fe3e --- /dev/null +++ b/location/java/android/location/GeocoderParams.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import android.content.Context; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Locale; + +/** + * This class contains extra parameters to pass to an IGeocodeProvider + * implementation from the Geocoder class. Currently this contains the + * language, country and variant information from the Geocoder's locale + * as well as the Geocoder client's package name for geocoder server + * logging. This information is kept in a separate class to allow for + * future expansion of the IGeocodeProvider interface. + * + * @hide + */ +public class GeocoderParams implements Parcelable { + private Locale mLocale; + private String mPackageName; + + // used only for parcelling + private GeocoderParams() { + } + + /** + * This object is only constructed by the Geocoder class + * + * @hide + */ + public GeocoderParams(Context context, Locale locale) { + mLocale = locale; + mPackageName = context.getPackageName(); + } + + /** + * returns the Geocoder's locale + */ + public Locale getLocale() { + return mLocale; + } + + /** + * returns the package name of the Geocoder's client + */ + public String getClientPackage() { + return mPackageName; + } + + public static final Parcelable.Creator<GeocoderParams> CREATOR = + new Parcelable.Creator<GeocoderParams>() { + public GeocoderParams createFromParcel(Parcel in) { + GeocoderParams gp = new GeocoderParams(); + String language = in.readString(); + String country = in.readString(); + String variant = in.readString(); + gp.mLocale = new Locale(language, country, variant); + gp.mPackageName = in.readString(); + return gp; + } + + public GeocoderParams[] newArray(int size) { + return new GeocoderParams[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeString(mLocale.getLanguage()); + parcel.writeString(mLocale.getCountry()); + parcel.writeString(mLocale.getVariant()); + parcel.writeString(mPackageName); + } +} diff --git a/location/java/android/location/IGeocodeProvider.aidl b/location/java/android/location/IGeocodeProvider.aidl index e79e8d2..aaa70c7 100644 --- a/location/java/android/location/IGeocodeProvider.aidl +++ b/location/java/android/location/IGeocodeProvider.aidl @@ -17,6 +17,7 @@ package android.location; import android.location.Address; +import android.location.GeocoderParams; /** * An interface for location providers implementing the Geocoder services. @@ -26,10 +27,10 @@ import android.location.Address; interface IGeocodeProvider { String getFromLocation(double latitude, double longitude, int maxResults, - String language, String country, String variant, String appName, out List<Address> addrs); + in GeocoderParams params, out List<Address> addrs); String getFromLocationName(String locationName, double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude, int maxResults, - String language, String country, String variant, String appName, out List<Address> addrs); + in GeocoderParams params, out List<Address> addrs); } diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index b6c59d6..2c0399e 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -18,10 +18,10 @@ package android.location; import android.app.PendingIntent; import android.location.Address; +import android.location.GeocoderParams; import android.location.IGeocodeProvider; import android.location.IGpsStatusListener; import android.location.ILocationListener; -import android.location.ILocationProvider; import android.location.Location; import android.os.Bundle; @@ -59,15 +59,17 @@ interface ILocationManager Location getLastKnownLocation(String provider); - /* used by location providers to tell the location manager when it has a new location */ - void reportLocation(in Location location); + // Used by location providers to tell the location manager when it has a new location. + // Passive is true if the location is coming from the passive provider, in which case + // it need not be shared with other providers. + void reportLocation(in Location location, boolean passive); String getFromLocation(double latitude, double longitude, int maxResults, - String language, String country, String variant, String appName, out List<Address> addrs); + in GeocoderParams params, out List<Address> addrs); String getFromLocationName(String locationName, double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude, int maxResults, - String language, String country, String variant, String appName, out List<Address> addrs); + in GeocoderParams params, out List<Address> addrs); void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite, boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude, @@ -80,10 +82,6 @@ interface ILocationManager void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime); void clearTestProviderStatus(String provider); - /* for installing external Location Providers */ - void installLocationProvider(String name, ILocationProvider provider); - void installGeocodeProvider(IGeocodeProvider provider); - // for NI support boolean sendNiResponse(int notifId, int userResponse); } diff --git a/location/java/android/location/ILocationProvider.aidl b/location/java/android/location/ILocationProvider.aidl index 7da16e4..97b283c 100644 --- a/location/java/android/location/ILocationProvider.aidl +++ b/location/java/android/location/ILocationProvider.aidl @@ -21,7 +21,7 @@ import android.net.NetworkInfo; import android.os.Bundle; /** - * Binder interface for location providers. + * Binder interface for services that implement location providers. * * {@hide} */ @@ -37,9 +37,9 @@ interface ILocationProvider { int getAccuracy(); void enable(); void disable(); - boolean isEnabled(); int getStatus(out Bundle extras); long getStatusUpdateTime(); + String getInternalState(); void enableLocationTracking(boolean enable); void setMinTime(long minTime); void updateNetworkState(int state, in NetworkInfo info); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 94ced22..9e4a16b 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -23,7 +23,6 @@ import android.os.Looper; import android.os.RemoteException; import android.os.Handler; import android.os.Message; -import android.util.Config; import android.util.Log; import com.android.internal.location.DummyLocationProvider; @@ -82,6 +81,19 @@ public class LocationManager { public static final String GPS_PROVIDER = "gps"; /** + * A special location provider for receiving locations without actually initiating + * a location fix. This provider can be used to passively receive location updates + * when other applications or services request them without actually requesting + * the locations yourself. This provider will return locations generated by other + * providers. You can query the {@link Location#getProvider()} method to determine + * the origin of the location update. + * + * Requires the permission android.permission.ACCESS_FINE_LOCATION, although if the GPS + * is not enabled this provider might only return coarse fixes. + */ + public static final String PASSIVE_PROVIDER = "passive"; + + /** * Key used for the Bundle extra holding a boolean indicating whether * a proximity alert is entering (true) or exiting (false).. */ @@ -206,7 +218,7 @@ public class LocationManager { * factory Context.getSystemService. */ public LocationManager(ILocationManager service) { - if (Config.LOGD) { + if (false) { Log.d(TAG, "Constructor: service = " + service); } mService = service; @@ -235,7 +247,7 @@ public class LocationManager { * @return list of Strings containing names of the providers */ public List<String> getAllProviders() { - if (Config.LOGD) { + if (false) { Log.d(TAG, "getAllProviders"); } try { @@ -305,7 +317,7 @@ public class LocationManager { List<String> providers = getProviders(enabledOnly); for (String providerName : providers) { LocationProvider provider = getProvider(providerName); - if (provider.meetsCriteria(criteria)) { + if (provider != null && provider.meetsCriteria(criteria)) { if (goodProviders.isEmpty()) { goodProviders = new ArrayList<String>(); } @@ -787,7 +799,7 @@ public class LocationManager { if (listener == null) { throw new IllegalArgumentException("listener==null"); } - if (Config.LOGD) { + if (false) { Log.d(TAG, "removeUpdates: listener = " + listener); } try { @@ -812,7 +824,7 @@ public class LocationManager { if (intent == null) { throw new IllegalArgumentException("intent==null"); } - if (Config.LOGD) { + if (false) { Log.d(TAG, "removeUpdates: intent = " + intent); } try { @@ -867,7 +879,7 @@ public class LocationManager { */ public void addProximityAlert(double latitude, double longitude, float radius, long expiration, PendingIntent intent) { - if (Config.LOGD) { + if (false) { Log.d(TAG, "addProximityAlert: latitude = " + latitude + ", longitude = " + longitude + ", radius = " + radius + ", expiration = " + expiration + @@ -888,7 +900,7 @@ public class LocationManager { * proximity alerts */ public void removeProximityAlert(PendingIntent intent) { - if (Config.LOGD) { + if (false) { Log.d(TAG, "removeProximityAlert: intent = " + intent); } try { @@ -1354,66 +1366,6 @@ public class LocationManager { return false; } } - - /** - * Installs a location provider. - * - * @param name of the location provider - * @param provider Binder interface for the location provider - * - * @return true if the command succeeds. - * - * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission. - * - * {@hide} - */ - public boolean installLocationProvider(String name, ILocationProvider provider) { - try { - mService.installLocationProvider(name, provider); - return true; - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in installLocationProvider: ", e); - return false; - } - } - - /** - * Installs a geocoder server. - * - * @param provider Binder interface for the geocoder provider - * - * @return true if the command succeeds. - * - * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission. - * - * {@hide} - */ - public boolean installGeocodeProvider(IGeocodeProvider provider) { - try { - mService.installGeocodeProvider(provider); - return true; - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in setGeocodeProvider: ", e); - return false; - } - } - - /** - * Used by location providers to report new locations. - * - * @param location new Location to report - * - * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission. - * - * {@hide} - */ - public void reportLocation(Location location) { - try { - mService.reportLocation(location); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in reportLocation: ", e); - } - } /** * Used by NetInitiatedActivity to report user response diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java index 3faba58..bb3e2a5 100644 --- a/location/java/android/location/LocationProvider.java +++ b/location/java/android/location/LocationProvider.java @@ -71,6 +71,10 @@ public abstract class LocationProvider { * false otherwise. */ public boolean meetsCriteria(Criteria criteria) { + // We do not want to match the special passive provider based on criteria. + if (LocationManager.PASSIVE_PROVIDER.equals(mName)) { + return false; + } if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) && (criteria.getAccuracy() < getAccuracy())) { return false; diff --git a/location/java/android/location/LocationProviderInterface.java b/location/java/android/location/LocationProviderInterface.java new file mode 100644 index 0000000..5ffe15c --- /dev/null +++ b/location/java/android/location/LocationProviderInterface.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import android.location.Location; +import android.net.NetworkInfo; +import android.os.Bundle; + +/** + * Location Manager's interface for location providers. + * + * {@hide} + */ +public interface LocationProviderInterface { + String getName(); + boolean requiresNetwork(); + boolean requiresSatellite(); + boolean requiresCell(); + boolean hasMonetaryCost(); + boolean supportsAltitude(); + boolean supportsSpeed(); + boolean supportsBearing(); + int getPowerRequirement(); + int getAccuracy(); + boolean isEnabled(); + void enable(); + void disable(); + int getStatus(Bundle extras); + long getStatusUpdateTime(); + void enableLocationTracking(boolean enable); + String getInternalState(); + void setMinTime(long minTime); + void updateNetworkState(int state, NetworkInfo info); + void updateLocation(Location location); + boolean sendExtraCommand(String command, Bundle extras); + void addListener(int uid); + void removeListener(int uid); +} diff --git a/location/java/android/location/provider/GeocodeProvider.java b/location/java/android/location/provider/GeocodeProvider.java new file mode 100644 index 0000000..86376a7 --- /dev/null +++ b/location/java/android/location/provider/GeocodeProvider.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location.provider; + +import android.os.IBinder; + +import android.location.Address; +import android.location.GeocoderParams; +import android.location.IGeocodeProvider; + +import java.util.List; + +/** + * An abstract superclass for geocode providers that are implemented + * outside of the core android platform. + * Geocode providers can be implemented as services and return the result of + * {@link GeocodeProvider#getBinder()} in its getBinder() method. + * + * @hide + */ +public abstract class GeocodeProvider { + + private IGeocodeProvider.Stub mProvider = new IGeocodeProvider.Stub() { + public String getFromLocation(double latitude, double longitude, int maxResults, + GeocoderParams params, List<Address> addrs) { + return GeocodeProvider.this.onGetFromLocation(latitude, longitude, maxResults, + params, addrs); + } + + public String getFromLocationName(String locationName, + double lowerLeftLatitude, double lowerLeftLongitude, + double upperRightLatitude, double upperRightLongitude, int maxResults, + GeocoderParams params, List<Address> addrs) { + return GeocodeProvider.this.onGetFromLocationName(locationName, lowerLeftLatitude, + lowerLeftLongitude, upperRightLatitude, upperRightLongitude, + maxResults, params, addrs); + } + }; + + /** + * This method is overridden to implement the + * {@link Geocoder#getFromLocation(double, double, int)} method. + * Classes implementing this method should not hold a reference to the params parameter. + */ + public abstract String onGetFromLocation(double latitude, double longitude, int maxResults, + GeocoderParams params, List<Address> addrs); + + /** + * This method is overridden to implement the + * {@link Geocoder#getFromLocationName(String, int, double, double, double, double)} method. + * Classes implementing this method should not hold a reference to the params parameter. + */ + public abstract String onGetFromLocationName(String locationName, + double lowerLeftLatitude, double lowerLeftLongitude, + double upperRightLatitude, double upperRightLongitude, int maxResults, + GeocoderParams params, List<Address> addrs); + + /** + * Returns the Binder interface for the geocode provider. + * This is intended to be used for the onBind() method of + * a service that implements a geocoder service. + * + * @return the IBinder instance for the provider + */ + public IBinder getBinder() { + return mProvider; + } +} + diff --git a/location/java/android/location/provider/LocationProvider.java b/location/java/android/location/provider/LocationProvider.java new file mode 100644 index 0000000..56cfb33 --- /dev/null +++ b/location/java/android/location/provider/LocationProvider.java @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location.provider; + +import android.content.Context; +import android.net.NetworkInfo; +import android.location.ILocationManager; +import android.location.ILocationProvider; +import android.location.Location; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +/** + * An abstract superclass for location providers that are implemented + * outside of the core android platform. + * Location providers can be implemented as services and return the result of + * {@link LocationProvider#getBinder()} in its getBinder() method. + * + * @hide + */ +public abstract class LocationProvider { + + private static final String TAG = "LocationProvider"; + + private ILocationManager mLocationManager; + + private ILocationProvider.Stub mProvider = new ILocationProvider.Stub() { + + public boolean requiresNetwork() { + return LocationProvider.this.onRequiresNetwork(); + } + + public boolean requiresSatellite() { + return LocationProvider.this.onRequiresSatellite(); + } + + public boolean requiresCell() { + return LocationProvider.this.onRequiresCell(); + } + + public boolean hasMonetaryCost() { + return LocationProvider.this.onHasMonetaryCost(); + } + + public boolean supportsAltitude() { + return LocationProvider.this.onSupportsAltitude(); + } + + public boolean supportsSpeed() { + return LocationProvider.this.onSupportsSpeed(); + } + + public boolean supportsBearing() { + return LocationProvider.this.onSupportsBearing(); + } + + public int getPowerRequirement() { + return LocationProvider.this.onGetPowerRequirement(); + } + + public int getAccuracy() { + return LocationProvider.this.onGetAccuracy(); + } + + public void enable() { + LocationProvider.this.onEnable(); + } + + public void disable() { + LocationProvider.this.onDisable(); + } + + public int getStatus(Bundle extras) { + return LocationProvider.this.onGetStatus(extras); + } + + public long getStatusUpdateTime() { + return LocationProvider.this.onGetStatusUpdateTime(); + } + + public String getInternalState() { + return LocationProvider.this.onGetInternalState(); + } + + public void enableLocationTracking(boolean enable) { + LocationProvider.this.onEnableLocationTracking(enable); + } + + public void setMinTime(long minTime) { + LocationProvider.this.onSetMinTime(minTime); + } + + public void updateNetworkState(int state, NetworkInfo info) { + LocationProvider.this.onUpdateNetworkState(state, info); + } + + public void updateLocation(Location location) { + LocationProvider.this.onUpdateLocation(location); + } + + public boolean sendExtraCommand(String command, Bundle extras) { + return LocationProvider.this.onSendExtraCommand(command, extras); + } + + public void addListener(int uid) { + LocationProvider.this.onAddListener(uid); + } + + public void removeListener(int uid) { + LocationProvider.this.onRemoveListener(uid); + } + }; + + public LocationProvider() { + IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE); + mLocationManager = ILocationManager.Stub.asInterface(b); + } + + /** + * {@hide} + */ + /* package */ ILocationProvider getInterface() { + return mProvider; + } + + /** + * Returns the Binder interface for the location provider. + * This is intended to be used for the onBind() method of + * a service that implements a location provider service. + * + * @return the IBinder instance for the provider + */ + public IBinder getBinder() { + return mProvider; + } + + /** + * Used by the location provider to report new locations. + * + * @param location new Location to report + * + * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission. + */ + public void reportLocation(Location location) { + try { + mLocationManager.reportLocation(location, false); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in reportLocation: ", e); + } + } + + /** + * Returns true if the provider requires access to a + * data network (e.g., the Internet), false otherwise. + */ + public abstract boolean onRequiresNetwork(); + + /** + * Returns true if the provider requires access to a + * satellite-based positioning system (e.g., GPS), false + * otherwise. + */ + public abstract boolean onRequiresSatellite(); + + /** + * Returns true if the provider requires access to an appropriate + * cellular network (e.g., to make use of cell tower IDs), false + * otherwise. + */ + public abstract boolean onRequiresCell(); + + /** + * Returns true if the use of this provider may result in a + * monetary charge to the user, false if use is free. It is up to + * each provider to give accurate information. + */ + public abstract boolean onHasMonetaryCost(); + + /** + * Returns true if the provider is able to provide altitude + * information, false otherwise. A provider that reports altitude + * under most circumstances but may occassionally not report it + * should return true. + */ + public abstract boolean onSupportsAltitude(); + + /** + * Returns true if the provider is able to provide speed + * information, false otherwise. A provider that reports speed + * under most circumstances but may occassionally not report it + * should return true. + */ + public abstract boolean onSupportsSpeed(); + + /** + * Returns true if the provider is able to provide bearing + * information, false otherwise. A provider that reports bearing + * under most circumstances but may occassionally not report it + * should return true. + */ + public abstract boolean onSupportsBearing(); + + /** + * Returns the power requirement for this provider. + * + * @return the power requirement for this provider, as one of the + * constants Criteria.POWER_REQUIREMENT_*. + */ + public abstract int onGetPowerRequirement(); + + /** + * Returns a constant describing horizontal accuracy of this provider. + * If the provider returns finer grain or exact location, + * {@link Criteria#ACCURACY_FINE} is returned, otherwise if the + * location is only approximate then {@link Criteria#ACCURACY_COARSE} + * is returned. + */ + public abstract int onGetAccuracy(); + + /** + * Enables the location provider + */ + public abstract void onEnable(); + + /** + * Disables the location provider + */ + public abstract void onDisable(); + + /** + * Returns a information on the status of this provider. + * {@link #OUT_OF_SERVICE} is returned if the provider is + * out of service, and this is not expected to change in the near + * future; {@link #TEMPORARILY_UNAVAILABLE} is returned if + * the provider is temporarily unavailable but is expected to be + * available shortly; and {@link #AVAILABLE} is returned + * if the provider is currently available. + * + * <p> If extras is non-null, additional status information may be + * added to it in the form of provider-specific key/value pairs. + */ + public abstract int onGetStatus(Bundle extras); + + /** + * Returns the time at which the status was last updated. It is the + * responsibility of the provider to appropriately set this value using + * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()}. + * there is a status update that it wishes to broadcast to all its + * listeners. The provider should be careful not to broadcast + * the same status again. + * + * @return time of last status update in millis since last reboot + */ + public abstract long onGetStatusUpdateTime(); + + /** + * Returns debugging information about the location provider. + * + * @return string describing the internal state of the location provider, or null. + */ + public abstract String onGetInternalState(); + + /** + * Notifies the location provider that clients are listening for locations. + * Called with enable set to true when the first client is added and + * called with enable set to false when the last client is removed. + * This allows the provider to prepare for receiving locations, + * and to shut down when no clients are remaining. + * + * @param enable true if location tracking should be enabled. + */ + public abstract void onEnableLocationTracking(boolean enable); + + /** + * Notifies the location provider of the smallest minimum time between updates amongst + * all clients that are listening for locations. This allows the provider to reduce + * the frequency of updates to match the requested frequency. + * + * @param minTime the smallest minTime value over all listeners for this provider. + */ + public abstract void onSetMinTime(long minTime); + + /** + * Updates the network state for the given provider. This function must + * be overwritten if {@link #requiresNetwork} returns true. The state is + * {@link #TEMPORARILY_UNAVAILABLE} (disconnected), OR {@link #AVAILABLE} + * (connected or connecting). + * + * @param state data state + */ + public abstract void onUpdateNetworkState(int state, NetworkInfo info); + + /** + * Informs the provider when a new location has been computed by a different + * location provider. This is intended to be used as aiding data for the + * receiving provider. + * + * @param location new location from other location provider + */ + public abstract void onUpdateLocation(Location location); + + /** + * Implements addditional location provider specific additional commands. + * + * @param command name of the command to send to the provider. + * @param extras optional arguments for the command (or null). + * The provider may optionally fill the extras Bundle with results from the command. + * + * @return true if the command succeeds. + */ + public abstract boolean onSendExtraCommand(String command, Bundle extras); + + /** + * Notifies the location provider when a new client is listening for locations. + * + * @param uid user ID of the new client. + */ + public abstract void onAddListener(int uid); + + /** + * Notifies the location provider when a client is no longer listening for locations. + * + * @param uid user ID of the client no longer listening. + */ + public abstract void onRemoveListener(int uid); +} + diff --git a/location/java/com/android/internal/location/GeocoderProxy.java b/location/java/com/android/internal/location/GeocoderProxy.java new file mode 100644 index 0000000..b06297b --- /dev/null +++ b/location/java/com/android/internal/location/GeocoderProxy.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.location; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.location.Address; +import android.location.GeocoderParams; +import android.location.IGeocodeProvider; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Log; + +import java.util.List; + +/** + * A class for proxying IGeocodeProvider implementations. + * + * {@hide} + */ +public class GeocoderProxy { + + private static final String TAG = "GeocoderProxy"; + + private final Context mContext; + private final Intent mIntent; + private final Connection mServiceConnection = new Connection(); + private IGeocodeProvider mProvider; + + public GeocoderProxy(Context context, String serviceName) { + mContext = context; + mIntent = new Intent(serviceName); + mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE); + } + + private class Connection implements ServiceConnection { + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "onServiceConnected " + className); + synchronized (this) { + mProvider = IGeocodeProvider.Stub.asInterface(service); + } + } + + public void onServiceDisconnected(ComponentName className) { + Log.d(TAG, "onServiceDisconnected " + className); + synchronized (this) { + mProvider = null; + } + } + } + + public String getFromLocation(double latitude, double longitude, int maxResults, + GeocoderParams params, List<Address> addrs) { + IGeocodeProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + return provider.getFromLocation(latitude, longitude, maxResults, + params, addrs); + } catch (RemoteException e) { + Log.e(TAG, "getFromLocation failed", e); + } + } + return "Service not Available"; + } + + public String getFromLocationName(String locationName, + double lowerLeftLatitude, double lowerLeftLongitude, + double upperRightLatitude, double upperRightLongitude, int maxResults, + GeocoderParams params, List<Address> addrs) { + IGeocodeProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + return provider.getFromLocationName(locationName, lowerLeftLatitude, + lowerLeftLongitude, upperRightLatitude, upperRightLongitude, + maxResults, params, addrs); + } catch (RemoteException e) { + Log.e(TAG, "getFromLocationName failed", e); + } + } + return "Service not Available"; + } +} diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java index ed3fdde..fa53ccf 100755 --- a/location/java/com/android/internal/location/GpsLocationProvider.java +++ b/location/java/com/android/internal/location/GpsLocationProvider.java @@ -26,22 +26,25 @@ import android.location.Criteria; import android.location.IGpsStatusListener; import android.location.IGpsStatusProvider; import android.location.ILocationManager; -import android.location.ILocationProvider; import android.location.INetInitiatedListener; import android.location.Location; import android.location.LocationManager; import android.location.LocationProvider; +import android.location.LocationProviderInterface; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.SntpClient; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; import android.os.PowerManager; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.provider.Settings; -import android.util.Config; import android.util.Log; import android.util.SparseIntArray; @@ -60,19 +63,20 @@ import java.util.ArrayList; import java.util.Date; import java.util.Properties; import java.util.Map.Entry; +import java.util.concurrent.CountDownLatch; /** * A GPS implementation of LocationProvider used by LocationManager. * * {@hide} */ -public class GpsLocationProvider extends ILocationProvider.Stub { +public class GpsLocationProvider implements LocationProviderInterface { private static final String TAG = "GpsLocationProvider"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private static final boolean VERBOSE = false; - + /** * Broadcast intent action indicating that the GPS has either been * enabled or disabled. An intent extra provides this state as a boolean, @@ -156,6 +160,17 @@ public class GpsLocationProvider extends ILocationProvider.Stub { private static final int AGPS_DATA_CONNECTION_OPENING = 1; private static final int AGPS_DATA_CONNECTION_OPEN = 2; + // Handler messages + private static final int CHECK_LOCATION = 1; + private static final int ENABLE = 2; + private static final int ENABLE_TRACKING = 3; + private static final int UPDATE_NETWORK_STATE = 4; + private static final int INJECT_NTP_TIME = 5; + private static final int DOWNLOAD_XTRA_DATA = 6; + private static final int UPDATE_LOCATION = 7; + private static final int ADD_LISTENER = 8; + private static final int REMOVE_LISTENER = 9; + private static final String PROPERTIES_FILE = "/etc/gps.conf"; private int mLocationFlags = LOCATION_INVALID; @@ -176,11 +191,16 @@ public class GpsLocationProvider extends ILocationProvider.Stub { private static final int NO_FIX_TIMEOUT = 60; // true if we are enabled - private boolean mEnabled; + private volatile boolean mEnabled; // true if we have network connectivity private boolean mNetworkAvailable; + // flags to trigger NTP or XTRA data download when network becomes available + // initialized to true so we do NTP and XTRA when the network comes up after booting + private boolean mInjectNtpTimePending = true; + private boolean mDownloadXtraDataPending = true; + // true if GPS is navigating private boolean mNavigating; @@ -216,9 +236,15 @@ public class GpsLocationProvider extends ILocationProvider.Stub { private Location mLocation = new Location(LocationManager.GPS_PROVIDER); private Bundle mLocationExtras = new Bundle(); private ArrayList<Listener> mListeners = new ArrayList<Listener>(); - private GpsEventThread mEventThread; - private GpsNetworkThread mNetworkThread; - private Object mNetworkThreadLock = new Object(); + + // GpsLocationProvider's handler thread + private final Thread mThread; + // Handler for processing events in mThread. + private Handler mHandler; + // Used to signal when our main thread has initialized everything + private final CountDownLatch mInitializedLatch = new CountDownLatch(1); + // Thread for receiving events from the native code + private Thread mEventThread; private String mAGpsApn; private int mAGpsDataConnectionState; @@ -322,7 +348,9 @@ public class GpsLocationProvider extends ILocationProvider.Stub { public GpsLocationProvider(Context context, ILocationManager locationManager) { mContext = context; mLocationManager = locationManager; - mNIHandler= new GpsNetInitiatedHandler(context, this); + mNIHandler = new GpsNetInitiatedHandler(context, this); + + mLocation.setExtras(mLocationExtras); // Create a wake lock PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -332,11 +360,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub { mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0); mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0); - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(ALARM_WAKEUP); - intentFilter.addAction(ALARM_TIMEOUT); - context.registerReceiver(mBroadcastReciever, intentFilter); - mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); // Battery statistics service to be notified when GPS turns on or off @@ -372,6 +395,33 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } catch (IOException e) { Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE); } + + // wait until we are fully initialized before returning + mThread = new GpsLocationProviderThread(); + mThread.start(); + while (true) { + try { + mInitializedLatch.await(); + break; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + private void initialize() { + // register our receiver on our thread rather than the main thread + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ALARM_WAKEUP); + intentFilter.addAction(ALARM_TIMEOUT); + mContext.registerReceiver(mBroadcastReciever, intentFilter); + } + + /** + * Returns the name of this provider. + */ + public String getName() { + return LocationManager.GPS_PROVIDER; } /** @@ -383,9 +433,17 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } public void updateNetworkState(int state, NetworkInfo info) { + mHandler.removeMessages(UPDATE_NETWORK_STATE); + Message m = Message.obtain(mHandler, UPDATE_NETWORK_STATE); + m.arg1 = state; + m.obj = info; + mHandler.sendMessage(m); + } + + private void handleUpdateNetworkState(int state, NetworkInfo info) { mNetworkAvailable = (state == LocationProvider.AVAILABLE); - if (Config.LOGD) { + if (DEBUG) { Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable") + " info: " + info); } @@ -406,10 +464,84 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } } - if (mNetworkAvailable && mNetworkThread != null && mEnabled) { - // signal the network thread when the network becomes available - mNetworkThread.signal(); - } + if (mNetworkAvailable) { + if (mInjectNtpTimePending) { + mHandler.removeMessages(INJECT_NTP_TIME); + mHandler.sendMessage(Message.obtain(mHandler, INJECT_NTP_TIME)); + } + if (mDownloadXtraDataPending) { + mHandler.removeMessages(DOWNLOAD_XTRA_DATA); + mHandler.sendMessage(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA)); + } + } + } + + private void handleInjectNtpTime() { + if (!mNetworkAvailable) { + // try again when network is up + mInjectNtpTimePending = true; + return; + } + mInjectNtpTimePending = false; + + SntpClient client = new SntpClient(); + long delay; + + if (client.requestTime(mNtpServer, 10000)) { + long time = client.getNtpTime(); + long timeReference = client.getNtpTimeReference(); + int certainty = (int)(client.getRoundTripTime()/2); + long now = System.currentTimeMillis(); + long systemTimeOffset = time - now; + + Log.d(TAG, "NTP server returned: " + + time + " (" + new Date(time) + + ") reference: " + timeReference + + " certainty: " + certainty + + " system time offset: " + systemTimeOffset); + + // sanity check NTP time and do not use if it is too far from system time + if (systemTimeOffset < 0) { + systemTimeOffset = -systemTimeOffset; + } + if (systemTimeOffset < MAX_NTP_SYSTEM_TIME_OFFSET) { + native_inject_time(time, timeReference, certainty); + } else { + Log.e(TAG, "NTP time differs from system time by " + systemTimeOffset + + "ms. Ignoring."); + } + delay = NTP_INTERVAL; + } else { + if (DEBUG) Log.d(TAG, "requestTime failed"); + delay = RETRY_INTERVAL; + } + + // send delayed message for next NTP injection + mHandler.removeMessages(INJECT_NTP_TIME); + mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay); + } + + private void handleDownloadXtraData() { + if (!mDownloadXtraDataPending) { + // try again when network is up + mDownloadXtraDataPending = true; + return; + } + mDownloadXtraDataPending = false; + + + GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mContext, mProperties); + byte[] data = xtraDownloader.downloadXtraData(); + if (data != null) { + if (DEBUG) { + Log.d(TAG, "calling native_inject_xtra_data"); + } + native_inject_xtra_data(data, data.length); + } else { + // try again later + mHandler.removeMessages(DOWNLOAD_XTRA_DATA); + mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA), RETRY_INTERVAL); + } } /** @@ -417,6 +549,13 @@ public class GpsLocationProvider extends ILocationProvider.Stub { * Someday we might use this for network location injection to aid the GPS */ public void updateLocation(Location location) { + mHandler.removeMessages(UPDATE_LOCATION); + Message m = Message.obtain(mHandler, UPDATE_LOCATION); + m.obj = location; + mHandler.sendMessage(m); + } + + private void handleUpdateLocation(Location location) { if (location.hasAccuracy()) { native_inject_location(location.getLatitude(), location.getLongitude(), location.getAccuracy()); @@ -505,8 +644,17 @@ public class GpsLocationProvider extends ILocationProvider.Stub { * must be handled. Hardware may be started up * when the provider is enabled. */ - public synchronized void enable() { - if (Config.LOGD) Log.d(TAG, "enable"); + public void enable() { + synchronized (mHandler) { + mHandler.removeMessages(ENABLE); + Message m = Message.obtain(mHandler, ENABLE); + m.arg1 = 1; + mHandler.sendMessage(m); + } + } + + private void handleEnable() { + if (DEBUG) Log.d(TAG, "handleEnable"); if (mEnabled) return; mEnabled = native_init(); @@ -521,16 +669,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub { // run event listener thread while we are enabled mEventThread = new GpsEventThread(); mEventThread.start(); - - if (requiresNetwork()) { - // run network thread for NTP and XTRA support - if (mNetworkThread == null) { - mNetworkThread = new GpsNetworkThread(); - mNetworkThread.start(); - } else { - mNetworkThread.signal(); - } - } } else { Log.w(TAG, "Failed to enable location provider"); } @@ -541,8 +679,17 @@ public class GpsLocationProvider extends ILocationProvider.Stub { * need not be handled. Hardware may be shut * down while the provider is disabled. */ - public synchronized void disable() { - if (Config.LOGD) Log.d(TAG, "disable"); + public void disable() { + synchronized (mHandler) { + mHandler.removeMessages(ENABLE); + Message m = Message.obtain(mHandler, ENABLE); + m.arg1 = 0; + mHandler.sendMessage(m); + } + } + + private void handleDisable() { + if (DEBUG) Log.d(TAG, "handleDisable"); if (!mEnabled) return; mEnabled = false; @@ -559,11 +706,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub { mEventThread = null; } - if (mNetworkThread != null) { - mNetworkThread.setDone(); - mNetworkThread = null; - } - // do this before releasing wakelock native_cleanup(); @@ -602,6 +744,15 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } public void enableLocationTracking(boolean enable) { + synchronized (mHandler) { + mHandler.removeMessages(ENABLE_TRACKING); + Message m = Message.obtain(mHandler, ENABLE_TRACKING); + m.arg1 = (enable ? 1 : 0); + mHandler.sendMessage(m); + } + } + + private void handleEnableLocationTracking(boolean enable) { if (enable) { mTTFF = 0; mLastFixTime = 0; @@ -614,7 +765,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } public void setMinTime(long minTime) { - if (Config.LOGD) Log.d(TAG, "setMinTime " + minTime); + if (DEBUG) Log.d(TAG, "setMinTime " + minTime); if (minTime >= 0) { int interval = (int)(minTime/1000); @@ -625,6 +776,10 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } } + public String getInternalState() { + return native_get_internal_state(); + } + private final class Listener implements IBinder.DeathRecipient { final IGpsStatusListener mListener; @@ -635,7 +790,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } public void binderDied() { - if (Config.LOGD) Log.d(TAG, "GPS status listener died"); + if (DEBUG) Log.d(TAG, "GPS status listener died"); synchronized(mListeners) { mListeners.remove(this); @@ -647,6 +802,12 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } public void addListener(int uid) { + Message m = Message.obtain(mHandler, ADD_LISTENER); + m.arg1 = uid; + mHandler.sendMessage(m); + } + + private void handleAddListener(int uid) { synchronized(mListeners) { if (mClientUids.indexOfKey(uid) >= 0) { // Shouldn't be here -- already have this uid. @@ -665,6 +826,12 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } public void removeListener(int uid) { + Message m = Message.obtain(mHandler, REMOVE_LISTENER); + m.arg1 = uid; + mHandler.sendMessage(m); + } + + private void handleRemoveListener(int uid) { synchronized(mListeners) { if (mClientUids.indexOfKey(uid) < 0) { // Shouldn't be here -- don't have this uid. @@ -688,10 +855,12 @@ public class GpsLocationProvider extends ILocationProvider.Stub { return deleteAidingData(extras); } if ("force_time_injection".equals(command)) { - return forceTimeInjection(); + mHandler.removeMessages(INJECT_NTP_TIME); + mHandler.sendMessage(Message.obtain(mHandler, INJECT_NTP_TIME)); + return true; } if ("force_xtra_injection".equals(command)) { - if (native_supports_xtra() && mNetworkThread != null) { + if (native_supports_xtra()) { xtraDownloadRequest(); return true; } @@ -732,16 +901,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { return false; } - private boolean forceTimeInjection() { - if (Config.LOGD) Log.d(TAG, "forceTimeInjection"); - if (mNetworkThread != null) { - mNetworkThread.timeInjectRequest(); - return true; - } - return false; - } - - public void startNavigating() { + private void startNavigating() { if (!mStarted) { if (DEBUG) Log.d(TAG, "startNavigating"); mStarted = true; @@ -753,7 +913,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { positionMode = GPS_POSITION_MODE_STANDALONE; } - if (!native_start(positionMode, false, mFixInterval)) { + if (!native_start(positionMode, false, 1)) { mStarted = false; Log.e(TAG, "native_start failed in startNavigating()"); return; @@ -772,7 +932,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } } - public void stopNavigating() { + private void stopNavigating() { if (DEBUG) Log.d(TAG, "stopNavigating"); if (mStarted) { mStarted = false; @@ -834,7 +994,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } try { - mLocationManager.reportLocation(mLocation); + mLocationManager.reportLocation(mLocation, false); } catch (RemoteException e) { Log.e(TAG, "RemoteException calling reportLocation"); } @@ -844,7 +1004,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { // report time to first fix if (mTTFF == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) { mTTFF = (int)(mLastFixTime - mFixRequestTime); - if (Config.LOGD) Log.d(TAG, "TTFF: " + mTTFF); + if (DEBUG) Log.d(TAG, "TTFF: " + mTTFF); // notify status listeners synchronized(mListeners) { @@ -852,7 +1012,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { for (int i = 0; i < size; i++) { Listener listener = mListeners.get(i); try { - listener.mListener.onFirstFix(mTTFF); + listener.mListener.onFirstFix(mTTFF); } catch (RemoteException e) { Log.w(TAG, "RemoteException in stopNavigating"); mListeners.remove(listener); @@ -864,7 +1024,12 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } if (mStarted && mStatus != LocationProvider.AVAILABLE) { - mAlarmManager.cancel(mTimeoutIntent); + // we still want to time out if we do not receive MIN_FIX_COUNT + // within the time out and we are requesting infrequent fixes + if (mFixInterval < NO_FIX_TIMEOUT) { + mAlarmManager.cancel(mTimeoutIntent); + } + // send an intent to notify that the GPS is receiving fixes. Intent intent = new Intent(GPS_FIX_CHANGE_ACTION); intent.putExtra(EXTRA_ENABLED, true); @@ -1075,11 +1240,13 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } } + /** + * called from native code to request XTRA data + */ private void xtraDownloadRequest() { - if (Config.LOGD) Log.d(TAG, "xtraDownloadRequest"); - if (mNetworkThread != null) { - mNetworkThread.xtraDownloadRequest(); - } + if (DEBUG) Log.d(TAG, "xtraDownloadRequest"); + mHandler.removeMessages(DOWNLOAD_XTRA_DATA); + mHandler.sendMessage(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA)); } //============================================================= @@ -1093,7 +1260,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { StringBuilder extrasBuf = new StringBuilder(); - if (Config.LOGD) Log.d(TAG, "sendNiResponse, notifId: " + notificationId + + if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId + ", response: " + userResponse); native_send_ni_response(notificationId, userResponse); @@ -1172,176 +1339,81 @@ public class GpsLocationProvider extends ILocationProvider.Stub { mNIHandler.handleNiNotification(notification); } - private class GpsEventThread extends Thread { + // this thread is used to receive events from the native code. + // native_wait_for_event() will callback to us via reportLocation(), reportStatus(), etc. + // this is necessary because native code cannot call Java on a thread that the JVM does + // not know about. + private final class GpsEventThread extends Thread { public GpsEventThread() { super("GpsEventThread"); } public void run() { - if (Config.LOGD) Log.d(TAG, "GpsEventThread starting"); + if (DEBUG) Log.d(TAG, "GpsEventThread starting"); // Exit as soon as disable() is called instead of waiting for the GPS to stop. while (mEnabled) { // this will wait for an event from the GPS, // which will be reported via reportLocation or reportStatus native_wait_for_event(); } - if (Config.LOGD) Log.d(TAG, "GpsEventThread exiting"); + if (DEBUG) Log.d(TAG, "GpsEventThread exiting"); } } - private class GpsNetworkThread extends Thread { - - private long mNextNtpTime = 0; - private long mNextXtraTime = 0; - private boolean mTimeInjectRequested = false; - private boolean mXtraDownloadRequested = false; - private boolean mDone = false; - - public GpsNetworkThread() { - super("GpsNetworkThread"); - } - - public void run() { - synchronized (mNetworkThreadLock) { - if (!mDone) { - runLocked(); - } - } - } - - public void runLocked() { - if (Config.LOGD) Log.d(TAG, "NetworkThread starting"); - - SntpClient client = new SntpClient(); - GpsXtraDownloader xtraDownloader = null; - - if (native_supports_xtra()) { - xtraDownloader = new GpsXtraDownloader(mContext, mProperties); - } - - // thread exits after disable() is called - while (!mDone) { - long waitTime = getWaitTime(); - do { - synchronized (this) { - try { - if (!mNetworkAvailable) { - if (Config.LOGD) Log.d(TAG, - "NetworkThread wait for network"); - wait(); - } else if (waitTime > 0) { - if (Config.LOGD) { - Log.d(TAG, "NetworkThread wait for " + - waitTime + "ms"); - } - wait(waitTime); - } - } catch (InterruptedException e) { - if (Config.LOGD) { - Log.d(TAG, "InterruptedException in GpsNetworkThread"); - } - } - } - waitTime = getWaitTime(); - } while (!mDone && ((!mXtraDownloadRequested && - !mTimeInjectRequested && waitTime > 0) - || !mNetworkAvailable)); - if (Config.LOGD) Log.d(TAG, "NetworkThread out of wake loop"); - - if (!mDone) { - if (mNtpServer != null && - (mTimeInjectRequested || mNextNtpTime <= System.currentTimeMillis())) { - if (Config.LOGD) { - Log.d(TAG, "Requesting time from NTP server " + mNtpServer); - } - mTimeInjectRequested = false; - if (client.requestTime(mNtpServer, 10000)) { - long time = client.getNtpTime(); - long timeReference = client.getNtpTimeReference(); - int certainty = (int)(client.getRoundTripTime()/2); - long now = System.currentTimeMillis(); - long systemTimeOffset = time - now; - - Log.d(TAG, "NTP server returned: " - + time + " (" + new Date(time) - + ") reference: " + timeReference - + " certainty: " + certainty - + " system time offset: " + systemTimeOffset); - - // sanity check NTP time and do not use if it is too far from system time - if (systemTimeOffset < 0) { - systemTimeOffset = -systemTimeOffset; - } - if (systemTimeOffset < MAX_NTP_SYSTEM_TIME_OFFSET) { - native_inject_time(time, timeReference, certainty); - } else { - Log.e(TAG, "NTP time differs from system time by " + systemTimeOffset - + "ms. Ignoring."); - } - mNextNtpTime = now + NTP_INTERVAL; - } else { - if (Config.LOGD) Log.d(TAG, "requestTime failed"); - mNextNtpTime = System.currentTimeMillis() + RETRY_INTERVAL; - } + private final class ProviderHandler extends Handler { + @Override + public void handleMessage(Message msg) + { + switch (msg.what) { + case ENABLE: + if (msg.arg1 == 1) { + handleEnable(); + } else { + handleDisable(); } - - if ((mXtraDownloadRequested || - (mNextXtraTime > 0 && mNextXtraTime <= System.currentTimeMillis())) - && xtraDownloader != null) { - mXtraDownloadRequested = false; - byte[] data = xtraDownloader.downloadXtraData(); - if (data != null) { - if (Config.LOGD) { - Log.d(TAG, "calling native_inject_xtra_data"); - } - native_inject_xtra_data(data, data.length); - mNextXtraTime = 0; - } else { - mNextXtraTime = System.currentTimeMillis() + RETRY_INTERVAL; - } + break; + case ENABLE_TRACKING: + handleEnableLocationTracking(msg.arg1 == 1); + break; + case UPDATE_NETWORK_STATE: + handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj); + break; + case INJECT_NTP_TIME: + handleInjectNtpTime(); + break; + case DOWNLOAD_XTRA_DATA: + if (native_supports_xtra()) { + handleDownloadXtraData(); } - } + break; + case UPDATE_LOCATION: + handleUpdateLocation((Location)msg.obj); + break; + case ADD_LISTENER: + handleAddListener(msg.arg1); + break; + case REMOVE_LISTENER: + handleRemoveListener(msg.arg1); + break; } - if (Config.LOGD) Log.d(TAG, "NetworkThread exiting"); - } - - synchronized void xtraDownloadRequest() { - mXtraDownloadRequested = true; - notify(); - } - - synchronized void timeInjectRequest() { - mTimeInjectRequested = true; - notify(); } + }; - synchronized void signal() { - notify(); - } + private final class GpsLocationProviderThread extends Thread { - synchronized void setDone() { - if (Config.LOGD) Log.d(TAG, "stopping NetworkThread"); - mDone = true; - notify(); + public GpsLocationProviderThread() { + super("GpsLocationProvider"); } - private long getWaitTime() { - long now = System.currentTimeMillis(); - long waitTime = Long.MAX_VALUE; - if (mNtpServer != null) { - waitTime = mNextNtpTime - now; - } - if (mNextXtraTime != 0) { - long xtraWaitTime = mNextXtraTime - now; - if (xtraWaitTime < waitTime) { - waitTime = xtraWaitTime; - } - } - if (waitTime < 0) { - waitTime = 0; - } - return waitTime; + public void run() { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + initialize(); + Looper.prepare(); + mHandler = new ProviderHandler(); + // signal when we are initialized and ready to go + mInitializedLatch.countDown(); + Looper.loop(); } } @@ -1380,12 +1452,15 @@ public class GpsLocationProvider extends ILocationProvider.Stub { private native int native_read_nmea(int index, byte[] buffer, int bufferSize); private native void native_inject_location(double latitude, double longitude, float accuracy); - // XTRA Support + // XTRA Support private native void native_inject_time(long time, long timeReference, int uncertainty); private native boolean native_supports_xtra(); private native void native_inject_xtra_data(byte[] data, int length); - // AGPS Support + // DEBUG Support + private native String native_get_internal_state(); + + // AGPS Support private native void native_agps_data_conn_open(String apn); private native void native_agps_data_conn_closed(); private native void native_agps_data_conn_failed(); diff --git a/location/java/com/android/internal/location/GpsXtraDownloader.java b/location/java/com/android/internal/location/GpsXtraDownloader.java index 33ebce7..978bda2 100644 --- a/location/java/com/android/internal/location/GpsXtraDownloader.java +++ b/location/java/com/android/internal/location/GpsXtraDownloader.java @@ -36,6 +36,8 @@ import android.net.http.AndroidHttpClient; import android.util.Config; import android.util.Log; + + /** * A class for downloading GPS XTRA data. * @@ -169,4 +171,3 @@ public class GpsXtraDownloader { } } - diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/location/java/com/android/internal/location/LocationProviderProxy.java index 89337b3..31ec09a 100644 --- a/location/java/com/android/internal/location/LocationProviderProxy.java +++ b/location/java/com/android/internal/location/LocationProviderProxy.java @@ -16,179 +16,262 @@ package com.android.internal.location; -import android.location.Address; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.location.ILocationProvider; import android.location.Location; -import android.location.LocationManager; +import android.location.LocationProviderInterface; import android.net.NetworkInfo; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.util.Log; -import java.util.List; - /** - * A class for proxying remote ILocationProvider implementations. + * A class for proxying location providers implemented as services. * * {@hide} */ -public class LocationProviderProxy implements IBinder.DeathRecipient { +public class LocationProviderProxy implements LocationProviderInterface { private static final String TAG = "LocationProviderProxy"; + private final Context mContext; private final String mName; - private final ILocationProvider mProvider; - private boolean mLocationTracking = false; - private long mMinTime = 0; - private boolean mDead; + private ILocationProvider mProvider; + private Handler mHandler; + private final Connection mServiceConnection = new Connection(); - public LocationProviderProxy(String name, ILocationProvider provider) { + // cached values set by the location manager + private boolean mLocationTracking = false; + private boolean mEnabled = false; + private long mMinTime = -1; + private int mNetworkState; + private NetworkInfo mNetworkInfo; + + // for caching requiresNetwork, requiresSatellite, etc. + private DummyLocationProvider mCachedAttributes; + + // constructor for proxying location providers implemented in a separate service + public LocationProviderProxy(Context context, String name, String serviceName, + Handler handler) { + mContext = context; mName = name; - mProvider = provider; - try { - provider.asBinder().linkToDeath(this, 0); - } catch (RemoteException e) { - Log.e(TAG, "linkToDeath failed", e); - mDead = true; - } + mHandler = handler; + mContext.bindService(new Intent(serviceName), mServiceConnection, Context.BIND_AUTO_CREATE); } - public void unlinkProvider() { - if (mProvider != null) { - mProvider.asBinder().unlinkToDeath(this, 0); + private class Connection implements ServiceConnection { + public void onServiceConnected(ComponentName className, IBinder service) { + Log.d(TAG, "LocationProviderProxy.onServiceConnected " + className); + synchronized (this) { + mProvider = ILocationProvider.Stub.asInterface(service); + if (mProvider != null) { + mHandler.post(mServiceConnectedTask); + } + } + } + + public void onServiceDisconnected(ComponentName className) { + Log.d(TAG, "LocationProviderProxy.onServiceDisconnected " + className); + synchronized (this) { + mProvider = null; + } } } + private Runnable mServiceConnectedTask = new Runnable() { + public void run() { + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + if (provider == null) { + return; + } + } + + if (mCachedAttributes == null) { + try { + mCachedAttributes = new DummyLocationProvider(mName); + mCachedAttributes.setRequiresNetwork(provider.requiresNetwork()); + mCachedAttributes.setRequiresSatellite(provider.requiresSatellite()); + mCachedAttributes.setRequiresCell(provider.requiresCell()); + mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost()); + mCachedAttributes.setSupportsAltitude(provider.supportsAltitude()); + mCachedAttributes.setSupportsSpeed(provider.supportsSpeed()); + mCachedAttributes.setSupportsBearing(provider.supportsBearing()); + mCachedAttributes.setPowerRequirement(provider.getPowerRequirement()); + mCachedAttributes.setAccuracy(provider.getAccuracy()); + } catch (RemoteException e) { + mCachedAttributes = null; + } + } + + // resend previous values from the location manager if the service has restarted + try { + if (mEnabled) { + provider.enable(); + } + if (mLocationTracking) { + provider.enableLocationTracking(true); + } + if (mMinTime >= 0) { + provider.setMinTime(mMinTime); + } + if (mNetworkInfo != null) { + provider.updateNetworkState(mNetworkState, mNetworkInfo); + } + } catch (RemoteException e) { + } + } + }; + public String getName() { return mName; } - public boolean isDead() { - return mDead; - } - public boolean requiresNetwork() { - try { - return mProvider.requiresNetwork(); - } catch (RemoteException e) { - Log.e(TAG, "requiresNetwork failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.requiresNetwork(); + } else { return false; } } public boolean requiresSatellite() { - try { - return mProvider.requiresSatellite(); - } catch (RemoteException e) { - Log.e(TAG, "requiresSatellite failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.requiresSatellite(); + } else { return false; } } public boolean requiresCell() { - try { - return mProvider.requiresCell(); - } catch (RemoteException e) { - Log.e(TAG, "requiresCell failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.requiresCell(); + } else { return false; } } public boolean hasMonetaryCost() { - try { - return mProvider.hasMonetaryCost(); - } catch (RemoteException e) { - Log.e(TAG, "hasMonetaryCost failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.hasMonetaryCost(); + } else { return false; } } public boolean supportsAltitude() { - try { - return mProvider.supportsAltitude(); - } catch (RemoteException e) { - Log.e(TAG, "supportsAltitude failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.supportsAltitude(); + } else { return false; } } public boolean supportsSpeed() { - try { - return mProvider.supportsSpeed(); - } catch (RemoteException e) { - Log.e(TAG, "supportsSpeed failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.supportsSpeed(); + } else { return false; } } public boolean supportsBearing() { - try { - return mProvider.supportsBearing(); - } catch (RemoteException e) { - Log.e(TAG, "supportsBearing failed", e); + if (mCachedAttributes != null) { + return mCachedAttributes.supportsBearing(); + } else { return false; } } public int getPowerRequirement() { - try { - return mProvider.getPowerRequirement(); - } catch (RemoteException e) { - Log.e(TAG, "getPowerRequirement failed", e); - return 0; + if (mCachedAttributes != null) { + return mCachedAttributes.getPowerRequirement(); + } else { + return -1; } } public int getAccuracy() { - try { - return mProvider.getAccuracy(); - } catch (RemoteException e) { - Log.e(TAG, "getAccuracy failed", e); - return 0; + if (mCachedAttributes != null) { + return mCachedAttributes.getAccuracy(); + } else { + return -1; } } public void enable() { - try { - mProvider.enable(); - } catch (RemoteException e) { - Log.e(TAG, "enable failed", e); + mEnabled = true; + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.enable(); + } catch (RemoteException e) { + } } } public void disable() { - try { - mProvider.disable(); - } catch (RemoteException e) { - Log.e(TAG, "disable failed", e); + mEnabled = false; + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.disable(); + } catch (RemoteException e) { + } } } public boolean isEnabled() { - try { - return mProvider.isEnabled(); - } catch (RemoteException e) { - Log.e(TAG, "isEnabled failed", e); - return false; - } + return mEnabled; } public int getStatus(Bundle extras) { - try { - return mProvider.getStatus(extras); - } catch (RemoteException e) { - Log.e(TAG, "getStatus failed", e); - return 0; + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + return provider.getStatus(extras); + } catch (RemoteException e) { + } } + return 0; } public long getStatusUpdateTime() { + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + return provider.getStatusUpdateTime(); + } catch (RemoteException e) { + } + } + return 0; + } + + public String getInternalState() { try { - return mProvider.getStatusUpdateTime(); + return mProvider.getInternalState(); } catch (RemoteException e) { - Log.e(TAG, "getStatusUpdateTime failed", e); - return 0; + Log.e(TAG, "getInternalState failed", e); + return null; } } @@ -198,10 +281,18 @@ public class LocationProviderProxy implements IBinder.DeathRecipient { public void enableLocationTracking(boolean enable) { mLocationTracking = enable; - try { - mProvider.enableLocationTracking(enable); - } catch (RemoteException e) { - Log.e(TAG, "enableLocationTracking failed", e); + if (!enable) { + mMinTime = -1; + } + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.enableLocationTracking(enable); + } catch (RemoteException e) { + } } } @@ -210,58 +301,84 @@ public class LocationProviderProxy implements IBinder.DeathRecipient { } public void setMinTime(long minTime) { - mMinTime = minTime; - try { - mProvider.setMinTime(minTime); - } catch (RemoteException e) { - Log.e(TAG, "setMinTime failed", e); + mMinTime = minTime; + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.setMinTime(minTime); + } catch (RemoteException e) { + } } } public void updateNetworkState(int state, NetworkInfo info) { - try { - mProvider.updateNetworkState(state, info); - } catch (RemoteException e) { - Log.e(TAG, "updateNetworkState failed", e); + mNetworkState = state; + mNetworkInfo = info; + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.updateNetworkState(state, info); + } catch (RemoteException e) { + } } } public void updateLocation(Location location) { - try { - mProvider.updateLocation(location); - } catch (RemoteException e) { - Log.e(TAG, "updateLocation failed", e); + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.updateLocation(location); + } catch (RemoteException e) { + } } } public boolean sendExtraCommand(String command, Bundle extras) { - try { - return mProvider.sendExtraCommand(command, extras); - } catch (RemoteException e) { - Log.e(TAG, "sendExtraCommand failed", e); - return false; + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.sendExtraCommand(command, extras); + } catch (RemoteException e) { + } } + return false; } public void addListener(int uid) { - try { - mProvider.addListener(uid); - } catch (RemoteException e) { - Log.e(TAG, "addListener failed", e); + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.addListener(uid); + } catch (RemoteException e) { + } } } public void removeListener(int uid) { - try { - mProvider.removeListener(uid); - } catch (RemoteException e) { - Log.e(TAG, "removeListener failed", e); + ILocationProvider provider; + synchronized (mServiceConnection) { + provider = mProvider; + } + if (provider != null) { + try { + provider.removeListener(uid); + } catch (RemoteException e) { + } } - } - - public void binderDied() { - Log.w(TAG, "Location Provider " + mName + " died"); - mDead = true; - mProvider.asBinder().unlinkToDeath(this, 0); } } diff --git a/location/java/com/android/internal/location/MockProvider.java b/location/java/com/android/internal/location/MockProvider.java index 2614f82..d912740 100644 --- a/location/java/com/android/internal/location/MockProvider.java +++ b/location/java/com/android/internal/location/MockProvider.java @@ -17,9 +17,9 @@ package com.android.internal.location; import android.location.ILocationManager; -import android.location.ILocationProvider; import android.location.Location; import android.location.LocationProvider; +import android.location.LocationProviderInterface; import android.net.NetworkInfo; import android.os.Bundle; import android.os.RemoteException; @@ -33,7 +33,7 @@ import java.io.PrintWriter; * * {@hide} */ -public class MockProvider extends ILocationProvider.Stub { +public class MockProvider implements LocationProviderInterface { private final String mName; private final ILocationManager mLocationManager; private final boolean mRequiresNetwork; @@ -73,6 +73,10 @@ public class MockProvider extends ILocationProvider.Stub { mLocation = new Location(name); } + public String getName() { + return mName; + } + public void disable() { mEnabled = false; } @@ -81,6 +85,10 @@ public class MockProvider extends ILocationProvider.Stub { mEnabled = true; } + public boolean isEnabled() { + return mEnabled; + } + public int getStatus(Bundle extras) { if (mHasStatus) { extras.clear(); @@ -95,10 +103,6 @@ public class MockProvider extends ILocationProvider.Stub { return mStatusUpdateTime; } - public boolean isEnabled() { - return mEnabled; - } - public int getAccuracy() { return mAccuracy; } @@ -139,7 +143,7 @@ public class MockProvider extends ILocationProvider.Stub { mLocation.set(l); mHasLocation = true; try { - mLocationManager.reportLocation(mLocation); + mLocationManager.reportLocation(mLocation, false); } catch (RemoteException e) { Log.e(TAG, "RemoteException calling reportLocation"); } @@ -164,6 +168,10 @@ public class MockProvider extends ILocationProvider.Stub { mStatusUpdateTime = 0; } + public String getInternalState() { + return null; + } + public void enableLocationTracking(boolean enable) { } diff --git a/location/java/com/android/internal/location/NmeaParser.java b/location/java/com/android/internal/location/NmeaParser.java deleted file mode 100644 index 43afa1d..0000000 --- a/location/java/com/android/internal/location/NmeaParser.java +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.location; - -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -import android.location.Location; -import android.os.Bundle; -import android.util.Log; - -/** - * {@hide} - */ -public class NmeaParser { - - private static final String TAG = "NmeaParser"; - - private static final TimeZone sUtcTimeZone = TimeZone.getTimeZone("UTC"); - - private static final float KNOTS_TO_METERS_PER_SECOND = 0.51444444444f; - - private final String mName; - - private int mYear = -1; - private int mMonth; - private int mDay; - - private long mTime = -1; - private long mBaseTime; - private double mLatitude; - private double mLongitude; - - private boolean mHasAltitude; - private double mAltitude; - private boolean mHasBearing; - private float mBearing; - private boolean mHasSpeed; - private float mSpeed; - - private boolean mNewWaypoint = false; - private Location mLocation = null; - private Bundle mExtras; - - public NmeaParser(String name) { - mName = name; - } - - private boolean updateTime(String time) { - if (time.length() < 6) { - return false; - } - if (mYear == -1) { - // Since we haven't seen a day/month/year yet, - // we can't construct a meaningful time stamp. - // Clean up any old data. - mLatitude = 0.0; - mLongitude = 0.0; - mHasAltitude = false; - mHasBearing = false; - mHasSpeed = false; - mExtras = null; - return false; - } - - int hour, minute; - float second; - try { - hour = Integer.parseInt(time.substring(0, 2)); - minute = Integer.parseInt(time.substring(2, 4)); - second = Float.parseFloat(time.substring(4, time.length())); - } catch (NumberFormatException nfe) { - Log.e(TAG, "Error parsing timestamp " + time); - return false; - } - - int isecond = (int) second; - int millis = (int) ((second - isecond) * 1000); - Calendar c = new GregorianCalendar(sUtcTimeZone); - c.set(mYear, mMonth, mDay, hour, minute, isecond); - long newTime = c.getTimeInMillis() + millis; - - if (mTime == -1) { - mTime = 0; - mBaseTime = newTime; - } - newTime -= mBaseTime; - - // If the timestamp has advanced, copy the temporary data - // into a new Location - if (newTime != mTime) { - mNewWaypoint = true; - mLocation = new Location(mName); - mLocation.setTime(mTime); - mLocation.setLatitude(mLatitude); - mLocation.setLongitude(mLongitude); - if (mHasAltitude) { - mLocation.setAltitude(mAltitude); - } - if (mHasBearing) { - mLocation.setBearing(mBearing); - } - if (mHasSpeed) { - mLocation.setSpeed(mSpeed); - } - mLocation.setExtras(mExtras); - mExtras = null; - - mTime = newTime; - mHasAltitude = false; - mHasBearing = false; - mHasSpeed = false; - } - return true; - } - - private boolean updateDate(String date) { - if (date.length() != 6) { - return false; - } - int month, day, year; - try { - day = Integer.parseInt(date.substring(0, 2)); - month = Integer.parseInt(date.substring(2, 4)); - year = 2000 + Integer.parseInt(date.substring(4, 6)); - } catch (NumberFormatException nfe) { - Log.e(TAG, "Error parsing date " + date); - return false; - } - - mYear = year; - mMonth = month; - mDay = day; - return true; - } - - private boolean updateTime(String time, String date) { - if (!updateDate(date)) { - return false; - } - return updateTime(time); - } - - private boolean updateIntExtra(String name, String value) { - int val; - try { - val = Integer.parseInt(value); - } catch (NumberFormatException nfe) { - Log.e(TAG, "Exception parsing int " + name + ": " + value, nfe); - return false; - } - if (mExtras == null) { - mExtras = new Bundle(); - } - mExtras.putInt(name, val); - return true; - } - - private boolean updateFloatExtra(String name, String value) { - float val; - try { - val = Float.parseFloat(value); - } catch (NumberFormatException nfe) { - Log.e(TAG, "Exception parsing float " + name + ": " + value, nfe); - return false; - } - if (mExtras == null) { - mExtras = new Bundle(); - } - mExtras.putFloat(name, val); - return true; - } - - private boolean updateDoubleExtra(String name, String value) { - double val; - try { - val = Double.parseDouble(value); - } catch (NumberFormatException nfe) { - Log.e(TAG, "Exception parsing double " + name + ": " + value, nfe); - return false; - } - if (mExtras == null) { - mExtras = new Bundle(); - } - mExtras.putDouble(name, val); - return true; - } - - private double convertFromHHMM(String coord) { - double val = Double.parseDouble(coord); - int degrees = ((int) Math.floor(val)) / 100; - double minutes = val - (degrees * 100); - double dcoord = degrees + minutes / 60.0; - return dcoord; - } - - private boolean updateLatLon(String latitude, String latitudeHemi, - String longitude, String longitudeHemi) { - if (latitude.length() == 0 || longitude.length() == 0) { - return false; - } - - // Lat/long values are expressed as {D}DDMM.MMMM - double lat, lon; - try { - lat = convertFromHHMM(latitude); - if (latitudeHemi.charAt(0) == 'S') { - lat = -lat; - } - } catch (NumberFormatException nfe1) { - Log.e(TAG, "Exception parsing lat/long: " + nfe1, nfe1); - return false; - } - - try { - lon = convertFromHHMM(longitude); - if (longitudeHemi.charAt(0) == 'W') { - lon = -lon; - } - } catch (NumberFormatException nfe2) { - Log.e(TAG, "Exception parsing lat/long: " + nfe2, nfe2); - return false; - } - - // Only update if both were parsed cleanly - mLatitude = lat; - mLongitude = lon; - return true; - } - - private boolean updateAltitude(String altitude) { - if (altitude.length() == 0) { - return false; - } - double alt; - try { - alt = Double.parseDouble(altitude); - } catch (NumberFormatException nfe) { - Log.e(TAG, "Exception parsing altitude " + altitude + ": " + nfe, - nfe); - return false; - } - - mHasAltitude = true; - mAltitude = alt; - return true; - } - - private boolean updateBearing(String bearing) { - float brg; - try { - brg = Float.parseFloat(bearing); - } catch (NumberFormatException nfe) { - Log.e(TAG, "Exception parsing bearing " + bearing + ": " + nfe, - nfe); - return false; - } - - mHasBearing = true; - mBearing = brg; - return true; - } - - private boolean updateSpeed(String speed) { - float spd; - try { - spd = Float.parseFloat(speed) * KNOTS_TO_METERS_PER_SECOND; - } catch (NumberFormatException nfe) { - Log.e(TAG, "Exception parsing speed " + speed + ": " + nfe, nfe); - return false; - } - - mHasSpeed = true; - mSpeed = spd; - return true; - } - - public boolean parseSentence(String s) { - int len = s.length(); - if (len < 9) { - return false; - } - if (s.charAt(len - 3) == '*') { - // String checksum = s.substring(len - 4, len); - s = s.substring(0, len - 3); - } - String[] tokens = s.split(","); - String sentenceId = tokens[0].substring(3, 6); - - int idx = 1; - try { - if (sentenceId.equals("GGA")) { - String time = tokens[idx++]; - String latitude = tokens[idx++]; - String latitudeHemi = tokens[idx++]; - String longitude = tokens[idx++]; - String longitudeHemi = tokens[idx++]; - String fixQuality = tokens[idx++]; - String numSatellites = tokens[idx++]; - String horizontalDilutionOfPrecision = tokens[idx++]; - String altitude = tokens[idx++]; - String altitudeUnits = tokens[idx++]; - String heightOfGeoid = tokens[idx++]; - String heightOfGeoidUnits = tokens[idx++]; - String timeSinceLastDgpsUpdate = tokens[idx++]; - - updateTime(time); - updateLatLon(latitude, latitudeHemi, - longitude, longitudeHemi); - updateAltitude(altitude); - // updateQuality(fixQuality); - updateIntExtra("numSatellites", numSatellites); - updateFloatExtra("hdop", horizontalDilutionOfPrecision); - - if (mNewWaypoint) { - mNewWaypoint = false; - return true; - } - } else if (sentenceId.equals("GSA")) { - // DOP and active satellites - String selectionMode = tokens[idx++]; // m=manual, a=auto 2d/3d - String mode = tokens[idx++]; // 1=no fix, 2=2d, 3=3d - for (int i = 0; i < 12; i++) { - String id = tokens[idx++]; - } - String pdop = tokens[idx++]; - String hdop = tokens[idx++]; - String vdop = tokens[idx++]; - - // TODO - publish satellite ids - updateFloatExtra("pdop", pdop); - updateFloatExtra("hdop", hdop); - updateFloatExtra("vdop", vdop); - } else if (sentenceId.equals("GSV")) { - // Satellites in view - String numMessages = tokens[idx++]; - String messageNum = tokens[idx++]; - String svsInView = tokens[idx++]; - for (int i = 0; i < 4; i++) { - if (idx + 2 < tokens.length) { - String prnNumber = tokens[idx++]; - String elevation = tokens[idx++]; - String azimuth = tokens[idx++]; - if (idx < tokens.length) { - String snr = tokens[idx++]; - } - } - } - // TODO - publish this info - } else if (sentenceId.equals("RMC")) { - // Recommended minimum navigation information - String time = tokens[idx++]; - String fixStatus = tokens[idx++]; - String latitude = tokens[idx++]; - String latitudeHemi = tokens[idx++]; - String longitude = tokens[idx++]; - String longitudeHemi = tokens[idx++]; - String speed = tokens[idx++]; - String bearing = tokens[idx++]; - String utcDate = tokens[idx++]; - String magneticVariation = tokens[idx++]; - String magneticVariationDir = tokens[idx++]; - String mode = tokens[idx++]; - - if (fixStatus.charAt(0) == 'A') { - updateTime(time, utcDate); - updateLatLon(latitude, latitudeHemi, - longitude, longitudeHemi); - updateBearing(bearing); - updateSpeed(speed); - } - - if (mNewWaypoint) { - return true; - } - } else { - Log.e(TAG, "Unknown sentence: " + s); - } - } catch (ArrayIndexOutOfBoundsException e) { - // do nothing - sentence will have no effect - Log.e(TAG, "AIOOBE", e); - - for (int i = 0; i < tokens.length; i++) { - Log.e(TAG, "Got token #" + i + " = " + tokens[i]); - } - } - - return false; - } - -// } else if (sentenceId.equals("GLL")) { -// // Geographics position lat/long -// String latitude = tokens[idx++]; -// String latitudeHemi = tokens[idx++]; -// String longitude = tokens[idx++]; -// String longitudeHemi = tokens[idx++]; -// String time = tokens[idx++]; -// String status = tokens[idx++]; -// String mode = tokens[idx++]; -// String checksum = tokens[idx++]; -// -// if (status.charAt(0) == 'A') { -// updateTime(time); -// updateLatLon(latitude, latitudeHemi, longitude, longitudeHemi); -// } -//} else if (sentenceId.equals("VTG")) { -// String trackMadeGood = tokens[idx++]; -// String t = tokens[idx++]; -// String unused1 = tokens[idx++]; -// String unused2 = tokens[idx++]; -// String groundSpeedKnots = tokens[idx++]; -// String n = tokens[idx++]; -// String groundSpeedKph = tokens[idx++]; -// String k = tokens[idx++]; -// String checksum = tokens[idx++]; -// -// updateSpeed(groundSpeedKph); - - public Location getLocation() { - return mLocation; - } -} diff --git a/location/java/com/android/internal/location/PassiveProvider.java b/location/java/com/android/internal/location/PassiveProvider.java new file mode 100644 index 0000000..ab90937 --- /dev/null +++ b/location/java/com/android/internal/location/PassiveProvider.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.location; + +import android.location.ILocationManager; +import android.location.Location; +import android.location.LocationManager; +import android.location.LocationProvider; +import android.location.LocationProviderInterface; +import android.net.NetworkInfo; +import android.os.Bundle; +import android.os.RemoteException; +import android.util.Log; + +/** + * A passive location provider reports locations received from other providers + * for clients that want to listen passively without actually triggering + * location updates. + * + * {@hide} + */ +public class PassiveProvider implements LocationProviderInterface { + + private static final String TAG = "PassiveProvider"; + + private final ILocationManager mLocationManager; + private boolean mTracking; + + public PassiveProvider(ILocationManager locationManager) { + mLocationManager = locationManager; + } + + public String getName() { + return LocationManager.PASSIVE_PROVIDER; + } + + public boolean requiresNetwork() { + return false; + } + + public boolean requiresSatellite() { + return false; + } + + public boolean requiresCell() { + return false; + } + + public boolean hasMonetaryCost() { + return false; + } + + public boolean supportsAltitude() { + return false; + } + + public boolean supportsSpeed() { + return false; + } + + public boolean supportsBearing() { + return false; + } + + public int getPowerRequirement() { + return -1; + } + + public int getAccuracy() { + return -1; + } + + public boolean isEnabled() { + return true; + } + + public void enable() { + } + + public void disable() { + } + + public int getStatus(Bundle extras) { + if (mTracking) { + return LocationProvider.AVAILABLE; + } else { + return LocationProvider.TEMPORARILY_UNAVAILABLE; + } + } + + public long getStatusUpdateTime() { + return -1; + } + + public String getInternalState() { + return null; + } + + public void enableLocationTracking(boolean enable) { + mTracking = enable; + } + + public void setMinTime(long minTime) { + } + + public void updateNetworkState(int state, NetworkInfo info) { + } + + public void updateLocation(Location location) { + if (mTracking) { + try { + // pass the location back to the location manager + mLocationManager.reportLocation(location, true); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException calling reportLocation"); + } + } + } + + public boolean sendExtraCommand(String command, Bundle extras) { + return false; + } + + public void addListener(int uid) { + } + + public void removeListener(int uid) { + } +} |