summaryrefslogtreecommitdiffstats
path: root/location/java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit54b6cfa9a9e5b861a9930af873580d6dc20f773c (patch)
tree35051494d2af230dce54d6b31c6af8fc24091316 /location/java
downloadframeworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2
Initial Contribution
Diffstat (limited to 'location/java')
-rw-r--r--location/java/android/location/Address.aidl19
-rw-r--r--location/java/android/location/Address.java516
-rw-r--r--location/java/android/location/Criteria.aidl19
-rw-r--r--location/java/android/location/Criteria.java242
-rw-r--r--location/java/android/location/DummyLocationProvider.java168
-rw-r--r--location/java/android/location/Geocoder.java242
-rw-r--r--location/java/android/location/GpsStatusListener.java58
-rw-r--r--location/java/android/location/IGpsStatusListener.aidl32
-rw-r--r--location/java/android/location/ILocationListener.aidl32
-rw-r--r--location/java/android/location/ILocationManager.aidl73
-rw-r--r--location/java/android/location/Location.aidl19
-rw-r--r--location/java/android/location/Location.java730
-rw-r--r--location/java/android/location/LocationListener.java82
-rw-r--r--location/java/android/location/LocationManager.java1179
-rw-r--r--location/java/android/location/LocationProvider.java160
-rw-r--r--location/java/android/location/LocationProviderImpl.java261
-rw-r--r--location/java/android/location/package.html12
-rw-r--r--location/java/com/android/internal/location/CellState.java152
-rw-r--r--location/java/com/android/internal/location/GpsLocationProvider.java879
-rw-r--r--location/java/com/android/internal/location/GpsXtraDownloader.java163
-rw-r--r--location/java/com/android/internal/location/LocationCache.java577
-rw-r--r--location/java/com/android/internal/location/LocationCollector.java499
-rw-r--r--location/java/com/android/internal/location/LocationMasfClient.java1103
-rw-r--r--location/java/com/android/internal/location/NetworkLocationProvider.java561
-rw-r--r--location/java/com/android/internal/location/NmeaParser.java437
-rw-r--r--location/java/com/android/internal/location/ProtoRequestListener.java61
-rw-r--r--location/java/com/android/internal/location/TrackProvider.java720
-rw-r--r--location/java/com/android/internal/location/protocol/GAddress.java23
-rw-r--r--location/java/com/android/internal/location/protocol/GAddressComponent.java23
-rw-r--r--location/java/com/android/internal/location/protocol/GAppProfile.java26
-rw-r--r--location/java/com/android/internal/location/protocol/GCell.java29
-rw-r--r--location/java/com/android/internal/location/protocol/GCellularPlatformProfile.java30
-rw-r--r--location/java/com/android/internal/location/protocol/GCellularProfile.java26
-rw-r--r--location/java/com/android/internal/location/protocol/GDebugProfile.java36
-rw-r--r--location/java/com/android/internal/location/protocol/GDeviceLocation.java24
-rw-r--r--location/java/com/android/internal/location/protocol/GFeature.java38
-rw-r--r--location/java/com/android/internal/location/protocol/GGeocodeRequest.java24
-rw-r--r--location/java/com/android/internal/location/protocol/GGpsProfile.java29
-rw-r--r--location/java/com/android/internal/location/protocol/GLatLng.java23
-rw-r--r--location/java/com/android/internal/location/protocol/GLocReply.java24
-rw-r--r--location/java/com/android/internal/location/protocol/GLocReplyElement.java24
-rw-r--r--location/java/com/android/internal/location/protocol/GLocRequest.java26
-rw-r--r--location/java/com/android/internal/location/protocol/GLocRequestElement.java26
-rw-r--r--location/java/com/android/internal/location/protocol/GLocation.java41
-rw-r--r--location/java/com/android/internal/location/protocol/GPlatformProfile.java28
-rw-r--r--location/java/com/android/internal/location/protocol/GPrefetchMode.java25
-rw-r--r--location/java/com/android/internal/location/protocol/GRectangle.java23
-rw-r--r--location/java/com/android/internal/location/protocol/GUserProfile.java23
-rw-r--r--location/java/com/android/internal/location/protocol/GWifiDevice.java26
-rw-r--r--location/java/com/android/internal/location/protocol/GWifiPlatformProfile.java29
-rw-r--r--location/java/com/android/internal/location/protocol/GWifiProfile.java24
-rw-r--r--location/java/com/android/internal/location/protocol/GaddressMessageTypes.java39
-rw-r--r--location/java/com/android/internal/location/protocol/GcellularMessageTypes.java57
-rw-r--r--location/java/com/android/internal/location/protocol/GdebugprofileMessageTypes.java35
-rw-r--r--location/java/com/android/internal/location/protocol/GfeatureMessageTypes.java39
-rw-r--r--location/java/com/android/internal/location/protocol/GlatlngMessageTypes.java32
-rw-r--r--location/java/com/android/internal/location/protocol/GlocationMessageTypes.java138
-rw-r--r--location/java/com/android/internal/location/protocol/GrectangleMessageTypes.java32
-rw-r--r--location/java/com/android/internal/location/protocol/GwifiMessageTypes.java47
-rw-r--r--location/java/com/android/internal/location/protocol/LocserverMessageTypes.java80
-rw-r--r--location/java/com/android/internal/location/protocol/ResponseCodes.java45
61 files changed, 10190 insertions, 0 deletions
diff --git a/location/java/android/location/Address.aidl b/location/java/android/location/Address.aidl
new file mode 100644
index 0000000..5be1498
--- /dev/null
+++ b/location/java/android/location/Address.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008, 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 Address;
diff --git a/location/java/android/location/Address.java b/location/java/android/location/Address.java
new file mode 100644
index 0000000..3551363
--- /dev/null
+++ b/location/java/android/location/Address.java
@@ -0,0 +1,516 @@
+/*
+ * 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 android.location;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class representing an Address, i.e, a set of Strings describing a location.
+ *
+ * The addres format is a simplified version of xAL (eXtensible Address Language)
+ * http://www.oasis-open.org/committees/ciq/ciq.html#6
+ */
+public class Address implements Parcelable {
+
+ private Locale mLocale;
+
+ private String mFeatureName;
+ private HashMap<Integer, String> mAddressLines;
+ private int mMaxAddressLineIndex = -1;
+ private String mAdminArea;
+ private String mSubAdminArea;
+ private String mLocality;
+ private String mThoroughfare;
+ private String mPostalCode;
+ private String mCountryCode;
+ private String mCountryName;
+ private double mLatitude;
+ private double mLongitude;
+ private boolean mHasLatitude = false;
+ private boolean mHasLongitude = false;
+ private String mPhone;
+ private String mUrl;
+ private Bundle mExtras = null;
+
+ /**
+ * Constructs a new Address object set to the given Locale and with all
+ * other fields initialized to null or false.
+ */
+ public Address(Locale locale) {
+ mLocale = locale;
+ }
+
+ /**
+ * Returns the Locale associated with this address.
+ */
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ /**
+ * Returns the largest index currently in use to specify an address line.
+ * If no address lines are specified, -1 is returned.
+ */
+ public int getMaxAddressLineIndex() {
+ return mMaxAddressLineIndex;
+ }
+
+ /**
+ * Returns a line of the address numbered by the given index
+ * (starting at 0), or null if no such line is present.
+ *
+ * @throws IllegalArgumentException if index < 0
+ */
+ public String getAddressLine(int index) {
+ if (index < 0) {
+ throw new IllegalArgumentException("index = " + index + " < 0");
+ }
+ return mAddressLines == null? null : mAddressLines.get(index);
+ }
+
+ /**
+ * Sets the line of the address numbered by index (starting at 0) to the
+ * given String, which may be null.
+ *
+ * @throws IllegalArgumentException if index < 0
+ */
+ public void setAddressLine(int index, String line) {
+ if (index < 0) {
+ throw new IllegalArgumentException("index = " + index + " < 0");
+ }
+ if (mAddressLines == null) {
+ mAddressLines = new HashMap<Integer, String>();
+ }
+ mAddressLines.put(index, line);
+
+ if (line == null) {
+ // We've eliminated a line, recompute the max index
+ mMaxAddressLineIndex = -1;
+ for (Integer i : mAddressLines.keySet()) {
+ mMaxAddressLineIndex = Math.max(mMaxAddressLineIndex, i);
+ }
+ } else {
+ mMaxAddressLineIndex = Math.max(mMaxAddressLineIndex, index);
+ }
+ }
+
+ /**
+ * Returns the feature name of the address, for example, "Golden Gate Bridge", or null
+ * if it is unknown
+ */
+ public String getFeatureName() {
+ return mFeatureName;
+ }
+
+ /**
+ * Sets the feature name of the address to the given String, which may be null
+ */
+ public void setFeatureName(String featureName) {
+ mFeatureName = featureName;
+ }
+
+ /**
+ * Returns the administrative area name of the address, for example, "CA", or null if
+ * it is unknown
+ */
+ public String getAdminArea() {
+ return mAdminArea;
+ }
+
+ /**
+ * Sets the administrative area name of the address to the given String, which may be null
+ */
+ public void setAdminArea(String adminArea) {
+ this.mAdminArea = adminArea;
+ }
+
+ /**
+ * Returns the sub-administrative area name of the address, for example, "Santa Clara County",
+ * or null if it is unknown
+ */
+ public String getSubAdminArea() {
+ return mSubAdminArea;
+ }
+
+ /**
+ * Sets the sub-administrative area name of the address to the given String, which may be null
+ */
+ public void setSubAdminArea(String subAdminArea) {
+ this.mSubAdminArea = subAdminArea;
+ }
+
+ /**
+ * Returns the locality of the address, for example "Mountain View", or null if it is unknown.
+ */
+ public String getLocality() {
+ return mLocality;
+ }
+
+ /**
+ * Sets the locality of the address to the given String, which may be null.
+ */
+ public void setLocality(String locality) {
+ mLocality = locality;
+ }
+
+ /**
+ * Returns the thoroughfare name of the address, for example, "1600 Ampitheater Parkway",
+ * which may be null
+ */
+ public String getThoroughfare() {
+ return mThoroughfare;
+ }
+
+ /**
+ * Sets the thoroughfare name of the address, which may be null.
+ */
+ public void setThoroughfare(String thoroughfare) {
+ this.mThoroughfare = thoroughfare;
+ }
+
+ /**
+ * Returns the postal code of the address, for example "94110",
+ * or null if it is unknown.
+ */
+ public String getPostalCode() {
+ return mPostalCode;
+ }
+
+ /**
+ * Sets the postal code of the address to the given String, which may
+ * be null.
+ */
+ public void setPostalCode(String postalCode) {
+ mPostalCode = postalCode;
+ }
+
+ /**
+ * Returns the country code of the address, for example "US",
+ * or null if it is unknown.
+ */
+ public String getCountryCode() {
+ return mCountryCode;
+ }
+
+ /**
+ * Sets the country code of the address to the given String, which may
+ * be null.
+ */
+ public void setCountryCode(String countryCode) {
+ mCountryCode = countryCode;
+ }
+
+ /**
+ * Returns the localized country name of the address, for example "Iceland",
+ * or null if it is unknown.
+ */
+ public String getCountryName() {
+ return mCountryName;
+ }
+
+ /**
+ * Sets the country name of the address to the given String, which may
+ * be null.
+ */
+ public void setCountryName(String countryName) {
+ mCountryName = countryName;
+ }
+
+ /**
+ * Returns true if a latitude has been assigned to this Address,
+ * false otherwise.
+ */
+ public boolean hasLatitude() {
+ return mHasLatitude;
+ }
+
+ /**
+ * Returns the latitude of the address if known.
+ *
+ * @throws IllegalStateException if this Address has not been assigned
+ * a latitude.
+ */
+ public double getLatitude() {
+ if (mHasLatitude) {
+ return mLatitude;
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Sets the latitude associated with this address.
+ */
+ public void setLatitude(double latitude) {
+ mLatitude = latitude;
+ mHasLatitude = true;
+ }
+
+ /**
+ * Removes any latitude associated with this address.
+ */
+ public void clearLatitude() {
+ mHasLatitude = false;
+ }
+
+ /**
+ * Returns true if a longitude has been assigned to this Address,
+ * false otherwise.
+ */
+ public boolean hasLongitude() {
+ return mHasLongitude;
+ }
+
+ /**
+ * Returns the longitude of the address if known.
+ *
+ * @throws IllegalStateException if this Address has not been assigned
+ * a longitude.
+ */
+ public double getLongitude() {
+ if (mHasLongitude) {
+ return mLongitude;
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Sets the longitude associated with this address.
+ */
+ public void setLongitude(double longitude) {
+ mLongitude = longitude;
+ mHasLongitude = true;
+ }
+
+ /**
+ * Removes any longitude associated with this address.
+ */
+ public void clearLongitude() {
+ mHasLongitude = false;
+ }
+
+ /**
+ * Returns the phone number of the address if known,
+ * or null if it is unknown.
+ *
+ * @throws IllegalStateException if this Address has not been assigned
+ * a latitude.
+ */
+ public String getPhone() {
+ return mPhone;
+ }
+
+ /**
+ * Sets the phone number associated with this address.
+ */
+ public void setPhone(String phone) {
+ mPhone = phone;
+ }
+
+ /**
+ * Returns the public URL for the address if known,
+ * or null if it is unknown.
+ */
+ public String getUrl() {
+ return mUrl;
+ }
+
+ /**
+ * Sets the public URL associated with this address.
+ */
+ public void setUrl(String Url) {
+ mUrl = Url;
+ }
+
+ /**
+ * Returns additional provider-specific information about the
+ * address as a Bundle. The keys and values are determined
+ * by the provider. If no additional information is available,
+ * null is returned.
+ *
+ * <!--
+ * <p> A number of common key/value pairs are listed
+ * below. Providers that use any of the keys on this list must
+ * provide the corresponding value as described below.
+ *
+ * <ul>
+ * </ul>
+ * -->
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Sets the extra information associated with this fix to the
+ * given Bundle.
+ */
+ public void setExtras(Bundle extras) {
+ mExtras = (extras == null) ? null : new Bundle(extras);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Address[addressLines=[");
+ for (int i = 0; i <= mMaxAddressLineIndex; i++) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(i);
+ sb.append(':');
+ String line = mAddressLines.get(i);
+ if (line == null) {
+ sb.append("null");
+ } else {
+ sb.append('\"');
+ sb.append(line);
+ sb.append('\"');
+ }
+ }
+ sb.append(']');
+ sb.append(",feature=");
+ sb.append(mFeatureName);
+ sb.append(",admin=");
+ sb.append(mAdminArea);
+ sb.append(",sub-admin=");
+ sb.append(mSubAdminArea);
+ sb.append(",locality=");
+ sb.append(mLocality);
+ sb.append(",thoroughfare=");
+ sb.append(mThoroughfare);
+ sb.append(",postalCode=");
+ sb.append(mPostalCode);
+ sb.append(",countryCode=");
+ sb.append(mCountryCode);
+ sb.append(",countryName=");
+ sb.append(mCountryName);
+ sb.append(",hasLatitude=");
+ sb.append(mHasLatitude);
+ sb.append(",latitude=");
+ sb.append(mLatitude);
+ sb.append(",hasLongitude=");
+ sb.append(mHasLongitude);
+ sb.append(",longitude=");
+ sb.append(mLongitude);
+ sb.append(",phone=");
+ sb.append(mPhone);
+ sb.append(",url=");
+ sb.append(mUrl);
+ sb.append(",extras=");
+ sb.append(mExtras);
+ sb.append(']');
+ return sb.toString();
+ }
+
+ public static final Parcelable.Creator<Address> CREATOR =
+ new Parcelable.Creator<Address>() {
+ public Address createFromParcel(Parcel in) {
+ String language = in.readString();
+ String country = in.readString();
+ Locale locale = country.length() > 0 ?
+ new Locale(language, country) :
+ new Locale(language);
+ Address a = new Address(locale);
+
+ int N = in.readInt();
+ if (N > 0) {
+ a.mAddressLines = new HashMap<Integer, String>(N);
+ for (int i = 0; i < N; i++) {
+ int index = in.readInt();
+ String line = in.readString();
+ a.mAddressLines.put(index, line);
+ a.mMaxAddressLineIndex =
+ Math.max(a.mMaxAddressLineIndex, index);
+ }
+ } else {
+ a.mAddressLines = null;
+ a.mMaxAddressLineIndex = -1;
+ }
+ a.mFeatureName = in.readString();
+ a.mAdminArea = in.readString();
+ a.mSubAdminArea = in.readString();
+ a.mLocality = in.readString();
+ a.mThoroughfare = in.readString();
+ a.mPostalCode = in.readString();
+ a.mCountryCode = in.readString();
+ a.mCountryName = in.readString();
+ a.mHasLatitude = in.readInt() == 0 ? false : true;
+ if (a.mHasLatitude) {
+ a.mLatitude = in.readDouble();
+ }
+ a.mHasLongitude = in.readInt() == 0 ? false : true;
+ if (a.mHasLongitude) {
+ a.mLongitude = in.readDouble();
+ }
+ a.mPhone = in.readString();
+ a.mUrl = in.readString();
+ a.mExtras = in.readBundle();
+ return a;
+ }
+
+ public Address[] newArray(int size) {
+ return new Address[size];
+ }
+ };
+
+ public int describeContents() {
+ return (mExtras != null) ? mExtras.describeContents() : 0;
+ }
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mLocale.getLanguage());
+ parcel.writeString(mLocale.getCountry());
+ if (mAddressLines == null) {
+ parcel.writeInt(0);
+ } else {
+ Set<Map.Entry<Integer, String>> entries = mAddressLines.entrySet();
+ parcel.writeInt(entries.size());
+ for (Map.Entry<Integer, String> e : entries) {
+ parcel.writeInt(e.getKey());
+ parcel.writeString(e.getValue());
+ }
+ }
+ parcel.writeString(mFeatureName);
+ parcel.writeString(mAdminArea);
+ parcel.writeString(mSubAdminArea);
+ parcel.writeString(mLocality);
+ parcel.writeString(mThoroughfare);
+ parcel.writeString(mPostalCode);
+ parcel.writeString(mCountryCode);
+ parcel.writeString(mCountryName);
+ parcel.writeInt(mHasLatitude ? 1 : 0);
+ if (mHasLatitude) {
+ parcel.writeDouble(mLatitude);
+ }
+ parcel.writeInt(mHasLongitude ? 1 : 0);
+ if (mHasLongitude){
+ parcel.writeDouble(mLongitude);
+ }
+ parcel.writeString(mPhone);
+ parcel.writeString(mUrl);
+ parcel.writeBundle(mExtras);
+ }
+}
diff --git a/location/java/android/location/Criteria.aidl b/location/java/android/location/Criteria.aidl
new file mode 100644
index 0000000..b9a8879
--- /dev/null
+++ b/location/java/android/location/Criteria.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.location;
+
+parcelable Criteria;
diff --git a/location/java/android/location/Criteria.java b/location/java/android/location/Criteria.java
new file mode 100644
index 0000000..9d258d0
--- /dev/null
+++ b/location/java/android/location/Criteria.java
@@ -0,0 +1,242 @@
+/*
+ * 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 android.location;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class indicating the application criteria for selecting a
+ * location provider. Providers maybe ordered according to accuracy,
+ * power usage, ability to report altitude, speed,
+ * and bearing, and monetary cost.
+ */
+public class Criteria implements Parcelable {
+ /**
+ * A constant indicating that the application does not choose to
+ * place requirement on a particular feature.
+ */
+ public static final int NO_REQUIREMENT = 0;
+
+ /**
+ * A constant indicating a low power requirement.
+ */
+ public static final int POWER_LOW = 1;
+
+ /**
+ * A constant indicating a medium power requirement.
+ */
+ public static final int POWER_MEDIUM = 2;
+
+ /**
+ * A constant indicating a high power requirement.
+ */
+ public static final int POWER_HIGH = 3;
+
+ /**
+ * A constant indicating a finer location accuracy requirement
+ */
+ public static final int ACCURACY_FINE = 1;
+
+ /**
+ * A constant indicating an approximate accuracy requirement
+ */
+ public static final int ACCURACY_COARSE = 2;
+
+ private int mAccuracy = NO_REQUIREMENT;
+ private int mPowerRequirement = NO_REQUIREMENT;
+// private int mPreferredResponseTime = NO_REQUIREMENT;
+ private boolean mAltitudeRequired = false;
+ private boolean mBearingRequired = false;
+ private boolean mSpeedRequired = false;
+ private boolean mCostAllowed = false;
+
+ /**
+ * Constructs a new Criteria object. The new object will have no
+ * requirements on accuracy, power, or response time; will not
+ * require altitude, speed, or bearing; and will not allow monetary
+ * cost.
+ */
+ public Criteria() {}
+
+ /**
+ * Constructs a new Criteria object that is a copy of the given criteria.
+ */
+ public Criteria(Criteria criteria) {
+ mAccuracy = criteria.mAccuracy;
+ mPowerRequirement = criteria.mPowerRequirement;
+// mPreferredResponseTime = criteria.mPreferredResponseTime;
+ mAltitudeRequired = criteria.mAltitudeRequired;
+ mBearingRequired = criteria.mBearingRequired;
+ mSpeedRequired = criteria.mSpeedRequired;
+ mCostAllowed = criteria.mCostAllowed;
+ }
+
+ /**
+ * Indicates the desired accuracy for latitude and longitude. Accuracy
+ * may be {@link #ACCURACY_FINE} if desired location
+ * is fine, else it can be {@link #ACCURACY_COARSE}.
+ * More accurate location usually consumes more power and may take
+ * longer.
+ *
+ * @throws IllegalArgumentException if accuracy is negative
+ */
+ public void setAccuracy(int accuracy) {
+ if (accuracy < NO_REQUIREMENT && accuracy > ACCURACY_COARSE) {
+ throw new IllegalArgumentException("accuracy=" + accuracy);
+ }
+ mAccuracy = accuracy;
+ }
+
+ /**
+ * Returns a constant indicating desired accuracy of location
+ * Accuracy may be {@link #ACCURACY_FINE} if desired location
+ * is fine, else it can be {@link #ACCURACY_COARSE}.
+ */
+ public int getAccuracy() {
+ return mAccuracy;
+ }
+
+ /**
+ * Indicates the desired maximum power level. The level parameter
+ * must be one of NO_REQUIREMENT, POWER_LOW, POWER_MEDIUM, or
+ * POWER_HIGH.
+ */
+ public void setPowerRequirement(int level) {
+ if (level < NO_REQUIREMENT || level > POWER_HIGH) {
+ throw new IllegalArgumentException("level=" + level);
+ }
+ mPowerRequirement = level;
+ }
+
+ /**
+ * Returns a constant indicating the desired power requirement. The
+ * returned
+ */
+ public int getPowerRequirement() {
+ return mPowerRequirement;
+ }
+
+// /**
+// * Indicates the preferred response time of the provider, in milliseconds.
+// */
+// public void setPreferredResponseTime(int time) {
+// mPreferredResponseTime = time;
+// }
+//
+// /**
+// * Returns the preferred response time of the provider, in milliseconds.
+// */
+// public int getPreferredResponseTime() {
+// return mPreferredResponseTime;
+// }
+
+ /**
+ * Indicates whether the provider is allowed to incur monetary cost.
+ */
+ public void setCostAllowed(boolean costAllowed) {
+ mCostAllowed = costAllowed;
+ }
+
+ /**
+ * Returns whether the provider is allowed to incur monetary cost.
+ */
+ public boolean isCostAllowed() {
+ return mCostAllowed;
+ }
+
+ /**
+ * Indicates whether the provider must provide altitude information.
+ * Not all fixes are guaranteed to contain such information.
+ */
+ public void setAltitudeRequired(boolean altitudeRequired) {
+ mAltitudeRequired = altitudeRequired;
+ }
+
+ /**
+ * Returns whether the provider must provide altitude information.
+ * Not all fixes are guaranteed to contain such information.
+ */
+ public boolean isAltitudeRequired() {
+ return mAltitudeRequired;
+ }
+
+ /**
+ * Indicates whether the provider must provide speed information.
+ * Not all fixes are guaranteed to contain such information.
+ */
+ public void setSpeedRequired(boolean speedRequired) {
+ mSpeedRequired = speedRequired;
+ }
+
+ /**
+ * Returns whether the provider must provide speed information.
+ * Not all fixes are guaranteed to contain such information.
+ */
+ public boolean isSpeedRequired() {
+ return mSpeedRequired;
+ }
+
+ /**
+ * Indicates whether the provider must provide bearing information.
+ * Not all fixes are guaranteed to contain such information.
+ */
+ public void setBearingRequired(boolean bearingRequired) {
+ mBearingRequired = bearingRequired;
+ }
+
+ /**
+ * Returns whether the provider must provide bearing information.
+ * Not all fixes are guaranteed to contain such information.
+ */
+ public boolean isBearingRequired() {
+ return mBearingRequired;
+ }
+
+ public static final Parcelable.Creator<Criteria> CREATOR =
+ new Parcelable.Creator<Criteria>() {
+ public Criteria createFromParcel(Parcel in) {
+ Criteria c = new Criteria();
+ c.mAccuracy = in.readInt();
+ c.mPowerRequirement = in.readInt();
+// c.mPreferredResponseTime = in.readInt();
+ c.mAltitudeRequired = in.readInt() != 0;
+ c.mBearingRequired = in.readInt() != 0;
+ c.mSpeedRequired = in.readInt() != 0;
+ c.mCostAllowed = in.readInt() != 0;
+ return c;
+ }
+
+ public Criteria[] newArray(int size) {
+ return new Criteria[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mAccuracy);
+ parcel.writeInt(mPowerRequirement);
+// parcel.writeInt(mPreferredResponseTime);
+ parcel.writeInt(mAltitudeRequired ? 1 : 0);
+ parcel.writeInt(mBearingRequired ? 1 : 0);
+ parcel.writeInt(mSpeedRequired ? 1 : 0);
+ parcel.writeInt(mCostAllowed ? 1 : 0);
+ }
+}
diff --git a/location/java/android/location/DummyLocationProvider.java b/location/java/android/location/DummyLocationProvider.java
new file mode 100644
index 0000000..e1cd4e9
--- /dev/null
+++ b/location/java/android/location/DummyLocationProvider.java
@@ -0,0 +1,168 @@
+/*
+ * 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 android.location;
+
+/**
+ * A stub implementation of LocationProvider used by LocationManager.
+ * A DummyLocationProvider may be queried to determine the properties
+ * of the provider whcih it shadows, but does not actually provide location
+ * data.
+ *
+ * {@hide}
+ */
+class DummyLocationProvider extends LocationProvider {
+
+ private static final String TAG = "DummyLocationProvider";
+
+ String mName;
+ boolean mRequiresNetwork;
+ boolean mRequiresSatellite;
+ boolean mRequiresCell;
+ boolean mHasMonetaryCost;
+ boolean mSupportsAltitude;
+ boolean mSupportsSpeed;
+ boolean mSupportsBearing;
+ int mPowerRequirement;
+ int mAccuracy;
+
+ /* package */ DummyLocationProvider(String name) {
+ super(name);
+ }
+
+ public void setRequiresNetwork(boolean requiresNetwork) {
+ mRequiresNetwork = requiresNetwork;
+ }
+
+ public void setRequiresSatellite(boolean requiresSatellite) {
+ mRequiresSatellite = requiresSatellite;
+ }
+
+ public void setRequiresCell(boolean requiresCell) {
+ mRequiresCell = requiresCell;
+ }
+
+ public void setHasMonetaryCost(boolean hasMonetaryCost) {
+ mHasMonetaryCost = hasMonetaryCost;
+ }
+
+ public void setSupportsAltitude(boolean supportsAltitude) {
+ mSupportsAltitude = supportsAltitude;
+ }
+
+ public void setSupportsSpeed(boolean supportsSpeed) {
+ mSupportsSpeed = supportsSpeed;
+ }
+
+ public void setSupportsBearing(boolean supportsBearing) {
+ mSupportsBearing = supportsBearing;
+ }
+
+ public void setPowerRequirement(int powerRequirement) {
+ mPowerRequirement = powerRequirement;
+ }
+
+ public void setAccuracy(int accuracy) {
+ mAccuracy = accuracy;
+ }
+
+ /**
+ * Returns true if the provider requires access to a
+ * data network (e.g., the Internet), false otherwise.
+ */
+ public boolean requiresNetwork() {
+ return mRequiresNetwork;
+ }
+
+ /**
+ * Returns true if the provider requires access to a
+ * satellite-based positioning system (e.g., GPS), false
+ * otherwise.
+ */
+ public boolean requiresSatellite() {
+ return mRequiresSatellite;
+ }
+
+ /**
+ * Returns true if the provider requires access to an appropriate
+ * cellular network (e.g., to make use of cell tower IDs), false
+ * otherwise.
+ */
+ public boolean requiresCell() {
+ return mRequiresCell;
+ }
+
+ /**
+ * 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 boolean hasMonetaryCost() {
+ return mHasMonetaryCost;
+ }
+
+ /**
+ * 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 boolean supportsAltitude() {
+ return mSupportsAltitude;
+ }
+
+ /**
+ * 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 boolean supportsSpeed() {
+ return mSupportsSpeed;
+ }
+
+ /**
+ * 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 boolean supportsBearing() {
+ return mSupportsBearing;
+ }
+
+ /**
+ * Returns the power requirement for this provider.
+ *
+ * @return the power requirement for this provider, as one of the
+ * constants Criteria.POWER_REQUIREMENT_*.
+ */
+ public int getPowerRequirement() {
+ return mPowerRequirement;
+ }
+
+ /**
+ * Returns a constant describing the horizontal accuracy returned
+ * by this provider.
+ *
+ * @return the horizontal accuracy for this provider, as one of the
+ * constants Criteria.ACCURACY_*.
+ */
+ public int getAccuracy() {
+ return mAccuracy;
+ }
+}
+
diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java
new file mode 100644
index 0000000..92a19e4
--- /dev/null
+++ b/location/java/android/location/Geocoder.java
@@ -0,0 +1,242 @@
+/*
+ * 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 android.location;
+
+import android.content.Context;
+import android.location.Address;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class for handling geocoding and reverse geocoding. Geocoding is
+ * the process of transforming a street address or other description
+ * of a location into a (latitude, longitude) coordinate. Reverse
+ * geocoding is the process of transforming a (latitude, longitude)
+ * coordinate into a (partial) address. The amount of detail in a
+ * reverse geocoded location description may vary, for example one
+ * might contain the full street address of the closest building, while
+ * another might contain only a city name and postal code.
+ */
+public final class Geocoder {
+ private static final String TAG = "Geocoder";
+
+ private String mLanguage;
+ private String mCountry;
+ private String mVariant;
+ private String mAppName;
+ private ILocationManager mService;
+
+ /**
+ * Constructs a Geocoder whose responses will be localized for the
+ * given Locale.
+ *
+ * @param context the Context of the calling Activity
+ * @param locale the desired Locale for the query results
+ *
+ * @throws NullPointerException if Locale is null
+ */
+ public Geocoder(Context context, Locale locale) {
+ if (locale == null) {
+ throw new NullPointerException("locale == null");
+ }
+ mLanguage = locale.getLanguage();
+ mCountry = locale.getCountry();
+ mVariant = locale.getVariant();
+ mAppName = context.getPackageName();
+
+ IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
+ mService = ILocationManager.Stub.asInterface(b);
+ }
+
+ /**
+ * Constructs a Geocoder whose responses will be localized for the
+ * default system Locale.
+ *
+ * @param context the Context of the calling Activity
+ */
+ public Geocoder(Context context) {
+ this(context, Locale.getDefault());
+ }
+
+ /**
+ * Returns an array of Addresses that are known to describe the
+ * area immediately surrounding the given latitude and longitude.
+ * The returned addresses will be localized for the locale
+ * provided to this class's constructor.
+ *
+ * <p> The returned values may be obtained by means of a network lookup.
+ * The results are a best guess and are not guaranteed to be meaningful or
+ * correct. It may be useful to call this method from a thread separate from your
+ * primary UI thread.
+ *
+ * @param latitude the latitude a point for the search
+ * @param longitude the longitude a point for the search
+ * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
+ *
+ * @return a list of Address objects or null if no matches were
+ * found.
+ *
+ * @throws IllegalArgumentException if latitude is
+ * less than -90 or greater than 90
+ * @throws IllegalArgumentException if longitude is
+ * less than -180 or greater than 180
+ * @throws IOException if the network is unavailable or any other
+ * I/O problem occurs
+ */
+ public List<Address> getFromLocation(double latitude, double longitude, int maxResults)
+ throws IOException {
+ if (latitude < -90.0 || latitude > 90.0) {
+ throw new IllegalArgumentException("latitude == " + latitude);
+ }
+ if (longitude < -180.0 || longitude > 180.0) {
+ throw new IllegalArgumentException("longitude == " + longitude);
+ }
+ try {
+ List<Address> results = new ArrayList<Address>();
+ String ex = mService.getFromLocation(latitude, longitude, maxResults,
+ mLanguage, mCountry, mVariant, mAppName, results);
+ if (ex != null) {
+ throw new IOException(ex);
+ } else {
+ return results;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getFromLocation: got RemoteException", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns an array of Addresses that are known to describe the
+ * named location, which may be a place name such as "Dalvik,
+ * Iceland", an address such as "1600 Amphitheatre Parkway,
+ * Mountain View, CA", an airport code such as "SFO", etc.. The
+ * returned addresses will be localized for the locale provided to
+ * this class's constructor.
+ *
+ * <p> The query will block and returned values will be obtained by means of a network lookup.
+ * The results are a best guess and are not guaranteed to be meaningful or
+ * correct. It may be useful to call this method from a thread separate from your
+ * primary UI thread.
+ *
+ * @param locationName a user-supplied description of a location
+ * @param maxResults max number of results to return. Smaller numbers (1 to 5) are recommended
+ *
+ * @return a list of Address objects or null if no matches were found.
+ *
+ * @throws IllegalArgumentException if locationName is null
+ * @throws IOException if the network is unavailable or any other
+ * I/O problem occurs
+ */
+ public List<Address> getFromLocationName(String locationName, int maxResults) throws IOException {
+ if (locationName == null) {
+ throw new IllegalArgumentException("locationName == null");
+ }
+ try {
+ List<Address> results = new ArrayList<Address>();
+ String ex = mService.getFromLocationName(locationName,
+ 0, 0, 0, 0, maxResults, mLanguage, mCountry, mVariant, mAppName, results);
+ if (ex != null) {
+ throw new IOException(ex);
+ } else {
+ return results;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getFromLocationName: got RemoteException", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns an array of Addresses that are known to describe the
+ * named location, which may be a place name such as "Dalvik,
+ * Iceland", an address such as "1600 Amphitheatre Parkway,
+ * Mountain View, CA", an airport code such as "SFO", etc.. The
+ * returned addresses will be localized for the locale provided to
+ * this class's constructor.
+ *
+ * <p> You may specify a bounding box for the search results by including
+ * the Latitude and Longitude of the Lower Left point and Upper Right
+ * point of the box.
+ *
+ * <p> The query will block and returned values will be obtained by means of a network lookup.
+ * The results are a best guess and are not guaranteed to be meaningful or
+ * correct. It may be useful to call this method from a thread separate from your
+ * primary UI thread.
+ *
+ * @param locationName a user-supplied description of a location
+ * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
+ * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box
+ * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box
+ * @param upperRightLatitude the latitude of the upper right corner of the bounding box
+ * @param upperRightLongitude the longitude of the upper right corner of the bounding box
+ *
+ * @return a list of Address objects or null if no matches were found.
+ *
+ * @throws IllegalArgumentException if locationName is null
+ * @throws IllegalArgumentException if any latitude is
+ * less than -90 or greater than 90
+ * @throws IllegalArgumentException if any longitude is
+ * less than -180 or greater than 180
+ * @throws IOException if the network is unavailable or any other
+ * I/O problem occurs
+ */
+ public List<Address> getFromLocationName(String locationName, int maxResults,
+ double lowerLeftLatitude, double lowerLeftLongitude,
+ double upperRightLatitude, double upperRightLongitude) throws IOException {
+ if (locationName == null) {
+ throw new IllegalArgumentException("locationName == null");
+ }
+ if (lowerLeftLatitude < -90.0 || lowerLeftLatitude > 90.0) {
+ throw new IllegalArgumentException("lowerLeftLatitude == "
+ + lowerLeftLatitude);
+ }
+ if (lowerLeftLongitude < -180.0 || lowerLeftLongitude > 180.0) {
+ throw new IllegalArgumentException("lowerLeftLongitude == "
+ + lowerLeftLongitude);
+ }
+ if (upperRightLatitude < -90.0 || upperRightLatitude > 90.0) {
+ throw new IllegalArgumentException("upperRightLatitude == "
+ + upperRightLatitude);
+ }
+ if (upperRightLongitude < -180.0 || upperRightLongitude > 180.0) {
+ throw new IllegalArgumentException("upperRightLongitude == "
+ + upperRightLongitude);
+ }
+ try {
+ ArrayList<Address> result = new ArrayList<Address>();
+ String ex = mService.getFromLocationName(locationName,
+ lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
+ maxResults, mLanguage, mCountry, mVariant, mAppName, new ArrayList<Address>());
+ if (ex != null) {
+ throw new IOException(ex);
+ } else {
+ return result;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getFromLocationName: got RemoteException", e);
+ return null;
+ }
+ }
+}
diff --git a/location/java/android/location/GpsStatusListener.java b/location/java/android/location/GpsStatusListener.java
new file mode 100644
index 0000000..e494887
--- /dev/null
+++ b/location/java/android/location/GpsStatusListener.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008 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;
+
+/**
+ * Used for receiving notifications from the SensorManager when
+ * sensor values have changed.
+ *
+ * @hide
+ */
+public interface GpsStatusListener {
+
+ /**
+ * Called when the GPS has started.
+ */
+ void onGpsStarted();
+
+ /**
+ * Called when the GPS has stopped.
+ */
+ void onGpsStopped();
+
+ /**
+ * Called when the GPS status has received its first fix since starting.
+ *
+ * @param ttff Time to first fix in milliseconds.
+ */
+ void onFirstFix(int ttff);
+
+ /**
+ * Called when the GPS SV status has changed.
+ *
+ * @param svCount The number of visible SVs
+ * @param prns Array of SV prns. Length of array is svCount.
+ * @param snrs Array of signal to noise ratios for SVs, in 1/10 dB units. Length of array is svCount.
+ * @param elevations Array of SV elevations in degrees. Length of array is svCount.
+ * @param azimuths Array of SV azimuths in degrees. Length of array is svCount.
+ * @param ephemerisMask Bit mask indicating which SVs the GPS has ephemeris data for.
+ * @param almanacMask Bit mask indicating which SVs the GPS has almanac data for.
+ * @param usedInFixMask Bit mask indicating which SVs were used in the most recent GPS fix.
+ */
+ public void onSvStatusChanged(int svCount, int[] prns, float[] snrs, float[] elevations,
+ float[] azimuths, int ephemerisMask, int almanacMask, int usedInFixMask);
+}
diff --git a/location/java/android/location/IGpsStatusListener.aidl b/location/java/android/location/IGpsStatusListener.aidl
new file mode 100644
index 0000000..5dc0fe8
--- /dev/null
+++ b/location/java/android/location/IGpsStatusListener.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008, 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;
+
+/**
+ * {@hide}
+ */
+oneway interface IGpsStatusListener
+{
+ void onGpsStarted();
+ void onGpsStopped();
+ void onFirstFix(int ttff);
+ void onSvStatusChanged(int svCount, in int[] prns, in float[] snrs,
+ in float[] elevations, in float[] azimuths,
+ int ephemerisMask, int almanacMask, int usedInFixMask);
+}
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
new file mode 100644
index 0000000..7627cf6
--- /dev/null
+++ b/location/java/android/location/ILocationListener.aidl
@@ -0,0 +1,32 @@
+/* //device/java/android/android/location/ILocationListener.aidl
+**
+** Copyright 2008, 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.os.Bundle;
+
+/**
+ * {@hide}
+ */
+oneway interface ILocationListener
+{
+ void onLocationChanged(in Location location);
+ void onStatusChanged(String provider, int status, in Bundle extras);
+ void onProviderEnabled(String provider);
+ void onProviderDisabled(String provider);
+}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
new file mode 100644
index 0000000..d3eefeb
--- /dev/null
+++ b/location/java/android/location/ILocationManager.aidl
@@ -0,0 +1,73 @@
+/*
+ * 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 android.location;
+
+import android.app.PendingIntent;
+import android.location.Address;
+import android.location.IGpsStatusListener;
+import android.location.ILocationListener;
+import android.location.Location;
+import android.os.Bundle;
+
+/**
+ * System private API for talking with the location service.
+ *
+ * {@hide}
+ */
+interface ILocationManager
+{
+ List getAllProviders();
+ List getProviders(boolean enabledOnly);
+
+ void updateProviders();
+
+ void requestLocationUpdates(String provider, long minTime, float minDistance,
+ in ILocationListener listener);
+ void removeUpdates(in ILocationListener listener);
+
+ boolean addGpsStatusListener(IGpsStatusListener listener);
+ void removeGpsStatusListener(IGpsStatusListener listener);
+
+ boolean sendExtraCommand(String provider, String command, inout Bundle extras);
+
+ void addProximityAlert(double latitude, double longitude, float distance,
+ long expiration, in PendingIntent intent);
+ void removeProximityAlert(in PendingIntent intent);
+
+ Bundle getProviderInfo(String provider);
+ boolean isProviderEnabled(String provider);
+
+ Location getLastKnownLocation(String provider);
+
+ String getFromLocation(double latitude, double longitude, int maxResults,
+ String language, String country, String variant, String appName, 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);
+
+ void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
+ boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
+ boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy);
+ void removeTestProvider(String provider);
+ void setTestProviderLocation(String provider, in Location loc);
+ void clearTestProviderLocation(String provider);
+ void setTestProviderEnabled(String provider, boolean enabled);
+ void clearTestProviderEnabled(String provider);
+ void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime);
+ void clearTestProviderStatus(String provider);
+}
diff --git a/location/java/android/location/Location.aidl b/location/java/android/location/Location.aidl
new file mode 100644
index 0000000..f47b488
--- /dev/null
+++ b/location/java/android/location/Location.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.location;
+
+parcelable Location;
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
new file mode 100644
index 0000000..d381f6e
--- /dev/null
+++ b/location/java/android/location/Location.java
@@ -0,0 +1,730 @@
+/*
+ * 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 android.location;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.text.DecimalFormat;
+import java.util.StringTokenizer;
+
+/**
+ * A class representing a geographic location sensed at a particular
+ * time (a "fix"). A location consists of a latitude and longitude, a
+ * UTC timestamp. and optionally information on altitude, speed, and
+ * bearing.
+ *
+ * <p> Information specific to a particular provider or class of
+ * providers may be communicated to the application using getExtras,
+ * which returns a Bundle of key/value pairs. Each provider will only
+ * provide those entries for which information is available.
+ */
+public class Location implements Parcelable {
+ /**
+ * Constant used to specify formatting of a latitude or longitude
+ * in the form "[+-]DDD.DDDDD where D indicates degrees.
+ */
+ public static final int FORMAT_DEGREES = 0;
+
+ /**
+ * Constant used to specify formatting of a latitude or longitude
+ * in the form "[+-]DDD:MM.MMMMM" where D indicates degrees and
+ * M indicates minutes of arc (1 minute = 1/60th of a degree).
+ */
+ public static final int FORMAT_MINUTES = 1;
+
+ /**
+ * Constant used to specify formatting of a latitude or longitude
+ * in the form "DDD:MM:SS.SSSSS" where D indicates degrees, M
+ * indicates minutes of arc, and S indicates seconds of arc (1
+ * minute = 1/60th of a degree, 1 second = 1/3600th of a degree).
+ */
+ public static final int FORMAT_SECONDS = 2;
+
+ private String mProvider;
+ private long mTime = 0;
+ private double mLatitude = 0.0;
+ private double mLongitude = 0.0;
+ private boolean mHasAltitude = false;
+ private double mAltitude = 0.0f;
+ private boolean mHasSpeed = false;
+ private float mSpeed = 0.0f;
+ private boolean mHasBearing = false;
+ private float mBearing = 0.0f;
+ private boolean mHasAccuracy = false;
+ private float mAccuracy = 0.0f;
+ private Bundle mExtras = null;
+
+ // Cache the inputs and outputs of computeDistanceAndBearing
+ // so calls to distanceTo() and bearingTo() can share work
+ private double mLat1 = 0.0;
+ private double mLon1 = 0.0;
+ private double mLat2 = 0.0;
+ private double mLon2 = 0.0;
+ private float mDistance = 0.0f;
+ private float mInitialBearing = 0.0f;
+ // Scratchpad
+ private float[] mResults = new float[2];
+
+ /**
+ * Constructs a new Location. By default, time, latitude,
+ * longitude, and numSatellites are 0; hasAltitude, hasSpeed, and
+ * hasBearing are false; and there is no extra information.
+ *
+ * @param provider the name of the location provider that generated this
+ * location fix.
+ */
+ public Location(String provider) {
+ mProvider = provider;
+ }
+
+ /**
+ * Constructs a new Location object that is a copy of the given
+ * location.
+ */
+ public Location(Location l) {
+ set(l);
+ }
+
+ /**
+ * Sets the contents of the location to the values from the given location.
+ */
+ public void set(Location l) {
+ mProvider = l.mProvider;
+ mTime = l.mTime;
+ mLatitude = l.mLatitude;
+ mLongitude = l.mLongitude;
+ mHasAltitude = l.mHasAltitude;
+ mAltitude = l.mAltitude;
+ mHasSpeed = l.mHasSpeed;
+ mSpeed = l.mSpeed;
+ mHasBearing = l.mHasBearing;
+ mBearing = l.mBearing;
+ mHasAccuracy = l.mHasAccuracy;
+ mAccuracy = l.mAccuracy;
+ mExtras = (l.mExtras == null) ? null : new Bundle(l.mExtras);
+ }
+
+ /**
+ * Clears the contents of the location.
+ */
+ public void reset() {
+ mProvider = null;
+ mTime = 0;
+ mLatitude = 0;
+ mLongitude = 0;
+ mHasAltitude = false;
+ mAltitude = 0;
+ mHasSpeed = false;
+ mSpeed = 0;
+ mHasBearing = false;
+ mBearing = 0;
+ mHasAccuracy = false;
+ mAccuracy = 0;
+ mExtras = null;
+ }
+
+ /**
+ * Converts a coordinate to a String representation. The outputType
+ * may be one of FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS.
+ * The coordinate must be a valid double between -180.0 and 180.0.
+ *
+ * @throws IllegalArgumentException if coordinate is less than
+ * -180.0, greater than 180.0, or is not a number.
+ * @throws IllegalArgumentException if outputType is not one of
+ * FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS.
+ */
+ public static String convert(double coordinate, int outputType) {
+ if (coordinate < -180.0 || coordinate > 180.0 ||
+ Double.isNaN(coordinate)) {
+ throw new IllegalArgumentException("coordinate=" + coordinate);
+ }
+ if ((outputType != FORMAT_DEGREES) &&
+ (outputType != FORMAT_MINUTES) &&
+ (outputType != FORMAT_SECONDS)) {
+ throw new IllegalArgumentException("outputType=" + outputType);
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ // Handle negative values
+ if (coordinate < 0) {
+ sb.append('-');
+ coordinate = -coordinate;
+ }
+
+ DecimalFormat df = new DecimalFormat("###.#####");
+ if (outputType == FORMAT_MINUTES || outputType == FORMAT_SECONDS) {
+ int degrees = (int) Math.floor(coordinate);
+ sb.append(degrees);
+ sb.append(':');
+ coordinate -= degrees;
+ coordinate *= 60.0;
+ if (outputType == FORMAT_SECONDS) {
+ int minutes = (int) Math.floor(coordinate);
+ sb.append(minutes);
+ sb.append(':');
+ coordinate -= minutes;
+ coordinate *= 60.0;
+ }
+ }
+ sb.append(df.format(coordinate));
+ return sb.toString();
+ }
+
+ /**
+ * Converts a String in one of the formats described by
+ * FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS into a
+ * double.
+ *
+ * @throws NullPointerException if coordinate is null
+ * @throws IllegalArgumentException if the coordinate is not
+ * in one of the valid formats.
+ */
+ public static double convert(String coordinate) {
+ // IllegalArgumentException if bad syntax
+ if (coordinate == null) {
+ throw new NullPointerException("coordinate");
+ }
+
+ boolean negative = false;
+ if (coordinate.charAt(0) == '-') {
+ coordinate = coordinate.substring(1);
+ negative = true;
+ }
+
+ StringTokenizer st = new StringTokenizer(coordinate, ":");
+ int tokens = st.countTokens();
+ if (tokens < 1) {
+ throw new IllegalArgumentException("coordinate=" + coordinate);
+ }
+ try {
+ String degrees = st.nextToken();
+ double val;
+ if (tokens == 1) {
+ val = Double.parseDouble(degrees);
+ return negative ? -val : val;
+ }
+
+ String minutes = st.nextToken();
+ int deg = Integer.parseInt(degrees);
+ double min;
+ double sec = 0.0;
+
+ if (st.hasMoreTokens()) {
+ min = Integer.parseInt(minutes);
+ String seconds = st.nextToken();
+ sec = Double.parseDouble(seconds);
+ } else {
+ min = Double.parseDouble(minutes);
+ }
+
+ boolean isNegative180 = negative && (deg == 180) &&
+ (min == 0) && (sec == 0);
+
+ // deg must be in [0, 179] except for the case of -180 degrees
+ if ((deg < 0.0) || (deg > 179 && !isNegative180)) {
+ throw new IllegalArgumentException("coordinate=" + coordinate);
+ }
+ if (min < 0 || min > 59) {
+ throw new IllegalArgumentException("coordinate=" +
+ coordinate);
+ }
+ if (sec < 0 || sec > 59) {
+ throw new IllegalArgumentException("coordinate=" +
+ coordinate);
+ }
+
+ val = deg*3600.0 + min*60.0 + sec;
+ val /= 3600.0;
+ return negative ? -val : val;
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("coordinate=" + coordinate);
+ }
+ }
+
+ private static void computeDistanceAndBearing(double lat1, double lon1,
+ double lat2, double lon2, float[] results) {
+ // Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
+ // using the "Inverse Formula" (section 4)
+
+ int MAXITERS = 20;
+ // Convert lat/long to radians
+ lat1 *= Math.PI / 180.0;
+ lat2 *= Math.PI / 180.0;
+ lon1 *= Math.PI / 180.0;
+ lon2 *= Math.PI / 180.0;
+
+ double a = 6378137.0; // WGS84 major axis
+ double b = 6356752.3142; // WGS84 semi-major axis
+ double f = (a - b) / a;
+ double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);
+
+ double L = lon2 - lon1;
+ double A = 0.0;
+ double U1 = Math.atan((1.0 - f) * Math.tan(lat1));
+ double U2 = Math.atan((1.0 - f) * Math.tan(lat2));
+
+ double cosU1 = Math.cos(U1);
+ double cosU2 = Math.cos(U2);
+ double sinU1 = Math.sin(U1);
+ double sinU2 = Math.sin(U2);
+ double cosU1cosU2 = cosU1 * cosU2;
+ double sinU1sinU2 = sinU1 * sinU2;
+
+ double sigma = 0.0;
+ double deltaSigma = 0.0;
+ double cosSqAlpha = 0.0;
+ double cos2SM = 0.0;
+ double cosSigma = 0.0;
+ double sinSigma = 0.0;
+ double cosLambda = 0.0;
+ double sinLambda = 0.0;
+
+ double lambda = L; // initial guess
+ for (int iter = 0; iter < MAXITERS; iter++) {
+ double lambdaOrig = lambda;
+ cosLambda = Math.cos(lambda);
+ sinLambda = Math.sin(lambda);
+ double t1 = cosU2 * sinLambda;
+ double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
+ double sinSqSigma = t1 * t1 + t2 * t2; // (14)
+ sinSigma = Math.sqrt(sinSqSigma);
+ cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15)
+ sigma = Math.atan2(sinSigma, cosSigma); // (16)
+ double sinAlpha = (sinSigma == 0) ? 0.0 :
+ cosU1cosU2 * sinLambda / sinSigma; // (17)
+ cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
+ cos2SM = (cosSqAlpha == 0) ? 0.0 :
+ cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha; // (18)
+
+ double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn
+ A = 1 + (uSquared / 16384.0) * // (3)
+ (4096.0 + uSquared *
+ (-768 + uSquared * (320.0 - 175.0 * uSquared)));
+ double B = (uSquared / 1024.0) * // (4)
+ (256.0 + uSquared *
+ (-128.0 + uSquared * (74.0 - 47.0 * uSquared)));
+ double C = (f / 16.0) *
+ cosSqAlpha *
+ (4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10)
+ double cos2SMSq = cos2SM * cos2SM;
+ deltaSigma = B * sinSigma * // (6)
+ (cos2SM + (B / 4.0) *
+ (cosSigma * (-1.0 + 2.0 * cos2SMSq) -
+ (B / 6.0) * cos2SM *
+ (-3.0 + 4.0 * sinSigma * sinSigma) *
+ (-3.0 + 4.0 * cos2SMSq)));
+
+ lambda = L +
+ (1.0 - C) * f * sinAlpha *
+ (sigma + C * sinSigma *
+ (cos2SM + C * cosSigma *
+ (-1.0 + 2.0 * cos2SM * cos2SM))); // (11)
+
+ double delta = (lambda - lambdaOrig) / lambda;
+ if (Math.abs(delta) < 1.0e-12) {
+ break;
+ }
+ }
+
+ float distance = (float) (b * A * (sigma - deltaSigma));
+ results[0] = distance;
+ if (results.length > 1) {
+ float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
+ cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
+ initialBearing *= 180.0 / Math.PI;
+ results[1] = initialBearing;
+ if (results.length > 2) {
+ float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
+ -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
+ finalBearing *= 180.0 / Math.PI;
+ results[2] = finalBearing;
+ }
+ }
+ }
+
+ /**
+ * Computes the approximate distance in meters between two
+ * locations, and optionally the initial and final bearings of the
+ * shortest path between them. Distance and bearing are defined using the
+ * WGS84 ellipsoid.
+ *
+ * <p> The computed distance is stored in results[0]. If results has length
+ * 2 or greater, the initial bearing is stored in results[1]. If results has
+ * length 3 or greater, the final bearing is stored in results[2].
+ *
+ * @param startLatitude the starting latitude
+ * @param startLongitude the starting longitude
+ * @param endLatitude the ending latitude
+ * @param endLongitude the ending longitude
+ * @param results an array of floats to hold the results
+ *
+ * @throws IllegalArgumentException if results is null or has length < 1
+ */
+ public static void distanceBetween(double startLatitude, double startLongitude,
+ double endLatitude, double endLongitude, float[] results) {
+ if (results == null || results.length < 1) {
+ throw new IllegalArgumentException("results is null or has length < 1");
+ }
+ computeDistanceAndBearing(startLatitude, startLongitude,
+ endLatitude, endLongitude, results);
+ }
+
+ /**
+ * Returns the approximate distance in meters between this
+ * location and the given location. Distance is defined using
+ * the WGS84 ellipsoid.
+ *
+ * @param dest the destination location
+ * @return the approximate distance in meters
+ */
+ public float distanceTo(Location dest) {
+ // See if we already have the result
+ synchronized (mResults) {
+ if (mLatitude != mLat1 || mLongitude != mLon1 ||
+ dest.mLatitude != mLat2 || dest.mLongitude != mLon2) {
+ computeDistanceAndBearing(mLatitude, mLongitude,
+ dest.mLatitude, dest.mLongitude, mResults);
+ mLat1 = mLatitude;
+ mLon1 = mLongitude;
+ mLat2 = dest.mLatitude;
+ mLon2 = dest.mLongitude;
+ mDistance = mResults[0];
+ mInitialBearing = mResults[1];
+ }
+ return mDistance;
+ }
+ }
+
+ /**
+ * Returns the approximate initial bearing in degrees East of true
+ * North when traveling along the shortest path between this
+ * location and the given location. The shortest path is defined
+ * using the WGS84 ellipsoid. Locations that are (nearly)
+ * antipodal may produce meaningless results.
+ *
+ * @param dest the destination location
+ * @return the initial bearing in degrees
+ */
+ public float bearingTo(Location dest) {
+ synchronized (mResults) {
+ // See if we already have the result
+ if (mLatitude != mLat1 || mLongitude != mLon1 ||
+ dest.mLatitude != mLat2 || dest.mLongitude != mLon2) {
+ computeDistanceAndBearing(mLatitude, mLongitude,
+ dest.mLatitude, dest.mLongitude, mResults);
+ mLat1 = mLatitude;
+ mLon1 = mLongitude;
+ mLat2 = dest.mLatitude;
+ mLon2 = dest.mLongitude;
+ mDistance = mResults[0];
+ mInitialBearing = mResults[1];
+ }
+ return mInitialBearing;
+ }
+ }
+
+ /**
+ * Returns the name of the provider that generated this fix,
+ * or null if it is not associated with a provider.
+ */
+ public String getProvider() {
+ return mProvider;
+ }
+
+ /**
+ * Sets the name of the provider that generated this fix.
+ */
+ public void setProvider(String provider) {
+ mProvider = provider;
+ }
+
+ /**
+ * Returns the UTC time of this fix, in milliseconds since January 1,
+ * 1970.
+ */
+ public long getTime() {
+ return mTime;
+ }
+
+ /**
+ * Sets the UTC time of this fix, in milliseconds since January 1,
+ * 1970.
+ */
+ public void setTime(long time) {
+ mTime = time;
+ }
+
+ /**
+ * Returns the latitude of this fix.
+ */
+ public double getLatitude() {
+ return mLatitude;
+ }
+
+ /**
+ * Sets the latitude of this fix.
+ */
+ public void setLatitude(double latitude) {
+ mLatitude = latitude;
+ }
+
+ /**
+ * Returns the longitude of this fix.
+ */
+ public double getLongitude() {
+ return mLongitude;
+ }
+
+ /**
+ * Sets the longitude of this fix.
+ */
+ public void setLongitude(double longitude) {
+ mLongitude = longitude;
+ }
+
+ /**
+ * Returns true if this fix contains altitude information, false
+ * otherwise.
+ */
+ public boolean hasAltitude() {
+ return mHasAltitude;
+ }
+
+ /**
+ * Returns the altitude of this fix. If {@link #hasAltitude} is false,
+ * 0.0f is returned.
+ */
+ public double getAltitude() {
+ return mAltitude;
+ }
+
+ /**
+ * Sets the altitude of this fix. Following this call,
+ * hasAltitude() will return true.
+ */
+ public void setAltitude(double altitude) {
+ mAltitude = altitude;
+ mHasAltitude = true;
+ }
+
+ /**
+ * Clears the altitude of this fix. Following this call,
+ * hasAltitude() will return false.
+ */
+ public void removeAltitude() {
+ mAltitude = 0.0f;
+ mHasAltitude = false;
+ }
+
+ /**
+ * Returns true if this fix contains speed information, false
+ * otherwise. The default implementation returns false.
+ */
+ public boolean hasSpeed() {
+ return mHasSpeed;
+ }
+
+ /**
+ * Returns the speed of the device over ground in meters/second.
+ * If hasSpeed() is false, 0.0f is returned.
+ */
+ public float getSpeed() {
+ return mSpeed;
+ }
+
+ /**
+ * Sets the speed of this fix, in meters/second. Following this
+ * call, hasSpeed() will return true.
+ */
+ public void setSpeed(float speed) {
+ mSpeed = speed;
+ mHasSpeed = true;
+ }
+
+ /**
+ * Clears the speed of this fix. Following this call, hasSpeed()
+ * will return false.
+ */
+ public void removeSpeed() {
+ mSpeed = 0.0f;
+ mHasSpeed = false;
+ }
+
+ /**
+ * Returns true if the provider is able to report bearing information,
+ * false otherwise. The default implementation returns false.
+ */
+ public boolean hasBearing() {
+ return mHasBearing;
+ }
+
+ /**
+ * Returns the direction of travel in degrees East of true
+ * North. If hasBearing() is false, 0.0 is returned.
+ */
+ public float getBearing() {
+ return mBearing;
+ }
+
+ /**
+ * Sets the bearing of this fix. Following this call, hasBearing()
+ * will return true.
+ */
+ public void setBearing(float bearing) {
+ while (bearing < 0.0f) {
+ bearing += 360.0f;
+ }
+ while (bearing >= 360.0f) {
+ bearing -= 360.0f;
+ }
+ mBearing = bearing;
+ mHasBearing = true;
+ }
+
+ /**
+ * Clears the bearing of this fix. Following this call, hasBearing()
+ * will return false.
+ */
+ public void removeBearing() {
+ mBearing = 0.0f;
+ mHasBearing = false;
+ }
+
+ /**
+ * Returns true if the provider is able to report accuracy information,
+ * false otherwise. The default implementation returns false.
+ */
+ public boolean hasAccuracy() {
+ return mHasAccuracy;
+ }
+
+ /**
+ * Returns the accuracy of the fix in meters. If hasAccuracy() is false,
+ * 0.0 is returned.
+ */
+ public float getAccuracy() {
+ return mAccuracy;
+ }
+
+ /**
+ * Sets the accuracy of this fix. Following this call, hasAccuracy()
+ * will return true.
+ */
+ public void setAccuracy(float accuracy) {
+ mAccuracy = accuracy;
+ mHasAccuracy = true;
+ }
+
+ /**
+ * Clears the accuracy of this fix. Following this call, hasAccuracy()
+ * will return false.
+ */
+ public void removeAccuracy() {
+ mAccuracy = 0.0f;
+ mHasAccuracy = false;
+ }
+
+ /**
+ * Returns additional provider-specific information about the
+ * location fix as a Bundle. The keys and values are determined
+ * by the provider. If no additional information is available,
+ * null is returned.
+ *
+ * <p> A number of common key/value pairs are listed
+ * below. Providers that use any of the keys on this list must
+ * provide the corresponding value as described below.
+ *
+ * <ul>
+ * <li> satellites - the number of satellites used to derive the fix
+ * </ul>
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Sets the extra information associated with this fix to the
+ * given Bundle.
+ */
+ public void setExtras(Bundle extras) {
+ mExtras = (extras == null) ? null : new Bundle(extras);
+ }
+
+ @Override public String toString() {
+ return "Location[mProvider=" + mProvider +
+ ",mTime=" + mTime +
+ ",mLatitude=" + mLatitude +
+ ",mLongitude=" + mLongitude +
+ ",mHasAltitude=" + mHasAltitude +
+ ",mAltitude=" + mAltitude +
+ ",mHasSpeed=" + mHasSpeed +
+ ",mSpeed=" + mSpeed +
+ ",mHasBearing=" + mHasBearing +
+ ",mBearing=" + mBearing +
+ ",mHasAccuracy=" + mHasAccuracy +
+ ",mAccuracy=" + mAccuracy +
+ ",mExtras=" + mExtras + "]";
+ }
+
+ public static final Parcelable.Creator<Location> CREATOR =
+ new Parcelable.Creator<Location>() {
+ public Location createFromParcel(Parcel in) {
+ String provider = in.readString();
+ Location l = new Location(provider);
+ l.mTime = in.readLong();
+ l.mLatitude = in.readDouble();
+ l.mLongitude = in.readDouble();
+ l.mHasAltitude = in.readInt() != 0;
+ l.mAltitude = in.readDouble();
+ l.mHasSpeed = in.readInt() != 0;
+ l.mSpeed = in.readFloat();
+ l.mHasBearing = in.readInt() != 0;
+ l.mBearing = in.readFloat();
+ l.mHasAccuracy = in.readInt() != 0;
+ l.mAccuracy = in.readFloat();
+ l.mExtras = in.readBundle();
+ return l;
+ }
+
+ public Location[] newArray(int size) {
+ return new Location[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mProvider);
+ parcel.writeLong(mTime);
+ parcel.writeDouble(mLatitude);
+ parcel.writeDouble(mLongitude);
+ parcel.writeInt(mHasAltitude ? 1 : 0);
+ parcel.writeDouble(mAltitude);
+ parcel.writeInt(mHasSpeed ? 1 : 0);
+ parcel.writeFloat(mSpeed);
+ parcel.writeInt(mHasBearing ? 1 : 0);
+ parcel.writeFloat(mBearing);
+ parcel.writeInt(mHasAccuracy ? 1 : 0);
+ parcel.writeFloat(mAccuracy);
+ parcel.writeBundle(mExtras);
+ }
+}
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
new file mode 100644
index 0000000..0f5f388
--- /dev/null
+++ b/location/java/android/location/LocationListener.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2008 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.os.Bundle;
+
+/**
+ * Used for receiving notifications from the LocationManager when
+ * the location has changed. These methods are called if the
+ * LocationListener has been registered with the location manager service
+ * using the {@link LocationManager#requestLocationUpdates(String, long, float, LocationListener)}
+ * method.
+ */
+public interface LocationListener {
+
+ /**
+ * Called when the location has changed.
+ *
+ * <p> There are no restrictions on the use of the supplied Location object.
+ *
+ * @param location The new location, as a Location object.
+ */
+ void onLocationChanged(Location location);
+
+ /**
+ * Called when the provider status changes. This method is called when
+ * a provider is unable to fetch a location or if the provider has recently
+ * become available after a period of unavailability.
+ *
+ * @param provider the name of the location provider associated with this
+ * update.
+ * @param status {@link LocationProvider#OUT_OF_SERVICE} if the
+ * provider is out of service, and this is not expected to change in the
+ * near future; {@link LocationProvider#TEMPORARILY_UNAVAILABLE} if
+ * the provider is temporarily unavailable but is expected to be available
+ * shortly; and {@link LocationProvider#AVAILABLE} if the
+ * provider is currently available.
+ * @param extras an optional Bundle which will contain provider specific
+ * status variables.
+ *
+ * <p> A number of common key/value pairs for the extras Bundle are listed
+ * below. Providers that use any of the keys on this list must
+ * provide the corresponding value as described below.
+ *
+ * <ul>
+ * <li> satellites - the number of satellites used to derive the fix
+ * </ul>
+ */
+ void onStatusChanged(String provider, int status, Bundle extras);
+
+ /**
+ * Called when the provider is enabled by the user.
+ *
+ * @param provider the name of the location provider associated with this
+ * update.
+ */
+ void onProviderEnabled(String provider);
+
+ /**
+ * Called when the provider is disabled by the user. If requestLocationUpdates
+ * is called on an already disabled provider, this method is called
+ * immediately.
+ *
+ * @param provider the name of the location provider associated with this
+ * update.
+ */
+ void onProviderDisabled(String provider);
+}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
new file mode 100644
index 0000000..b5adb33
--- /dev/null
+++ b/location/java/android/location/LocationManager.java
@@ -0,0 +1,1179 @@
+/*
+ * 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 android.location;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+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 java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This class provides access to the system location services. These
+ * services allow applications to obtain periodic updates of the
+ * device's geographical location, or to fire an application-specified
+ * {@link Intent} when the device enters the proximity of a given
+ * geographical location.
+ *
+ * <p>You do not
+ * instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.LOCATION_SERVICE)}.
+ */
+public class LocationManager {
+ private static final String TAG = "LocationManager";
+ private ILocationManager mService;
+ private HashMap<GpsStatusListener, GpsStatusListenerTransport> mGpsStatusListeners =
+ new HashMap<GpsStatusListener, GpsStatusListenerTransport>();
+
+ /**
+ * Name of the network location provider. This provider determines location based on
+ * availability of cell tower and WiFi access points. Results are retrieved
+ * by means of a network lookup.
+ *
+ * Requires either of the permissions android.permission.ACCESS_COARSE_LOCATION
+ * or android.permission.ACCESS_FINE_LOCATION.
+ */
+ public static final String NETWORK_PROVIDER = "network";
+
+ /**
+ * Name of the GPS location provider. This provider determines location using
+ * satellites. Depending on conditions, this provider may take a while to return
+ * a location fix.
+ *
+ * Requires the permission android.permissions.ACCESS_FINE_LOCATION.
+ *
+ * <p> The extras Bundle for the GPS location provider can contain the
+ * following key/value pairs:
+ *
+ * <ul>
+ * <li> satellites - the number of satellites used to derive the fix
+ * </ul>
+ */
+ public static final String GPS_PROVIDER = "gps";
+
+ /**
+ * Key used for the Bundle extra holding a boolean indicating whether
+ * a proximity alert is entering (true) or exiting (false)..
+ */
+ public static final String KEY_PROXIMITY_ENTERING = "entering";
+
+ /** @hide -- does this belong here? */
+ public static final String PROVIDER_DIR = "/data/location";
+
+ /** @hide */
+ public static final String SYSTEM_DIR = "/data/system/location";
+
+ // Map from LocationListeners to their associated ListenerTransport objects
+ private HashMap<LocationListener,ListenerTransport> mListeners =
+ new HashMap<LocationListener,ListenerTransport>();
+
+ private class ListenerTransport extends ILocationListener.Stub {
+ private static final int TYPE_LOCATION_CHANGED = 1;
+ private static final int TYPE_STATUS_CHANGED = 2;
+ private static final int TYPE_PROVIDER_ENABLED = 3;
+ private static final int TYPE_PROVIDER_DISABLED = 4;
+
+ private LocationListener mListener;
+ private final Handler mListenerHandler;
+
+ ListenerTransport(LocationListener listener, Looper looper) {
+ mListener = listener;
+
+ if (looper == null) {
+ mListenerHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ _handleMessage(msg);
+ }
+ };
+ } else {
+ mListenerHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ _handleMessage(msg);
+ }
+ };
+ }
+ }
+
+ public void onLocationChanged(Location location) {
+ Message msg = Message.obtain();
+ msg.what = TYPE_LOCATION_CHANGED;
+ msg.obj = location;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ Message msg = Message.obtain();
+ msg.what = TYPE_STATUS_CHANGED;
+ Bundle b = new Bundle();
+ b.putString("provider", provider);
+ b.putInt("status", status);
+ if (extras != null) {
+ b.putBundle("extras", extras);
+ }
+ msg.obj = b;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ public void onProviderEnabled(String provider) {
+ Message msg = Message.obtain();
+ msg.what = TYPE_PROVIDER_ENABLED;
+ msg.obj = provider;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ public void onProviderDisabled(String provider) {
+ Message msg = Message.obtain();
+ msg.what = TYPE_PROVIDER_DISABLED;
+ msg.obj = provider;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ private void _handleMessage(Message msg) {
+ switch (msg.what) {
+ case TYPE_LOCATION_CHANGED:
+ Location location = new Location((Location) msg.obj);
+ mListener.onLocationChanged(location);
+ break;
+ case TYPE_STATUS_CHANGED:
+ Bundle b = (Bundle) msg.obj;
+ String provider = b.getString("provider");
+ int status = b.getInt("status");
+ Bundle extras = b.getBundle("extras");
+ mListener.onStatusChanged(provider, status, extras);
+ break;
+ case TYPE_PROVIDER_ENABLED:
+ mListener.onProviderEnabled((String) msg.obj);
+ break;
+ case TYPE_PROVIDER_DISABLED:
+ mListener.onProviderDisabled((String) msg.obj);
+ break;
+ }
+ }
+ }
+ /**
+ * @hide - hide this constructor because it has a parameter
+ * of type ILocationManager, which is a system private class. The
+ * right way to create an instance of this class is using the
+ * factory Context.getSystemService.
+ */
+ public LocationManager(ILocationManager service) {
+ if (Config.LOGD) {
+ Log.d(TAG, "Constructor: service = " + service);
+ }
+ mService = service;
+ }
+
+ private LocationProvider createProvider(String name, Bundle info) {
+ DummyLocationProvider provider =
+ new DummyLocationProvider(name);
+ provider.setRequiresNetwork(info.getBoolean("network"));
+ provider.setRequiresSatellite(info.getBoolean("satellite"));
+ provider.setRequiresCell(info.getBoolean("cell"));
+ provider.setHasMonetaryCost(info.getBoolean("cost"));
+ provider.setSupportsAltitude(info.getBoolean("altitude"));
+ provider.setSupportsSpeed(info.getBoolean("speed"));
+ provider.setSupportsBearing(info.getBoolean("bearing"));
+ provider.setPowerRequirement(info.getInt("power"));
+ provider.setAccuracy(info.getInt("accuracy"));
+ return provider;
+ }
+
+ /**
+ * Returns a list of the names of all known location providers. All
+ * providers are returned, including ones that are not permitted to be
+ * accessed by the calling activity or are currently disabled.
+ *
+ * @return list of Strings containing names of the providers
+ */
+ public List<String> getAllProviders() {
+ if (Config.LOGD) {
+ Log.d(TAG, "getAllProviders");
+ }
+ try {
+ return mService.getAllProviders();
+ } catch (RemoteException ex) {
+ Log.e(TAG, "getAllProviders: RemoteException", ex);
+ }
+ return null;
+ }
+
+ /**
+ * Returns a list of the names of location providers. Only providers that
+ * are permitted to be accessed by the calling activity will be returned.
+ *
+ * @param enabledOnly if true then only the providers which are currently
+ * enabled are returned.
+ * @return list of Strings containing names of the providers
+ */
+ public List<String> getProviders(boolean enabledOnly) {
+ try {
+ return mService.getProviders(enabledOnly);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "getProviders: RemoteException", ex);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the information associated with the location provider of the
+ * given name, or null if no provider exists by that name.
+ *
+ * @param name the provider name
+ * @return a LocationProvider, or null
+ *
+ * @throws IllegalArgumentException if name is null
+ * @throws SecurityException if the caller is not permitted to access the
+ * given provider.
+ */
+ public LocationProvider getProvider(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("name==null");
+ }
+ try {
+ Bundle info = mService.getProviderInfo(name);
+ if (info == null) {
+ return null;
+ }
+ return createProvider(name, info);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "getProvider: RemoteException", ex);
+ }
+ return null;
+ }
+
+ /**
+ * Returns a list of the names of LocationProviders that satisfy the given
+ * criteria, or null if none do. Only providers that are permitted to be
+ * accessed by the calling activity will be returned.
+ *
+ * @param criteria the criteria that the returned providers must match
+ * @param enabledOnly if true then only the providers which are currently
+ * enabled are returned.
+ * @return list of Strings containing names of the providers
+ */
+ public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
+ List<String> goodProviders = Collections.emptyList();
+ List<String> providers = getProviders(enabledOnly);
+ for (String providerName : providers) {
+ LocationProvider provider = getProvider(providerName);
+ if (provider.meetsCriteria(criteria)) {
+ if (goodProviders.isEmpty()) {
+ goodProviders = new ArrayList<String>();
+ }
+ goodProviders.add(providerName);
+ }
+ }
+ return goodProviders;
+ }
+
+ /**
+ * Propagates the enabled/disabled state of the providers from the system
+ * settings to the providers themselves.
+ *
+ * {@hide}
+ */
+ public void updateProviders() {
+ try {
+ mService.updateProviders();
+ } catch (RemoteException ex) {
+ Log.e(TAG, "updateProviders: RemoteException", ex);
+ }
+ }
+
+ /**
+ * Returns the next looser power requirement, in the sequence:
+ *
+ * POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT
+ */
+ private int nextPower(int power) {
+ switch (power) {
+ case Criteria.POWER_LOW:
+ return Criteria.POWER_MEDIUM;
+ case Criteria.POWER_MEDIUM:
+ return Criteria.POWER_HIGH;
+ case Criteria.POWER_HIGH:
+ return Criteria.NO_REQUIREMENT;
+ case Criteria.NO_REQUIREMENT:
+ default:
+ return Criteria.NO_REQUIREMENT;
+ }
+ }
+
+ /**
+ * Returns the next looser accuracy requirement, in the sequence:
+ *
+ * ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT
+ */
+ private int nextAccuracy(int accuracy) {
+ if (accuracy == Criteria.ACCURACY_FINE) {
+ return Criteria.ACCURACY_COARSE;
+ } else {
+ return Criteria.NO_REQUIREMENT;
+ }
+ }
+
+ private abstract class LpComparator implements Comparator<LocationProvider> {
+
+ public int compare(int a1, int a2) {
+ if (a1 < a2) {
+ return -1;
+ } else if (a1 > a2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ public int compare(float a1, float a2) {
+ if (a1 < a2) {
+ return -1;
+ } else if (a1 > a2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ private class LpPowerComparator extends LpComparator {
+ public int compare(LocationProvider l1, LocationProvider l2) {
+ int a1 = l1.getPowerRequirement();
+ int a2 = l2.getPowerRequirement();
+ return compare(a1, a2); // Smaller is better
+ }
+
+ public boolean equals(LocationProvider l1, LocationProvider l2) {
+ int a1 = l1.getPowerRequirement();
+ int a2 = l2.getPowerRequirement();
+ return a1 == a2;
+ }
+ }
+
+ private class LpAccuracyComparator extends LpComparator {
+ public int compare(LocationProvider l1, LocationProvider l2) {
+ int a1 = l1.getAccuracy();
+ int a2 = l2.getAccuracy();
+ return compare(a1, a2); // Smaller is better
+ }
+
+ public boolean equals(LocationProvider l1, LocationProvider l2) {
+ int a1 = l1.getAccuracy();
+ int a2 = l2.getAccuracy();
+ return a1 == a2;
+ }
+ }
+
+ private class LpCapabilityComparator extends LpComparator {
+
+ private static final int ALTITUDE_SCORE = 4;
+ private static final int BEARING_SCORE = 4;
+ private static final int SPEED_SCORE = 4;
+
+ private int score(LocationProvider p) {
+ return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) +
+ (p.supportsBearing() ? BEARING_SCORE : 0) +
+ (p.supportsSpeed() ? SPEED_SCORE : 0);
+ }
+
+ public int compare(LocationProvider l1, LocationProvider l2) {
+ int a1 = score(l1);
+ int a2 = score(l2);
+ return compare(-a1, -a2); // Bigger is better
+ }
+
+ public boolean equals(LocationProvider l1, LocationProvider l2) {
+ int a1 = score(l1);
+ int a2 = score(l2);
+ return a1 == a2;
+ }
+ }
+
+ private LocationProvider best(List<String> providerNames) {
+ List<LocationProvider> providers = new ArrayList<LocationProvider>(providerNames.size());
+ for (String name : providerNames) {
+ providers.add(getProvider(name));
+ }
+
+ if (providers.size() < 2) {
+ return providers.get(0);
+ }
+
+ // First, sort by power requirement
+ Collections.sort(providers, new LpPowerComparator());
+ int power = providers.get(0).getPowerRequirement();
+ if (power < providers.get(1).getPowerRequirement()) {
+ return providers.get(0);
+ }
+
+ int idx, size;
+
+ List<LocationProvider> tmp = new ArrayList<LocationProvider>();
+ idx = 0;
+ size = providers.size();
+ while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) {
+ tmp.add(providers.get(idx));
+ idx++;
+ }
+
+ // Next, sort by accuracy
+ Collections.sort(tmp, new LpAccuracyComparator());
+ int acc = tmp.get(0).getAccuracy();
+ if (acc < tmp.get(1).getAccuracy()) {
+ return tmp.get(0);
+ }
+
+ List<LocationProvider> tmp2 = new ArrayList<LocationProvider>();
+ idx = 0;
+ size = tmp.size();
+ while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) {
+ tmp2.add(tmp.get(idx));
+ idx++;
+ }
+
+ // Finally, sort by capability "score"
+ Collections.sort(tmp2, new LpCapabilityComparator());
+ return tmp2.get(0);
+ }
+
+ /**
+ * Returns the name of the provider that best meets the given criteria. Only providers
+ * that are permitted to be accessed by the calling activity will be
+ * returned. If several providers meet the criteria, the one with the best
+ * accuracy is returned. If no provider meets the criteria,
+ * the criteria are loosened in the following sequence:
+ *
+ * <ul>
+ * <li> power requirement
+ * <li> accuracy
+ * <li> bearing
+ * <li> speed
+ * <li> altitude
+ * </ul>
+ *
+ * <p> Note that the requirement on monetary cost is not removed
+ * in this process.
+ *
+ * @param criteria the criteria that need to be matched
+ * @param enabledOnly if true then only a provider that is currently enabled is returned
+ * @return name of the provider that best matches the requirements
+ */
+ public String getBestProvider(Criteria criteria, boolean enabledOnly) {
+ List<String> goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Make a copy of the criteria that we can modify
+ criteria = new Criteria(criteria);
+
+ // Loosen power requirement
+ int power = criteria.getPowerRequirement();
+ while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) {
+ power = nextPower(power);
+ criteria.setPowerRequirement(power);
+ goodProviders = getProviders(criteria, enabledOnly);
+ }
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+// // Loosen response time requirement
+// int responseTime = criteria.getPreferredResponseTime();
+// while (goodProviders.isEmpty() &&
+// (responseTime != Criteria.NO_REQUIREMENT)) {
+// responseTime += 1000;
+// if (responseTime > 60000) {
+// responseTime = Criteria.NO_REQUIREMENT;
+// }
+// criteria.setPreferredResponseTime(responseTime);
+// goodProviders = getProviders(criteria);
+// }
+// if (!goodProviders.isEmpty()) {
+// return best(goodProviders);
+// }
+
+ // Loosen accuracy requirement
+ int accuracy = criteria.getAccuracy();
+ while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) {
+ accuracy = nextAccuracy(accuracy);
+ criteria.setAccuracy(accuracy);
+ goodProviders = getProviders(criteria, enabledOnly);
+ }
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Remove bearing requirement
+ criteria.setBearingRequired(false);
+ goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Remove speed requirement
+ criteria.setSpeedRequired(false);
+ goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ // Remove altitude requirement
+ criteria.setAltitudeRequired(false);
+ goodProviders = getProviders(criteria, enabledOnly);
+ if (!goodProviders.isEmpty()) {
+ return best(goodProviders).getName();
+ }
+
+ return null;
+ }
+
+ /**
+ * Registers the current activity to be notified periodically by
+ * the named provider. Periodically, the supplied LocationListener will
+ * be called with the current Location or with status updates.
+ *
+ * <p> It may take a while to receive the most recent location. If
+ * an immediate location is required, applications may use the
+ * {@link #getLastKnownLocation(String)} method.
+ *
+ * <p> In case the provider is disabled by the user, updates will stop,
+ * and the {@link LocationListener#onProviderDisabled(String)}
+ * method will be called. As soon as the provider is enabled again,
+ * the {@link LocationListener#onProviderEnabled(String)} method will
+ * be called and location updates will start again.
+ *
+ * <p> The frequency of notification may be controlled using the
+ * minTime and minDistance parameters. If minTime is greater than 0,
+ * the LocationManager could potentially rest for minTime milliseconds
+ * between location updates to conserve power. If minDistance is greater than 0,
+ * a location will only be broadcasted if the device moves by minDistance meters.
+ * To obtain notifications as frequently as possible, set both parameters to 0.
+ *
+ * <p> Background services should be careful about setting a sufficiently high
+ * minTime so that the device doesn't consume too much power by keeping the
+ * GPS or wireless radios on all the time. In particular, values under 60000ms
+ * are not recommended.
+ *
+ * <p> The calling thread must be a {@link android.os.Looper} thread such as
+ * the main thread of the calling Activity.
+ *
+ * @param provider the name of the provider with which to register
+ * @param minTime the minimum time interval for notifications, in
+ * milliseconds. This field is only used as a hint to conserve power, and actual
+ * time between location updates may be greater or lesser than this value.
+ * @param minDistance the minimum distance interval for notifications,
+ * in meters
+ * @param listener a {#link LocationListener} whose
+ * {@link LocationListener#onLocationChanged} method will be called for
+ * each location update
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if listener is null
+ * @throws RuntimeException if the calling thread has no Looper
+ * @throws SecurityException if no suitable permission is present for the provider.
+ */
+ public void requestLocationUpdates(String provider,
+ long minTime, float minDistance, LocationListener listener) {
+ if (provider == null) {
+ throw new IllegalArgumentException("provider==null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener==null");
+ }
+ _requestLocationUpdates(provider, minTime, minDistance, listener, null);
+ }
+
+ /**
+ * Registers the current activity to be notified periodically by
+ * the named provider. Periodically, the supplied LocationListener will
+ * be called with the current Location or with status updates.
+ *
+ * <p> It may take a while to receive the most recent location. If
+ * an immediate location is required, applications may use the
+ * {@link #getLastKnownLocation(String)} method.
+ *
+ * <p> In case the provider is disabled by the user, updates will stop,
+ * and the {@link LocationListener#onProviderDisabled(String)}
+ * method will be called. As soon as the provider is enabled again,
+ * the {@link LocationListener#onProviderEnabled(String)} method will
+ * be called and location updates will start again.
+ *
+ * <p> The frequency of notification may be controlled using the
+ * minTime and minDistance parameters. If minTime is greater than 0,
+ * the LocationManager could potentially rest for minTime milliseconds
+ * between location updates to conserve power. If minDistance is greater than 0,
+ * a location will only be broadcasted if the device moves by minDistance meters.
+ * To obtain notifications as frequently as possible, set both parameters to 0.
+ *
+ * <p> Background services should be careful about setting a sufficiently high
+ * minTime so that the device doesn't consume too much power by keeping the
+ * GPS or wireless radios on all the time. In particular, values under 60000ms
+ * are not recommended.
+ *
+ * <p> The supplied Looper is used to implement the callback mechanism.
+ *
+ * @param provider the name of the provider with which to register
+ * @param minTime the minimum time interval for notifications, in
+ * milliseconds. This field is only used as a hint to conserve power, and actual
+ * time between location updates may be greater or lesser than this value.
+ * @param minDistance the minimum distance interval for notifications,
+ * in meters
+ * @param listener a {#link LocationListener} whose
+ * {@link LocationListener#onLocationChanged} method will be called for
+ * each location update
+ * @param looper a Looper object whose message queue will be used to
+ * implement the callback mechanism.
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if listener is null
+ * @throws IllegalArgumentException if looper is null
+ * @throws SecurityException if no suitable permission is present for the provider.
+ */
+ public void requestLocationUpdates(String provider,
+ long minTime, float minDistance, LocationListener listener,
+ Looper looper) {
+ if (provider == null) {
+ throw new IllegalArgumentException("provider==null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener==null");
+ }
+ if (looper == null) {
+ throw new IllegalArgumentException("looper==null");
+ }
+ _requestLocationUpdates(provider, minTime, minDistance, listener, looper);
+ }
+
+ private void _requestLocationUpdates(String provider,
+ long minTime, float minDistance, LocationListener listener,
+ Looper looper) {
+ if (minTime < 0L) {
+ minTime = 0L;
+ }
+ if (minDistance < 0.0f) {
+ minDistance = 0.0f;
+ }
+
+ try {
+ synchronized (mListeners) {
+ ListenerTransport transport = mListeners.get(listener);
+ if (transport == null) {
+ transport = new ListenerTransport(listener, looper);
+ mListeners.put(listener, transport);
+ }
+ mListeners.put(listener, transport);
+ mService.requestLocationUpdates(provider, minTime, minDistance,
+ transport);
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "requestLocationUpdates: DeadObjectException", ex);
+ }
+ }
+
+ /**
+ * Removes any current registration for location updates of the current activity
+ * with the given LocationListener. Following this call, updates will no longer
+ * occur for this listener.
+ *
+ * @param listener {#link LocationListener} object that no longer needs location updates
+ * @throws IllegalArgumentException if listener is null
+ */
+ public void removeUpdates(LocationListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener==null");
+ }
+ if (Config.LOGD) {
+ Log.d(TAG, "removeUpdates: listener = " + listener);
+ }
+ try {
+ ListenerTransport transport = mListeners.remove(listener);
+ if (transport != null) {
+ mService.removeUpdates(transport);
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "removeUpdates: DeadObjectException", ex);
+ }
+ }
+
+ /**
+ * Sets a proximity alert for the location given by the position
+ * (latitude, longitude) and the given radius. When the device
+ * detects that it has entered or exited the area surrounding the
+ * location, the given PendingIntent will be used to create an Intent
+ * to be fired.
+ *
+ * <p> The fired Intent will have a boolean extra added with key
+ * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
+ * entering the proximity region; if false, it is exiting.
+ *
+ * <p> Due to the approximate nature of position estimation, if the
+ * device passes through the given area briefly, it is possible
+ * that no Intent will be fired. Similarly, an Intent could be
+ * fired if the device passes very close to the given area but
+ * does not actually enter it.
+ *
+ * <p> After the number of milliseconds given by the expiration
+ * parameter, the location manager will delete this proximity
+ * alert and no longer monitor it. A value of -1 indicates that
+ * there should be no expiration time.
+ *
+ * <p> In case the screen goes to sleep, checks for proximity alerts
+ * happen only once every 4 minutes. This conserves battery life by
+ * ensuring that the device isn't perpetually awake.
+ *
+ * <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
+ * and {@link #GPS_PROVIDER}.
+ *
+ * @param latitude the latitude of the central point of the
+ * alert region
+ * @param longitude the longitude of the central point of the
+ * alert region
+ * @param radius the radius of the central point of the
+ * alert region, in meters
+ * @param expiration time for this proximity alert, in milliseconds,
+ * or -1 to indicate no expiration
+ * @param intent a PendingIntent that will be used to generate an Intent to
+ * fire when entry to or exit from the alert region is detected
+ *
+ * @throws SecurityException if no permission exists for the required
+ * providers.
+ */
+ public void addProximityAlert(double latitude, double longitude,
+ float radius, long expiration, PendingIntent intent) {
+ if (Config.LOGD) {
+ Log.d(TAG, "addProximityAlert: latitude = " + latitude +
+ ", longitude = " + longitude + ", radius = " + radius +
+ ", expiration = " + expiration +
+ ", intent = " + intent);
+ }
+ try {
+ mService.addProximityAlert(latitude, longitude, radius,
+ expiration, intent);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "addProximityAlert: RemoteException", ex);
+ }
+ }
+
+ /**
+ * Removes the proximity alert with the given PendingIntent.
+ *
+ * @param intent the PendingIntent that no longer needs to be notified of
+ * proximity alerts
+ */
+ public void removeProximityAlert(PendingIntent intent) {
+ if (Config.LOGD) {
+ Log.d(TAG, "removeProximityAlert: intent = " + intent);
+ }
+ try {
+ mService.removeProximityAlert(intent);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "removeProximityAlert: RemoteException", ex);
+ }
+ }
+
+ /**
+ * Returns the current enabled/disabled status of the given provider. If the
+ * user has enabled this provider in the Settings menu, true is returned
+ * otherwise false is returned
+ *
+ * @param provider the name of the provider
+ * @return true if the provider is enabled
+ *
+ * @throws SecurityException if no suitable permission is present for the provider.
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ */
+ public boolean isProviderEnabled(String provider) {
+ if (provider == null) {
+ throw new IllegalArgumentException("provider==null");
+ }
+ try {
+ return mService.isProviderEnabled(provider);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "isProviderEnabled: RemoteException", ex);
+ return false;
+ }
+ }
+
+ /**
+ * Returns a Location indicating the data from the last known
+ * location fix obtained from the given provider. This can be done
+ * without starting the provider. Note that this location could
+ * be out-of-date, for example if the device was turned off and
+ * moved to another location.
+ *
+ * <p> If the provider is currently disabled, null is returned.
+ *
+ * @param provider the name of the provider
+ * @return the last known location for the provider, or null
+ *
+ * @throws SecurityException if no suitable permission is present for the provider.
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ */
+ public Location getLastKnownLocation(String provider) {
+ if (provider == null) {
+ throw new IllegalArgumentException("provider==null");
+ }
+ try {
+ return mService.getLastKnownLocation(provider);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "getLastKnowLocation: RemoteException", ex);
+ return null;
+ }
+ }
+
+ // Mock provider support
+
+ /**
+ * Creates a mock location provider and adds it to the set of active providers.
+ *
+ * @param name the provider name
+ * @param requiresNetwork
+ * @param requiresSatellite
+ * @param requiresCell
+ * @param hasMonetaryCost
+ * @param supportsAltitude
+ * @param supportsSpeed
+ * @param supportsBearing
+ * @param powerRequirement
+ * @param accuracy
+ *
+ * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * @throws IllegalArgumentException if a provider with the given name already exists
+ *
+ * {@hide}
+ */
+ public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
+ boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
+ boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ try {
+ mService.addTestProvider(name, requiresNetwork, requiresSatellite, requiresCell,
+ hasMonetaryCost, supportsAltitude, supportsSpeed, supportsBearing, powerRequirement,
+ accuracy);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "addTestProvider: RemoteException", ex);
+ }
+ }
+
+ /**
+ * Removes the mock location provider with the given name.
+ *
+ * @param provider the provider name
+ *
+ * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * {@hide}
+ */
+ public void removeTestProvider(String provider) {
+ try {
+ mService.removeTestProvider(provider);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "removeTestProvider: RemoteException", ex);
+ }
+ }
+
+ /**
+ * Sets a mock location for the given provider. This location will be used in place
+ * of any actual location from the provider.
+ *
+ * @param provider the provider name
+ * @param loc the mock location
+ *
+ * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * {@hide}
+ */
+ public void setTestProviderLocation(String provider, Location loc) {
+ try {
+ mService.setTestProviderLocation(provider, loc);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "setTestProviderLocation: RemoteException", ex);
+ }
+ }
+
+ /**
+ * Removes any mock location associated with the given provider.
+ *
+ * @param provider the provider name
+ *
+ * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * {@hide}
+ */
+ public void clearTestProviderLocation(String provider) {
+ try {
+ mService.clearTestProviderLocation(provider);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "clearTestProviderLocation: RemoteException", ex);
+ }
+ }
+
+ /**
+ * Sets a mock enabled value for the given provider. This value will be used in place
+ * of any actual value from the provider.
+ *
+ * @param provider the provider name
+ * @param enabled the mock enabled value
+ *
+ * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * {@hide}
+ */
+ public void setTestProviderEnabled(String provider, boolean enabled) {
+ try {
+ mService.setTestProviderEnabled(provider, enabled);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "setTestProviderEnabled: RemoteException", ex);
+ }
+ }
+
+ /**
+ * Removes any mock enabled value associated with the given provider.
+ *
+ * @param provider the provider name
+ *
+ * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * {@hide}
+ */
+ public void clearTestProviderEnabled(String provider) {
+ try {
+ mService.clearTestProviderEnabled(provider);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "clearTestProviderEnabled: RemoteException", ex);
+ }
+
+ }
+
+ /**
+ * Sets mock status values for the given provider. These values will be used in place
+ * of any actual values from the provider.
+ *
+ * @param provider the provider name
+ * @param status the mock status
+ * @param extras a Bundle containing mock extras
+ * @param updateTime the mock update time
+ *
+ * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * {@hide}
+ */
+ public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
+ try {
+ mService.setTestProviderStatus(provider, status, extras, updateTime);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "setTestProviderStatus: RemoteException", ex);
+ }
+ }
+
+ /**
+ * Removes any mock status values associated with the given provider.
+ *
+ * @param provider the provider name
+ *
+ * @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * @throws IllegalArgumentException if no provider with the given name exists
+ *
+ * {@hide}
+ */
+ public void clearTestProviderStatus(String provider) {
+ try {
+ mService.clearTestProviderStatus(provider);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "clearTestProviderStatus: RemoteException", ex);
+ }
+ }
+
+ // GPS-specific support
+
+ // This class is used to send GPS status events to the client's main thread.
+ private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
+
+ private GpsStatusListener mListener;
+ private int mTTFF;
+ private int mSvCount;
+ private int[] mPrns;
+ private float[] mSnrs;
+ private float[] mElevations;
+ private float[] mAzimuths;
+ private int mEphemerisMask;
+ private int mAlmanacMask;
+ private int mUsedInFixMask;
+
+ private static final int GPS_STARTED = 0;
+ private static final int GPS_STOPPED = 1;
+ private static final int GPS_FIRST_FIX = 2;
+ private static final int GPS_SV_STATUS = 3;
+
+ GpsStatusListenerTransport(GpsStatusListener listener) {
+ mListener = listener;
+ }
+
+ public void onGpsStarted() {
+ Message msg = Message.obtain();
+ msg.what = GPS_STARTED;
+ mGpsHandler.sendMessage(msg);
+ }
+
+ public void onGpsStopped() {
+ Message msg = Message.obtain();
+ msg.what = GPS_STOPPED;
+ mGpsHandler.sendMessage(msg);
+ }
+
+ public void onFirstFix(int ttff) {
+ mTTFF = ttff;
+ Message msg = Message.obtain();
+ msg.what = GPS_FIRST_FIX;
+ mGpsHandler.sendMessage(msg);
+ }
+
+ public void onSvStatusChanged(int svCount, int[] prns, float[] snrs,
+ float[] elevations, float[] azimuths, int ephemerisMask,
+ int almanacMask, int usedInFixMask) {
+ // synchronize here to ensure SV count matches data in the arrays
+ synchronized(this) {
+ mSvCount = svCount;
+ mPrns = prns;
+ mSnrs = snrs;
+ mElevations = elevations;
+ mAzimuths = azimuths;
+ mEphemerisMask = ephemerisMask;
+ mAlmanacMask = almanacMask;
+ mUsedInFixMask = usedInFixMask;
+ }
+
+ Message msg = Message.obtain();
+ msg.what = GPS_SV_STATUS;
+ // remove any SV status messages already in the queue
+ mGpsHandler.removeMessages(GPS_SV_STATUS);
+ mGpsHandler.sendMessage(msg);
+ }
+
+ private final Handler mGpsHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case GPS_STARTED:
+ mListener.onGpsStarted();
+ break;
+ case GPS_STOPPED:
+ mListener.onGpsStopped();
+ break;
+ case GPS_FIRST_FIX:
+ mListener.onFirstFix(mTTFF);
+ break;
+ case GPS_SV_STATUS:
+ // synchronize here to ensure SV count matches data in the arrays
+ synchronized(this) {
+ mListener.onSvStatusChanged(mSvCount, mPrns, mSnrs, mElevations,
+ mAzimuths, mEphemerisMask, mAlmanacMask, mUsedInFixMask);
+ break;
+ }
+ }
+ }
+ };
+ }
+
+ /**
+ * Registers a GPS status listener.
+ *
+ * @param listener GPS status listener object to register.
+ *
+ * @return true if the listener was successfully registered.
+ *
+ * {@hide}
+ */
+ public boolean registerGpsStatusListener(GpsStatusListener listener) {
+ boolean result;
+
+ if (mGpsStatusListeners.get(listener) != null) {
+ // listener is already registered
+ return true;
+ }
+ try {
+ GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
+ result = mService.addGpsStatusListener(transport);
+ if (result) {
+ mGpsStatusListeners.put(listener, transport);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
+ result = false;
+ }
+
+ return result;
+ }
+
+ /**
+ * Unegisters a GPS status listener.
+ *
+ * @param listener GPS status listener object to unregister.
+ *
+ * {@hide}
+ */
+ public void unregisterGpsStatusListener(GpsStatusListener listener) {
+ try {
+ GpsStatusListenerTransport transport = mGpsStatusListeners.remove(listener);
+ if (transport != null) {
+ mService.removeGpsStatusListener(transport);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
+ }
+ }
+
+ /**
+ * Sends additional commands to a location provider.
+ * Can be used to support provider specific extensions to the Location Manager API
+ *
+ * @param provider name of the location provider.
+ * @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.
+ *
+ * {@hide}
+ */
+ public boolean sendExtraCommand(String provider, String command, Bundle extras) {
+ try {
+ return mService.sendExtraCommand(provider, command, extras);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in sendExtraCommand: ", e);
+ return false;
+ }
+ }
+}
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
new file mode 100644
index 0000000..b1670d5
--- /dev/null
+++ b/location/java/android/location/LocationProvider.java
@@ -0,0 +1,160 @@
+/*
+ * 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 android.location;
+
+/**
+ * An abstract superclass for location providers. A location provider
+ * provides periodic reports on the geographical location of the
+ * device.
+ *
+ * <p> Each provider has a set of criteria under which it may be used;
+ * for example, some providers require GPS hardware and visibility to
+ * a number of satellites; others require the use of the cellular
+ * radio, or access to a specific carrier's network, or to the
+ * internet. They may also have different battery consumption
+ * characteristics or monetary costs to the user. The {@link
+ * Criteria} class allows providers to be selected based on
+ * user-specified criteria.
+ */
+public abstract class LocationProvider {
+ private static final String TAG = "LocationProvider";
+ // A regular expression matching characters that may not appear
+ // in the name of a LocationProvider.
+ static final String BAD_CHARS_REGEX = "[^a-zA-Z0-9]";
+
+ private String mName;
+
+ public static final int OUT_OF_SERVICE = 0;
+ public static final int TEMPORARILY_UNAVAILABLE = 1;
+ public static final int AVAILABLE = 2;
+
+ /**
+ * Constructs a LocationProvider with the given name. Provider names must
+ * consist only of the characters [a-zA-Z0-9].
+ *
+ * @throws IllegalArgumentException if name contains an illegal character
+ */
+ LocationProvider(String name) {
+ if (name.matches(BAD_CHARS_REGEX)) {
+ throw new IllegalArgumentException("name " + name +
+ " contains an illegal character");
+ }
+ // Log.d(TAG, "Constructor: name = " + name);
+ mName = name;
+ }
+
+ /**
+ * Returns the name of this provider.
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns true if this provider meets the given criteria,
+ * false otherwise.
+ */
+ public boolean meetsCriteria(Criteria criteria) {
+ if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
+ (criteria.getAccuracy() < getAccuracy())) {
+ return false;
+ }
+ int criteriaPower = criteria.getPowerRequirement();
+ if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
+ (criteriaPower < getPowerRequirement())) {
+ return false;
+ }
+ if (criteria.isAltitudeRequired() && !supportsAltitude()) {
+ return false;
+ }
+ if (criteria.isSpeedRequired() && !supportsSpeed()) {
+ return false;
+ }
+ if (criteria.isBearingRequired() && !supportsBearing()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if the provider requires access to a
+ * data network (e.g., the Internet), false otherwise.
+ */
+ public abstract boolean requiresNetwork();
+
+ /**
+ * Returns true if the provider requires access to a
+ * satellite-based positioning system (e.g., GPS), false
+ * otherwise.
+ */
+ public abstract boolean requiresSatellite();
+
+ /**
+ * 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 requiresCell();
+
+ /**
+ * 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 hasMonetaryCost();
+
+ /**
+ * 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 supportsAltitude();
+
+ /**
+ * 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 supportsSpeed();
+
+ /**
+ * 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 supportsBearing();
+
+ /**
+ * 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 getPowerRequirement();
+
+ /**
+ * 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 getAccuracy();
+}
diff --git a/location/java/android/location/LocationProviderImpl.java b/location/java/android/location/LocationProviderImpl.java
new file mode 100644
index 0000000..0962992
--- /dev/null
+++ b/location/java/android/location/LocationProviderImpl.java
@@ -0,0 +1,261 @@
+/*
+ * 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 android.location;
+
+import com.android.internal.location.CellState;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import android.os.Bundle;
+import android.util.Config;
+import android.util.Log;
+
+/**
+ * An abstract superclass for location provider implementations.
+ * Location provider implementations are typically instantiated by the
+ * location manager service in the system process, and location
+ * information is made available to implementations via the manager.
+ *
+ * {@hide}
+ */
+public abstract class LocationProviderImpl extends LocationProvider {
+ private static final String TAG = "LocationProviderImpl";
+
+ private static ArrayList<LocationProviderImpl> sProviders =
+ new ArrayList<LocationProviderImpl>();
+ private static HashMap<String, LocationProviderImpl> sProvidersByName
+ = new HashMap<String, LocationProviderImpl>();
+
+ private boolean mLocationTracking = false;
+ private long mMinTime = 0;
+
+ protected LocationProviderImpl(String name) {
+ super(name);
+ }
+
+ public static void addProvider(LocationProviderImpl provider) {
+ sProviders.add(provider);
+ sProvidersByName.put(provider.getName(), provider);
+ }
+
+ public static void removeProvider(LocationProviderImpl provider) {
+ sProviders.remove(provider);
+ sProvidersByName.remove(provider.getName());
+ }
+
+ public static List<LocationProviderImpl> getProviders() {
+ return new ArrayList<LocationProviderImpl>(sProviders);
+ }
+
+ public static LocationProviderImpl getProvider(String name) {
+ return sProvidersByName.get(name);
+ }
+
+ public static LocationProviderImpl loadFromClass(File classFile) {
+ if (!classFile.exists()) {
+ return null;
+ }
+ if (Config.LOGD) {
+ Log.d(TAG, "Loading class specifier file " + classFile.getPath());
+ }
+ String className = null;
+ try {
+ BufferedReader br =
+ new BufferedReader(new FileReader(classFile), 8192);
+ className = br.readLine();
+ br.close();
+ Class providerClass = Class.forName(className);
+ if (Config.LOGD) {
+ Log.d(TAG, "Loading provider class " + providerClass.getName());
+ }
+ LocationProviderImpl provider =
+ (LocationProviderImpl) providerClass.newInstance();
+ if (Config.LOGD) {
+ Log.d(TAG, "Got provider instance " + provider);
+ }
+
+ return provider;
+ } catch (IOException ioe) {
+ Log.e(TAG, "IOException loading config file " +
+ classFile.getPath(), ioe);
+ } catch (IllegalAccessException iae) {
+ Log.e(TAG, "IllegalAccessException loading class " +
+ className, iae);
+ } catch (InstantiationException ie) {
+ Log.e(TAG, "InstantiationException loading class " +
+ className, ie);
+ } catch (ClassNotFoundException cnfe) {
+ Log.e(TAG, "ClassNotFoundException loading class " +
+ className, cnfe);
+ } catch (ClassCastException cce) {
+ Log.e(TAG, "ClassCastException loading class " +
+ className, cce);
+ }
+ return null;
+ }
+
+ /**
+ * Enables this provider. When enabled, calls to {@link #getStatus()}
+ * and {@link #getLocation} must be handled. Hardware may be started up
+ * when the provider is enabled.
+ */
+ public abstract void enable();
+
+ /**
+ * Disables this provider. When disabled, calls to {@link #getStatus()}
+ * and {@link #getLocation} need not be handled. Hardware may be shut
+ * down while the provider is disabled.
+ */
+ public abstract void disable();
+
+ /**
+ * Returns true if this provider is enabled, false otherwise;
+ */
+ public abstract boolean isEnabled();
+
+ /**
+ * 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.
+ */
+ public int getStatus() {
+ return getStatus(null);
+ }
+
+ /**
+ * 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 getStatus(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()} each time
+ * 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 long getStatusUpdateTime() {
+ return 0;
+ }
+
+ /**
+ * Sets a Location object with the information gathered
+ * during the most recent fix.
+ *
+ * @param l location object to set
+ * @return true if a location fix is available
+ */
+ public abstract boolean getLocation(Location l);
+
+ /**
+ * 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 void enableLocationTracking(boolean enable) {
+ mLocationTracking = enable;
+ }
+
+ /**
+ * Returns true if the provider has any listeners
+ *
+ * @return true if provider is being tracked
+ */
+ public boolean isLocationTracking() {
+ return mLocationTracking;
+ }
+
+ /**
+ * 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 void setMinTime(long minTime) {
+ mMinTime = minTime;
+ }
+
+ /**
+ * Gets the smallest minimum time between updates amongst all the clients listening
+ * for locations. By default this value is 0 (as frqeuently as possible)
+ *
+ * @return the smallest minTime value over all listeners for this provider
+ */
+ public long getMinTime() {
+ return mMinTime;
+ }
+
+ /**
+ * 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 void updateNetworkState(int state) {
+ }
+
+ /**
+ * Updates the cell state for the given provider. This function must be
+ * overwritten if {@link #requiresCell} returns true.
+ *
+ * @param state cell state
+ */
+ public void updateCellState(CellState state) {
+ }
+
+ /**
+ * 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 boolean sendExtraCommand(String command, Bundle extras) {
+ return false;
+ }
+}
diff --git a/location/java/android/location/package.html b/location/java/android/location/package.html
new file mode 100644
index 0000000..bbaeb42
--- /dev/null
+++ b/location/java/android/location/package.html
@@ -0,0 +1,12 @@
+<html>
+<head>
+<script type="text/javascript" src="http://www.corp.google.com/style/prettify.js"></script>
+<script src="http://www.corp.google.com/eng/techpubs/include/navbar.js" type="text/javascript"></script>
+</head>
+
+<body>
+
+<p>Classes defining Android location-based and related services.</p>
+
+</body>
+</html>
diff --git a/location/java/com/android/internal/location/CellState.java b/location/java/com/android/internal/location/CellState.java
new file mode 100644
index 0000000..697fa92
--- /dev/null
+++ b/location/java/com/android/internal/location/CellState.java
@@ -0,0 +1,152 @@
+/*
+ * 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 android.telephony.CellLocation;
+import android.telephony.ServiceState;
+import android.telephony.gsm.GsmCellLocation;
+import com.android.internal.telephony.TelephonyProperties;
+
+import android.os.SystemProperties;
+
+/**
+ * Stores the cell tower state
+ *
+ * {@hide}
+ */
+public class CellState {
+
+ public static String TAG = "CellState";
+
+ public static int RADIO_TYPE_GPRS = 1;
+ public static int RADIO_TYPE_CDMA = 2;
+ public static int RADIO_TYPE_WCDMA = 3;
+
+ private int mCid = -1;
+ private int mLac = -1;
+ private int mMcc = -1;
+ private int mMnc = -1;
+ private int mHomeMcc = -1;
+ private int mHomeMnc = -1;
+ private String mCarrier = null;
+ private int mRadioType = -1;
+ private long mTime = 0;
+
+ public CellState() {
+ // constructor for invalid cell location
+ }
+
+ public CellState(ServiceState service, CellLocation location) {
+ GsmCellLocation loc = (GsmCellLocation)location;
+ mLac = loc.getLac(); // example: 6032
+ mCid = loc.getCid(); // example: 31792
+ mTime = System.currentTimeMillis();
+
+ // Get radio type
+ String radioType = SystemProperties.get(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE);
+ if (radioType != null && (radioType.equals("GPRS") || radioType.equals("EDGE"))) {
+ mRadioType = RADIO_TYPE_GPRS;
+ } else if (radioType != null && radioType.equals("UMTS")) {
+ mRadioType = RADIO_TYPE_WCDMA;
+ }
+
+ // Get MCC/MNC
+ String operator = service.getOperatorNumeric();
+ if (operator != null && !operator.equals("")) {
+ String mcc = operator.substring(0, 3);
+ String mnc = operator.substring(3);
+
+ mMcc = Integer.parseInt(mcc);
+ mMnc = Integer.parseInt(mnc);
+ }
+
+ // Get Home MCC/MNC
+ String homeOperator = SystemProperties.get(TelephonyProperties.PROPERTY_SIM_OPERATOR_NUMERIC);
+ if (homeOperator != null && !homeOperator.equals("")) {
+ String mcc = homeOperator.substring(0, 3);
+ String mnc = homeOperator.substring(3);
+
+ mHomeMcc = Integer.parseInt(mcc);
+ mHomeMnc = Integer.parseInt(mnc);
+ }
+
+ // Get Carrier
+ String carrier = service.getOperatorAlphaLong();
+ if (carrier != null && !carrier.equals("")) {
+ mCarrier = carrier;
+ }
+
+ //Log.d(TAG, mLac +"," + mCid + "," + mMnc +"," + mMcc + "," + mHomeMcc + "," +
+ // mHomeMnc + "," + mCarrier + "," + mRadioType);
+ }
+
+ public int getCid() {
+ return mCid;
+ }
+
+ public int getLac() {
+ return mLac;
+ }
+
+ public int getMcc() {
+ return mMcc;
+ }
+
+ public int getMnc() {
+ return mMnc;
+ }
+
+ public int getHomeMcc() {
+ return mHomeMcc;
+ }
+
+ public void setHomeMcc(int homeMcc) {
+ this.mHomeMcc = homeMcc;
+ }
+
+ public int getHomeMnc() {
+ return mHomeMnc;
+ }
+
+ public void setHomeMnc(int homeMnc) {
+ this.mHomeMnc = homeMnc;
+ }
+
+ public String getCarrier() {
+ return mCarrier;
+ }
+
+ public void setCarrier(String carrier) {
+ this.mCarrier = carrier;
+ }
+
+ public int getRadioType() {
+ return mRadioType;
+ }
+
+ public long getTime() {
+ return mTime;
+ }
+
+ public boolean equals(CellState other) {
+ return (mCid == other.mCid && mLac == other.mLac);
+ }
+
+ public boolean isValid() {
+ return (mCid != -1 && mLac != -1);
+ }
+}
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
new file mode 100644
index 0000000..d0e4f49
--- /dev/null
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -0,0 +1,879 @@
+/*
+ * Copyright (C) 2008 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.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Properties;
+
+import android.content.Context;
+import android.content.Intent;
+import android.location.Criteria;
+import android.location.IGpsStatusListener;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.location.LocationProviderImpl;
+import android.net.SntpClient;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Config;
+import android.util.Log;
+
+/**
+ * A GPS implementation of LocationProvider used by LocationManager.
+ *
+ * {@hide}
+ */
+public class GpsLocationProvider extends LocationProviderImpl {
+
+ private static final String TAG = "GpsLocationProvider";
+
+ /**
+ * Broadcast intent action indicating that the GPS has either been
+ * enabled or disabled. An intent extra provides this state as a boolean,
+ * where {@code true} means enabled.
+ * @see #EXTRA_ENABLED
+ *
+ * {@hide}
+ */
+ public static final String GPS_ENABLED_CHANGE_ACTION =
+ "android.location.GPS_ENABLED_CHANGE";
+
+ /**
+ * Broadcast intent action indicating that the GPS has either started or
+ * stopped receiving GPS fixes. An intent extra provides this state as a
+ * boolean, where {@code true} means that the GPS is actively receiving fixes.
+ * @see #EXTRA_ENABLED
+ *
+ * {@hide}
+ */
+ public static final String GPS_FIX_CHANGE_ACTION =
+ "android.location.GPS_FIX_CHANGE";
+
+ /**
+ * The lookup key for a boolean that indicates whether GPS is enabled or
+ * disabled. {@code true} means GPS is enabled. Retrieve it with
+ * {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_ENABLED = "enabled";
+
+ // these need to match GpsStatusValue defines in gps.h
+ private static final int GPS_STATUS_NONE = 0;
+ private static final int GPS_STATUS_SESSION_BEGIN = 1;
+ private static final int GPS_STATUS_SESSION_END = 2;
+ private static final int GPS_STATUS_ENGINE_ON = 3;
+ private static final int GPS_STATUS_ENGINE_OFF = 4;
+
+ // these need to match GpsLocationFlags enum in gps.h
+ private static final int LOCATION_INVALID = 0;
+ private static final int LOCATION_HAS_LAT_LONG = 1;
+ private static final int LOCATION_HAS_ALTITUDE = 2;
+ private static final int LOCATION_HAS_SPEED = 4;
+ private static final int LOCATION_HAS_BEARING = 8;
+ private static final int LOCATION_HAS_ACCURACY = 16;
+
+// IMPORTANT - the GPS_DELETE_* symbols here must match constants in GpsLocationProvider.java
+ private static final int GPS_DELETE_EPHEMERIS = 0x0001;
+ private static final int GPS_DELETE_ALMANAC = 0x0002;
+ private static final int GPS_DELETE_POSITION = 0x0004;
+ private static final int GPS_DELETE_TIME = 0x0008;
+ private static final int GPS_DELETE_IONO = 0x0010;
+ private static final int GPS_DELETE_UTC = 0x0020;
+ private static final int GPS_DELETE_HEALTH = 0x0040;
+ private static final int GPS_DELETE_SVDIR = 0x0080;
+ private static final int GPS_DELETE_SVSTEER = 0x0100;
+ private static final int GPS_DELETE_SADATA = 0x0200;
+ private static final int GPS_DELETE_RTI = 0x0400;
+ private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
+ private static final int GPS_DELETE_ALL = 0xFFFF;
+
+ private static final String PROPERTIES_FILE = "/etc/gps.conf";
+
+ private int mLocationFlags = LOCATION_INVALID;
+
+ // current status
+ private int mStatus = TEMPORARILY_UNAVAILABLE;
+
+ // time for last status update
+ private long mStatusUpdateTime = SystemClock.elapsedRealtime();
+
+ // turn off GPS fix icon if we haven't received a fix in 10 seconds
+ private static final long RECENT_FIX_TIMEOUT = 10 * 1000;
+
+ // true if we are enabled
+ private boolean mEnabled;
+ // true if we are enabled for location updates
+ private boolean mLocationTracking;
+
+ // true if we have network connectivity
+ private boolean mNetworkAvailable;
+
+ // true if GPS is navigating
+ private boolean mNavigating;
+
+ // requested frequency of fixes, in seconds
+ private int mFixInterval = 1;
+
+ // true if we started navigation
+ private boolean mStarted;
+
+ // for calculating time to first fix
+ private long mFixRequestTime = 0;
+ // time to first fix for most recent session
+ private int mTTFF = 0;
+ // time we received our last fix
+ private long mLastFixTime;
+
+ // properties loaded from PROPERTIES_FILE
+ private Properties mProperties;
+
+ private Context mContext;
+ 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;
+
+ // how often to request NTP time, in milliseconds
+ // current setting 4 hours
+ private static final long NTP_INTERVAL = 4*60*60*1000;
+ // how long to wait if we have a network error in NTP or XTRA downloading
+ // current setting - 5 minutes
+ private static final long RETRY_INTERVAL = 5*60*1000;
+
+ private LocationCollector mCollector;
+
+ public static boolean isSupported() {
+ return native_is_supported();
+ }
+
+ public GpsLocationProvider(Context context, LocationCollector collector) {
+ super(LocationManager.GPS_PROVIDER);
+ mContext = context;
+ mCollector = collector;
+
+ mProperties = new Properties();
+ try {
+ File file = new File(PROPERTIES_FILE);
+ FileInputStream stream = new FileInputStream(file);
+ mProperties.load(stream);
+ stream.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE, e);
+ }
+ }
+
+ /**
+ * Returns true if the provider requires access to a
+ * data network (e.g., the Internet), false otherwise.
+ */
+ @Override
+ public boolean requiresNetwork() {
+ // We want updateNetworkState() to get called when the network state changes
+ // for XTRA and NTP time injection support.
+ return true;
+ }
+
+ public void updateNetworkState(int state) {
+ mNetworkAvailable = (state == LocationProvider.AVAILABLE);
+
+ if (Config.LOGD) {
+ Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable"));
+ }
+
+ if (mNetworkAvailable && mNetworkThread != null && mEnabled) {
+ // signal the network thread when the network becomes available
+ mNetworkThread.signal();
+ }
+ }
+
+ /**
+ * Returns true if the provider requires access to a
+ * satellite-based positioning system (e.g., GPS), false
+ * otherwise.
+ */
+ @Override
+ public boolean requiresSatellite() {
+ return true;
+ }
+
+ /**
+ * Returns true if the provider requires access to an appropriate
+ * cellular network (e.g., to make use of cell tower IDs), false
+ * otherwise.
+ */
+ @Override
+ public boolean requiresCell() {
+ return false;
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ public boolean hasMonetaryCost() {
+ return false;
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ public boolean supportsAltitude() {
+ return true;
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ public boolean supportsSpeed() {
+ return true;
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ public boolean supportsBearing() {
+ return true;
+ }
+
+ /**
+ * Returns the power requirement for this provider.
+ *
+ * @return the power requirement for this provider, as one of the
+ * constants Criteria.POWER_REQUIREMENT_*.
+ */
+ @Override
+ public int getPowerRequirement() {
+ return Criteria.POWER_HIGH;
+ }
+
+ /**
+ * Returns the horizontal accuracy of this provider
+ *
+ * @return the accuracy of location from this provider, as one
+ * of the constants Criteria.ACCURACY_*.
+ */
+ @Override
+ public int getAccuracy() {
+ return Criteria.ACCURACY_FINE;
+ }
+
+ /**
+ * Enables this provider. When enabled, calls to getStatus()
+ * and getLocation() must be handled. Hardware may be started up
+ * when the provider is enabled.
+ */
+ @Override
+ public void enable() {
+ if (Config.LOGD) Log.d(TAG, "enable");
+ mEnabled = native_init();
+
+ if (mEnabled) {
+ // run event listener thread while we are enabled
+ mEventThread = new GpsEventThread();
+ mEventThread.start();
+
+ // 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");
+ }
+ }
+
+ /**
+ * Disables this provider. When disabled, calls to getStatus()
+ * and getLocation() need not be handled. Hardware may be shut
+ * down while the provider is disabled.
+ */
+ @Override
+ public void disable() {
+ if (Config.LOGD) Log.d(TAG, "disable");
+ mEnabled = false;
+ stopNavigating();
+ native_disable();
+
+ // make sure our event thread exits
+ if (mEventThread != null) {
+ try {
+ mEventThread.join();
+ } catch (InterruptedException e) {
+ Log.w(TAG, "InterruptedException when joining mEventThread");
+ }
+ mEventThread = null;
+ }
+
+ native_cleanup();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ @Override
+ public int getStatus(Bundle extras) {
+ if (extras != null) {
+ extras.putInt("satellites", mSvCount);
+ }
+ return mStatus;
+ }
+
+ private void updateStatus(int status, int svCount) {
+ if (status != mStatus || svCount != mSvCount) {
+ mStatus = status;
+ mSvCount = svCount;
+ mLocationExtras.putInt("satellites", svCount);
+ mStatusUpdateTime = SystemClock.elapsedRealtime();
+ }
+ }
+
+ @Override
+ public long getStatusUpdateTime() {
+ return mStatusUpdateTime;
+ }
+
+ @Override
+ public boolean getLocation(Location l) {
+ synchronized (mLocation) {
+ // don't report locations without latitude and longitude
+ if ((mLocationFlags & LOCATION_HAS_LAT_LONG) == 0) {
+ return false;
+ }
+ l.set(mLocation);
+ l.setExtras(mLocationExtras);
+ return true;
+ }
+ }
+
+ @Override
+ public void enableLocationTracking(boolean enable) {
+ if (mLocationTracking == enable) {
+ return;
+ }
+
+ if (enable) {
+ mFixRequestTime = System.currentTimeMillis();
+ mTTFF = 0;
+ mLastFixTime = 0;
+ startNavigating();
+ } else {
+ stopNavigating();
+ }
+ mLocationTracking = enable;
+ }
+
+ @Override
+ public boolean isLocationTracking() {
+ return mLocationTracking;
+ }
+
+ @Override
+ public void setMinTime(long minTime) {
+ super.setMinTime(minTime);
+ if (Config.LOGD) Log.d(TAG, "setMinTime " + minTime);
+
+ if (minTime >= 0) {
+ int interval = (int)(minTime/1000);
+ if (interval < 1) {
+ interval = 1;
+ }
+ mFixInterval = interval;
+ native_set_fix_frequency(mFixInterval);
+ }
+ }
+
+ private final class Listener implements IBinder.DeathRecipient {
+ final IGpsStatusListener mListener;
+
+ int mSensors = 0;
+
+ Listener(IGpsStatusListener listener) {
+ mListener = listener;
+ }
+
+ public void binderDied() {
+ if (Config.LOGD) Log.d(TAG, "GPS status listener died");
+
+ synchronized(mListeners) {
+ mListeners.remove(this);
+ }
+ }
+ }
+
+ public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
+ if (listener == null) throw new NullPointerException("listener is null in addGpsStatusListener");
+
+ synchronized(mListeners) {
+ IBinder binder = listener.asBinder();
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener test = mListeners.get(i);
+ if (binder.equals(test.mListener.asBinder())) {
+ // listener already added
+ return;
+ }
+ }
+
+ Listener l = new Listener(listener);
+ binder.linkToDeath(l, 0);
+ mListeners.add(l);
+ }
+ }
+
+ public void removeGpsStatusListener(IGpsStatusListener listener) {
+ if (listener == null) throw new NullPointerException("listener is null in addGpsStatusListener");
+
+ synchronized(mListeners) {
+ IBinder binder = listener.asBinder();
+ Listener l = null;
+ int size = mListeners.size();
+ for (int i = 0; i < size && l == null; i++) {
+ Listener test = mListeners.get(i);
+ if (binder.equals(test.mListener.asBinder())) {
+ // listener already added
+ return;
+ }
+ }
+
+ if (l != null) {
+ mListeners.remove(l);
+ binder.unlinkToDeath(l, 0);
+ }
+ }
+ }
+
+ @Override
+ public boolean sendExtraCommand(String command, Bundle extras) {
+
+ if ("delete_aiding_data".equals(command)) {
+ return deleteAidingData(extras);
+ }
+
+ Log.w(TAG, "sendExtraCommand: unknown command " + command);
+ return false;
+ }
+
+ private boolean deleteAidingData(Bundle extras) {
+ int flags;
+
+ if (extras == null) {
+ flags = GPS_DELETE_ALL;
+ } else {
+ flags = 0;
+ if (extras.getBoolean("ephemeris")) flags |= GPS_DELETE_EPHEMERIS;
+ if (extras.getBoolean("almanac")) flags |= GPS_DELETE_ALMANAC;
+ if (extras.getBoolean("position")) flags |= GPS_DELETE_POSITION;
+ if (extras.getBoolean("time")) flags |= GPS_DELETE_TIME;
+ if (extras.getBoolean("iono")) flags |= GPS_DELETE_IONO;
+ if (extras.getBoolean("utc")) flags |= GPS_DELETE_UTC;
+ if (extras.getBoolean("health")) flags |= GPS_DELETE_HEALTH;
+ if (extras.getBoolean("svdir")) flags |= GPS_DELETE_SVDIR;
+ if (extras.getBoolean("svsteer")) flags |= GPS_DELETE_SVSTEER;
+ if (extras.getBoolean("sadata")) flags |= GPS_DELETE_SADATA;
+ if (extras.getBoolean("rti")) flags |= GPS_DELETE_RTI;
+ if (extras.getBoolean("celldb-info")) flags |= GPS_DELETE_CELLDB_INFO;
+ if (extras.getBoolean("all")) flags |= GPS_DELETE_ALL;
+ }
+
+ if (flags != 0) {
+ native_delete_aiding_data(flags);
+ return true;
+ }
+
+ return false;
+ }
+
+ public void startNavigating() {
+ if (!mStarted) {
+ if (Config.LOGV) Log.v(TAG, "startNavigating");
+ mStarted = true;
+ if (!native_start(false, mFixInterval)) {
+ mStarted = false;
+ Log.e(TAG, "native_start failed in startNavigating()");
+ }
+
+ // reset SV count to zero
+ updateStatus(TEMPORARILY_UNAVAILABLE, 0);
+ }
+ }
+
+ public void stopNavigating() {
+ if (Config.LOGV) Log.v(TAG, "stopNavigating");
+ if (mStarted) {
+ mStarted = false;
+ native_stop();
+ mTTFF = 0;
+ mLastFixTime = 0;
+ mLocationFlags = LOCATION_INVALID;
+
+ // reset SV count to zero
+ updateStatus(TEMPORARILY_UNAVAILABLE, 0);
+ }
+ }
+
+ /**
+ * called from native code to update our position.
+ */
+ private void reportLocation(int flags, double latitude, double longitude, double altitude,
+ float speed, float bearing, float accuracy, long timestamp) {
+ if (Config.LOGV) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +
+ " timestamp: " + timestamp);
+
+ mLastFixTime = System.currentTimeMillis();
+ // report time to first fix
+ if (mTTFF == 0) {
+ mTTFF = (int)(mLastFixTime - mFixRequestTime);
+ if (Config.LOGD) Log.d(TAG, "TTFF: " + mTTFF);
+
+ // notify status listeners
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ listener.mListener.onFirstFix(mTTFF);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in stopNavigating");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+ }
+
+ synchronized (mLocation) {
+ mLocationFlags = flags;
+ if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ mLocation.setLatitude(latitude);
+ mLocation.setLongitude(longitude);
+ mLocation.setTime(timestamp);
+ }
+ if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
+ mLocation.setAltitude(altitude);
+ } else {
+ mLocation.removeAltitude();
+ }
+ if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
+ mLocation.setSpeed(speed);
+ } else {
+ mLocation.removeSpeed();
+ }
+ if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
+ mLocation.setBearing(bearing);
+ } else {
+ mLocation.removeBearing();
+ }
+ if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
+ mLocation.setAccuracy(accuracy);
+ } else {
+ mLocation.removeAccuracy();
+ }
+
+ // Send to collector
+ if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ mCollector.updateLocation(mLocation);
+ }
+ }
+
+ if (mStarted && mStatus != AVAILABLE) {
+ // send an intent to notify that the GPS is receiving fixes.
+ Intent intent = new Intent(GPS_FIX_CHANGE_ACTION);
+ intent.putExtra(EXTRA_ENABLED, true);
+ mContext.sendBroadcast(intent);
+ updateStatus(AVAILABLE, mSvCount);
+ }
+ }
+
+ /**
+ * called from native code to update our status
+ */
+ private void reportStatus(int status) {
+ if (Config.LOGV) Log.v(TAG, "reportStatus status: " + status);
+
+ boolean wasNavigating = mNavigating;
+ mNavigating = (status == GPS_STATUS_SESSION_BEGIN || status == GPS_STATUS_ENGINE_ON);
+
+ if (wasNavigating != mNavigating) {
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ if (mNavigating) {
+ listener.mListener.onGpsStarted();
+ } else {
+ listener.mListener.onGpsStopped();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportStatus");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+
+ // send an intent to notify that the GPS has been enabled or disabled.
+ Intent intent = new Intent(GPS_ENABLED_CHANGE_ACTION);
+ intent.putExtra(EXTRA_ENABLED, mNavigating);
+ mContext.sendBroadcast(intent);
+ }
+ }
+
+ /**
+ * called from native code to update SV info
+ */
+ private void reportSvStatus() {
+
+ int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
+
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
+ mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
+ mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportSvInfo");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+
+ if (Config.LOGD) {
+ if (Config.LOGV) Log.v(TAG, "SV count: " + svCount +
+ " ephemerisMask: " + Integer.toHexString(mSvMasks[EPHEMERIS_MASK]) +
+ " almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
+ for (int i = 0; i < svCount; i++) {
+ if (Config.LOGV) Log.v(TAG, "sv: " + mSvs[i] +
+ " snr: " + (float)mSnrs[i]/10 +
+ " elev: " + mSvElevations[i] +
+ " azimuth: " + mSvAzimuths[i] +
+ ((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " E") +
+ ((mSvMasks[ALMANAC_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " A") +
+ ((mSvMasks[USED_FOR_FIX_MASK] & (1 << (mSvs[i] - 1))) == 0 ? "" : "U"));
+ }
+ }
+
+ updateStatus(mStatus, svCount);
+
+ if (mNavigating && mStatus == AVAILABLE && mLastFixTime > 0 &&
+ System.currentTimeMillis() - mLastFixTime > RECENT_FIX_TIMEOUT) {
+ // send an intent to notify that the GPS is no longer receiving fixes.
+ Intent intent = new Intent(GPS_FIX_CHANGE_ACTION);
+ intent.putExtra(EXTRA_ENABLED, false);
+ mContext.sendBroadcast(intent);
+ updateStatus(TEMPORARILY_UNAVAILABLE, mSvCount);
+ }
+ }
+
+ private void xtraDownloadRequest() {
+ if (Config.LOGD) Log.d(TAG, "xtraDownloadRequest");
+ if (mNetworkThread != null) {
+ mNetworkThread.xtraDownloadRequest();
+ }
+ }
+
+ private class GpsEventThread extends Thread {
+
+ public GpsEventThread() {
+ super("GpsEventThread");
+ }
+
+ public void run() {
+ if (Config.LOGD) Log.d(TAG, "GpsEventThread starting");
+ // thread exits after disable() is called and navigation has stopped
+ while (mEnabled || mNavigating) {
+ // 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");
+ }
+ }
+
+ private class GpsNetworkThread extends Thread {
+
+ private long mNextNtpTime = 0;
+ private long mNextXtraTime = 0;
+ private boolean mXtraDownloadRequested = false;
+
+ public GpsNetworkThread() {
+ super("GpsNetworkThread");
+ }
+
+ public void run() {
+ 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 (mEnabled) {
+ 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 (mEnabled && ((!mXtraDownloadRequested && waitTime > 0) || !mNetworkAvailable));
+ if (Config.LOGD) Log.d(TAG, "NetworkThread out of wake loop");
+
+ if (mEnabled) {
+ if (mNextNtpTime <= System.currentTimeMillis()) {
+ String ntpServer = mProperties.getProperty("NTP_SERVER", "pool.ntp.org");
+ if (Config.LOGD) Log.d(TAG, "Requesting time from NTP server " + ntpServer);
+ if (client.requestTime(ntpServer, 10000)) {
+ long time = client.getNtpTime();
+ long timeReference = client.getNtpTimeReference();
+ int certainty = (int)(client.getRoundTripTime()/2);
+
+ if (Config.LOGD) Log.d(TAG, "calling native_inject_time: " +
+ time + " reference: " + timeReference
+ + " certainty: " + certainty);
+
+ native_inject_time(time, timeReference, certainty);
+ mNextNtpTime = System.currentTimeMillis() + NTP_INTERVAL;
+ } else {
+ if (Config.LOGD) Log.d(TAG, "requestTime failed");
+ mNextNtpTime = System.currentTimeMillis() + RETRY_INTERVAL;
+ }
+ }
+
+ if ((mXtraDownloadRequested ||
+ (mNextXtraTime > 0 && mNextXtraTime <= System.currentTimeMillis())) &&
+ xtraDownloader != null) {
+ 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;
+ mXtraDownloadRequested = false;
+ } else {
+ mNextXtraTime = System.currentTimeMillis() + RETRY_INTERVAL;
+ }
+ }
+ }
+ }
+ if (Config.LOGD) Log.d(TAG, "NetworkThread exiting");
+ }
+
+ synchronized void xtraDownloadRequest() {
+ mXtraDownloadRequested = true;
+ notify();
+ }
+
+ synchronized void signal() {
+ notify();
+ }
+
+ private long getWaitTime() {
+ long now = System.currentTimeMillis();
+ long waitTime = mNextNtpTime - now;
+ if (mNextXtraTime != 0) {
+ long xtraWaitTime = mNextXtraTime - now;
+ if (xtraWaitTime < waitTime) {
+ waitTime = xtraWaitTime;
+ }
+ }
+ if (waitTime < 0) {
+ waitTime = 0;
+ }
+ return waitTime;
+ }
+ }
+
+ // for GPS SV statistics
+ private static final int MAX_SVS = 32;
+ private static final int EPHEMERIS_MASK = 0;
+ private static final int ALMANAC_MASK = 1;
+ private static final int USED_FOR_FIX_MASK = 2;
+
+ // preallocated arrays, to avoid memory allocation in reportStatus()
+ private int mSvs[] = new int[MAX_SVS];
+ private float mSnrs[] = new float[MAX_SVS];
+ private float mSvElevations[] = new float[MAX_SVS];
+ private float mSvAzimuths[] = new float[MAX_SVS];
+ private int mSvMasks[] = new int[3];
+ private int mSvCount;
+
+ static { class_init_native(); }
+ private static native void class_init_native();
+ private static native boolean native_is_supported();
+
+ private native boolean native_init();
+ private native void native_disable();
+ private native void native_cleanup();
+ private native boolean native_start(boolean singleFix, int fixInterval);
+ private native boolean native_stop();
+ private native void native_set_fix_frequency(int fixFrequency);
+ private native void native_delete_aiding_data(int flags);
+ private native void native_wait_for_event();
+ // returns number of SVs
+ // mask[0] is ephemeris mask and mask[1] is almanac mask
+ private native int native_read_sv_status(int[] svs, float[] snrs,
+ float[] elevations, float[] azimuths, int[] masks);
+
+ 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);
+}
diff --git a/location/java/com/android/internal/location/GpsXtraDownloader.java b/location/java/com/android/internal/location/GpsXtraDownloader.java
new file mode 100644
index 0000000..6efbbf6
--- /dev/null
+++ b/location/java/com/android/internal/location/GpsXtraDownloader.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2008 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 org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.params.ConnRouteParams;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import android.content.Context;
+import android.net.Proxy;
+import android.net.http.AndroidHttpClient;
+import android.util.Log;
+
+/**
+ * A class for downloading GPS XTRA data.
+ *
+ * {@hide}
+ */
+public class GpsXtraDownloader {
+
+ private static final String TAG = "GpsXtraDownloader";
+
+ private Context mContext;
+ private String[] mXtraServers;
+ // to load balance our server requests
+ private int mNextServerIndex = 0;
+
+ GpsXtraDownloader(Context context, Properties properties) {
+ mContext = context;
+
+ // read XTRA servers from the Properties object
+ int count = 0;
+ String server1 = properties.getProperty("XTRA_SERVER_1");
+ String server2 = properties.getProperty("XTRA_SERVER_2");
+ String server3 = properties.getProperty("XTRA_SERVER_3");
+ if (server1 != null) count++;
+ if (server2 != null) count++;
+ if (server3 != null) count++;
+
+ if (count == 0) {
+ Log.e(TAG, "No XTRA servers were specified in the GPS configuration");
+ } else {
+ mXtraServers = new String[count];
+ count = 0;
+ if (server1 != null) mXtraServers[count++] = server1;
+ if (server2 != null) mXtraServers[count++] = server2;
+ if (server3 != null) mXtraServers[count++] = server3;
+ }
+ }
+
+ byte[] downloadXtraData() {
+ String proxyHost = Proxy.getHost(mContext);
+ int proxyPort = Proxy.getPort(mContext);
+ boolean useProxy = (proxyHost != null && proxyPort != -1);
+ byte[] result = null;
+ int startIndex = mNextServerIndex;
+
+ if (mXtraServers == null) {
+ return null;
+ }
+
+ // load balance our requests among the available servers
+ while (result == null) {
+ result = doDownload(mXtraServers[mNextServerIndex], useProxy, proxyHost, proxyPort);
+
+ // increment mNextServerIndex and wrap around if necessary
+ mNextServerIndex++;
+ if (mNextServerIndex == mXtraServers.length) {
+ mNextServerIndex = 0;
+ }
+ // break if we have tried all the servers
+ if (mNextServerIndex == startIndex) break;
+ }
+
+ return result;
+ }
+
+ protected static byte[] doDownload(String url, boolean isProxySet,
+ String proxyHost, int proxyPort) {
+ AndroidHttpClient client = null;
+ try {
+ client = AndroidHttpClient.newInstance("Android");
+ HttpUriRequest req = new HttpGet(url);
+
+ if (isProxySet) {
+ HttpHost proxy = new HttpHost(proxyHost, proxyPort);
+ ConnRouteParams.setDefaultProxy(req.getParams(), proxy);
+ }
+
+ req.addHeader(
+ "Accept",
+ "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic");
+
+ req.addHeader(
+ "x-wap-profile",
+ "http://www.openmobilealliance.org/tech/profiles/UAPROF/ccppschema-20021212#");
+
+ HttpResponse response = client.execute(req);
+ StatusLine status = response.getStatusLine();
+ if (status.getStatusCode() != 200) { // HTTP 200 is success.
+ Log.d(TAG, "HTTP error: " + status.getReasonPhrase());
+ return null;
+ }
+
+ HttpEntity entity = response.getEntity();
+ byte[] body = null;
+ if (entity != null) {
+ try {
+ if (entity.getContentLength() > 0) {
+ body = new byte[(int) entity.getContentLength()];
+ DataInputStream dis = new DataInputStream(entity.getContent());
+ try {
+ dis.readFully(body);
+ } finally {
+ try {
+ dis.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Unexpected IOException.", e);
+ }
+ }
+ }
+ } finally {
+ if (entity != null) {
+ entity.consumeContent();
+ }
+ }
+ }
+ return body;
+ } catch (Exception e) {
+ Log.d(TAG, "error " + e);
+ } finally {
+ if (client != null) {
+ client.close();
+ }
+ }
+ return null;
+ }
+
+}
+
diff --git a/location/java/com/android/internal/location/LocationCache.java b/location/java/com/android/internal/location/LocationCache.java
new file mode 100644
index 0000000..f0928f9
--- /dev/null
+++ b/location/java/com/android/internal/location/LocationCache.java
@@ -0,0 +1,577 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.android.internal.location;
+
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import android.location.Location;
+import android.location.LocationManager;
+import android.net.wifi.ScanResult;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Data store to cache cell-id and wifi locations from the network
+ *
+ * {@hide}
+ */
+public class LocationCache {
+ private static final String TAG = "LocationCache";
+
+ // Version of cell cache
+ private static final int CACHE_DB_VERSION = 1;
+
+ // Don't save cache more than once every minute
+ private static final long SAVE_FREQUENCY = 60 * 1000;
+
+ // Location of the cache file;
+ private static final String mCellCacheFile = "cache.cell";
+ private static final String mWifiCacheFile = "cache.wifi";
+
+ // Maximum time (in millis) that a record is valid for, before it needs
+ // to be refreshed from the server.
+ private static final long MAX_CELL_REFRESH_RECORD_AGE = 12 * 60 * 60 * 1000; // 12 hours
+ private static final long MAX_WIFI_REFRESH_RECORD_AGE = 48 * 60 * 60 * 1000; // 48 hours
+
+ // Cache sizes
+ private static final int MAX_CELL_RECORDS = 50;
+ private static final int MAX_WIFI_RECORDS = 200;
+
+ // Cache constants
+ private static final long CELL_SMOOTHING_WINDOW = 30 * 1000; // 30 seconds
+ private static final int WIFI_MIN_AP_REQUIRED = 2;
+ private static final int WIFI_MAX_MISS_ALLOWED = 5;
+ private static final int MAX_ACCURACY_ALLOWED = 5000; // 5km
+
+ // Caches
+ private final Cache<Record> mCellCache;
+ private final Cache<Record> mWifiCache;
+
+ // Currently calculated centroids
+ private final LocationCentroid mCellCentroid = new LocationCentroid();
+ private final LocationCentroid mWifiCentroid = new LocationCentroid();
+
+ // Extra key and values
+ private final String EXTRA_KEY_LOCATION_TYPE = "networkLocationType";
+ private final String EXTRA_VALUE_LOCATION_TYPE_CELL = "cell";
+ private final String EXTRA_VALUE_LOCATION_TYPE_WIFI = "wifi";
+
+ public LocationCache() {
+ mCellCache = new Cache<Record>(LocationManager.SYSTEM_DIR, mCellCacheFile,
+ MAX_CELL_RECORDS, MAX_CELL_REFRESH_RECORD_AGE);
+ mWifiCache = new Cache<Record>(LocationManager.SYSTEM_DIR, mWifiCacheFile,
+ MAX_WIFI_RECORDS, MAX_WIFI_REFRESH_RECORD_AGE);
+ }
+
+ /**
+ * Looks up network location on device cache
+ *
+ * @param cellState primary cell state
+ * @param cellHistory history of cell states
+ * @param scanResults wifi scan results
+ * @param result location object to fill if location is found
+ * @return true if cache was able to answer query (successfully or not), false if call to
+ * server is required
+ */
+ public synchronized boolean lookup(CellState cellState, List<CellState> cellHistory,
+ List<ScanResult> scanResults, Location result) {
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "including cell:" + (cellState != null) +
+ ", wifi:" + ((scanResults != null)? scanResults.size() : "null"));
+ }
+
+ long now = System.currentTimeMillis();
+
+ mCellCentroid.reset();
+ mWifiCentroid.reset();
+
+ if (cellState != null) {
+ String primaryCellKey = getCellCacheKey(cellState.getMcc(), cellState.getMnc(),
+ cellState.getLac(), cellState.getCid());
+ Record record = mCellCache.lookup(primaryCellKey);
+
+ if (record == null) {
+ // Make a server request if primary cell doesn't exist in DB
+ return false;
+ }
+
+ if (record.isValid()) {
+ mCellCentroid.addLocation(record.getLat(), record.getLng(), record.getAccuracy(),
+ record.getConfidence());
+ }
+ }
+
+ if (cellHistory != null) {
+ for (CellState historicalCell : cellHistory) {
+ // Cell location might need to be smoothed if you are on the border of two cells
+ if (now - historicalCell.getTime() < CELL_SMOOTHING_WINDOW) {
+ String historicalCellKey = getCellCacheKey(historicalCell.getMcc(),
+ historicalCell.getMnc(), historicalCell.getLac(), historicalCell.getCid());
+ Record record = mCellCache.lookup(historicalCellKey);
+ if (record != null && record.isValid()) {
+ mCellCentroid.addLocation(record.getLat(), record.getLng(),
+ record.getAccuracy(), record.getConfidence());
+ }
+ }
+ }
+ }
+
+ if (scanResults != null) {
+ int miss = 0;
+ for (ScanResult scanResult : scanResults) {
+ String wifiKey = scanResult.BSSID;
+ Record record = mWifiCache.lookup(wifiKey);
+ if (record == null) {
+ miss++;
+ } else {
+ if (record.isValid()) {
+ mWifiCentroid.addLocation(record.getLat(), record.getLng(),
+ record.getAccuracy(), record.getConfidence());
+ }
+ }
+ }
+
+ if (mWifiCentroid.getNumber() >= WIFI_MIN_AP_REQUIRED) {
+ // Try to return best out of the available cell or wifi location
+ } else if (miss > Math.min(WIFI_MAX_MISS_ALLOWED, (scanResults.size()+1)/2)) {
+ // Make a server request
+ return false;
+ } else {
+ // Don't use wifi location, only consider using cell location
+ mWifiCache.save();
+ mWifiCentroid.reset();
+ }
+ }
+
+ if (mCellCentroid.getNumber() > 0) {
+ mCellCache.save();
+ }
+ if (mWifiCentroid.getNumber() > 0) {
+ mWifiCache.save();
+ }
+
+ int cellAccuracy = mCellCentroid.getAccuracy();
+ int wifiAccuracy = mWifiCentroid.getAccuracy();
+
+ int cellConfidence = mCellCentroid.getConfidence();
+ int wifiConfidence = mWifiCentroid.getConfidence();
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "cellAccuracy:" + cellAccuracy+ ", wifiAccuracy:" + wifiAccuracy);
+ }
+
+ if (mCellCentroid.getNumber() != 0 && cellAccuracy <= MAX_ACCURACY_ALLOWED &&
+ (mWifiCentroid.getNumber() == 0 || cellConfidence >= wifiConfidence ||
+ cellAccuracy < wifiAccuracy)) {
+ // Use cell results
+ result.setAccuracy(cellAccuracy);
+ result.setLatitude(mCellCentroid.getCentroidLat());
+ result.setLongitude(mCellCentroid.getCentroidLng());
+ result.setTime(now);
+
+ Bundle extras = result.getExtras() == null ? new Bundle() : result.getExtras();
+ extras.putString(EXTRA_KEY_LOCATION_TYPE, EXTRA_VALUE_LOCATION_TYPE_CELL);
+ result.setExtras(extras);
+
+ } else if (mWifiCentroid.getNumber() != 0 && wifiAccuracy <= MAX_ACCURACY_ALLOWED) {
+ // Use wifi results
+ result.setAccuracy(wifiAccuracy);
+ result.setLatitude(mWifiCentroid.getCentroidLat());
+ result.setLongitude(mWifiCentroid.getCentroidLng());
+ result.setTime(now);
+
+ Bundle extras = result.getExtras() == null ? new Bundle() : result.getExtras();
+ extras.putString(EXTRA_KEY_LOCATION_TYPE, EXTRA_VALUE_LOCATION_TYPE_WIFI);
+ result.setExtras(extras);
+
+ } else {
+ // Return invalid location
+ result.setAccuracy(-1);
+ }
+
+ // don't make a server request
+ return true;
+ }
+
+ public synchronized void insert(int mcc, int mnc, int lac, int cid, double lat, double lng,
+ int accuracy, int confidence, long time) {
+ String key = getCellCacheKey(mcc, mnc, lac, cid);
+ if (accuracy <= 0) {
+ mCellCache.insert(key, new Record());
+ } else {
+ mCellCache.insert(key, new Record(accuracy, confidence, lat, lng, time));
+ }
+ }
+
+ public synchronized void insert(String bssid, double lat, double lng, int accuracy,
+ int confidence, long time) {
+ if (accuracy <= 0) {
+ mWifiCache.insert(bssid, new Record());
+ } else {
+ mWifiCache.insert(bssid, new Record(accuracy, confidence, lat, lng, time));
+ }
+ }
+
+ public synchronized void save() {
+ mCellCache.save();
+ mWifiCache.save();
+ }
+
+ /**
+ * Cell or Wifi location record
+ */
+ public static class Record {
+
+ private final double lat;
+ private final double lng;
+ private final int accuracy;
+ private final int confidence;
+
+ // Time (since the epoch) of original reading.
+ private final long originTime;
+
+ public static Record read(DataInput dataInput) throws IOException {
+ final int accuracy = dataInput.readInt();
+ final int confidence = dataInput.readInt();
+ final double lat = dataInput.readDouble();
+ final double lng = dataInput.readDouble();
+ final long readingTime = dataInput.readLong();
+ return new Record(accuracy, confidence, lat, lng, readingTime);
+ }
+
+ /**
+ * Creates an "invalid" record indicating there was no location data
+ * available for the given data
+ */
+ public Record() {
+ this(-1, 0, 0, 0, System.currentTimeMillis());
+ }
+
+ /**
+ * Creates a Record
+ *
+ * @param accuracy acuracy in meters. If < 0, then this is an invalid record.
+ * @param confidence confidence (0-100)
+ * @param lat latitude
+ * @param lng longitude
+ * @param time Time of the original location reading from the server
+ */
+ public Record(int accuracy, int confidence, double lat, double lng, long time) {
+ this.accuracy = accuracy;
+ this.confidence = confidence;
+ this.originTime = time;
+ this.lat = lat;
+ this.lng = lng;
+ }
+
+ public double getLat() {
+ return lat;
+ }
+
+ public double getLng() {
+ return lng;
+ }
+
+ public int getAccuracy() {
+ return accuracy;
+ }
+
+ public int getConfidence() {
+ return confidence;
+ }
+
+ public boolean isValid() {
+ return accuracy > 0;
+ }
+
+ public long getTime() {
+ return originTime;
+ }
+
+ public void write(DataOutput dataOut) throws IOException {
+ dataOut.writeInt(accuracy);
+ dataOut.writeInt(confidence);
+ dataOut.writeDouble(lat);
+ dataOut.writeDouble(lng);
+ dataOut.writeLong(originTime);
+ }
+
+ @Override
+ public String toString() {
+ return lat + "," + lng + "," + originTime +"," + accuracy + "," + confidence;
+ }
+ }
+
+ public class Cache<T> extends LinkedHashMap {
+ private final long mMaxAge;
+ private final int mCapacity;
+ private final String mDir;
+ private final String mFile;
+ private long mLastSaveTime = 0;
+
+ public Cache(String dir, String file, int capacity, long maxAge) {
+ super(capacity + 1, 1.1f, true);
+ this.mCapacity = capacity;
+ this.mDir = dir;
+ this.mFile = file;
+ this.mMaxAge = maxAge;
+ load();
+ }
+
+ private LocationCache.Record lookup(String key) {
+ LocationCache.Record result = (LocationCache.Record) get(key);
+
+ if (result == null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "lookup: " + key + " failed");
+ }
+ return null;
+ }
+
+ // Cache entry needs refresh
+ if (result.getTime() + mMaxAge < System.currentTimeMillis()) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "lookup: " + key + " expired");
+ }
+ return null;
+ }
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "lookup: " + key + " " + result.toString());
+ }
+
+ return result;
+ }
+
+ private void insert(String key, LocationCache.Record record) {
+ remove(key);
+ put(key, record);
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "insert: " + key + " " + record.toString());
+ }
+ }
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ // Remove cache entries when it has more than capacity
+ return size() > mCapacity;
+ }
+
+ private void load() {
+ FileInputStream istream;
+ try {
+ File f = new File(mDir, mFile);
+ istream = new FileInputStream(f);
+ } catch (FileNotFoundException e) {
+ // No existing DB - return new CellCache
+ return;
+ }
+
+ DataInputStream dataInput = new DataInputStream(istream);
+
+ try {
+ int version = dataInput.readUnsignedShort();
+ if (version != CACHE_DB_VERSION) {
+ // Ignore records - invalid version ID.
+ dataInput.close();
+ return;
+ }
+ int records = dataInput.readUnsignedShort();
+
+ for (int i = 0; i < records; i++) {
+ final String key = dataInput.readUTF();
+ final LocationCache.Record record = LocationCache.Record.read(dataInput);
+ //Log.d(TAG, key + " " + record.toString());
+ put(key, record);
+ }
+
+ dataInput.close();
+ } catch (IOException e) {
+ // Something's corrupted - return a new CellCache
+ }
+ }
+
+ private void save() {
+ long now = System.currentTimeMillis();
+ if (mLastSaveTime != 0 && (now - mLastSaveTime < SAVE_FREQUENCY)) {
+ // Don't save to file more often than SAVE_FREQUENCY
+ return;
+ }
+
+ FileOutputStream ostream;
+
+ File systemDir = new File(mDir);
+ if (!systemDir.exists()) {
+ if (!systemDir.mkdirs()) {
+ Log.e(TAG, "Cache.save(): couldn't create directory");
+ return;
+ }
+ }
+
+ try {
+ File f = new File(mDir, mFile);
+ ostream = new FileOutputStream(f);
+ } catch (FileNotFoundException e) {
+ Log.d(TAG, "Cache.save(): unable to create cache file", e);
+ return;
+ }
+
+ DataOutputStream dataOut = new DataOutputStream(ostream);
+ try {
+ dataOut.writeShort(CACHE_DB_VERSION);
+
+ dataOut.writeShort(size());
+
+ for (Iterator iter = entrySet().iterator(); iter.hasNext();) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String key = (String) entry.getKey();
+ LocationCache.Record record = (LocationCache.Record) entry.getValue();
+ dataOut.writeUTF(key);
+ record.write(dataOut);
+ }
+
+ dataOut.close();
+ mLastSaveTime = now;
+
+ } catch (IOException e) {
+ Log.e(TAG, "Cache.save(): unable to write cache", e);
+ // This should never happen
+ }
+ }
+ }
+
+ public class LocationCentroid {
+
+ double mLatSum = 0;
+ double mLngSum = 0;
+ int mNumber = 0;
+ int mConfidenceSum = 0;
+
+ double mCentroidLat = 0;
+ double mCentroidLng = 0;
+
+ // Probably never have to calculate centroid for more than 10 locations
+ final static int MAX_SIZE = 10;
+ double[] mLats = new double[MAX_SIZE];
+ double[] mLngs = new double[MAX_SIZE];
+ int[] mRadii = new int[MAX_SIZE];
+
+ LocationCentroid() {
+ reset();
+ }
+
+ public void reset() {
+ mLatSum = 0;
+ mLngSum = 0;
+ mNumber = 0;
+ mConfidenceSum = 0;
+
+ mCentroidLat = 0;
+ mCentroidLng = 0;
+
+ for (int i = 0; i < MAX_SIZE; i++) {
+ mLats[i] = 0;
+ mLngs[i] = 0;
+ mRadii[i] = 0;
+ }
+ }
+
+ public void addLocation(double lat, double lng, int accuracy, int confidence) {
+ if (mNumber < MAX_SIZE && accuracy <= MAX_ACCURACY_ALLOWED) {
+ mLatSum += lat;
+ mLngSum += lng;
+ mConfidenceSum += confidence;
+
+ mLats[mNumber] = lat;
+ mLngs[mNumber] = lng;
+ mRadii[mNumber] = accuracy;
+ mNumber++;
+ }
+ }
+
+ public int getNumber() {
+ return mNumber;
+ }
+
+ public double getCentroidLat() {
+ if (mCentroidLat == 0 && mNumber != 0) {
+ mCentroidLat = mLatSum/mNumber;
+ }
+ return mCentroidLat;
+ }
+
+ public double getCentroidLng() {
+ if (mCentroidLng == 0 && mNumber != 0) {
+ mCentroidLng = mLngSum/mNumber;
+ }
+ return mCentroidLng;
+ }
+
+ public int getConfidence() {
+ if (mNumber != 0) {
+ return mConfidenceSum/mNumber;
+ } else {
+ return 0;
+ }
+ }
+
+ public int getAccuracy() {
+ if (mNumber == 0) {
+ return 0;
+ }
+
+ if (mNumber == 1) {
+ return mRadii[0];
+ }
+
+ double cLat = getCentroidLat();
+ double cLng = getCentroidLng();
+
+ int meanDistanceSum = 0;
+ int meanRadiiSum = 0;
+ int smallestCircle = MAX_ACCURACY_ALLOWED;
+ int smallestCircleDistance = MAX_ACCURACY_ALLOWED;
+ float[] distance = new float[1];
+ boolean outlierExists = false;
+
+ for (int i = 0; i < mNumber; i++) {
+ Location.distanceBetween(cLat, cLng, mLats[i], mLngs[i], distance);
+ meanDistanceSum += (int)distance[0];
+ if (distance[0] > mRadii[i]) {
+ outlierExists = true;
+ }
+ if (mRadii[i] < smallestCircle) {
+ smallestCircle = mRadii[i];
+ smallestCircleDistance = (int)distance[0];
+ }
+ meanRadiiSum += mRadii[i];
+ }
+
+ if (outlierExists) {
+ return (meanDistanceSum + meanRadiiSum)/mNumber;
+ } else {
+ return Math.max(smallestCircle, smallestCircleDistance);
+ }
+ }
+
+ }
+
+ private String getCellCacheKey(int mcc, int mnc, int lac, int cid) {
+ return mcc + ":" + mnc + ":" + lac + ":" + cid;
+ }
+
+}
diff --git a/location/java/com/android/internal/location/LocationCollector.java b/location/java/com/android/internal/location/LocationCollector.java
new file mode 100644
index 0000000..3d2f6bf
--- /dev/null
+++ b/location/java/com/android/internal/location/LocationCollector.java
@@ -0,0 +1,499 @@
+/*
+ * 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 com.android.internal.location.protocol.GDebugProfile;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import android.location.Location;
+import android.net.wifi.ScanResult;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Log;
+
+/**
+ * Listens for GPS and cell/wifi changes and anonymously uploads to server for
+ * improving quality of service of NetworkLocationProvider. This service is only enabled when
+ * the user has enabled the network location provider.
+ *
+ * {@hide}
+ */
+public class LocationCollector {
+
+ private static final String TAG = "LocationCollector";
+
+ // last location valid for 12 minutes
+ private static final long MIN_VALID_LOCATION_TIME = 12 * 60 * 1000L;
+
+ // don't send wifi more than every 10 min
+ private static final long MIN_TIME_BETWEEN_WIFI_REPORTS = 10 * 60 * 1000L;
+
+ // atleast 5 changed APs for wifi collection
+ private static final int MIN_CHANGED_WIFI_POINTS = 5;
+
+ // don't collect if distance moved less than 200 meters
+ private static final int MIN_DISTANCE_BETWEEN_REPORTS = 200;
+
+ // don't collect if battery level less than 20%
+ private static final double MIN_BATTERY_LEVEL = 0.2;
+
+ // if battery level is greater than 90% and plugged in, collect more frequently
+ private static final double CHARGED_BATTERY_LEVEL = 0.9;
+
+ // collect bursts every 15 minutes (running on battery)
+ private static final long BURST_REST_TIME_ON_BATTERY = 15 * 60 * 1000L;
+
+ // collect bursts every 8 minutes (when plugged in)
+ private static final long BURST_REST_TIME_PLUGGED = 8 * 60 * 1000L;
+
+ // collect burst samples every 12 seconds
+ private static final int BURST_MEASUREMENT_INTERVAL = 12 * 1000;
+
+ // collect 11 burst samples before resting (11 samples every 12 seconds = 2 minute bursts)
+ private static final int BURST_NUM_SAMPLES = 11;
+
+ // don't collect bursts if user in same loc for 2 bursts
+ private static final int MAX_BURSTS_FROM_SAME_LOCATION = 2;
+
+ // don't send more than 2 bursts if user hasn't moved more than 25 meters
+ private static final int MIN_DISTANCE_BETWEEN_BURSTS = 25;
+
+ // Cell State
+ private CellState mCellState = null;
+ private CellUploads mCellUploads = new CellUploads();
+
+ // GPS state
+ private Location mLastKnownLocation = null;
+ private Location mLastUploadedLocation = null;
+ private long mLastKnownLocationTime = 0;
+ private long mLastUploadedLocationTime = 0;
+
+ // Burst state
+ private Location mLastBurstLocation = null;
+ private long mLastBurstEndTime = 0;
+ private long mCurrentBurstStartTime = 0;
+ private int mCurrentBurstNumSamples = 0;
+ private int mNumBurstsFromLastLocation = 0;
+
+ // WiFi state
+ private List<ScanResult> mWifiLastScanResults = null;
+ private List<ScanResult> mWifiCurrentScanResults = null;
+ private long mLastWifiScanElapsedTime = 0;
+ private long mLastWifiScanRealTime = 0;
+ private boolean mWifiUploadedWithoutLocation = false;
+
+ // Collection state
+ private boolean mNetworkProviderIsEnabled = true;
+ private boolean mBatteryLevelIsHealthy = true;
+ private boolean mBatteryChargedAndPlugged = false;
+
+ // Location masf service
+ private LocationMasfClient mMasfClient;
+
+ public LocationCollector(LocationMasfClient masfClient) {
+ mMasfClient = masfClient;
+ }
+
+ /**
+ * Updates cell tower state. This is usually always up to date so should be uploaded
+ * each time a new location is available.
+ *
+ * @param newState cell state
+ */
+ public synchronized void updateCellState(CellState newState) {
+ if (newState == null) {
+ throw new IllegalArgumentException("cell state is null");
+ }
+
+ if (!newState.isValid()) {
+ return;
+ }
+
+ if (mCellState != null && mCellState.equals(newState)) {
+ return;
+ }
+
+ mCellState = newState;
+ log("updateCellState(): Updated to " + mCellState.getCid() + "," + mCellState.getLac());
+
+ if (isCollectionEnabled()) {
+ addToQueue(GDebugProfile.TRIGGER_CELL_CHANGE);
+ }
+ }
+
+ /**
+ * Updates GPS location if collection is enabled
+ *
+ * @param location location object
+ */
+ public synchronized void updateLocation(Location location) {
+
+ // Don't do anything if collection is disabled
+ if (!isCollectionEnabled()) {
+ return;
+ }
+
+ long now = SystemClock.elapsedRealtime();
+
+ // Update last known location
+ if (mLastKnownLocation == null) {
+ mLastKnownLocation = new Location(location);
+ } else {
+ mLastKnownLocation.set(location);
+ }
+ mLastKnownLocationTime = now;
+
+ // Burst rest time depends on battery state
+ long restTime = BURST_REST_TIME_ON_BATTERY;
+ if (mBatteryChargedAndPlugged) {
+ restTime = BURST_REST_TIME_PLUGGED;
+ }
+
+ int trigger;
+
+ // In burst mode if either first burst or enough time has passed since last burst
+ if (mLastBurstEndTime == 0 || (now - mLastBurstEndTime > restTime)) {
+
+ // If location is too recent, then don't do anything!
+ if (now - mLastUploadedLocationTime < BURST_MEASUREMENT_INTERVAL) {
+ return;
+ }
+
+ int distanceFromLastBurst = -1;
+ if (mLastBurstLocation != null) {
+ distanceFromLastBurst = (int) mLastBurstLocation.distanceTo(location);
+
+ // Too many bursts from same location, don't upload
+ if (distanceFromLastBurst < MIN_DISTANCE_BETWEEN_BURSTS &&
+ mNumBurstsFromLastLocation >= MAX_BURSTS_FROM_SAME_LOCATION) {
+ log("NO UPLOAD: Too many bursts from same location.");
+ return;
+ }
+ }
+
+ if (mCurrentBurstStartTime == 0) {
+ // Start the burst!
+ mCurrentBurstStartTime = now;
+ mCurrentBurstNumSamples = 1;
+ trigger = GDebugProfile.TRIGGER_COLLECTION_START_BURST;
+
+ } else if (now - mCurrentBurstStartTime > restTime) {
+ // Burst got old, start a new one
+ mCurrentBurstStartTime = now;
+ mCurrentBurstNumSamples = 1;
+ trigger = GDebugProfile.TRIGGER_COLLECTION_RESTART_BURST;
+
+ } else if (mCurrentBurstNumSamples == BURST_NUM_SAMPLES - 1) {
+ // Finished a burst
+ mLastBurstEndTime = now;
+ mCurrentBurstStartTime = 0;
+ mCurrentBurstNumSamples = 0;
+
+ // Make sure we don't upload too many bursts from same location
+ if (mLastBurstLocation == null) {
+ mLastBurstLocation = new Location(location);
+ mNumBurstsFromLastLocation = 1;
+ trigger = GDebugProfile.TRIGGER_COLLECTION_END_BURST;
+
+ } else {
+
+ if (distanceFromLastBurst != -1 &&
+ distanceFromLastBurst < MIN_DISTANCE_BETWEEN_BURSTS) {
+ // User hasnt moved much from last location, keep track of count,
+ // don't update last burst loc
+ mNumBurstsFromLastLocation++;
+ trigger = GDebugProfile.TRIGGER_COLLECTION_END_BURST_AT_SAME_LOCATION;
+
+ } else {
+ // User has moved enough, update last burst loc
+ mLastBurstLocation.set(location);
+ mNumBurstsFromLastLocation = 1;
+ trigger = GDebugProfile.TRIGGER_COLLECTION_END_BURST;
+ }
+ }
+
+ } else {
+ // Increment burst sample count
+ mCurrentBurstNumSamples++;
+ trigger = GDebugProfile.TRIGGER_COLLECTION_CONTINUE_BURST;
+ }
+
+ } else if (mLastUploadedLocation != null
+ && (mLastUploadedLocation.distanceTo(location) > MIN_DISTANCE_BETWEEN_REPORTS)) {
+ // If not in burst mode but has moved a reasonable distance, upload!
+ trigger = GDebugProfile.TRIGGER_COLLECTION_MOVED_DISTANCE;
+
+ } else {
+ // Not in burst mode or hasn't moved enough
+ log("NO UPLOAD: Not in burst or moving mode. Resting for " + restTime + " ms");
+ return;
+ }
+
+ log("updateLocation(): Updated location with trigger " + trigger);
+ addToQueue(trigger);
+ }
+
+ /**
+ * Updates wifi scan results if collection is enabled
+ *
+ * @param currentScanResults scan results
+ */
+ public synchronized void updateWifiScanResults(List<ScanResult> currentScanResults) {
+ if (!isCollectionEnabled()) {
+ return;
+ }
+
+ if (currentScanResults == null || currentScanResults.size() == 0) {
+ return;
+ }
+
+ long now = SystemClock.elapsedRealtime();
+
+ // If wifi scan recently received, then don't upload
+ if ((mLastWifiScanElapsedTime != 0)
+ && ((now - mLastWifiScanElapsedTime) <= MIN_TIME_BETWEEN_WIFI_REPORTS)) {
+ return;
+ }
+
+ if (mWifiCurrentScanResults == null) {
+ mWifiCurrentScanResults = new ArrayList<ScanResult>();
+ } else {
+ mWifiCurrentScanResults.clear();
+ }
+ mWifiCurrentScanResults.addAll(currentScanResults);
+
+ // If wifi has changed enough
+ boolean wifiHasChanged = false;
+
+ if (mWifiLastScanResults == null) {
+ wifiHasChanged = true;
+ } else {
+ // Calculate the number of new AP points received
+ HashSet<String> previous = new HashSet<String>();
+ HashSet<String> current = new HashSet<String>();
+ for (ScanResult s : mWifiLastScanResults) {
+ previous.add(s.BSSID);
+ }
+ for (ScanResult s : mWifiCurrentScanResults) {
+ current.add(s.BSSID);
+ }
+ current.removeAll(previous);
+
+ if (current.size() >
+ Math.min(MIN_CHANGED_WIFI_POINTS, ((mWifiCurrentScanResults.size()+1)/2))) {
+ wifiHasChanged = true;
+ }
+ }
+
+ if (!wifiHasChanged) {
+ log("updateWifiScanResults(): Wifi results haven't changed much");
+ return;
+ }
+
+ if (mWifiLastScanResults == null) {
+ mWifiLastScanResults = new ArrayList<ScanResult>();
+ } else {
+ mWifiLastScanResults.clear();
+ }
+ mWifiLastScanResults.addAll(mWifiCurrentScanResults);
+
+ mLastWifiScanElapsedTime = now;
+ mLastWifiScanRealTime = System.currentTimeMillis();
+
+ log("updateWifiScanResults(): Updated " + mWifiLastScanResults.size() + " APs");
+ addToQueue(GDebugProfile.TRIGGER_WIFI_CHANGE);
+ }
+
+ /**
+ * Updates the status of the network location provider.
+ *
+ * @param enabled true if user has enabled network location based on Google's database
+ * of wifi points and cell towers.
+ */
+ public void updateNetworkProviderStatus(boolean enabled) {
+ mNetworkProviderIsEnabled = enabled;
+ }
+
+ /**
+ * Updates the battery health. Battery level is healthy if there is greater than
+ * {@link #MIN_BATTERY_LEVEL} percentage left or if the device is plugged in
+ *
+ * @param scale maximum scale for battery
+ * @param level current level
+ * @param plugged true if device is plugged in
+ */
+ public void updateBatteryState(int scale, int level, boolean plugged) {
+ mBatteryLevelIsHealthy = (plugged || (level >= (MIN_BATTERY_LEVEL * scale)));
+ mBatteryChargedAndPlugged = (plugged && (level >= (CHARGED_BATTERY_LEVEL * scale)));
+ }
+
+ /**
+ * Anonymous data collection is only enabled when the user has enabled the network
+ * location provider, i.e. is making use of the service and if the device battery level
+ * is healthy.
+ *
+ * Additionally, data collection will *never* happen if the system
+ * property ro.com.google.enable_google_location_features is not set.
+ *
+ * @return true if anonymous location collection is enabled
+ */
+ private boolean isCollectionEnabled() {
+ // This class provides a Google-specific location feature, so it's enabled only
+ // when the system property ro.com.google.enable_google_location_features is set.
+ if (!SystemProperties.get("ro.com.google.enable_google_location_features").equals("1")) {
+ return false;
+ }
+ return mBatteryLevelIsHealthy && mNetworkProviderIsEnabled;
+ }
+
+ /**
+ * Adds to the MASF request queue
+ *
+ * @param trigger the event that triggered this collection event
+ */
+ private synchronized void addToQueue(int trigger) {
+
+ long now = SystemClock.elapsedRealtime();
+
+ // Include location if:
+ // It has been received in the last 12 minutes.
+ boolean includeLocation = false;
+ if (mLastKnownLocation != null &&
+ (now - mLastKnownLocationTime <= MIN_VALID_LOCATION_TIME)) {
+ includeLocation = true;
+ }
+
+ // Include wifi if:
+ // Wifi is new OR
+ // Wifi is old but last wifi upload was without location
+ boolean includeWifi = false;
+ if (trigger == GDebugProfile.TRIGGER_WIFI_CHANGE || (mWifiUploadedWithoutLocation &&
+ includeLocation && (now - mLastWifiScanElapsedTime < MIN_VALID_LOCATION_TIME))) {
+ includeWifi = true;
+ mWifiUploadedWithoutLocation = !includeLocation;
+ }
+
+ // Include cell if:
+ // Wifi or location information is already being included
+ // The cell hasn't been uploaded with the same location recently
+ boolean includeCell = false;
+
+ if (mCellState != null && (includeWifi || includeLocation)) {
+ includeCell = true;
+
+ if (!includeWifi && includeLocation) {
+ if (mCellUploads.contains(mCellState, mLastKnownLocation)) {
+ includeCell = false;
+ }
+ }
+ }
+
+ if (!includeLocation && !includeWifi) {
+ log("NO UPLOAD: includeLocation=false, includeWifi=false");
+ return;
+ } else if (!includeCell && trigger == GDebugProfile.TRIGGER_CELL_CHANGE) {
+ log("NO UPLOAD: includeCell=false");
+ return;
+ } else {
+ log("UPLOAD: includeLocation=" + includeLocation + ", includeWifi=" +
+ includeWifi + ", includeCell=" + includeCell);
+ }
+
+ if (includeLocation) {
+ // Update last uploaded location
+ if (mLastUploadedLocation == null) {
+ mLastUploadedLocation = new Location(mLastKnownLocation);
+ } else {
+ mLastUploadedLocation.set(mLastKnownLocation);
+ }
+ mLastUploadedLocationTime = now;
+ }
+
+ // Immediately send output if finishing a burst for live traffic requirements
+ boolean immediate = false;
+ if (trigger == GDebugProfile.TRIGGER_COLLECTION_END_BURST||
+ trigger == GDebugProfile.TRIGGER_COLLECTION_END_BURST_AT_SAME_LOCATION) {
+ immediate = true;
+ }
+
+ try {
+ CellState cell = includeCell ? mCellState : null;
+ List<ScanResult> wifi = includeWifi ? mWifiLastScanResults : null;
+ Location loc = includeLocation ? mLastUploadedLocation : null;
+
+ mMasfClient.queueCollectionReport(
+ trigger, loc, cell, wifi, mLastWifiScanRealTime, immediate);
+
+ } catch(Exception e) {
+ Log.e(TAG, "addToQueue got exception:", e);
+ }
+ }
+
+ private class CellUploads {
+
+ private final int MIN_DISTANCE = MIN_DISTANCE_BETWEEN_REPORTS / 4; // 50 meters
+ private final int SIZE = 5;
+ private final String[] cells = new String[SIZE];
+ private final boolean[] valid = new boolean[SIZE];
+ private final double[] latitudes = new double[SIZE];
+ private final double[] longitudes = new double[SIZE];
+ private final float[] distance = new float[1];
+ private int index = 0;
+
+ private CellUploads() {
+ for (int i = 0; i < SIZE; i++) {
+ valid[i] = false;
+ }
+ }
+
+ private boolean contains(CellState cellState, Location loc) {
+ String cell =
+ cellState.getCid() + ":" + cellState.getLac() + ":" +
+ cellState.getMnc() + ":" + cellState.getMcc();
+ double lat = loc.getLatitude();
+ double lng = loc.getLongitude();
+
+ for (int i = 0; i < SIZE; i++) {
+ if (valid[i] && cells[i].equals(cell)) {
+ Location.distanceBetween(latitudes[i], longitudes[i], lat, lng, distance);
+ if (distance[0] < MIN_DISTANCE) {
+ return true;
+ }
+ }
+ }
+ cells[index] = cell;
+ latitudes[index] = lat;
+ longitudes[index] = lng;
+ valid[index] = true;
+
+ index++;
+ if (index == SIZE) {
+ index = 0;
+ }
+ return false;
+ }
+ }
+
+ private void log(String string) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, string);
+ }
+ }
+}
diff --git a/location/java/com/android/internal/location/LocationMasfClient.java b/location/java/com/android/internal/location/LocationMasfClient.java
new file mode 100644
index 0000000..ed9f3d6
--- /dev/null
+++ b/location/java/com/android/internal/location/LocationMasfClient.java
@@ -0,0 +1,1103 @@
+// Copyright 2008 The Android Open Source Project
+
+package com.android.internal.location;
+
+import com.google.common.Config;
+import com.google.common.android.AndroidConfig;
+import com.google.common.io.protocol.ProtoBuf;
+import com.google.masf.MobileServiceMux;
+import com.google.masf.ServiceCallback;
+import com.google.masf.protocol.PlainRequest;
+import com.google.masf.protocol.Request;
+
+import com.android.internal.location.protocol.GAddress;
+import com.android.internal.location.protocol.GAddressComponent;
+import com.android.internal.location.protocol.GAppProfile;
+import com.android.internal.location.protocol.GCell;
+import com.android.internal.location.protocol.GCellularPlatformProfile;
+import com.android.internal.location.protocol.GCellularProfile;
+import com.android.internal.location.protocol.GDebugProfile;
+import com.android.internal.location.protocol.GDeviceLocation;
+import com.android.internal.location.protocol.GFeature;
+import com.android.internal.location.protocol.GGeocodeRequest;
+import com.android.internal.location.protocol.GLatLng;
+import com.android.internal.location.protocol.GLocReply;
+import com.android.internal.location.protocol.GLocReplyElement;
+import com.android.internal.location.protocol.GLocRequest;
+import com.android.internal.location.protocol.GLocRequestElement;
+import com.android.internal.location.protocol.GLocation;
+import com.android.internal.location.protocol.GPlatformProfile;
+import com.android.internal.location.protocol.GPrefetchMode;
+import com.android.internal.location.protocol.GRectangle;
+import com.android.internal.location.protocol.GWifiDevice;
+import com.android.internal.location.protocol.GWifiProfile;
+import com.android.internal.location.protocol.GcellularMessageTypes;
+import com.android.internal.location.protocol.GdebugprofileMessageTypes;
+import com.android.internal.location.protocol.GlatlngMessageTypes;
+import com.android.internal.location.protocol.GlocationMessageTypes;
+import com.android.internal.location.protocol.GrectangleMessageTypes;
+import com.android.internal.location.protocol.GwifiMessageTypes;
+import com.android.internal.location.protocol.LocserverMessageTypes;
+import com.android.internal.location.protocol.ResponseCodes;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+
+import android.content.Context;
+import android.location.Address;
+import android.location.Location;
+import android.location.LocationManager;
+import android.net.wifi.ScanResult;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.EventLog;
+import android.util.Log;
+
+/**
+ * Service to communicate to the Google Location Server (GLS) via MASF server
+ *
+ * {@hide}
+ */
+public class LocationMasfClient {
+
+ static final String TAG = "LocationMasfClient";
+
+ // Address of the MASF server to connect to.
+ private static final String MASF_SERVER_ADDRESS = "http://www.google.com/loc/m/api";
+
+ // MobileServiceMux app/platform-specific values (application name matters!)
+ private static final String APPLICATION_NAME = "location";
+ private static final String APPLICATION_VERSION = "1.0";
+ private static final String PLATFORM_ID = "android";
+ private static final String DISTRIBUTION_CHANNEL = "android";
+ private static String PLATFORM_BUILD = null;
+
+ // Methods exposed by the MASF server
+ private static final String REQUEST_QUERY_LOC = "g:loc/ql";
+ private static final String REQUEST_UPLOAD_LOC = "g:loc/ul";
+
+ // Max time to wait for request to end
+ private static final long REQUEST_TIMEOUT = 5000;
+
+ // Constant to divide Lat, Lng returned by server
+ private static final double E7 = 10000000.0;
+
+ // Max wifi points to include
+ private static final int MAX_WIFI_TO_INCLUDE = 25;
+
+ // Location of GLS cookie
+ private static final String PLATFORM_KEY_FILE = "gls.platform.key";
+ private String mPlatformKey;
+
+ // Cell cache
+ private LocationCache mLocationCache;
+
+ // Location object that the cache manages
+ private Location mLocation = new Location(LocationManager.NETWORK_PROVIDER);
+
+ // ProtoBuf objects we can reuse for subsequent requests
+ private final int MAX_COLLECTION_BUFFER_SIZE = 30;
+ private final long MIN_COLLECTION_INTERVAL = 15 * 60 * 1000; // 15 minutes
+ private ProtoBuf mPlatformProfile = null;
+ private ProtoBuf mCellularPlatformProfile = null;
+ private ProtoBuf mCurrentCollectionRequest = null;
+ private long mLastCollectionUploadTime = 0;
+
+ // Objects for current request
+ private List<ScanResult> mWifiScanResults = new ArrayList<ScanResult>();
+ private CellState mCellState = null;
+ private List<CellState> mCellHistory;
+
+ // This tag is used for the event log.
+ private static final int COLLECTION_EVENT_LOG_TAG = 2740;
+
+ // Extra values to designate whether location is from cache or network request
+ private static final String EXTRA_KEY_LOCATION_SOURCE = "networkLocationSource";
+ private static final String EXTRA_VALUE_LOCATION_SOURCE_CACHED = "cached";
+ private static final String EXTRA_VALUE_LOCATION_SOURCE_SERVER = "server";
+
+ /**
+ * Initializes the MobileServiceMux. Must be called before using any other function in the
+ * class.
+ */
+ public LocationMasfClient(Context context) {
+ MobileServiceMux mux = MobileServiceMux.getSingleton();
+ if (mux == null) {
+ AndroidConfig config = new AndroidConfig(context);
+ Config.setConfig(config);
+
+ MobileServiceMux.initialize
+ (MASF_SERVER_ADDRESS,
+ APPLICATION_NAME,
+ APPLICATION_VERSION,
+ PLATFORM_ID,
+ DISTRIBUTION_CHANNEL);
+ }
+ mLocationCache = new LocationCache();
+
+ if (Build.FINGERPRINT != null) {
+ PLATFORM_BUILD = PLATFORM_ID + "/" + Build.FINGERPRINT;
+ } else {
+ PLATFORM_BUILD = PLATFORM_ID;
+ }
+ }
+
+ /**
+ * Returns the location for the given cell or wifi information.
+ *
+ * @param apps list of apps requesting location
+ * @param trigger event that triggered this network request
+ * @param cellState cell tower state
+ * @param cellHistory history of acquired cell states
+ * @param scanResults list of wifi scan results
+ * @param scanTime time at which wireless scan was triggered
+ * @param callback function to call with received location
+ */
+ public synchronized void getNetworkLocation(Collection<String> apps, int trigger,
+ CellState cellState, List<CellState> cellHistory, List<ScanResult> scanResults,
+ long scanTime, NetworkLocationProvider.Callback callback) {
+
+ final NetworkLocationProvider.Callback finalCallback = callback;
+
+ boolean foundInCache =
+ mLocationCache.lookup(cellState, cellHistory, scanResults, mLocation);
+
+ if (foundInCache) {
+
+ if (SystemClock.elapsedRealtime() - mLastCollectionUploadTime > MIN_COLLECTION_INTERVAL) {
+ uploadCollectionReport(true);
+ }
+
+ Bundle extras = mLocation.getExtras() == null ? new Bundle() : mLocation.getExtras();
+ extras.putString(EXTRA_KEY_LOCATION_SOURCE, EXTRA_VALUE_LOCATION_SOURCE_CACHED);
+ mLocation.setExtras(extras);
+
+ Log.d(TAG, "getNetworkLocation(): Returning cache location with accuracy " +
+ mLocation.getAccuracy());
+ finalCallback.locationReceived(mLocation, true);
+ return;
+ }
+
+ Log.d(TAG, "getNetworkLocation(): Location not found in cache, making network request");
+
+ // Copy over to objects for this request
+ mWifiScanResults.clear();
+ if (scanResults != null) {
+ mWifiScanResults.addAll(scanResults);
+ }
+ mCellState = cellState;
+ mCellHistory = cellHistory;
+
+ // Create a RequestElement
+ ProtoBuf requestElement = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST_ELEMENT);
+
+ // Debug profile
+ if (trigger != -1) {
+ ProtoBuf debugProfile = new ProtoBuf(GdebugprofileMessageTypes.GDEBUG_PROFILE);
+ debugProfile.setInt(GDebugProfile.TRIGGER, trigger);
+ requestElement.setProtoBuf(GLocRequestElement.DEBUG_PROFILE, debugProfile);
+ }
+
+ // Cellular profile
+ if (mCellState != null && mCellState.isValid()) {
+ ProtoBuf cellularProfile = new ProtoBuf(GcellularMessageTypes.GCELLULAR_PROFILE);
+ cellularProfile.setLong(GCellularProfile.TIMESTAMP, mCellState.getTime());
+ cellularProfile.setInt(GCellularProfile.PREFETCH_MODE,
+ GPrefetchMode.PREFETCH_MODE_MORE_NEIGHBORS);
+
+ // Primary cell
+ ProtoBuf primaryCell = new ProtoBuf(GcellularMessageTypes.GCELL);
+ primaryCell.setInt(GCell.LAC, mCellState.getLac());
+ primaryCell.setInt(GCell.CELLID, mCellState.getCid());
+
+ if ((mCellState.getMcc() != -1) && (mCellState.getMnc() != -1)) {
+ primaryCell.setInt(GCell.MCC, mCellState.getMcc());
+ primaryCell.setInt(GCell.MNC, mCellState.getMnc());
+ }
+ cellularProfile.setProtoBuf(GCellularProfile.PRIMARY_CELL, primaryCell);
+
+ // History of cells
+ for (CellState c : cellHistory) {
+ ProtoBuf pastCell = new ProtoBuf(GcellularMessageTypes.GCELL);
+ pastCell.setInt(GCell.LAC, c.getLac());
+ pastCell.setInt(GCell.CELLID, c.getCid());
+ if ((c.getMcc() != -1) && (c.getMnc() != -1)) {
+ pastCell.setInt(GCell.MCC, c.getMcc());
+ pastCell.setInt(GCell.MNC, c.getMnc());
+ }
+ pastCell.setInt(GCell.AGE, (int)(mCellState.getTime() - c.getTime()));
+ cellularProfile.addProtoBuf(GCellularProfile.HISTORICAL_CELLS, pastCell);
+ }
+
+ requestElement.setProtoBuf(GLocRequestElement.CELLULAR_PROFILE, cellularProfile);
+ }
+
+ // Wifi profile
+ if (mWifiScanResults != null && mWifiScanResults.size() > 0) {
+ ProtoBuf wifiProfile = new ProtoBuf(GwifiMessageTypes.GWIFI_PROFILE);
+ wifiProfile.setLong(GWifiProfile.TIMESTAMP, scanTime);
+ wifiProfile.setInt(GWifiProfile.PREFETCH_MODE,
+ GPrefetchMode.PREFETCH_MODE_MORE_NEIGHBORS);
+
+ int count = 0;
+ for (ScanResult s : mWifiScanResults) {
+ ProtoBuf wifiDevice = new ProtoBuf(GwifiMessageTypes.GWIFI_DEVICE);
+ wifiDevice.setString(GWifiDevice.MAC, s.BSSID);
+ wifiProfile.addProtoBuf(GWifiProfile.WIFI_DEVICES, wifiDevice);
+ count++;
+ if (count >= MAX_WIFI_TO_INCLUDE) {
+ break;
+ }
+ }
+
+ requestElement.setProtoBuf(GLocRequestElement.WIFI_PROFILE, wifiProfile);
+ }
+
+ // Request to send over wire
+ ProtoBuf request = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST);
+ request.addProtoBuf(GLocRequest.REQUEST_ELEMENTS, requestElement);
+
+ // Create a Platform Profile
+ ProtoBuf platformProfile = createPlatformProfile();
+ if (mCellState != null && mCellState.isValid()) {
+ // Include cellular platform Profile
+ ProtoBuf cellularPlatform = createCellularPlatformProfile(mCellState);
+ platformProfile.setProtoBuf(GPlatformProfile.CELLULAR_PLATFORM_PROFILE,
+ cellularPlatform);
+ }
+ request.setProtoBuf(GLocRequest.PLATFORM_PROFILE, platformProfile);
+
+ // Include App Profiles
+ if (apps != null) {
+ for (String app : apps) {
+ ProtoBuf appProfile = new ProtoBuf(GlocationMessageTypes.GAPP_PROFILE);
+ appProfile.setString(GAppProfile.APP_NAME, app);
+ request.addProtoBuf(GLocRequest.APP_PROFILES, appProfile);
+ }
+ }
+
+ // Queue any waiting collection events as well
+ uploadCollectionReport(false);
+
+ ByteArrayOutputStream payload = new ByteArrayOutputStream();
+ try {
+ request.outputTo(payload);
+ } catch (IOException e) {
+ Log.e(TAG, "getNetworkLocation(): unable to write request to payload", e);
+ return;
+ }
+
+ // Creates request and a listener with a call back function
+ ProtoBuf reply = new ProtoBuf(LocserverMessageTypes.GLOC_REPLY);
+ Request plainRequest =
+ new PlainRequest(REQUEST_QUERY_LOC, (short)0, payload.toByteArray());
+
+ ProtoRequestListener listener = new ProtoRequestListener(reply, new ServiceCallback() {
+ public void onRequestComplete(Object result) {
+ ProtoBuf response = (ProtoBuf) result;
+ boolean successful = parseNetworkLocationReply(response);
+ finalCallback.locationReceived(mLocation, successful);
+
+ }
+ });
+ plainRequest.setListener(listener);
+
+ // Send request
+ MobileServiceMux serviceMux = MobileServiceMux.getSingleton();
+ serviceMux.submitRequest(plainRequest, true);
+ }
+
+ private synchronized boolean parseNetworkLocationReply(ProtoBuf response) {
+ if (response == null) {
+ Log.e(TAG, "getNetworkLocation(): response is null");
+ return false;
+ }
+
+ int status1 = response.getInt(GLocReply.STATUS);
+ if (status1 != ResponseCodes.STATUS_STATUS_SUCCESS) {
+ Log.e(TAG, "getNetworkLocation(): RPC failed with status " + status1);
+ return false;
+ }
+
+ if (response.has(GLocReply.PLATFORM_KEY)) {
+ String platformKey = response.getString(GLocReply.PLATFORM_KEY);
+ if (!TextUtils.isEmpty(platformKey)) {
+ setPlatformKey(platformKey);
+ }
+ }
+
+ if (!response.has(GLocReply.REPLY_ELEMENTS)) {
+ Log.e(TAG, "getNetworkLocation(): no ReplyElement");
+ return false;
+ }
+ ProtoBuf replyElement = response.getProtoBuf(GLocReply.REPLY_ELEMENTS);
+
+ int status2 = replyElement.getInt(GLocReplyElement.STATUS);
+ if (status2 != ResponseCodes.STATUS_STATUS_SUCCESS &&
+ status2 != ResponseCodes.STATUS_STATUS_FAILED) {
+ Log.e(TAG, "getNetworkLocation(): GLS failed with status " + status2);
+ return false;
+ }
+
+ // Get prefetched data to add to the device cache
+ Log.d(TAG, "getNetworkLocation(): Number of prefetched entries " +
+ replyElement.getCount(GLocReplyElement.DEVICE_LOCATION));
+ long now = System.currentTimeMillis();
+ for (int i = 0; i < replyElement.getCount(GLocReplyElement.DEVICE_LOCATION); i++ ) {
+ ProtoBuf device = replyElement.getProtoBuf(GLocReplyElement.DEVICE_LOCATION, i);
+ double lat = 0;
+ double lng = 0;
+ int accuracy = -1;
+ int confidence = -1;
+ int locType = -1;
+ if (device.has(GDeviceLocation.LOCATION)) {
+ ProtoBuf deviceLocation = device.getProtoBuf(GDeviceLocation.LOCATION);
+ if (deviceLocation.has(GLocation.ACCURACY) &&
+ deviceLocation.has(GLocation.LAT_LNG)
+ && deviceLocation.has(GLocation.CONFIDENCE)) {
+ lat = deviceLocation.getProtoBuf(GLocation.LAT_LNG).
+ getInt(GLatLng.LAT_E7) / E7;
+ lng = deviceLocation.getProtoBuf(GLocation.LAT_LNG).
+ getInt(GLatLng.LNG_E7) / E7;
+ accuracy = deviceLocation.getInt(GLocation.ACCURACY);
+ confidence = deviceLocation.getInt(GLocation.CONFIDENCE);
+ }
+ if (deviceLocation.has(GLocation.LOC_TYPE)) {
+ locType = deviceLocation.getInt(GLocation.LOC_TYPE);
+ }
+ }
+
+ // Get cell key
+ if (device.has(GDeviceLocation.CELL) && locType != GLocation.LOCTYPE_TOWER_LOCATION) {
+ ProtoBuf deviceCell = device.getProtoBuf(GDeviceLocation.CELL);
+ int cid = deviceCell.getInt(GCell.CELLID);
+ int lac = deviceCell.getInt(GCell.LAC);
+ int mcc = -1;
+ int mnc = -1;
+ if (deviceCell.has(GCell.MNC) && deviceCell.has(GCell.MCC)) {
+ mcc = deviceCell.getInt(GCell.MCC);
+ mnc = deviceCell.getInt(GCell.MNC);
+ }
+ mLocationCache.
+ insert(mcc, mnc, lac, cid, lat, lng, accuracy, confidence, now);
+ }
+
+ // Get wifi key
+ if (device.has(GDeviceLocation.WIFI_DEVICE)) {
+ ProtoBuf deviceWifi = device.getProtoBuf(GDeviceLocation.WIFI_DEVICE);
+ String bssid = deviceWifi.getString(GWifiDevice.MAC);
+ mLocationCache.insert(bssid, lat, lng, accuracy, confidence, now);
+ }
+ }
+
+ mLocationCache.save();
+
+ // For consistent results for user, always return cache computed location
+ boolean foundInCache =
+ mLocationCache.lookup(mCellState, mCellHistory, mWifiScanResults, mLocation);
+
+ if (foundInCache) {
+
+ Bundle extras = mLocation.getExtras() == null ? new Bundle() : mLocation.getExtras();
+ extras.putString(EXTRA_KEY_LOCATION_SOURCE, EXTRA_VALUE_LOCATION_SOURCE_SERVER);
+ mLocation.setExtras(extras);
+
+ Log.d(TAG, "getNetworkLocation(): Returning network location with accuracy " +
+ mLocation.getAccuracy());
+ return true;
+ }
+
+ if (status2 == ResponseCodes.STATUS_STATUS_FAILED) {
+ Log.e(TAG, "getNetworkLocation(): GLS does not have location");
+ // We return true here since even though there is no location, there is no need to retry
+ // since server doesn't have location
+ return true;
+ }
+
+ // Get server computed location to return for now
+ if (!replyElement.has(GLocReplyElement.LOCATION)) {
+ Log.e(TAG, "getNetworkLocation(): no location in ReplyElement");
+ return false;
+ }
+ ProtoBuf location = replyElement.getProtoBuf(GLocReplyElement.LOCATION);
+
+ if (!location.has(GLocation.LAT_LNG)) {
+ Log.e(TAG, "getNetworkLocation(): no Lat,Lng in location");
+ return false;
+ }
+
+ ProtoBuf point = location.getProtoBuf(GLocation.LAT_LNG);
+ double lat = point.getInt(GLatLng.LAT_E7) / E7;
+ double lng = point.getInt(GLatLng.LNG_E7) / E7;
+
+ int accuracy = 0;
+ if (location.has(GLocation.ACCURACY)) {
+ accuracy = location.getInt(GLocation.ACCURACY);
+ }
+
+ mLocation.setLatitude(lat);
+ mLocation.setLongitude(lng);
+ mLocation.setTime(System.currentTimeMillis());
+ mLocation.setAccuracy(accuracy);
+
+ Bundle extras = mLocation.getExtras() == null ? new Bundle() : mLocation.getExtras();
+ extras.putString(EXTRA_KEY_LOCATION_SOURCE, EXTRA_VALUE_LOCATION_SOURCE_SERVER);
+ mLocation.setExtras(extras);
+
+ Log.e(TAG, "getNetworkLocation(): Returning *server* computed location with accuracy " +
+ accuracy);
+
+ return true;
+ }
+
+ /**
+ * Gets a reverse geocoded location from the given lat,lng point. Also attaches the name
+ * of the requesting application with the request
+ *
+ * @param locale locale for geocoded location
+ * @param appPackageName name of the package, may be null
+ * @param lat latitude
+ * @param lng longitude
+ * @param maxResults maximum number of addresses to return
+ * @param addrs the list of addresses to fill up
+ * @throws IOException if network is unavailable or some other issue
+ */
+ public void reverseGeocode(Locale locale, String appPackageName,
+ double lat, double lng, int maxResults, List<Address> addrs) throws IOException {
+
+ // Reverse geocoding request element
+ ProtoBuf requestElement = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST_ELEMENT);
+
+ ProtoBuf latlngElement = new ProtoBuf(GlatlngMessageTypes.GLAT_LNG);
+ latlngElement.setInt(GLatLng.LAT_E7, (int)(lat * E7));
+ latlngElement.setInt(GLatLng.LNG_E7, (int)(lng * E7));
+
+ ProtoBuf locationElement = new ProtoBuf(GlocationMessageTypes.GLOCATION);
+ locationElement.setProtoBuf(GLocation.LAT_LNG, latlngElement);
+ locationElement.setLong(GLocation.TIMESTAMP, System.currentTimeMillis());
+ requestElement.setProtoBuf(GLocRequestElement.LOCATION, locationElement);
+
+ ProtoBuf geocodeElement =
+ new ProtoBuf(LocserverMessageTypes.GGEOCODE_REQUEST);
+ geocodeElement.setInt(GGeocodeRequest.NUM_FEATURE_LIMIT, maxResults);
+ requestElement.setProtoBuf(GLocRequestElement.GEOCODE, geocodeElement);
+
+ // Request to send over wire
+ ProtoBuf request = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST);
+ request.addProtoBuf(GLocRequest.REQUEST_ELEMENTS, requestElement);
+
+ // Create platform profile
+ ProtoBuf platformProfile = createPlatformProfile(locale);
+ request.setProtoBuf(GLocRequest.PLATFORM_PROFILE, platformProfile);
+
+ // Include app name
+ if (appPackageName != null) {
+ ProtoBuf appProfile = new ProtoBuf(GlocationMessageTypes.GAPP_PROFILE);
+ appProfile.setString(GAppProfile.APP_NAME, appPackageName);
+ request.setProtoBuf(GLocRequest.APP_PROFILES, appProfile);
+ }
+
+ // Queue any waiting collection events as well
+ uploadCollectionReport(false);
+
+ ByteArrayOutputStream payload = new ByteArrayOutputStream();
+ try {
+ request.outputTo(payload);
+ } catch (IOException e) {
+ Log.e(TAG, "reverseGeocode(): unable to write request to payload");
+ throw e;
+ }
+
+ // Creates request and a listener with no callback function
+ ProtoBuf reply = new ProtoBuf(LocserverMessageTypes.GLOC_REPLY);
+ Request plainRequest =
+ new PlainRequest(REQUEST_QUERY_LOC, (short)0, payload.toByteArray());
+ ProtoRequestListener listener = new ProtoRequestListener(reply, null);
+ plainRequest.setListener(listener);
+
+ // Immediately send request and block for response until REQUEST_TIMEOUT
+ MobileServiceMux serviceMux = MobileServiceMux.getSingleton();
+ serviceMux.submitRequest(plainRequest, true);
+ ProtoBuf response;
+ try {
+ response = (ProtoBuf)listener.getAsyncResult().get(REQUEST_TIMEOUT);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "reverseGeocode(): response timeout");
+ throw new IOException("response time-out");
+ }
+
+ if (response == null) {
+ throw new IOException("Unable to parse response from server");
+ }
+
+ // Parse the response
+ int status1 = response.getInt(GLocReply.STATUS);
+ if (status1 != ResponseCodes.STATUS_STATUS_SUCCESS) {
+ Log.e(TAG, "reverseGeocode(): RPC failed with status " + status1);
+ throw new IOException("RPC failed with status " + status1);
+ }
+
+ if (response.has(GLocReply.PLATFORM_KEY)) {
+ String platformKey = response.getString(GLocReply.PLATFORM_KEY);
+ if (!TextUtils.isEmpty(platformKey)) {
+ setPlatformKey(platformKey);
+ }
+ }
+
+ if (!response.has(GLocReply.REPLY_ELEMENTS)) {
+ Log.e(TAG, "reverseGeocode(): no ReplyElement");
+ return;
+ }
+ ProtoBuf replyElement = response.getProtoBuf(GLocReply.REPLY_ELEMENTS);
+
+ int status2 = replyElement.getInt(GLocReplyElement.STATUS);
+ if (status2 != ResponseCodes.STATUS_STATUS_SUCCESS) {
+ Log.e(TAG, "reverseGeocode(): GLS failed with status " + status2);
+ return;
+ }
+
+ if (!replyElement.has(GLocReplyElement.LOCATION)) {
+ Log.e(TAG, "reverseGeocode(): no location in ReplyElement");
+ return;
+ }
+
+ ProtoBuf location = replyElement.getProtoBuf(GLocReplyElement.LOCATION);
+ if (!location.has(GLocation.FEATURE)) {
+ Log.e(TAG, "reverseGeocode(): no feature in GLocation");
+ return;
+ }
+
+ getAddressFromProtoBuf(location, locale, addrs);
+ }
+
+ /**
+ * Gets a forward geocoded location from the given location string. Also attaches the name
+ * of the requesting application with the request
+ *
+ * Optionally, can specify the bounding box that the search results should be restricted to
+ *
+ * @param locale locale for geocoded location
+ * @param appPackageName name of the package, may be null
+ * @param locationString string to forward geocode
+ * @param lowerLeftLatitude latitude of lower left point of bounding box
+ * @param lowerLeftLongitude longitude of lower left point of bounding box
+ * @param upperRightLatitude latitude of upper right point of bounding box
+ * @param upperRightLongitude longitude of upper right point of bounding box
+ * @param maxResults maximum number of results to return
+ * @param addrs the list of addresses to fill up
+ * @throws IOException if network is unavailable or some other issue
+ */
+ public void forwardGeocode(Locale locale, String appPackageName, String locationString,
+ double lowerLeftLatitude, double lowerLeftLongitude,
+ double upperRightLatitude, double upperRightLongitude, int maxResults, List<Address> addrs)
+ throws IOException {
+
+ // Forward geocoding request element
+ ProtoBuf requestElement = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST_ELEMENT);
+
+ ProtoBuf locationElement = new ProtoBuf(GlocationMessageTypes.GLOCATION);
+ locationElement.setLong(GLocation.TIMESTAMP, System.currentTimeMillis());
+ locationElement.setString(GLocation.LOCATION_STRING, locationString);
+ requestElement.setProtoBuf(GLocRequestElement.LOCATION, locationElement);
+
+ ProtoBuf geocodeElement =
+ new ProtoBuf(LocserverMessageTypes.GGEOCODE_REQUEST);
+ geocodeElement.setInt(GGeocodeRequest.NUM_FEATURE_LIMIT, maxResults);
+
+ if (lowerLeftLatitude != 0 && lowerLeftLongitude !=0 &&
+ upperRightLatitude !=0 && upperRightLongitude !=0) {
+ ProtoBuf lowerLeft = new ProtoBuf(GlatlngMessageTypes.GLAT_LNG);
+ lowerLeft.setInt(GLatLng.LAT_E7, (int)(lowerLeftLatitude * E7));
+ lowerLeft.setInt(GLatLng.LNG_E7, (int)(lowerLeftLongitude * E7));
+
+ ProtoBuf upperRight = new ProtoBuf(GlatlngMessageTypes.GLAT_LNG);
+ upperRight.setInt(GLatLng.LAT_E7, (int)(upperRightLatitude * E7));
+ upperRight.setInt(GLatLng.LNG_E7, (int)(upperRightLongitude * E7));
+
+ ProtoBuf boundingBox = new ProtoBuf(GrectangleMessageTypes.GRECTANGLE);
+ boundingBox.setProtoBuf(GRectangle.LOWER_LEFT, lowerLeft);
+ boundingBox.setProtoBuf(GRectangle.UPPER_RIGHT, upperRight);
+ geocodeElement.setProtoBuf(GGeocodeRequest.BOUNDING_BOX, boundingBox);
+ }
+ requestElement.setProtoBuf(GLocRequestElement.GEOCODE, geocodeElement);
+
+ // Request to send over wire
+ ProtoBuf request = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST);
+ request.addProtoBuf(GLocRequest.REQUEST_ELEMENTS, requestElement);
+
+ // Create platform profile
+ ProtoBuf platformProfile = createPlatformProfile(locale);
+ request.setProtoBuf(GLocRequest.PLATFORM_PROFILE, platformProfile);
+
+ // Include app name
+ if (appPackageName != null) {
+ ProtoBuf appProfile = new ProtoBuf(GlocationMessageTypes.GAPP_PROFILE);
+ appProfile.setString(GAppProfile.APP_NAME, appPackageName);
+ request.setProtoBuf(GLocRequest.APP_PROFILES, appProfile);
+ }
+
+ // Queue any waiting collection events as well
+ uploadCollectionReport(false);
+
+ ByteArrayOutputStream payload = new ByteArrayOutputStream();
+ try {
+ request.outputTo(payload);
+ } catch (IOException e) {
+ Log.e(TAG, "forwardGeocode(): unable to write request to payload");
+ throw e;
+ }
+
+ // Creates request and a listener with no callback function
+ ProtoBuf reply = new ProtoBuf(LocserverMessageTypes.GLOC_REPLY);
+ Request plainRequest =
+ new PlainRequest(REQUEST_QUERY_LOC, (short)0, payload.toByteArray());
+ ProtoRequestListener listener = new ProtoRequestListener(reply, null);
+ plainRequest.setListener(listener);
+
+ // Immediately send request and block for response until REQUEST_TIMEOUT
+ MobileServiceMux serviceMux = MobileServiceMux.getSingleton();
+ serviceMux.submitRequest(plainRequest, true);
+ ProtoBuf response;
+ try {
+ response = (ProtoBuf)listener.getAsyncResult().get(REQUEST_TIMEOUT);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "forwardGeocode(): response timeout");
+ throw new IOException("response time-out");
+ }
+
+ if (response == null) {
+ throw new IOException("Unable to parse response from server");
+ }
+
+ // Parse the response
+ int status1 = response.getInt(GLocReply.STATUS);
+ if (status1 != ResponseCodes.STATUS_STATUS_SUCCESS) {
+ Log.e(TAG, "forwardGeocode(): RPC failed with status " + status1);
+ throw new IOException("RPC failed with status " + status1);
+ }
+
+ if (response.has(GLocReply.PLATFORM_KEY)) {
+ String platformKey = response.getString(GLocReply.PLATFORM_KEY);
+ if (!TextUtils.isEmpty(platformKey)) {
+ setPlatformKey(platformKey);
+ }
+ }
+
+ if (!response.has(GLocReply.REPLY_ELEMENTS)) {
+ Log.e(TAG, "forwardGeocode(): no ReplyElement");
+ return;
+ }
+ ProtoBuf replyElement = response.getProtoBuf(GLocReply.REPLY_ELEMENTS);
+
+ int status2 = replyElement.getInt(GLocReplyElement.STATUS);
+ if (status2 != ResponseCodes.STATUS_STATUS_SUCCESS) {
+ Log.e(TAG, "forwardGeocode(): GLS failed with status " + status2);
+ return;
+ }
+
+ if (!replyElement.has(GLocReplyElement.LOCATION)) {
+ Log.e(TAG, "forwardGeocode(): no location in ReplyElement");
+ return;
+ }
+
+ ProtoBuf location = replyElement.getProtoBuf(GLocReplyElement.LOCATION);
+ if (!location.has(GLocation.FEATURE)) {
+ Log.e(TAG, "forwardGeocode(): no feature in GLocation");
+ return;
+ }
+
+ getAddressFromProtoBuf(location, locale, addrs);
+ }
+
+ /**
+ * Queues a location collection request to be sent to the server
+ *
+ * @param trigger what triggered this collection event
+ * @param location last known location
+ * @param cellState cell tower state
+ * @param scanResults list of wifi points
+ * @param scanTime real time at which wifi scan happened
+ * @param immediate true if request should be sent immediately instead of being queued
+ */
+ public synchronized void queueCollectionReport(int trigger, Location location,
+ CellState cellState, List<ScanResult> scanResults, long scanTime, boolean immediate) {
+
+ // Create a RequestElement
+ ProtoBuf requestElement = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST_ELEMENT);
+
+ // Include debug profile
+ if (trigger != -1) {
+ ProtoBuf debugProfile = new ProtoBuf(GdebugprofileMessageTypes.GDEBUG_PROFILE);
+ debugProfile.setInt(GDebugProfile.TRIGGER, trigger);
+ requestElement.setProtoBuf(GLocRequestElement.DEBUG_PROFILE, debugProfile);
+
+ EventLog.writeEvent(COLLECTION_EVENT_LOG_TAG, trigger);
+ }
+
+ // Include cell profile
+ if (cellState != null && cellState.isValid()) {
+ ProtoBuf cellularProfile = new ProtoBuf(GcellularMessageTypes.GCELLULAR_PROFILE);
+ cellularProfile.setLong(GCellularProfile.TIMESTAMP, cellState.getTime());
+
+ // Primary cell
+ ProtoBuf primaryCell = new ProtoBuf(GcellularMessageTypes.GCELL);
+ primaryCell.setInt(GCell.LAC, cellState.getLac());
+ primaryCell.setInt(GCell.CELLID, cellState.getCid());
+ if ((cellState.getMcc() != -1) && (cellState.getMnc() != -1)) {
+ primaryCell.setInt(GCell.MCC, cellState.getMcc());
+ primaryCell.setInt(GCell.MNC, cellState.getMnc());
+ }
+
+ cellularProfile.setProtoBuf(GCellularProfile.PRIMARY_CELL, primaryCell);
+ requestElement.setProtoBuf(GLocRequestElement.CELLULAR_PROFILE, cellularProfile);
+ }
+
+ // Include Wifi profile
+ if (scanResults != null && scanResults.size() > 0) {
+ ProtoBuf wifiProfile = new ProtoBuf(GwifiMessageTypes.GWIFI_PROFILE);
+ wifiProfile.setLong(GWifiProfile.TIMESTAMP, scanTime);
+
+ int count = 0;
+ for (ScanResult s : scanResults) {
+ ProtoBuf wifiDevice = new ProtoBuf(GwifiMessageTypes.GWIFI_DEVICE);
+ wifiDevice.setString(GWifiDevice.MAC, s.BSSID);
+ wifiDevice.setString(GWifiDevice.SSID, s.SSID);
+ wifiDevice.setInt(GWifiDevice.RSSI, s.level);
+ wifiProfile.addProtoBuf(GWifiProfile.WIFI_DEVICES, wifiDevice);
+ count++;
+ if (count >= MAX_WIFI_TO_INCLUDE) {
+ break;
+ }
+ }
+
+ requestElement.setProtoBuf(GLocRequestElement.WIFI_PROFILE, wifiProfile);
+ }
+
+ // Location information
+ if (location != null) {
+ ProtoBuf latlngElement = new ProtoBuf(GlatlngMessageTypes.GLAT_LNG);
+ latlngElement.setInt(GLatLng.LAT_E7, (int)(location.getLatitude() * E7));
+ latlngElement.setInt(GLatLng.LNG_E7, (int)(location.getLongitude() * E7));
+
+ ProtoBuf locationElement = new ProtoBuf(GlocationMessageTypes.GLOCATION);
+ locationElement.setProtoBuf(GLocation.LAT_LNG, latlngElement);
+ locationElement.setInt(GLocation.LOC_TYPE, GLocation.LOCTYPE_GPS);
+ locationElement.setLong(GLocation.TIMESTAMP, location.getTime());
+ if (location.hasAccuracy()) {
+ locationElement.setInt(GLocation.ACCURACY, (int)location.getAccuracy());
+ }
+ if (location.hasSpeed()) {
+ locationElement.setInt(GLocation.VELOCITY, (int)location.getSpeed());
+ }
+ if (location.hasBearing()) {
+ locationElement.setInt(GLocation.HEADING, (int)location.getBearing());
+ }
+
+ requestElement.setProtoBuf(GLocRequestElement.LOCATION, locationElement);
+ }
+
+ if (mCurrentCollectionRequest == null) {
+ mCurrentCollectionRequest = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST);
+
+ // Create a Platform Profile
+ ProtoBuf platformProfile = createPlatformProfile();
+ if (cellState != null && cellState.isValid()) {
+ ProtoBuf cellularPlatform = createCellularPlatformProfile(cellState);
+ platformProfile.setProtoBuf(GPlatformProfile.CELLULAR_PLATFORM_PROFILE,
+ cellularPlatform);
+ }
+ mCurrentCollectionRequest.setProtoBuf(GLocRequest.PLATFORM_PROFILE, platformProfile);
+ }
+ mCurrentCollectionRequest.addProtoBuf(GLocRequest.REQUEST_ELEMENTS, requestElement);
+
+ // Immediately upload collection events if buffer exceeds certain size
+ if (mCurrentCollectionRequest.getCount(GLocRequest.REQUEST_ELEMENTS)
+ >= MAX_COLLECTION_BUFFER_SIZE) {
+ immediate = true;
+ }
+
+ if (immediate) {
+ // Request to send over wire
+ uploadCollectionReport(immediate);
+ }
+ }
+
+ /**
+ * Uploads the collection report either immediately or based on MASF's queueing logic.
+ * Does not need a reply back
+ *
+ * @param immediate true if request should be sent immediately instead of being queued
+ */
+ private synchronized void uploadCollectionReport(boolean immediate) {
+ // There may be nothing to upload
+ if (mCurrentCollectionRequest == null ||
+ mCurrentCollectionRequest.getCount(GLocRequest.REQUEST_ELEMENTS) == 0) {
+ return;
+ }
+
+ ByteArrayOutputStream payload = new ByteArrayOutputStream();
+ try {
+ mCurrentCollectionRequest.outputTo(payload);
+ } catch (IOException e) {
+ Log.e(TAG, "uploadCollectionReport(): unable to write request to payload");
+ return;
+ }
+
+ mLastCollectionUploadTime = SystemClock.elapsedRealtime();
+
+ // Since this has already been written to the wire, we can clear this request
+ int count = mCurrentCollectionRequest.getCount(GLocRequest.REQUEST_ELEMENTS);
+ while (count > 0) {
+ mCurrentCollectionRequest.remove(GLocRequest.REQUEST_ELEMENTS, count - 1);
+ count--;
+ }
+
+ // Creates request and a listener with a call back function
+ ProtoBuf reply = new ProtoBuf(LocserverMessageTypes.GLOC_REPLY);
+ Request plainRequest =
+ new PlainRequest(REQUEST_UPLOAD_LOC, (short)0, payload.toByteArray());
+
+ ProtoRequestListener listener = new ProtoRequestListener(reply, new ServiceCallback() {
+ public void onRequestComplete(Object result) {
+ ProtoBuf response = (ProtoBuf) result;
+
+ if (response == null) {
+ Log.e(TAG, "uploadCollectionReport(): response is null");
+ return;
+ }
+
+ int status1 = response.getInt(GLocReply.STATUS);
+ if (status1 != ResponseCodes.STATUS_STATUS_SUCCESS) {
+ Log.w(TAG, "uploadCollectionReport(): RPC failed with status " + status1);
+ return;
+ }
+
+ if (response.has(GLocReply.PLATFORM_KEY)) {
+ String platformKey = response.getString(GLocReply.PLATFORM_KEY);
+ if (!TextUtils.isEmpty(platformKey)) {
+ setPlatformKey(platformKey);
+ }
+ }
+
+ if (!response.has(GLocReply.REPLY_ELEMENTS)) {
+ Log.w(TAG, "uploadCollectionReport(): no ReplyElement");
+ return;
+ }
+
+ int count = response.getCount(GLocReply.REPLY_ELEMENTS);
+ for (int i = 0; i < count; i++) {
+ ProtoBuf replyElement = response.getProtoBuf(GLocReply.REPLY_ELEMENTS, i);
+ int status2 = replyElement.getInt(GLocReplyElement.STATUS);
+ if (status2 != ResponseCodes.STATUS_STATUS_SUCCESS) {
+ Log.w(TAG, "uploadCollectionReport(): GLS failed with " + status2);
+ }
+ }
+
+ }
+ });
+ plainRequest.setListener(listener);
+
+ // Send request
+ MobileServiceMux serviceMux = MobileServiceMux.getSingleton();
+ serviceMux.submitRequest(plainRequest, immediate);
+
+ }
+
+ private String getPlatformKey() {
+ if (mPlatformKey != null) {
+ return mPlatformKey;
+ }
+
+ try {
+ File file = new File(LocationManager.SYSTEM_DIR, PLATFORM_KEY_FILE);
+ FileInputStream istream = new FileInputStream(file);
+ DataInputStream dataInput = new DataInputStream(istream);
+ String platformKey = dataInput.readUTF();
+ dataInput.close();
+ mPlatformKey = platformKey;
+ return mPlatformKey;
+ } catch(FileNotFoundException e) {
+ // No file, just ignore
+ return null;
+ } catch(IOException e) {
+ // Unable to read from file, just ignore
+ return null;
+ }
+ }
+
+ private void setPlatformKey(String platformKey) {
+ File systemDir = new File(LocationManager.SYSTEM_DIR);
+ if (!systemDir.exists()) {
+ if (!systemDir.mkdirs()) {
+ Log.w(TAG, "setPlatformKey(): couldn't create directory");
+ return;
+ }
+ }
+
+ try {
+ File file = new File(LocationManager.SYSTEM_DIR, PLATFORM_KEY_FILE);
+ FileOutputStream ostream = new FileOutputStream(file);
+ DataOutputStream dataOut = new DataOutputStream(ostream);
+ dataOut.writeUTF(platformKey);
+ dataOut.close();
+ mPlatformKey = platformKey;
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "setPlatformKey(): unable to create platform key file");
+ } catch (IOException e) {
+ Log.w(TAG, "setPlatformKey(): unable to write to platform key");
+ }
+ }
+
+ private ProtoBuf createPlatformProfile() {
+ Locale locale = Locale.getDefault();
+ return createPlatformProfile(locale);
+ }
+
+ private ProtoBuf createPlatformProfile(Locale locale) {
+ if (mPlatformProfile == null) {
+ mPlatformProfile = new ProtoBuf(GlocationMessageTypes.GPLATFORM_PROFILE);
+ mPlatformProfile.setString(GPlatformProfile.VERSION, APPLICATION_VERSION);
+ mPlatformProfile.setString(GPlatformProfile.PLATFORM, PLATFORM_BUILD);
+ }
+
+ // Add Locale
+ if ((locale != null) && (locale.toString() != null)) {
+ mPlatformProfile.setString(GPlatformProfile.LOCALE, locale.toString());
+ }
+
+ // Add Platform Key
+ String platformKey = getPlatformKey();
+ if (!TextUtils.isEmpty(platformKey)) {
+ mPlatformProfile.setString(GPlatformProfile.PLATFORM_KEY, platformKey);
+ }
+
+ // Clear out cellular platform profile
+ mPlatformProfile.setProtoBuf(GPlatformProfile.CELLULAR_PLATFORM_PROFILE, null);
+
+ return mPlatformProfile;
+ }
+
+ private ProtoBuf createCellularPlatformProfile(CellState cellState) {
+ if (mCellularPlatformProfile == null) {
+ // Radio type
+ int radioType = -1;
+ if (cellState.getRadioType() == CellState.RADIO_TYPE_GPRS) {
+ radioType = GCellularPlatformProfile.RADIO_TYPE_GPRS;
+ } else if (cellState.getRadioType() == CellState.RADIO_TYPE_CDMA) {
+ radioType = GCellularPlatformProfile.RADIO_TYPE_CDMA;
+ } else if (cellState.getRadioType() == CellState.RADIO_TYPE_WCDMA) {
+ radioType = GCellularPlatformProfile.RADIO_TYPE_WCDMA;
+ }
+
+ // Cellular platform profile
+ ProtoBuf cellularPlatform =
+ new ProtoBuf(GlocationMessageTypes.GCELLULAR_PLATFORM_PROFILE);
+ cellularPlatform.setInt(GCellularPlatformProfile.RADIO_TYPE, radioType);
+ if ((cellState.getHomeMcc() != -1) && (cellState.getHomeMnc() != -1)) {
+ cellularPlatform.setInt(GCellularPlatformProfile.HOME_MCC, cellState.getHomeMcc());
+ cellularPlatform.setInt(GCellularPlatformProfile.HOME_MNC, cellState.getHomeMnc());
+ }
+ if (cellState.getCarrier() != null) {
+ cellularPlatform.setString(GCellularPlatformProfile.CARRIER,
+ cellState.getCarrier());
+ }
+ mCellularPlatformProfile = cellularPlatform;
+ }
+
+ return mCellularPlatformProfile;
+ }
+
+ private void getAddressFromProtoBuf(ProtoBuf location, Locale locale, List<Address> addrs) {
+
+ double lat = -1;
+ double lng = -1;
+
+ if (location.has(GLocation.LAT_LNG)) {
+ ProtoBuf latlng = location.getProtoBuf(GLocation.LAT_LNG);
+ lat = latlng.getInt(GLatLng.LAT_E7)/E7;
+ lng = latlng.getInt(GLatLng.LNG_E7)/E7;
+ }
+
+ for (int a = 0; a < location.getCount(GLocation.FEATURE); a++) {
+
+ Address output = new Address(locale);
+
+ ProtoBuf feature = location.getProtoBuf(GLocation.FEATURE, a);
+ output.setFeatureName(feature.getString(GFeature.NAME));
+
+ if (feature.has(GFeature.CENTER)) {
+ ProtoBuf center = feature.getProtoBuf(GFeature.CENTER);
+ output.setLatitude(center.getInt(GLatLng.LAT_E7)/E7);
+ output.setLongitude(center.getInt(GLatLng.LNG_E7)/E7);
+
+ } else if (location.has(GLocation.LAT_LNG)) {
+ output.setLatitude(lat);
+ output.setLongitude(lng);
+ }
+
+ ProtoBuf address = feature.getProtoBuf(GFeature.ADDRESS);
+
+ for (int i = 0; i < address.getCount(GAddress.FORMATTED_ADDRESS_LINE); i++) {
+ String line = address.getString(GAddress.FORMATTED_ADDRESS_LINE, i);
+ output.setAddressLine(i, line);
+ }
+
+ for (int i = 0; i < address.getCount(GAddress.COMPONENT); i++) {
+ ProtoBuf component = address.getProtoBuf(GAddress.COMPONENT, i);
+ int type = component.getInt(GAddressComponent.FEATURE_TYPE);
+ String name = component.getString(GAddressComponent.NAME);
+
+ switch(type) {
+ case GFeature.FEATURE_TYPE_ADMINISTRATIVE_AREA :
+ output.setAdminArea(name);
+ break;
+
+ case GFeature.FEATURE_TYPE_SUB_ADMINISTRATIVE_AREA :
+ output.setSubAdminArea(name);
+ break;
+
+ case GFeature.FEATURE_TYPE_LOCALITY :
+ output.setLocality(name);
+ break;
+
+ case GFeature.FEATURE_TYPE_THOROUGHFARE :
+ output.setThoroughfare(name);
+ break;
+
+ case GFeature.FEATURE_TYPE_POST_CODE :
+ output.setPostalCode(name);
+ break;
+
+ case GFeature.FEATURE_TYPE_COUNTRY :
+ output.setCountryName(name);
+ break;
+
+ case GFeature.FEATURE_TYPE_COUNTRY_CODE :
+ output.setCountryCode(name);
+ break;
+
+ default :
+ if (android.util.Config.LOGD) {
+ Log.d(TAG, "getAddressFromProtoBuf(): Ignore feature " + type + "," + name);
+ }
+ break;
+ }
+ }
+
+ addrs.add(output);
+ }
+ }
+
+}
diff --git a/location/java/com/android/internal/location/NetworkLocationProvider.java b/location/java/com/android/internal/location/NetworkLocationProvider.java
new file mode 100644
index 0000000..7d3fda1
--- /dev/null
+++ b/location/java/com/android/internal/location/NetworkLocationProvider.java
@@ -0,0 +1,561 @@
+/*
+ * 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 com.android.internal.location.protocol.GDebugProfile;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+
+import android.content.Context;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationProviderImpl;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Log;
+
+/**
+ * A location provider which gets approximate location from Google's
+ * database of cell-tower and wi-fi locations.
+ *
+ * <p> It is the responsibility of the LocationManagerService to
+ * notify this class of any changes in the radio information
+ * by calling {@link #updateCellState} and of data state
+ * changes by calling {@link #updateNetworkState}
+ *
+ * <p> The LocationManagerService must also notify the provider
+ * of Wifi updates using the {@link #updateWifiScanResults}
+ * and {@link #updateWifiEnabledState}
+ * methods.
+ *
+ * <p> The provider uses whichever radio is available - Cell
+ * or WiFi. If neither is available, it does NOT attempt to
+ * switch them on.
+ *
+ * {@hide}
+ */
+public class NetworkLocationProvider extends LocationProviderImpl {
+ private static final String TAG = "NetworkLocationProvider";
+
+ // Wait at least 60 seconds between network queries
+ private static final int MIN_NETWORK_RETRY_MILLIS = 60000;
+
+ // Max time to wait for radio update
+ private static final long MAX_TIME_TO_WAIT_FOR_RADIO = 5 * 1000; // 5 seconds
+
+ // State of entire provider
+ private int mStatus = AVAILABLE;
+ private long mStatusUpdateTime = 0;
+
+ // Network state
+ private int mNetworkState = TEMPORARILY_UNAVAILABLE;
+
+ // Cell state
+ private static final int MAX_CELL_HISTORY_TO_KEEP = 4;
+ private LinkedList<CellState> mCellHistory = new LinkedList<CellState>();
+ private CellState mCellState = null;
+ private long mLastCellStateChangeTime = 0;
+ private long mLastCellLockTime = 0;
+
+ // Wifi state
+ private static final long MIN_TIME_BETWEEN_WIFI_REPORTS = 45 * 1000; // 45 seconds
+ private List<ScanResult> mWifiLastScanResults = null;
+ private long mLastWifiScanTriggerTime = 0;
+ private long mLastWifiScanElapsedTime = 0;
+ private long mLastWifiScanRealTime = 0;
+ private long mWifiScanFrequency = MIN_TIME_BETWEEN_WIFI_REPORTS;
+ private boolean mWifiEnabled = false;
+
+ // Last known location state
+ private Location mLocation = new Location(LocationManager.NETWORK_PROVIDER);
+ private long mLastNetworkQueryTime = 0; // Last network request, successful or not
+ private long mLastSuccessfulNetworkQueryTime = 0; // Last successful network query time
+
+ // Is provider enabled by user -- ignored by this class
+ private boolean mEnabled;
+
+ // Is provider being used by an application
+ private HashSet<String> mApplications = new HashSet<String>();
+ private boolean mTracking = false;
+
+ // Location masf service
+ private LocationMasfClient mMasfClient;
+
+ // Context of location manager service
+ private Context mContext;
+
+ public static boolean isSupported() {
+ // This class provides a Google-specific location feature, so it's enabled only
+ // when the system property ro.com.google.enable_google_location_features is set.
+ if (!SystemProperties.get("ro.com.google.enable_google_location_features").equals("1")) {
+ return false;
+ }
+
+ // Otherwise, assume cell location should work if we are not running in the emulator
+ return !SystemProperties.get("ro.kernel.qemu").equals("1");
+ }
+
+ public NetworkLocationProvider(Context context, LocationMasfClient masfClient) {
+ super(LocationManager.NETWORK_PROVIDER);
+ mContext = context;
+ mMasfClient = masfClient;
+ }
+
+ @Override
+ public void updateNetworkState(int state) {
+ if (state == mNetworkState) {
+ return;
+ }
+ log("updateNetworkState(): Updating network state to " + state);
+ mNetworkState = state;
+
+ updateStatus(mNetworkState);
+ }
+
+ @Override
+ public void updateCellState(CellState newState) {
+ if (newState == null) {
+ log("updateCellState(): Cell state is invalid");
+ return;
+ }
+
+ if (mCellState != null && mCellState.equals(newState)) {
+ log("updateCellState(): Cell state is the same");
+ return;
+ }
+
+ // Add previous state to history
+ if ((mCellState != null) && mCellState.isValid()) {
+ if (mCellHistory.size() >= MAX_CELL_HISTORY_TO_KEEP) {
+ mCellHistory.remove(0);
+ }
+ mCellHistory.add(mCellState);
+ }
+
+ mCellState = newState;
+ log("updateCellState(): Received");
+
+ mLastCellLockTime = 0;
+ mLastCellStateChangeTime = SystemClock.elapsedRealtime();
+ }
+
+ public void updateCellLockStatus(boolean acquired) {
+ if (acquired) {
+ mLastCellLockTime = SystemClock.elapsedRealtime();
+ } else {
+ mLastCellLockTime = 0;
+ }
+ }
+
+ @Override
+ public boolean requiresNetwork() {
+ return true;
+ }
+
+ @Override
+ public boolean requiresSatellite() {
+ return false;
+ }
+
+ @Override
+ public boolean requiresCell() {
+ return true;
+ }
+
+ @Override
+ public boolean hasMonetaryCost() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsAltitude() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSpeed() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsBearing() {
+ return false;
+ }
+
+ @Override
+ public int getPowerRequirement() {
+ return Criteria.POWER_LOW;
+ }
+
+ @Override
+ public void enable() {
+ // Nothing else needs to be done
+ mEnabled = true;
+ }
+
+ @Override
+ public void disable() {
+ // Nothing else needs to be done
+ mEnabled = false;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ @Override
+ public int getAccuracy() {
+ return Criteria.ACCURACY_COARSE;
+ }
+
+ @Override
+ public int getStatus(Bundle extras) {
+ return mStatus;
+ }
+
+ @Override
+ public long getStatusUpdateTime() {
+ return mStatusUpdateTime;
+ }
+
+ @Override
+ public void setMinTime(long minTime) {
+ if (minTime < MIN_TIME_BETWEEN_WIFI_REPORTS) {
+ mWifiScanFrequency = MIN_TIME_BETWEEN_WIFI_REPORTS;
+ } else {
+ mWifiScanFrequency = minTime;
+ }
+ super.setMinTime(minTime);
+ }
+
+ @Override
+ public boolean getLocation(Location l) {
+
+ long now = SystemClock.elapsedRealtime();
+
+ // Trigger a wifi scan and wait for its results if necessary
+ if ((mWifiEnabled) &&
+ (mWifiLastScanResults == null ||
+ ((now - mLastWifiScanElapsedTime) > mWifiScanFrequency))) {
+
+ boolean fallback = false;
+
+ // If scan has been recently triggered
+ if (mLastWifiScanTriggerTime != 0 &&
+ ((now - mLastWifiScanTriggerTime) < mWifiScanFrequency)) {
+ if ((now - mLastWifiScanTriggerTime) > MAX_TIME_TO_WAIT_FOR_RADIO) {
+ // If no results from last trigger available, use cell results
+ // This will also trigger a new scan
+ log("getLocation(): falling back to cell");
+ fallback = true;
+ } else {
+ // Just wait for the Wifi results to be available
+ return false;
+ }
+ }
+
+ WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ log("getLocation(): triggering a wifi scan");
+ mLastWifiScanTriggerTime = now;
+ boolean succeeded = wifiManager.startScan();
+ if (!succeeded) {
+ log("getLocation(): wifi scan did not succeed");
+ // Wifi trigger failed, use cell results
+ fallback = true;
+ }
+
+ // Wait for scan results
+ if (!fallback) {
+ return false;
+ }
+ }
+
+ // If waiting for cell location
+ if (mLastCellLockTime != 0 && ((now - mLastCellLockTime) < MAX_TIME_TO_WAIT_FOR_RADIO)) {
+ return false;
+ }
+
+ // Update Location
+ // 1) If there has been a cell state change
+ // 2) If there was no successful reply for last network request
+ if (mLastCellStateChangeTime > mLastNetworkQueryTime) {
+ updateLocation();
+ return false;
+
+ } else if ((mLastNetworkQueryTime != 0)
+ && (mLastNetworkQueryTime > mLastSuccessfulNetworkQueryTime)
+ && ((now - mLastNetworkQueryTime) > MIN_NETWORK_RETRY_MILLIS)) {
+ updateLocation();
+ return false;
+ }
+
+ if (mLocation != null && mLocation.getAccuracy() > 0) {
+
+ // We could have a Cell Id location which hasn't changed in a
+ // while because we haven't switched towers so if the last location
+ // time + mWifiScanFrequency is less than current time update the
+ // locations time.
+ long currentTime = System.currentTimeMillis();
+ if ((mLocation.getTime() + mWifiScanFrequency) < currentTime) {
+ mLocation.setTime(currentTime);
+ }
+ l.set(mLocation);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void enableLocationTracking(boolean enable) {
+ if (enable == mTracking) {
+ return;
+ }
+
+ log("enableLocationTracking(): " + enable);
+ mTracking = enable;
+
+ if (!enable) {
+ // When disabling the location provider, be sure to clear out old location
+ clearLocation();
+ } else {
+ // When enabling provider, force location
+ forceLocation();
+ }
+ }
+
+ @Override
+ public boolean isLocationTracking() {
+ return mTracking;
+ }
+
+ /**
+ * Notifies the provider that there are scan results available.
+ *
+ * @param scanResults list of wifi scan results
+ */
+ public void updateWifiScanResults(List<ScanResult> scanResults) {
+ if (!mTracking) {
+ return;
+ }
+
+ long now = SystemClock.elapsedRealtime();
+
+ if (scanResults == null) {
+ mWifiLastScanResults = null;
+ mLastWifiScanElapsedTime = now;
+ mLastWifiScanRealTime = System.currentTimeMillis();
+
+ log("updateWifIScanResults(): NULL APs");
+
+ // Force cell location since no wifi results available
+ if (mWifiEnabled) {
+ mLastCellLockTime = 0;
+ mLastCellStateChangeTime = SystemClock.elapsedRealtime();
+ }
+
+ } else if ((mWifiLastScanResults == null)
+ || (mWifiLastScanResults.size() <= 2 && scanResults.size() > mWifiLastScanResults.size())
+ || ((now - mLastWifiScanElapsedTime) > mWifiScanFrequency)) {
+
+ if (mWifiLastScanResults == null) {
+ mWifiLastScanResults = new ArrayList<ScanResult>();
+ } else {
+ mWifiLastScanResults.clear();
+ }
+ mWifiLastScanResults.addAll(scanResults);
+ mLastWifiScanElapsedTime = now;
+ mLastWifiScanRealTime = System.currentTimeMillis();
+
+ log("updateWifIScanResults(): " + mWifiLastScanResults.size() + " APs");
+ updateLocation();
+
+ }
+ }
+
+ /**
+ * Notifies the provider if Wifi has been enabled or disabled
+ * by the user
+ *
+ * @param enabled true if wifi is enabled; false otherwise
+ */
+ public void updateWifiEnabledState(boolean enabled) {
+ mWifiEnabled = enabled;
+
+ log("updateWifiEnabledState(): " + enabled);
+
+ // Force location update
+ forceLocation();
+ }
+
+ public void addListener(String[] applications) {
+ if (applications != null) {
+ for (String app : applications) {
+ String a = app.replaceAll("com.google.android.", "");
+ a = a.replaceAll("com.android.", "");
+ mApplications.add(a);
+ log("addListener(): " + a);
+ }
+ }
+ }
+
+ public void removeListener(String[] applications) {
+ if (applications != null) {
+ for (String app : applications) {
+ String a = app.replaceAll("com.google.android.", "");
+ a = a.replaceAll("com.android.", "");
+ mApplications.remove(a);
+ log("removeListener(): " + a);
+ }
+ }
+ }
+
+ private void clearLocation() {
+ mLocation.setAccuracy(-1);
+ updateStatus(TEMPORARILY_UNAVAILABLE);
+ }
+
+ private void forceLocation() {
+ if (mWifiEnabled) {
+ // Force another wifi scan
+ mWifiLastScanResults = null;
+ mLastWifiScanTriggerTime = 0;
+ mLastWifiScanElapsedTime = 0;
+ mLastWifiScanRealTime = 0;
+ } else {
+ // Force another cell location request
+ mLastCellLockTime = 0;
+ mLastCellStateChangeTime = SystemClock.elapsedRealtime();
+ }
+ }
+
+ private void updateStatus(int status) {
+ if (status != mStatus) {
+ mStatus = status;
+ mStatusUpdateTime = SystemClock.elapsedRealtime();
+ }
+ }
+
+ /**
+ * Gets location from the server is applications are tracking this provider
+ *
+ */
+ private void updateLocation() {
+
+ // If not being tracked, no need to do anything.
+ if (!mTracking) {
+ return;
+ }
+
+ // If network is not available, can't do anything
+ if (mNetworkState != AVAILABLE) {
+ return;
+ }
+
+ final long now = SystemClock.elapsedRealtime();
+
+ // There is a pending network request
+ if ((mLastNetworkQueryTime != 0) &&
+ (mLastNetworkQueryTime > mLastSuccessfulNetworkQueryTime) &&
+ ((now - mLastNetworkQueryTime) <= MIN_NETWORK_RETRY_MILLIS)) {
+ return;
+ }
+
+ // Don't include wifi points if they're too old
+ List<ScanResult> scanResults = null;
+ if (mWifiEnabled && (mWifiLastScanResults != null &&
+ ((now - mLastWifiScanElapsedTime) < (mWifiScanFrequency + MAX_TIME_TO_WAIT_FOR_RADIO)))) {
+ scanResults = mWifiLastScanResults;
+ }
+
+ // If no valid cell information available
+ boolean noCell = mCellState == null || !mCellState.isValid();
+
+ // If no valid wifi information available
+ boolean noWifi = scanResults == null || (scanResults.size() == 0);
+
+ // If no cell-id or wi-fi update, just return invalid location
+ if (noCell && noWifi) {
+ clearLocation();
+ return;
+ }
+
+ // What kind of a network location request was it
+ int trigger;
+ if (!mWifiEnabled) {
+ if (!noCell) {
+ trigger = GDebugProfile.TRIGGER_CELL_AND_WIFI_CHANGE;
+ } else {
+ trigger = GDebugProfile.TRIGGER_WIFI_CHANGE;
+ }
+ } else {
+ trigger = GDebugProfile.TRIGGER_CELL_CHANGE;
+ }
+
+ try {
+ mLastNetworkQueryTime = now;
+ mMasfClient.getNetworkLocation(mApplications, trigger, mCellState, mCellHistory,
+ scanResults, mLastWifiScanRealTime, new Callback() {
+ public void locationReceived(Location location, boolean networkSuccessful) {
+ // If location is valid and not the same as previously known location
+ if ((location != null) && (location.getAccuracy() > 0) &&
+ (location.getTime() != mLocation.getTime())) {
+ mLocation.set(location);
+ updateStatus(AVAILABLE);
+ } else {
+ // Location is unavailable
+ clearLocation();
+ }
+
+ // Even if no location is available, network request could have succeeded
+ if (networkSuccessful) {
+ mLastSuccessfulNetworkQueryTime = SystemClock.elapsedRealtime();
+ }
+
+ }
+ });
+ } catch(Exception e) {
+ Log.e(TAG, "updateLocation got exception:", e);
+ }
+ }
+
+ public interface Callback {
+
+ /**
+ * Callback function to notify of a received network location
+ *
+ * @param location location object that is received. may be null if not a valid location
+ * @param successful true if network query was successful, even if no location was found
+ */
+ void locationReceived(Location location, boolean successful);
+ }
+
+ private void log(String log) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, log);
+ }
+ }
+
+}
diff --git a/location/java/com/android/internal/location/NmeaParser.java b/location/java/com/android/internal/location/NmeaParser.java
new file mode 100644
index 0000000..43afa1d
--- /dev/null
+++ b/location/java/com/android/internal/location/NmeaParser.java
@@ -0,0 +1,437 @@
+/*
+ * 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/ProtoRequestListener.java b/location/java/com/android/internal/location/ProtoRequestListener.java
new file mode 100644
index 0000000..d73cd05
--- /dev/null
+++ b/location/java/com/android/internal/location/ProtoRequestListener.java
@@ -0,0 +1,61 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.android.internal.location;
+
+import com.google.common.io.GoogleHttpConnection;
+import com.google.common.io.protocol.ProtoBuf;
+import com.google.masf.ServiceCallback;
+import com.google.masf.protocol.Request;
+import com.google.masf.protocol.Response;
+import com.google.masf.services.AsyncResult;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.util.Log;
+
+/**
+ * Listener for protocol buffer requests
+ *
+ * {@hide}
+ */
+
+public class ProtoRequestListener implements Request.Listener {
+ private final static String TAG = "ProtoRequestListener";
+ private AsyncResult result;
+ private ProtoBuf protoResponse;
+
+ /**
+ * @return the asynchronous result object
+ */
+ public AsyncResult getAsyncResult() {
+ return result;
+ }
+
+ /**
+ * Constructor for a ProtoRequestListener
+ *
+ * @param protoResponse ProtoBuf with correct type to fill response with
+ * @param callback function to call after completed request (may be null)
+ */
+ public ProtoRequestListener(ProtoBuf protoResponse, ServiceCallback callback) {
+ this.result = new AsyncResult(callback);
+ this.protoResponse = protoResponse;
+ }
+
+ public boolean requestComplete(Request request, Response response)
+ throws IOException {
+ InputStream is = response.getInputStream();
+ if (response.getStatusCode() == GoogleHttpConnection.HTTP_OK) {
+ protoResponse.parse(is);
+ result.setResult(protoResponse);
+ } else {
+ result.setResult(null);
+ }
+ return true;
+ }
+
+ public void requestException(Request request, Exception exception) {
+ Log.e(TAG, "requestException()", exception);
+ }
+}
diff --git a/location/java/com/android/internal/location/TrackProvider.java b/location/java/com/android/internal/location/TrackProvider.java
new file mode 100644
index 0000000..545d7dc
--- /dev/null
+++ b/location/java/com/android/internal/location/TrackProvider.java
@@ -0,0 +1,720 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.android.internal.location;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationProviderImpl;
+import android.os.Bundle;
+import android.util.Config;
+import android.util.Log;
+
+/**
+ * A dummy provider that returns positions interpolated from a sequence
+ * of caller-supplied waypoints. The waypoints are supplied as a
+ * String containing one or more numeric quadruples of the form:
+ * <br>
+ * <code>
+ * <time in millis> <latitude> <longitude> <altitude>
+ * </code>
+ *
+ * <p> The waypoints must be supplied in increasing timestamp order.
+ *
+ * <p> The time at which the provider is constructed is considered to
+ * be time 0, and further requests for positions will return a
+ * position that is linearly interpolated between the waypoints whose
+ * timestamps are closest to the amount of wall clock time that has
+ * elapsed since time 0.
+ *
+ * <p> Following the time of the last waypoint, the position of that
+ * waypoint will continue to be returned indefinitely.
+ *
+ * {@hide}
+ */
+public class TrackProvider extends LocationProviderImpl {
+ static final String LOG_TAG = "TrackProvider";
+
+ private static final long INTERVAL = 1000L;
+
+ private boolean mEnabled = true;
+
+ private double mLatitude;
+ private double mLongitude;
+ private boolean mHasAltitude;
+ private boolean mHasBearing;
+ private boolean mHasSpeed;
+ private double mAltitude;
+ private float mBearing;
+ private float mSpeed;
+ private Bundle mExtras;
+
+ private long mBaseTime;
+ private long mLastTime = -1L;
+ private long mTime;
+
+ private long mMinTime;
+ private long mMaxTime;
+
+ private List<Waypoint> mWaypoints = new ArrayList<Waypoint>();
+ private int mWaypointIndex = 0;
+
+ private boolean mRequiresNetwork = false;
+ private boolean mRequiresSatellite = false;
+ private boolean mRequiresCell = false;
+ private boolean mHasMonetaryCost = false;
+ private boolean mSupportsAltitude = true;
+ private boolean mSupportsSpeed = true;
+ private boolean mSupportsBearing = true;
+ private boolean mRepeat = false;
+ private int mPowerRequirement = Criteria.POWER_LOW;
+ private int mAccuracy = Criteria.ACCURACY_COARSE;
+
+ private float mTrackSpeed = 100.0f; // km/hr - default for kml tracks
+
+ private Location mInitialLocation;
+
+ private void close(Reader rdr) {
+ try {
+ if (rdr != null) {
+ rdr.close();
+ }
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Exception closing reader", e);
+ }
+ }
+
+ public void readTrack(File trackFile) {
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader(trackFile), 8192);
+ String s;
+
+ long lastTime = -Long.MAX_VALUE;
+ while ((s = br.readLine()) != null) {
+ String[] tokens = s.split("\\s+");
+ if (tokens.length != 4 && tokens.length != 6) {
+ Log.e(LOG_TAG, "Got track \"" + s +
+ "\", wanted <time> <long> <lat> <alt> [<bearing> <speed>]");
+ continue;
+ }
+ long time;
+ double longitude, latitude, altitude;
+ try {
+ time = Long.parseLong(tokens[0]);
+ longitude = Double.parseDouble(tokens[1]);
+ latitude = Double.parseDouble(tokens[2]);
+ altitude = Double.parseDouble(tokens[3]);
+ } catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Got track \"" + s +
+ "\", wanted <time> <long> <lat> <alt> " +
+ "[<bearing> <speed>]", e);
+ continue;
+ }
+
+ Waypoint w = new Waypoint(getName(), time, latitude, longitude, altitude);
+ if (tokens.length >= 6) {
+ float bearing, speed;
+ try {
+ bearing = Float.parseFloat(tokens[4]);
+ speed = Float.parseFloat(tokens[5]);
+ w.setBearing(bearing);
+ w.setSpeed(speed);
+ } catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Ignoring bearing and speed \"" +
+ tokens[4] + "\", \"" + tokens[5] + "\"", e);
+ }
+ }
+
+ if (mInitialLocation == null) {
+ mInitialLocation = w.getLocation();
+ }
+
+ // Ignore waypoints whose time is less than or equal to 0 or
+ // the time of the previous waypoint
+ if (time < 0) {
+ Log.e(LOG_TAG, "Ignoring waypoint at negative time=" + time);
+ continue;
+ }
+ if (time <= lastTime) {
+ Log.e(LOG_TAG, "Ignoring waypoint at time=" + time +
+ " (< " + lastTime + ")");
+ continue;
+ }
+
+ mWaypoints.add(w);
+ lastTime = time;
+ }
+
+ setTimes();
+ return;
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Exception reading track file", e);
+ mWaypoints.clear();
+ } finally {
+ close(br);
+ }
+ }
+
+ public void readKml(File kmlFile) {
+ FileReader kmlReader = null;
+ try {
+ kmlReader = new FileReader(kmlFile);
+ XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
+ XmlPullParser xpp = factory.newPullParser();
+ xpp.setInput(kmlReader);
+
+ // Concatenate the text of each <coordinates> tag
+ boolean inCoordinates = false;
+ StringBuilder sb = new StringBuilder();
+ int eventType = xpp.getEventType();
+ do {
+ if (eventType == XmlPullParser.START_DOCUMENT) {
+ // do nothing
+ } else if (eventType == XmlPullParser.END_DOCUMENT) {
+ // do nothing
+ } else if (eventType == XmlPullParser.START_TAG) {
+ String startTagName = xpp.getName();
+ if (startTagName.equals("coordinates")) {
+ inCoordinates = true;
+ }
+ } else if (eventType == XmlPullParser.END_TAG) {
+ String endTagName = xpp.getName();
+ if (endTagName.equals("coordinates")) {
+ inCoordinates = false;
+ }
+ } else if (eventType == XmlPullParser.TEXT) {
+ if (inCoordinates) {
+ sb.append(xpp.getText());
+ sb.append(' ');
+ }
+ }
+ eventType = xpp.next();
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+
+ String coordinates = sb.toString();
+
+ // Parse the "lon,lat,alt" triples and supply times
+ // for each waypoint based on a constant speed
+ Location loc = null;
+ double KM_PER_HOUR = mTrackSpeed;
+ double KM_PER_METER = 1.0 / 1000.0;
+ double MILLIS_PER_HOUR = 60.0 * 60.0 * 1000.0;
+ double MILLIS_PER_METER =
+ (1.0 / KM_PER_HOUR) * (KM_PER_METER) * (MILLIS_PER_HOUR);
+ long time = 0L;
+
+ StringTokenizer st = new StringTokenizer(coordinates, ", ");
+ while (st.hasMoreTokens()) {
+ try {
+ String lon = st.nextToken();
+ String lat = st.nextToken();
+ String alt = st.nextToken();
+ if (Config.LOGD) {
+ Log.d(LOG_TAG,
+ "lon=" + lon + ", lat=" + lat + ", alt=" + alt);
+ }
+
+ double nLongitude = Double.parseDouble(lon);
+ double nLatitude = Double.parseDouble(lat);
+ double nAltitude = Double.parseDouble(alt);
+
+ Location nLoc = new Location(getName());
+ nLoc.setLatitude(nLatitude);
+ nLoc.setLongitude(nLongitude);
+ if (loc != null) {
+ double distance = loc.distanceTo(nLoc);
+ if (Config.LOGD) {
+ Log.d(LOG_TAG, "distance = " + distance);
+ }
+ time += (long) (distance * MILLIS_PER_METER);
+ }
+
+ Waypoint w = new Waypoint(getName(), time,
+ nLatitude, nLongitude, nAltitude);
+ if (supportsSpeed()) {
+ w.setSpeed(mTrackSpeed);
+ }
+ if (supportsBearing()) {
+ w.setBearing(0.0f);
+ }
+ mWaypoints.add(w);
+
+ if (mInitialLocation == null) {
+ mInitialLocation = w.getLocation();
+ }
+
+ loc = nLoc;
+ } catch (NumberFormatException nfe) {
+ Log.e(LOG_TAG, "Got NumberFormatException reading KML data: " +
+ nfe, nfe);
+ }
+ }
+
+ setTimes();
+ return;
+ } catch (IOException ioe) {
+ mWaypoints.clear();
+ Log.e(LOG_TAG, "Exception reading KML data: " + ioe, ioe);
+ // fall through
+ } catch (XmlPullParserException xppe) {
+ mWaypoints.clear();
+ Log.e(LOG_TAG, "Exception reading KML data: " + xppe, xppe);
+ // fall through
+ } finally {
+ close(kmlReader);
+ }
+ }
+
+ public void readNmea(String name, File file) {
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader(file), 8192);
+ String s;
+
+ String provider = getName();
+ NmeaParser parser = new NmeaParser(name);
+ while ((s = br.readLine()) != null) {
+ boolean newWaypoint = parser.parseSentence(s);
+ if (newWaypoint) {
+ Location loc = parser.getLocation();
+ Waypoint w = new Waypoint(loc);
+ mWaypoints.add(w);
+ // Log.i(TAG, "Got waypoint " + w);
+ }
+ }
+
+ setTimes();
+ return;
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Exception reading NMEA data: " + ioe);
+ mWaypoints.clear();
+ } finally {
+ close(br);
+ }
+ }
+
+ private static boolean booleanVal(String tf) {
+ return (tf == null) || (tf.equalsIgnoreCase("true"));
+ }
+
+ private static int intVal(String val) {
+ try {
+ return (val == null) ? 0 : Integer.parseInt(val);
+ } catch (NumberFormatException nfe) {
+ return 0;
+ }
+ }
+
+ private static float floatVal(String val) {
+ try {
+ return (val == null) ? 0 : Float.parseFloat(val);
+ } catch (NumberFormatException nfe) {
+ return 0.0f;
+ }
+ }
+
+ public void readProperties(File propertiesFile) {
+ BufferedReader br = null;
+ if (!propertiesFile.exists()) {
+ return;
+ }
+ try {
+ if (Config.LOGD) {
+ Log.d(LOG_TAG, "Loading properties file " +
+ propertiesFile.getPath());
+ }
+ br = new BufferedReader(new FileReader(propertiesFile), 8192);
+
+ String s;
+ while ((s = br.readLine()) != null) {
+ StringTokenizer st = new StringTokenizer(s);
+ String command = null;
+ String value = null;
+ if (!st.hasMoreTokens()) {
+ continue;
+ }
+ command = st.nextToken();
+ if (st.hasMoreTokens()) {
+ value = st.nextToken();
+ }
+
+ if (command.equalsIgnoreCase("requiresNetwork")) {
+ setRequiresNetwork(booleanVal(value));
+ } else if (command.equalsIgnoreCase("requiresSatellite")) {
+ setRequiresSatellite(booleanVal(value));
+ } else if (command.equalsIgnoreCase("requiresCell")) {
+ setRequiresCell(booleanVal(value));
+ } else if (command.equalsIgnoreCase("hasMonetaryCost")) {
+ setHasMonetaryCost(booleanVal(value));
+ } else if (command.equalsIgnoreCase("supportsAltitude")) {
+ setSupportsAltitude(booleanVal(value));
+ } else if (command.equalsIgnoreCase("supportsBearing")) {
+ setSupportsBearing(booleanVal(value));
+ } else if (command.equalsIgnoreCase("repeat")) {
+ setRepeat(booleanVal(value));
+ } else if (command.equalsIgnoreCase("supportsSpeed")) {
+ setSupportsSpeed(booleanVal(value));
+ } else if (command.equalsIgnoreCase("powerRequirement")) {
+ setPowerRequirement(intVal(value));
+ } else if (command.equalsIgnoreCase("accuracy")) {
+ setAccuracy(intVal(value));
+ } else if (command.equalsIgnoreCase("trackspeed")) {
+ setTrackSpeed(floatVal(value));
+ } else {
+ Log.e(LOG_TAG, "Unknown command \"" + command + "\"");
+ }
+ }
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "IOException reading properties file " +
+ propertiesFile.getPath(), ioe);
+ } finally {
+ try {
+ if (br != null) {
+ br.close();
+ }
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "IOException closing properties file " +
+ propertiesFile.getPath(), e);
+ }
+ }
+ }
+
+ public TrackProvider(String name) {
+ super(name);
+ setTimes();
+ }
+
+ public TrackProvider(String name, File file) {
+ this(name);
+
+ String filename = file.getName();
+ if (filename.endsWith("kml")) {
+ readKml(file);
+ } else if (filename.endsWith("nmea")) {
+ readNmea(getName(), file);
+ } else if (filename.endsWith("track")) {
+ readTrack(file);
+ } else {
+ Log.e(LOG_TAG, "Can't initialize TrackProvider from file " +
+ filename + " (not *kml, *nmea, or *track)");
+ }
+ setTimes();
+ }
+
+ private void setTimes() {
+ mBaseTime = System.currentTimeMillis();
+ if (mWaypoints.size() >= 2) {
+ mMinTime = mWaypoints.get(0).getTime();
+ mMaxTime = mWaypoints.get(mWaypoints.size() - 1).getTime();
+ } else {
+ mMinTime = mMaxTime = 0;
+ }
+ }
+
+ private double interp(double d0, double d1, float frac) {
+ return d0 + frac * (d1 - d0);
+ }
+
+ private void update() {
+ // Don't update the position at all unless INTERVAL milliseconds
+ // have passed since the last request
+ long time = System.currentTimeMillis() - mBaseTime;
+ if (time - mLastTime < INTERVAL) {
+ return;
+ }
+
+ List<Waypoint> waypoints = mWaypoints;
+ if (waypoints == null) {
+ return;
+ }
+ int size = waypoints.size();
+ if (size < 2) {
+ return;
+ }
+
+ long t = time;
+ if (t < mMinTime) {
+ t = mMinTime;
+ }
+ if (mRepeat) {
+ t -= mMinTime;
+ long deltaT = mMaxTime - mMinTime;
+ t %= 2 * deltaT;
+ if (t > deltaT) {
+ t = 2 * deltaT - t;
+ }
+ t += mMinTime;
+ } else if (t > mMaxTime) {
+ t = mMaxTime;
+ }
+
+ // Locate the time interval for the current time
+ // We slide the window since we don't expect to move
+ // much between calls
+
+ Waypoint w0 = waypoints.get(mWaypointIndex);
+ Waypoint w1 = waypoints.get(mWaypointIndex + 1);
+
+ // If the right end of the current interval is too early,
+ // move forward to the next waypoint
+ while (t > w1.getTime()) {
+ w0 = w1;
+ w1 = waypoints.get(++mWaypointIndex + 1);
+ }
+ // If the left end of the current interval is too late,
+ // move back to the previous waypoint
+ while (t < w0.getTime()) {
+ w1 = w0;
+ w0 = waypoints.get(--mWaypointIndex);
+ }
+
+ // Now we know that w0.mTime <= t <= w1.mTime
+ long w0Time = w0.getTime();
+ long w1Time = w1.getTime();
+ long dt = w1Time - w0Time;
+
+ float frac = (dt == 0) ? 0 : ((float) (t - w0Time) / dt);
+ mLatitude = interp(w0.getLatitude(), w1.getLatitude(), frac);
+ mLongitude = interp(w0.getLongitude(), w1.getLongitude(), frac);
+ mHasAltitude = w0.hasAltitude() && w1.hasAltitude();
+ if (mSupportsAltitude && mHasAltitude) {
+ mAltitude = interp(w0.getAltitude(), w1.getAltitude(), frac);
+ }
+ if (mSupportsBearing) {
+ mHasBearing = frac <= 0.5f ? w0.hasBearing() : w1.hasBearing();
+ if (mHasBearing) {
+ mBearing = frac <= 0.5f ? w0.getBearing() : w1.getBearing();
+ }
+ }
+ if (mSupportsSpeed) {
+ mHasSpeed = frac <= 0.5f ? w0.hasSpeed() : w1.hasSpeed();
+ if (mHasSpeed) {
+ mSpeed = frac <= 0.5f ? w0.getSpeed() : w1.getSpeed();
+ }
+ }
+ mLastTime = time;
+ mTime = time;
+ }
+
+ public void setRequiresNetwork(boolean requiresNetwork) {
+ mRequiresNetwork = requiresNetwork;
+ }
+
+ @Override public boolean requiresNetwork() {
+ return mRequiresNetwork;
+ }
+
+ public void setRequiresSatellite(boolean requiresSatellite) {
+ mRequiresSatellite = requiresSatellite;
+ }
+
+ @Override public boolean requiresSatellite() {
+ return mRequiresSatellite;
+ }
+
+ public void setRequiresCell(boolean requiresCell) {
+ mRequiresCell = requiresCell;
+ }
+
+ @Override public boolean requiresCell() {
+ return mRequiresCell;
+ }
+
+ public void setHasMonetaryCost(boolean hasMonetaryCost) {
+ mHasMonetaryCost = hasMonetaryCost;
+ }
+
+ @Override public boolean hasMonetaryCost() {
+ return mHasMonetaryCost;
+ }
+
+ public void setSupportsAltitude(boolean supportsAltitude) {
+ mSupportsAltitude = supportsAltitude;
+ }
+
+ @Override public boolean supportsAltitude() {
+ return mSupportsAltitude;
+ }
+
+ public void setSupportsSpeed(boolean supportsSpeed) {
+ mSupportsSpeed = supportsSpeed;
+ }
+
+ @Override public boolean supportsSpeed() {
+ return mSupportsSpeed;
+ }
+
+ public void setSupportsBearing(boolean supportsBearing) {
+ mSupportsBearing = supportsBearing;
+ }
+
+ @Override public boolean supportsBearing() {
+ return mSupportsBearing;
+ }
+
+ public void setRepeat(boolean repeat) {
+ mRepeat = repeat;
+ }
+
+ public void setPowerRequirement(int powerRequirement) {
+ if (powerRequirement < Criteria.POWER_LOW ||
+ powerRequirement > Criteria.POWER_HIGH) {
+ throw new IllegalArgumentException("powerRequirement = " +
+ powerRequirement);
+ }
+ mPowerRequirement = powerRequirement;
+ }
+
+ @Override public int getPowerRequirement() {
+ return mPowerRequirement;
+ }
+
+ public void setAccuracy(int accuracy) {
+ mAccuracy = accuracy;
+ }
+
+ @Override public int getAccuracy() {
+ return mAccuracy;
+ }
+
+ public void setTrackSpeed(float trackSpeed) {
+ mTrackSpeed = trackSpeed;
+ }
+
+ @Override public void enable() {
+ mEnabled = true;
+ }
+
+ @Override public void disable() {
+ mEnabled = false;
+ }
+
+ @Override public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ @Override public int getStatus(Bundle extras) {
+ return AVAILABLE;
+ }
+
+ @Override public boolean getLocation(Location l) {
+ if (mEnabled) {
+ update();
+ l.setProvider(getName());
+ l.setTime(mTime + mBaseTime);
+ l.setLatitude(mLatitude);
+ l.setLongitude(mLongitude);
+ if (mSupportsAltitude && mHasAltitude) {
+ l.setAltitude(mAltitude);
+ }
+ if (mSupportsBearing && mHasBearing) {
+ l.setBearing(mBearing);
+ }
+ if (mSupportsSpeed && mHasSpeed) {
+ l.setSpeed(mSpeed);
+ }
+ if (mExtras != null) {
+ l.setExtras(mExtras);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public Location getInitialLocation() {
+ return mInitialLocation;
+ }
+}
+
+/**
+ * A simple tuple of (time stamp, latitude, longitude, altitude), plus optional
+ * extras.
+ *
+ * {@hide}
+ */
+class Waypoint {
+ public Location mLocation;
+
+ public Waypoint(Location location) {
+ mLocation = location;
+ }
+
+ public Waypoint(String providerName, long time, double latitude, double longitude,
+ double altitude) {
+ mLocation = new Location(providerName);
+ mLocation.setTime(time);
+ mLocation.setLatitude(latitude);
+ mLocation.setLongitude(longitude);
+ mLocation.setAltitude(altitude);
+ }
+
+ public long getTime() {
+ return mLocation.getTime();
+ }
+
+ public double getLatitude() {
+ return mLocation.getLatitude();
+ }
+
+ public double getLongitude() {
+ return mLocation.getLongitude();
+ }
+
+ public boolean hasAltitude() {
+ return mLocation.hasAltitude();
+ }
+
+ public double getAltitude() {
+ return mLocation.getAltitude();
+ }
+
+ public boolean hasBearing() {
+ return mLocation.hasBearing();
+ }
+
+ public void setBearing(float bearing) {
+ mLocation.setBearing(bearing);
+ }
+
+ public float getBearing() {
+ return mLocation.getBearing();
+ }
+
+ public boolean hasSpeed() {
+ return mLocation.hasSpeed();
+ }
+
+ public void setSpeed(float speed) {
+ mLocation.setSpeed(speed);
+ }
+
+ public float getSpeed() {
+ return mLocation.getSpeed();
+ }
+
+ public Bundle getExtras() {
+ return mLocation.getExtras();
+ }
+
+ public Location getLocation() {
+ return new Location(mLocation);
+ }
+
+ @Override public String toString() {
+ return "Waypoint[mLocation=" + mLocation + "]";
+ }
+}
diff --git a/location/java/com/android/internal/location/protocol/GAddress.java b/location/java/com/android/internal/location/protocol/GAddress.java
new file mode 100644
index 0000000..86a3912
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GAddress.java
@@ -0,0 +1,23 @@
+/*
+ * 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.protocol;
+
+public interface GAddress {
+ static final int FORMATTED_ADDRESS_LINE = 1;
+ static final int COMPONENT = 2;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GAddressComponent.java b/location/java/com/android/internal/location/protocol/GAddressComponent.java
new file mode 100644
index 0000000..a06a23d
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GAddressComponent.java
@@ -0,0 +1,23 @@
+/*
+ * 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.protocol;
+
+public interface GAddressComponent {
+ static final int NAME = 1;
+ static final int FEATURE_TYPE = 2;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GAppProfile.java b/location/java/com/android/internal/location/protocol/GAppProfile.java
new file mode 100644
index 0000000..e3332eb
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GAppProfile.java
@@ -0,0 +1,26 @@
+/*
+ * 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.protocol;
+
+public interface GAppProfile {
+ static final int APP_NAME = 1;
+ static final int APP_KEY = 2;
+ static final int REQUEST_TYPE = 3;
+ static final int SEARCH_TYPE = 4;
+ static final int SEARCH_TERM = 5;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GCell.java b/location/java/com/android/internal/location/protocol/GCell.java
new file mode 100644
index 0000000..21d1c48
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GCell.java
@@ -0,0 +1,29 @@
+/*
+ * 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.protocol;
+
+public interface GCell {
+ static final int LAC = 1;
+ static final int CELLID = 2;
+ static final int MNC = 3;
+ static final int MCC = 4;
+ static final int RSSI = 5;
+ static final int AGE = 6;
+ static final int TIMING_ADVANCE = 7;
+ static final int PRIMARY_SCRAMBLING_CODE = 8;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GCellularPlatformProfile.java b/location/java/com/android/internal/location/protocol/GCellularPlatformProfile.java
new file mode 100644
index 0000000..a17da20
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GCellularPlatformProfile.java
@@ -0,0 +1,30 @@
+/*
+ * 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.protocol;
+
+public interface GCellularPlatformProfile {
+ static final int RADIO_TYPE_GPRS = 3;
+ static final int RADIO_TYPE_CDMA = 4;
+ static final int RADIO_TYPE_WCDMA = 5;
+
+ static final int RADIO_TYPE = 1;
+ static final int CARRIER = 2;
+ static final int IP = 3;
+ static final int HOME_MNC = 4;
+ static final int HOME_MCC = 5;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GCellularProfile.java b/location/java/com/android/internal/location/protocol/GCellularProfile.java
new file mode 100644
index 0000000..8c85bf7
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GCellularProfile.java
@@ -0,0 +1,26 @@
+/*
+ * 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.protocol;
+
+public interface GCellularProfile {
+ static final int PRIMARY_CELL = 1;
+ static final int TIMESTAMP = 2;
+ static final int NEIGHBORS = 3;
+ static final int HISTORICAL_CELLS = 4;
+ static final int PREFETCH_MODE = 5;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GDebugProfile.java b/location/java/com/android/internal/location/protocol/GDebugProfile.java
new file mode 100644
index 0000000..e5c9fe6
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GDebugProfile.java
@@ -0,0 +1,36 @@
+/*
+ * 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.protocol;
+
+public interface GDebugProfile {
+ static final int TRIGGER_CELL_CHANGE = 1;
+ static final int TRIGGER_WIFI_CHANGE = 2;
+ static final int TRIGGER_CELL_AND_WIFI_CHANGE = 3;
+ static final int TRIGGER_GPS_CHANGE = 4;
+ static final int TRIGGER_OTHER = 5;
+ static final int TRIGGER_COLLECTION_START_BURST = 6;
+ static final int TRIGGER_COLLECTION_RESTART_BURST = 7;
+ static final int TRIGGER_COLLECTION_CONTINUE_BURST = 8;
+ static final int TRIGGER_COLLECTION_END_BURST = 9;
+ static final int TRIGGER_COLLECTION_END_BURST_AT_SAME_LOCATION = 10;
+ static final int TRIGGER_COLLECTION_MOVED_DISTANCE = 11;
+
+ static final int TRIGGER = 1;
+ static final int ACTUAL_REQUEST = 2;
+ static final int CACHE_LOCATION = 3;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GDeviceLocation.java b/location/java/com/android/internal/location/protocol/GDeviceLocation.java
new file mode 100644
index 0000000..462ab07
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GDeviceLocation.java
@@ -0,0 +1,24 @@
+/*
+ * 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.protocol;
+
+public interface GDeviceLocation {
+ static final int LOCATION = 1;
+ static final int CELL = 2;
+ static final int WIFI_DEVICE = 3;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GFeature.java b/location/java/com/android/internal/location/protocol/GFeature.java
new file mode 100644
index 0000000..73fc1b3
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GFeature.java
@@ -0,0 +1,38 @@
+/*
+ * 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.protocol;
+
+public interface GFeature {
+ static final int NAME = 1;
+ static final int FEATURE_TYPE = 2;
+ static final int ADDRESS = 3;
+ static final int BOUNDS = 4;
+ static final int CENTER = 5;
+
+ static final int FEATURE_TYPE_UNKNOWN_TYPE = 0;
+ static final int FEATURE_TYPE_COUNTRY = 1;
+ static final int FEATURE_TYPE_COUNTRY_CODE = 2;
+ static final int FEATURE_TYPE_ADMINISTRATIVE_AREA = 3;
+ static final int FEATURE_TYPE_SUB_ADMINISTRATIVE_AREA = 4;
+ static final int FEATURE_TYPE_LOCALITY = 5;
+ static final int FEATURE_TYPE_SUB_LOCALITY = 6;
+ static final int FEATURE_TYPE_PREMISES = 7;
+ static final int FEATURE_TYPE_THOROUGHFARE = 8;
+ static final int FEATURE_TYPE_SUB_THOROUGHFARE = 9;
+ static final int FEATURE_TYPE_POST_CODE = 10;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GGeocodeRequest.java b/location/java/com/android/internal/location/protocol/GGeocodeRequest.java
new file mode 100644
index 0000000..4d56cc0
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GGeocodeRequest.java
@@ -0,0 +1,24 @@
+/*
+ * 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.protocol;
+
+public interface GGeocodeRequest {
+ static final int NUM_FEATURE_LIMIT = 1;
+ static final int INCLUDE_BOUNDING_BOXES = 2;
+ static final int BOUNDING_BOX = 3;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GGpsProfile.java b/location/java/com/android/internal/location/protocol/GGpsProfile.java
new file mode 100644
index 0000000..be69eb0
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GGpsProfile.java
@@ -0,0 +1,29 @@
+/*
+ * 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.protocol;
+
+public interface GGpsProfile {
+ static final int FIX_QUALITY_INVALID = 0;
+ static final int FIX_QUALITY_GPS_FIX = 1;
+ static final int FIX_QUALITY_DGPS_FIX = 2;
+
+ static final int GPS_FIX_TYPE = 1;
+ static final int PDOP = 2;
+ static final int HDOP = 3;
+ static final int VDOP = 4;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GLatLng.java b/location/java/com/android/internal/location/protocol/GLatLng.java
new file mode 100644
index 0000000..90e23df
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GLatLng.java
@@ -0,0 +1,23 @@
+/*
+ * 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.protocol;
+
+public interface GLatLng {
+ static final int LAT_E7 = 1;
+ static final int LNG_E7 = 2;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GLocReply.java b/location/java/com/android/internal/location/protocol/GLocReply.java
new file mode 100644
index 0000000..7a0504f
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GLocReply.java
@@ -0,0 +1,24 @@
+/*
+ * 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.protocol;
+
+public interface GLocReply {
+ static final int STATUS = 1;
+ static final int REPLY_ELEMENTS = 2;
+ static final int PLATFORM_KEY = 3;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GLocReplyElement.java b/location/java/com/android/internal/location/protocol/GLocReplyElement.java
new file mode 100644
index 0000000..bc47fcf
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GLocReplyElement.java
@@ -0,0 +1,24 @@
+/*
+ * 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.protocol;
+
+public interface GLocReplyElement {
+ static final int STATUS = 1;
+ static final int LOCATION = 2;
+ static final int DEVICE_LOCATION = 3;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GLocRequest.java b/location/java/com/android/internal/location/protocol/GLocRequest.java
new file mode 100644
index 0000000..7761c11
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GLocRequest.java
@@ -0,0 +1,26 @@
+/*
+ * 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.protocol;
+
+public interface GLocRequest {
+ static final int PLATFORM_PROFILE = 1;
+ static final int APP_PROFILES = 2;
+ static final int USER_PROFILE = 3;
+ static final int REQUEST_ELEMENTS = 4;
+ static final int MASF_CLIENT_INFO = 257;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GLocRequestElement.java b/location/java/com/android/internal/location/protocol/GLocRequestElement.java
new file mode 100644
index 0000000..d758953
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GLocRequestElement.java
@@ -0,0 +1,26 @@
+/*
+ * 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.protocol;
+
+public interface GLocRequestElement {
+ static final int CELLULAR_PROFILE = 1;
+ static final int WIFI_PROFILE = 2;
+ static final int LOCATION = 3;
+ static final int GEOCODE = 4;
+ static final int DEBUG_PROFILE = 99;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GLocation.java b/location/java/com/android/internal/location/protocol/GLocation.java
new file mode 100644
index 0000000..9a1eb1f
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GLocation.java
@@ -0,0 +1,41 @@
+/*
+ * 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.protocol;
+
+public interface GLocation {
+ static final int LOCTYPE_GPS = 0;
+ static final int LOCTYPE_MAPCENTER = 1;
+ static final int LOCTYPE_CENTROID = 2;
+ static final int LOCTYPE_TOWER_LOCATION = 3;
+
+ static final int LAT_LNG = 1;
+ static final int SOURCE = 2;
+ static final int ACCURACY = 3;
+ static final int CONFIDENCE = 4;
+ static final int FEATURE = 5;
+ static final int TIMESTAMP = 6;
+ static final int OBSOLETE = 7;
+ static final int LOC_TYPE = 8;
+ static final int MISC = 9;
+ static final int ALTITUDE = 10;
+ static final int VERTICAL_ACCURACY = 11;
+ static final int VELOCITY = 12;
+ static final int HEADING = 13;
+ static final int GPS_PROFILE = 14;
+ static final int LOCATION_STRING = 15;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GPlatformProfile.java b/location/java/com/android/internal/location/protocol/GPlatformProfile.java
new file mode 100644
index 0000000..32a1f8f
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GPlatformProfile.java
@@ -0,0 +1,28 @@
+/*
+ * 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.protocol;
+
+public interface GPlatformProfile {
+ static final int VERSION = 1;
+ static final int PLATFORM = 2;
+ static final int PLATFORM_KEY = 3;
+ static final int DISTRIBUTION_CHANNEL = 4;
+ static final int LOCALE = 5;
+ static final int CELLULAR_PLATFORM_PROFILE = 6;
+ static final int WIFI_PLATFORM_PROFILE = 7;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GPrefetchMode.java b/location/java/com/android/internal/location/protocol/GPrefetchMode.java
new file mode 100644
index 0000000..041b686
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GPrefetchMode.java
@@ -0,0 +1,25 @@
+/*
+ * 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.protocol;
+
+public interface GPrefetchMode {
+ static final int PREFETCH_MODE_NO_PREFETCH = 0;
+ static final int PREFETCH_MODE_REQUESTED_NEIGHBORS_ONLY = 1;
+ static final int PREFETCH_MODE_MORE_NEIGHBORS = 2;
+
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GRectangle.java b/location/java/com/android/internal/location/protocol/GRectangle.java
new file mode 100644
index 0000000..b8412e6
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GRectangle.java
@@ -0,0 +1,23 @@
+/*
+ * 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.protocol;
+
+public interface GRectangle {
+ static final int LOWER_LEFT = 1;
+ static final int UPPER_RIGHT = 2;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GUserProfile.java b/location/java/com/android/internal/location/protocol/GUserProfile.java
new file mode 100644
index 0000000..2ce962c
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GUserProfile.java
@@ -0,0 +1,23 @@
+/*
+ * 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.protocol;
+
+public interface GUserProfile {
+ static final int USER_NAME = 1;
+ static final int AUTH_TOKEN = 2;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GWifiDevice.java b/location/java/com/android/internal/location/protocol/GWifiDevice.java
new file mode 100644
index 0000000..62bd03a
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GWifiDevice.java
@@ -0,0 +1,26 @@
+/*
+ * 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.protocol;
+
+public interface GWifiDevice {
+ static final int MAC = 1;
+ static final int SSID = 2;
+ static final int CHANNEL = 3;
+ static final int RSSI = 4;
+ static final int NOISE = 5;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GWifiPlatformProfile.java b/location/java/com/android/internal/location/protocol/GWifiPlatformProfile.java
new file mode 100644
index 0000000..7f1efcb
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GWifiPlatformProfile.java
@@ -0,0 +1,29 @@
+/*
+ * 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.protocol;
+
+public interface GWifiPlatformProfile {
+ static final int RADIO_TYPE_WIFI802_11_A = 1;
+ static final int RADIO_TYPE_WIFI802_11_B = 2;
+ static final int RADIO_TYPE_WIFI802_11_G = 3;
+ static final int RADIO_TYPE_WIFI802_11_N = 4;
+
+ static final int SCANNER_MAC = 1;
+ static final int SCANNER_IP = 2;
+ static final int RADIO_TYPE = 3;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GWifiProfile.java b/location/java/com/android/internal/location/protocol/GWifiProfile.java
new file mode 100644
index 0000000..e731027
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GWifiProfile.java
@@ -0,0 +1,24 @@
+/*
+ * 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.protocol;
+
+public interface GWifiProfile {
+ static final int TIMESTAMP = 1;
+ static final int WIFI_DEVICES = 2;
+ static final int PREFETCH_MODE = 3;
+}
+
diff --git a/location/java/com/android/internal/location/protocol/GaddressMessageTypes.java b/location/java/com/android/internal/location/protocol/GaddressMessageTypes.java
new file mode 100644
index 0000000..7b6ffd0
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GaddressMessageTypes.java
@@ -0,0 +1,39 @@
+/*
+ * 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.protocol;
+
+import com.google.common.io.protocol.ProtoBufType;
+
+public class GaddressMessageTypes {
+ public static final ProtoBufType GADDRESS = new ProtoBufType();
+ public static final ProtoBufType GADDRESS_COMPONENT = new ProtoBufType();
+
+ static {
+ GADDRESS
+ .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_DATA,
+ GAddress.FORMATTED_ADDRESS_LINE, null)
+ .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE,
+ GAddress.COMPONENT, GADDRESS_COMPONENT);
+
+ GADDRESS_COMPONENT
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_DATA,
+ GAddressComponent.NAME, null)
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT32,
+ GAddressComponent.FEATURE_TYPE, null);
+
+ }
+}
diff --git a/location/java/com/android/internal/location/protocol/GcellularMessageTypes.java b/location/java/com/android/internal/location/protocol/GcellularMessageTypes.java
new file mode 100644
index 0000000..37a6d52
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GcellularMessageTypes.java
@@ -0,0 +1,57 @@
+/*
+ * 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.protocol;
+
+import com.google.common.io.protocol.ProtoBufType;
+
+public class GcellularMessageTypes {
+ public static final ProtoBufType GCELL = new ProtoBufType();
+ public static final ProtoBufType GCELLULAR_PROFILE = new ProtoBufType();
+
+ static {
+ GCELL
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT32,
+ GCell.LAC, null)
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT32,
+ GCell.CELLID, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GCell.MNC, new Long(-1))
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GCell.MCC, new Long(-1))
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GCell.RSSI, new Long(-9999))
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GCell.AGE, new Long(0))
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GCell.TIMING_ADVANCE, new Long(-1))
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GCell.PRIMARY_SCRAMBLING_CODE, null);
+
+ GCELLULAR_PROFILE
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_MESSAGE,
+ GCellularProfile.PRIMARY_CELL, GCELL)
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT64,
+ GCellularProfile.TIMESTAMP, null)
+ .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE,
+ GCellularProfile.NEIGHBORS, GCELL)
+ .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE,
+ GCellularProfile.HISTORICAL_CELLS, GCELL)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GCellularProfile.PREFETCH_MODE, null);
+
+ }
+}
diff --git a/location/java/com/android/internal/location/protocol/GdebugprofileMessageTypes.java b/location/java/com/android/internal/location/protocol/GdebugprofileMessageTypes.java
new file mode 100644
index 0000000..faf5b89
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GdebugprofileMessageTypes.java
@@ -0,0 +1,35 @@
+/*
+ * 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.protocol;
+
+import com.google.common.io.protocol.ProtoBuf;
+import com.google.common.io.protocol.ProtoBufType;
+
+public class GdebugprofileMessageTypes {
+ public static final ProtoBufType GDEBUG_PROFILE = new ProtoBufType();
+
+ static {
+ GDEBUG_PROFILE
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GDebugProfile.TRIGGER, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_BOOL,
+ GDebugProfile.ACTUAL_REQUEST, ProtoBuf.TRUE)
+ .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE,
+ GDebugProfile.CACHE_LOCATION, GlocationMessageTypes.GDEVICE_LOCATION);
+
+ }
+}
diff --git a/location/java/com/android/internal/location/protocol/GfeatureMessageTypes.java b/location/java/com/android/internal/location/protocol/GfeatureMessageTypes.java
new file mode 100644
index 0000000..24b182a
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GfeatureMessageTypes.java
@@ -0,0 +1,39 @@
+/*
+ * 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.protocol;
+
+import com.google.common.io.protocol.ProtoBufType;
+
+public class GfeatureMessageTypes {
+ public static final ProtoBufType GFEATURE_TYPE = new ProtoBufType();
+ public static final ProtoBufType GFEATURE = new ProtoBufType();
+
+ static {
+ GFEATURE
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_DATA,
+ GFeature.NAME, null)
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT32,
+ GFeature.FEATURE_TYPE, null)
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_MESSAGE,
+ GFeature.ADDRESS, GaddressMessageTypes.GADDRESS)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GFeature.BOUNDS, GrectangleMessageTypes.GRECTANGLE)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GFeature.CENTER, GlatlngMessageTypes.GLAT_LNG);
+
+ }
+}
diff --git a/location/java/com/android/internal/location/protocol/GlatlngMessageTypes.java b/location/java/com/android/internal/location/protocol/GlatlngMessageTypes.java
new file mode 100644
index 0000000..b6a9086
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GlatlngMessageTypes.java
@@ -0,0 +1,32 @@
+/*
+ * 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.protocol;
+
+import com.google.common.io.protocol.ProtoBufType;
+
+public class GlatlngMessageTypes {
+ public static final ProtoBufType GLAT_LNG = new ProtoBufType();
+
+ static {
+ GLAT_LNG
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_FIXED32,
+ GLatLng.LAT_E7, null)
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_FIXED32,
+ GLatLng.LNG_E7, null);
+
+ }
+}
diff --git a/location/java/com/android/internal/location/protocol/GlocationMessageTypes.java b/location/java/com/android/internal/location/protocol/GlocationMessageTypes.java
new file mode 100644
index 0000000..067d47c
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GlocationMessageTypes.java
@@ -0,0 +1,138 @@
+/*
+ * 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.protocol;
+
+import com.google.common.io.protocol.ProtoBufType;
+
+public class GlocationMessageTypes {
+ public static final ProtoBufType GGPS_PROFILE = new ProtoBufType();
+ public static final ProtoBufType GLOCATION = new ProtoBufType();
+ public static final ProtoBufType GDEVICE_LOCATION = new ProtoBufType();
+ public static final ProtoBufType GCELLULAR_PLATFORM_PROFILE = new ProtoBufType();
+ public static final ProtoBufType GWIFI_PLATFORM_PROFILE = new ProtoBufType();
+ public static final ProtoBufType GPREFETCH_MODE = new ProtoBufType();
+ public static final ProtoBufType GPLATFORM_PROFILE = new ProtoBufType();
+ public static final ProtoBufType GAPP_PROFILE = new ProtoBufType();
+ public static final ProtoBufType GUSER_PROFILE = new ProtoBufType();
+
+ static {
+ GGPS_PROFILE
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GGpsProfile.GPS_FIX_TYPE, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GGpsProfile.PDOP, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GGpsProfile.HDOP, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GGpsProfile.VDOP, null);
+
+ GLOCATION
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GLocation.LAT_LNG, GlatlngMessageTypes.GLAT_LNG)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GLocation.SOURCE, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GLocation.ACCURACY, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GLocation.CONFIDENCE, null)
+ .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE,
+ GLocation.FEATURE, GfeatureMessageTypes.GFEATURE)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT64,
+ GLocation.TIMESTAMP, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_BOOL,
+ GLocation.OBSOLETE, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GLocation.LOC_TYPE, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GLocation.MISC, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GLocation.ALTITUDE, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GLocation.VERTICAL_ACCURACY, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GLocation.VELOCITY, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GLocation.HEADING, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GLocation.GPS_PROFILE, GGPS_PROFILE)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GLocation.LOCATION_STRING, null);
+
+ GDEVICE_LOCATION
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GDeviceLocation.LOCATION, GLOCATION)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GDeviceLocation.CELL, GcellularMessageTypes.GCELL)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GDeviceLocation.WIFI_DEVICE, GwifiMessageTypes.GWIFI_DEVICE);
+
+ GCELLULAR_PLATFORM_PROFILE
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GCellularPlatformProfile.RADIO_TYPE, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GCellularPlatformProfile.CARRIER, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GCellularPlatformProfile.IP, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GCellularPlatformProfile.HOME_MNC, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GCellularPlatformProfile.HOME_MCC, null);
+
+ GWIFI_PLATFORM_PROFILE
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GWifiPlatformProfile.SCANNER_MAC, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GWifiPlatformProfile.SCANNER_IP, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GWifiPlatformProfile.RADIO_TYPE, null);
+
+ GPLATFORM_PROFILE
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_DATA,
+ GPlatformProfile.VERSION, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GPlatformProfile.PLATFORM, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GPlatformProfile.PLATFORM_KEY, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GPlatformProfile.DISTRIBUTION_CHANNEL, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GPlatformProfile.LOCALE, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GPlatformProfile.CELLULAR_PLATFORM_PROFILE, GCELLULAR_PLATFORM_PROFILE)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GPlatformProfile.WIFI_PLATFORM_PROFILE, GWIFI_PLATFORM_PROFILE);
+
+ GAPP_PROFILE
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GAppProfile.APP_NAME, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GAppProfile.APP_KEY, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GAppProfile.REQUEST_TYPE, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GAppProfile.SEARCH_TYPE, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GAppProfile.SEARCH_TERM, null);
+
+ GUSER_PROFILE
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GUserProfile.USER_NAME, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GUserProfile.AUTH_TOKEN, null);
+
+ }
+}
diff --git a/location/java/com/android/internal/location/protocol/GrectangleMessageTypes.java b/location/java/com/android/internal/location/protocol/GrectangleMessageTypes.java
new file mode 100644
index 0000000..aeb0047
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GrectangleMessageTypes.java
@@ -0,0 +1,32 @@
+/*
+ * 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.protocol;
+
+import com.google.common.io.protocol.ProtoBufType;
+
+public class GrectangleMessageTypes {
+ public static final ProtoBufType GRECTANGLE = new ProtoBufType();
+
+ static {
+ GRECTANGLE
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_MESSAGE,
+ GRectangle.LOWER_LEFT, GlatlngMessageTypes.GLAT_LNG)
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_MESSAGE,
+ GRectangle.UPPER_RIGHT, GlatlngMessageTypes.GLAT_LNG);
+
+ }
+}
diff --git a/location/java/com/android/internal/location/protocol/GwifiMessageTypes.java b/location/java/com/android/internal/location/protocol/GwifiMessageTypes.java
new file mode 100644
index 0000000..cd7119b
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/GwifiMessageTypes.java
@@ -0,0 +1,47 @@
+/*
+ * 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.protocol;
+
+import com.google.common.io.protocol.ProtoBufType;
+
+public class GwifiMessageTypes {
+ public static final ProtoBufType GWIFI_DEVICE = new ProtoBufType();
+ public static final ProtoBufType GWIFI_PROFILE = new ProtoBufType();
+
+ static {
+ GWIFI_DEVICE
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_DATA,
+ GWifiDevice.MAC, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GWifiDevice.SSID, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GWifiDevice.CHANNEL, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GWifiDevice.RSSI, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GWifiDevice.NOISE, null);
+
+ GWIFI_PROFILE
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT64,
+ GWifiProfile.TIMESTAMP, null)
+ .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE,
+ GWifiProfile.WIFI_DEVICES, GWIFI_DEVICE)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_INT32,
+ GWifiProfile.PREFETCH_MODE, null);
+
+ }
+}
diff --git a/location/java/com/android/internal/location/protocol/LocserverMessageTypes.java b/location/java/com/android/internal/location/protocol/LocserverMessageTypes.java
new file mode 100644
index 0000000..8ffd004
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/LocserverMessageTypes.java
@@ -0,0 +1,80 @@
+/*
+ * 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.protocol;
+
+import com.google.common.io.protocol.ProtoBuf;
+import com.google.common.io.protocol.ProtoBufType;
+
+public class LocserverMessageTypes {
+ public static final ProtoBufType RESPONSE_CODES = new ProtoBufType();
+ public static final ProtoBufType GLOC_REQUEST_ELEMENT = new ProtoBufType();
+ public static final ProtoBufType GLOC_REQUEST = new ProtoBufType();
+ public static final ProtoBufType GGEOCODE_REQUEST = new ProtoBufType();
+ public static final ProtoBufType GLOC_REPLY_ELEMENT = new ProtoBufType();
+ public static final ProtoBufType GLOC_REPLY = new ProtoBufType();
+
+ static {
+ GLOC_REQUEST_ELEMENT
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GLocRequestElement.CELLULAR_PROFILE, GcellularMessageTypes.GCELLULAR_PROFILE)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GLocRequestElement.WIFI_PROFILE, GwifiMessageTypes.GWIFI_PROFILE)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GLocRequestElement.LOCATION, GlocationMessageTypes.GLOCATION)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GLocRequestElement.GEOCODE, GGEOCODE_REQUEST)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GLocRequestElement.DEBUG_PROFILE, GdebugprofileMessageTypes.GDEBUG_PROFILE);
+
+ GLOC_REQUEST
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_MESSAGE,
+ GLocRequest.PLATFORM_PROFILE, GlocationMessageTypes.GPLATFORM_PROFILE)
+ .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE,
+ GLocRequest.APP_PROFILES, GlocationMessageTypes.GAPP_PROFILE)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GLocRequest.USER_PROFILE, GlocationMessageTypes.GUSER_PROFILE)
+ .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE,
+ GLocRequest.REQUEST_ELEMENTS, GLOC_REQUEST_ELEMENT)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GLocRequest.MASF_CLIENT_INFO, null);
+
+ GGEOCODE_REQUEST
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_FIXED32,
+ GGeocodeRequest.NUM_FEATURE_LIMIT, new Long(1))
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_BOOL,
+ GGeocodeRequest.INCLUDE_BOUNDING_BOXES, ProtoBuf.FALSE)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GGeocodeRequest.BOUNDING_BOX, GrectangleMessageTypes.GRECTANGLE);
+
+ GLOC_REPLY_ELEMENT
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT32,
+ GLocReplyElement.STATUS, null)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_MESSAGE,
+ GLocReplyElement.LOCATION, GlocationMessageTypes.GLOCATION)
+ .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE,
+ GLocReplyElement.DEVICE_LOCATION, GlocationMessageTypes.GDEVICE_LOCATION);
+
+ GLOC_REPLY
+ .addElement(ProtoBufType.REQUIRED | ProtoBufType.TYPE_INT32,
+ GLocReply.STATUS, null)
+ .addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE,
+ GLocReply.REPLY_ELEMENTS, GLOC_REPLY_ELEMENT)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_DATA,
+ GLocReply.PLATFORM_KEY, null);
+
+ }
+}
diff --git a/location/java/com/android/internal/location/protocol/ResponseCodes.java b/location/java/com/android/internal/location/protocol/ResponseCodes.java
new file mode 100644
index 0000000..2ea9318
--- /dev/null
+++ b/location/java/com/android/internal/location/protocol/ResponseCodes.java
@@ -0,0 +1,45 @@
+/*
+ * 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.protocol;
+
+public interface ResponseCodes {
+ static final int STATUS_STATUS_SUCCESS = 0;
+ static final int STATUS_STATUS_FAILED = 1;
+ static final int STATUS_AUTHORIZATION_REJECTED = 2;
+ static final int STATUS_NO_SOURCE_EXISTS = 3;
+ static final int STATUS_SIGNAL_TOO_WEAK = 4;
+ static final int STATUS_INVALID_REQUEST = 5;
+ static final int STATUS_INVALID_NUM_REQUESTS = 6;
+ static final int STATUS_INVALID_USERLOCATION_FORMAT = 7;
+ static final int STATUS_INVALID_OPERATION_CODE = 8;
+ static final int STATUS_INVALID_MAC_STRING_FORMAT = 9;
+ static final int STATUS_INVALID_CELLID_STRING_FORMAT = 10;
+ static final int STATUS_NON_EXISTENT_AP = 11;
+ static final int STATUS_NON_EXISTENT_CELLID = 12;
+ static final int STATUS_STATUS_FAILED_NO_SOURCE = 13;
+ static final int STATUS_STATUS_FAILED_NO_SAVE = 14;
+ static final int STATUS_PLATFORM_KEY_EXPIRED = 15;
+ static final int STATUS_NO_STORE_EXISTS = 16;
+ static final int STATUS_NO_CELLIDDATA_FOR_UPDATE = 17;
+ static final int STATUS_NON_SUPPORTED_OPERATION_IN_UPDATE = 18;
+ static final int STATUS_NON_SUPPORTED_OPERATION = 19;
+ static final int STATUS_STATUS_FAILED_NO_GEOCODE = 20;
+ static final int STATUS_BLACKLISTED_IP_CELLID = 100;
+ static final int STATUS_BLACKLISTED_IP_WIFI = 101;
+
+}
+