summaryrefslogtreecommitdiffstats
path: root/location/java/com
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/com
downloadframeworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.zip
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.gz
frameworks_base-54b6cfa9a9e5b861a9930af873580d6dc20f773c.tar.bz2
Initial Contribution
Diffstat (limited to 'location/java/com')
-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
44 files changed, 6346 insertions, 0 deletions
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;
+
+}
+