summaryrefslogtreecommitdiffstats
path: root/location/java
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2009-11-12 18:45:53 -0800
committerJean-Baptiste Queru <jbq@google.com>2009-11-13 13:53:39 -0800
commit9db3d07b9620b4269ab33f78604a36327e536ce1 (patch)
tree41e294f34b9695187af098cd42167489fb0c8fb0 /location/java
parent6c63ee4fc4acae4bbbbd2a49e0a68206221f0de0 (diff)
downloadframeworks_base-9db3d07b9620b4269ab33f78604a36327e536ce1.zip
frameworks_base-9db3d07b9620b4269ab33f78604a36327e536ce1.tar.gz
frameworks_base-9db3d07b9620b4269ab33f78604a36327e536ce1.tar.bz2
eclair snapshot
Diffstat (limited to 'location/java')
-rw-r--r--location/java/android/location/Geocoder.java6
-rw-r--r--location/java/android/location/GpsStatus.java12
-rw-r--r--location/java/android/location/IGpsStatusListener.aidl1
-rw-r--r--location/java/android/location/ILocationManager.aidl3
-rw-r--r--location/java/android/location/ILocationProvider.aidl3
-rwxr-xr-xlocation/java/android/location/INetInitiatedListener.aidl26
-rw-r--r--location/java/android/location/LocationManager.java163
-rwxr-xr-xlocation/java/com/android/internal/location/GpsLocationProvider.java213
-rwxr-xr-xlocation/java/com/android/internal/location/GpsNetInitiatedHandler.java457
-rw-r--r--location/java/com/android/internal/location/GpsXtraDownloader.java1
-rw-r--r--location/java/com/android/internal/location/LocationProviderProxy.java5
-rw-r--r--location/java/com/android/internal/location/MockProvider.java3
12 files changed, 826 insertions, 67 deletions
diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java
index 53e46b7..2ce1273 100644
--- a/location/java/android/location/Geocoder.java
+++ b/location/java/android/location/Geocoder.java
@@ -36,11 +36,11 @@ import java.util.List;
* coordinate into a (partial) address. The amount of detail in a
* reverse geocoded location description may vary, for example one
* might contain the full street address of the closest building, while
- * another might contain only a city name and postal code.
+ * another might contain only a city name and postal code.
*
* The Geocoder class requires a backend service that is not included in
- * the core android framework. The Geocoder query methods will return an
- * empty list if there no backend service in the platform.
+ * the core android framework. The Geocoder query methods will return an
+ * empty list if there no backend service in the platform.
*/
public final class Geocoder {
private static final String TAG = "Geocoder";
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 2cda7fa..ce69ac1 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -115,6 +115,18 @@ public final class GpsStatus {
void onGpsStatusChanged(int event);
}
+ /**
+ * Used for receiving NMEA sentences from the GPS.
+ * NMEA 0183 is a standard for communicating with marine electronic devices
+ * and is a common method for receiving data from a GPS, typically over a serial port.
+ * See <a href="http://en.wikipedia.org/wiki/NMEA_0183">NMEA 0183</a> for more details.
+ * You can implement this interface and call {@link LocationManager#addNmeaListener}
+ * to receive NMEA data from the GPS engine.
+ */
+ public interface NmeaListener {
+ void onNmeaReceived(long timestamp, String nmea);
+ }
+
GpsStatus() {
for (int i = 0; i < mSatellites.length; i++) {
mSatellites[i] = new GpsSatellite(i + 1);
diff --git a/location/java/android/location/IGpsStatusListener.aidl b/location/java/android/location/IGpsStatusListener.aidl
index 5dc0fe8..62b1c6b 100644
--- a/location/java/android/location/IGpsStatusListener.aidl
+++ b/location/java/android/location/IGpsStatusListener.aidl
@@ -29,4 +29,5 @@ oneway interface IGpsStatusListener
void onSvStatusChanged(int svCount, in int[] prns, in float[] snrs,
in float[] elevations, in float[] azimuths,
int ephemerisMask, int almanacMask, int usedInFixMask);
+ void onNmeaReceived(long timestamp, String nmea);
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index caf9516..b6c59d6 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -83,4 +83,7 @@ interface ILocationManager
/* for installing external Location Providers */
void installLocationProvider(String name, ILocationProvider provider);
void installGeocodeProvider(IGeocodeProvider provider);
+
+ // for NI support
+ boolean sendNiResponse(int notifId, int userResponse);
}
diff --git a/location/java/android/location/ILocationProvider.aidl b/location/java/android/location/ILocationProvider.aidl
index 4fe0494..7da16e4 100644
--- a/location/java/android/location/ILocationProvider.aidl
+++ b/location/java/android/location/ILocationProvider.aidl
@@ -17,6 +17,7 @@
package android.location;
import android.location.Location;
+import android.net.NetworkInfo;
import android.os.Bundle;
/**
@@ -41,7 +42,7 @@ interface ILocationProvider {
long getStatusUpdateTime();
void enableLocationTracking(boolean enable);
void setMinTime(long minTime);
- void updateNetworkState(int state);
+ void updateNetworkState(int state, in NetworkInfo info);
void updateLocation(in Location location);
boolean sendExtraCommand(String command, inout Bundle extras);
void addListener(int uid);
diff --git a/location/java/android/location/INetInitiatedListener.aidl b/location/java/android/location/INetInitiatedListener.aidl
new file mode 100755
index 0000000..f2f5a32
--- /dev/null
+++ b/location/java/android/location/INetInitiatedListener.aidl
@@ -0,0 +1,26 @@
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.location;
+
+/**
+ * {@hide}
+ */
+interface INetInitiatedListener
+{
+ boolean sendNiResponse(int notifId, int userResponse);
+}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 86ea66f..94ced22 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -51,6 +51,8 @@ public class LocationManager {
private ILocationManager mService;
private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners =
new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>();
+ private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners =
+ new HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport>();
private final GpsStatus mGpsStatus = new GpsStatus();
/**
@@ -68,7 +70,7 @@ public class LocationManager {
* satellites. Depending on conditions, this provider may take a while to return
* a location fix.
*
- * Requires the permission android.permissions.ACCESS_FINE_LOCATION.
+ * Requires the permission android.permission.ACCESS_FINE_LOCATION.
*
* <p> The extras Bundle for the GPS location provider can contain the
* following key/value pairs:
@@ -103,9 +105,6 @@ public class LocationManager {
*/
public static final String KEY_LOCATION_CHANGED = "location";
- /** @hide */
- public static final String SYSTEM_DIR = "/data/system/location";
-
// Map from LocationListeners to their associated ListenerTransport objects
private HashMap<LocationListener,ListenerTransport> mListeners =
new HashMap<LocationListener,ListenerTransport>();
@@ -1123,49 +1122,103 @@ public class LocationManager {
private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
private final GpsStatus.Listener mListener;
+ private final GpsStatus.NmeaListener mNmeaListener;
+
+ // This must not equal any of the GpsStatus event IDs
+ private static final int NMEA_RECEIVED = 1000;
+
+ private class Nmea {
+ long mTimestamp;
+ String mNmea;
+
+ Nmea(long timestamp, String nmea) {
+ mTimestamp = timestamp;
+ mNmea = nmea;
+ }
+ }
+ private ArrayList<Nmea> mNmeaBuffer;
GpsStatusListenerTransport(GpsStatus.Listener listener) {
mListener = listener;
+ mNmeaListener = null;
+ }
+
+ GpsStatusListenerTransport(GpsStatus.NmeaListener listener) {
+ mNmeaListener = listener;
+ mListener = null;
+ mNmeaBuffer = new ArrayList<Nmea>();
}
public void onGpsStarted() {
- Message msg = Message.obtain();
- msg.what = GpsStatus.GPS_EVENT_STARTED;
- mGpsHandler.sendMessage(msg);
+ if (mListener != null) {
+ Message msg = Message.obtain();
+ msg.what = GpsStatus.GPS_EVENT_STARTED;
+ mGpsHandler.sendMessage(msg);
+ }
}
public void onGpsStopped() {
- Message msg = Message.obtain();
- msg.what = GpsStatus.GPS_EVENT_STOPPED;
- mGpsHandler.sendMessage(msg);
+ if (mListener != null) {
+ Message msg = Message.obtain();
+ msg.what = GpsStatus.GPS_EVENT_STOPPED;
+ mGpsHandler.sendMessage(msg);
+ }
}
public void onFirstFix(int ttff) {
- mGpsStatus.setTimeToFirstFix(ttff);
- Message msg = Message.obtain();
- msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
- mGpsHandler.sendMessage(msg);
+ if (mListener != null) {
+ mGpsStatus.setTimeToFirstFix(ttff);
+ Message msg = Message.obtain();
+ msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
+ mGpsHandler.sendMessage(msg);
+ }
}
public void onSvStatusChanged(int svCount, int[] prns, float[] snrs,
float[] elevations, float[] azimuths, int ephemerisMask,
int almanacMask, int usedInFixMask) {
- mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
- ephemerisMask, almanacMask, usedInFixMask);
+ if (mListener != null) {
+ mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
+ ephemerisMask, almanacMask, usedInFixMask);
+
+ Message msg = Message.obtain();
+ msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
+ // remove any SV status messages already in the queue
+ mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
+ mGpsHandler.sendMessage(msg);
+ }
+ }
- Message msg = Message.obtain();
- msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
- // remove any SV status messages already in the queue
- mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
- mGpsHandler.sendMessage(msg);
+ public void onNmeaReceived(long timestamp, String nmea) {
+ if (mNmeaListener != null) {
+ synchronized (mNmeaBuffer) {
+ mNmeaBuffer.add(new Nmea(timestamp, nmea));
+ }
+ Message msg = Message.obtain();
+ msg.what = NMEA_RECEIVED;
+ // remove any NMEA_RECEIVED messages already in the queue
+ mGpsHandler.removeMessages(NMEA_RECEIVED);
+ mGpsHandler.sendMessage(msg);
+ }
}
private final Handler mGpsHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- // synchronize on mGpsStatus to ensure the data is copied atomically.
- synchronized(mGpsStatus) {
- mListener.onGpsStatusChanged(msg.what);
+ if (msg.what == NMEA_RECEIVED) {
+ synchronized (mNmeaBuffer) {
+ int length = mNmeaBuffer.size();
+ for (int i = 0; i < length; i++) {
+ Nmea nmea = mNmeaBuffer.get(i);
+ mNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
+ }
+ mNmeaBuffer.clear();
+ }
+ } else {
+ // synchronize on mGpsStatus to ensure the data is copied atomically.
+ synchronized(mGpsStatus) {
+ mListener.onGpsStatusChanged(msg.what);
+ }
}
}
};
@@ -1217,6 +1270,52 @@ public class LocationManager {
}
}
+ /**
+ * Adds an NMEA listener.
+ *
+ * @param listener a {#link GpsStatus.NmeaListener} object to register
+ *
+ * @return true if the listener was successfully added
+ *
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
+ boolean result;
+
+ if (mNmeaListeners.get(listener) != null) {
+ // listener is already registered
+ return true;
+ }
+ try {
+ GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
+ result = mService.addGpsStatusListener(transport);
+ if (result) {
+ mNmeaListeners.put(listener, transport);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
+ result = false;
+ }
+
+ return result;
+ }
+
+ /**
+ * Removes an NMEA listener.
+ *
+ * @param listener a {#link GpsStatus.NmeaListener} object to remove
+ */
+ public void removeNmeaListener(GpsStatus.NmeaListener listener) {
+ try {
+ GpsStatusListenerTransport transport = mNmeaListeners.remove(listener);
+ if (transport != null) {
+ mService.removeGpsStatusListener(transport);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
+ }
+ }
+
/**
* Retrieves information about the current status of the GPS engine.
* This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
@@ -1315,4 +1414,20 @@ public class LocationManager {
Log.e(TAG, "RemoteException in reportLocation: ", e);
}
}
+
+ /**
+ * Used by NetInitiatedActivity to report user response
+ * for network initiated GPS fix requests.
+ *
+ * {@hide}
+ */
+ public boolean sendNiResponse(int notifId, int userResponse) {
+ try {
+ return mService.sendNiResponse(notifId, userResponse);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in sendNiResponse: ", e);
+ return false;
+ }
+ }
+
}
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 4a51e31..cd62ed1 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -27,10 +27,12 @@ import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
import android.location.ILocationManager;
import android.location.ILocationProvider;
+import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.net.SntpClient;
import android.os.Bundle;
import android.os.IBinder;
@@ -38,21 +40,25 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.provider.Settings;
import android.util.Config;
import android.util.Log;
import android.util.SparseIntArray;
import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.location.GpsNetInitiatedHandler;
+import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.StringBufferInputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Properties;
+import java.util.Map.Entry;
/**
* A GPS implementation of LocationProvider used by LocationManager.
@@ -183,8 +189,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
// number of fixes we have received since we started navigating
private int mFixCount;
- private int mPositionMode = GPS_POSITION_MODE_STANDALONE;
-
// true if we started navigation
private boolean mStarted;
@@ -198,6 +202,10 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
// properties loaded from PROPERTIES_FILE
private Properties mProperties;
private String mNtpServer;
+ private String mSuplServerHost;
+ private int mSuplServerPort;
+ private String mC2KServerHost;
+ private int mC2KServerPort;
private final Context mContext;
private final ILocationManager mLocationManager;
@@ -211,6 +219,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
private String mAGpsApn;
private int mAGpsDataConnectionState;
private final ConnectivityManager mConnMgr;
+ private final GpsNetInitiatedHandler mNIHandler;
// Wakelocks
private final static String WAKELOCK_KEY = "GpsLocationProvider";
@@ -294,22 +303,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
} else if (action.equals(ALARM_TIMEOUT)) {
if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
hibernate();
- } else if (action.equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
- String state = intent.getStringExtra(Phone.STATE_KEY);
- String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
- String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY);
-
- if (Config.LOGD) {
- Log.d(TAG, "state: " + state + " apnName: " + apnName + " reason: " + reason);
- }
- // FIXME - might not have an APN on CDMA
- if ("CONNECTED".equals(state) && apnName != null && apnName.length() > 0) {
- mAGpsApn = apnName;
- if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
- native_agps_data_conn_open(mAGpsApn);
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
- }
- }
}
}
};
@@ -321,6 +314,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
public GpsLocationProvider(Context context, ILocationManager locationManager) {
mContext = context;
mLocationManager = locationManager;
+ mNIHandler= new GpsNetInitiatedHandler(context, this);
// Create a wake lock
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -333,7 +327,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ALARM_WAKEUP);
intentFilter.addAction(ALARM_TIMEOUT);
- intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
context.registerReceiver(mBroadcastReciever, intentFilter);
mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -349,27 +342,21 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
stream.close();
mNtpServer = mProperties.getProperty("NTP_SERVER", null);
- String host = mProperties.getProperty("SUPL_HOST");
+ mSuplServerHost = mProperties.getProperty("SUPL_HOST");
String portString = mProperties.getProperty("SUPL_PORT");
- if (host != null && portString != null) {
+ if (mSuplServerHost != null && portString != null) {
try {
- int port = Integer.parseInt(portString);
- native_set_agps_server(AGPS_TYPE_SUPL, host, port);
- // use MS-Based position mode if SUPL support is enabled
- mPositionMode = GPS_POSITION_MODE_MS_BASED;
+ mSuplServerPort = Integer.parseInt(portString);
} catch (NumberFormatException e) {
Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
}
}
- host = mProperties.getProperty("C2K_HOST");
+ mC2KServerHost = mProperties.getProperty("C2K_HOST");
portString = mProperties.getProperty("C2K_PORT");
- if (host != null && portString != null) {
+ if (mC2KServerHost != null && portString != null) {
try {
- int port = Integer.parseInt(portString);
- native_set_agps_server(AGPS_TYPE_C2K, host, port);
- // use MS-Based position mode if SUPL support is enabled
- mPositionMode = GPS_POSITION_MODE_MS_BASED;
+ mC2KServerPort = Integer.parseInt(portString);
} catch (NumberFormatException e) {
Log.e(TAG, "unable to parse C2K_PORT: " + portString);
}
@@ -387,13 +374,30 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
return true;
}
- public void updateNetworkState(int state) {
+ public void updateNetworkState(int state, NetworkInfo info) {
mNetworkAvailable = (state == LocationProvider.AVAILABLE);
if (Config.LOGD) {
- Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable"));
+ Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable")
+ + " info: " + info);
}
-
+
+ if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
+ && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
+ String apnName = info.getExtraInfo();
+ if (mNetworkAvailable && apnName != null && apnName.length() > 0) {
+ mAGpsApn = apnName;
+ if (DEBUG) Log.d(TAG, "call native_agps_data_conn_open");
+ native_agps_data_conn_open(apnName);
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
+ } else {
+ if (DEBUG) Log.d(TAG, "call native_agps_data_conn_failed");
+ mAGpsApn = null;
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
+ native_agps_data_conn_failed();
+ }
+ }
+
if (mNetworkAvailable && mNetworkThread != null && mEnabled) {
// signal the network thread when the network becomes available
mNetworkThread.signal();
@@ -499,6 +503,13 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
mEnabled = native_init();
if (mEnabled) {
+ if (mSuplServerHost != null) {
+ native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
+ }
+ if (mC2KServerHost != null) {
+ native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
+ }
+
// run event listener thread while we are enabled
mEventThread = new GpsEventThread();
mEventThread.start();
@@ -722,7 +733,15 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
if (!mStarted) {
if (DEBUG) Log.d(TAG, "startNavigating");
mStarted = true;
- if (!native_start(mPositionMode, false, mFixInterval)) {
+ int positionMode;
+ if (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) {
+ positionMode = GPS_POSITION_MODE_MS_BASED;
+ } else {
+ positionMode = GPS_POSITION_MODE_STANDALONE;
+ }
+
+ if (!native_start(positionMode, false, mFixInterval)) {
mStarted = false;
Log.e(TAG, "native_start failed in startNavigating()");
return;
@@ -1002,6 +1021,32 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
}
}
+ /**
+ * called from native code to report NMEA data received
+ */
+ private void reportNmea(int index, long timestamp) {
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ if (size > 0) {
+ // don't bother creating the String if we have no listeners
+ int length = native_read_nmea(index, mNmeaBuffer, mNmeaBuffer.length);
+ String nmea = new String(mNmeaBuffer, 0, length);
+
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ listener.mListener.onNmeaReceived(timestamp, nmea);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportNmea");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+ }
+ }
+
private void xtraDownloadRequest() {
if (Config.LOGD) Log.d(TAG, "xtraDownloadRequest");
if (mNetworkThread != null) {
@@ -1009,6 +1054,96 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
}
}
+ //=============================================================
+ // NI Client support
+ //=============================================================
+ private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
+ // Sends a response for an NI reqeust to HAL.
+ public boolean sendNiResponse(int notificationId, int userResponse)
+ {
+ // TODO Add Permission check
+
+ StringBuilder extrasBuf = new StringBuilder();
+
+ if (Config.LOGD) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
+ ", response: " + userResponse);
+
+ native_send_ni_response(notificationId, userResponse);
+
+ return true;
+ }
+ };
+
+ public INetInitiatedListener getNetInitiatedListener() {
+ return mNetInitiatedListener;
+ }
+
+ // Called by JNI function to report an NI request.
+ @SuppressWarnings("deprecation")
+ public void reportNiNotification(
+ int notificationId,
+ int niType,
+ int notifyFlags,
+ int timeout,
+ int defaultResponse,
+ String requestorId,
+ String text,
+ int requestorIdEncoding,
+ int textEncoding,
+ String extras // Encoded extra data
+ )
+ {
+ Log.i(TAG, "reportNiNotification: entered");
+ Log.i(TAG, "notificationId: " + notificationId +
+ ", niType: " + niType +
+ ", notifyFlags: " + notifyFlags +
+ ", timeout: " + timeout +
+ ", defaultResponse: " + defaultResponse);
+
+ Log.i(TAG, "requestorId: " + requestorId +
+ ", text: " + text +
+ ", requestorIdEncoding: " + requestorIdEncoding +
+ ", textEncoding: " + textEncoding);
+
+ GpsNiNotification notification = new GpsNiNotification();
+
+ notification.notificationId = notificationId;
+ notification.niType = niType;
+ notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
+ notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
+ notification.privacyOverride = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
+ notification.timeout = timeout;
+ notification.defaultResponse = defaultResponse;
+ notification.requestorId = requestorId;
+ notification.text = text;
+ notification.requestorIdEncoding = requestorIdEncoding;
+ notification.textEncoding = textEncoding;
+
+ // Process extras, assuming the format is
+ // one of more lines of "key = value"
+ Bundle bundle = new Bundle();
+
+ if (extras == null) extras = "";
+ Properties extraProp = new Properties();
+
+ try {
+ extraProp.load(new StringBufferInputStream(extras));
+ }
+ catch (IOException e)
+ {
+ Log.e(TAG, "reportNiNotification cannot parse extras data: " + extras);
+ }
+
+ for (Entry<Object, Object> ent : extraProp.entrySet())
+ {
+ bundle.putString((String) ent.getKey(), (String) ent.getValue());
+ }
+
+ notification.extras = bundle;
+
+ mNIHandler.handleNiNotification(notification);
+ }
+
private class GpsEventThread extends Thread {
public GpsEventThread() {
@@ -1182,6 +1317,8 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
private float mSvAzimuths[] = new float[MAX_SVS];
private int mSvMasks[] = new int[3];
private int mSvCount;
+ // preallocated to avoid memory allocation in reportNmea()
+ private byte[] mNmeaBuffer = new byte[120];
static { class_init_native(); }
private static native void class_init_native();
@@ -1199,6 +1336,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
// 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 int native_read_nmea(int index, byte[] buffer, int bufferSize);
private native void native_inject_location(double latitude, double longitude, float accuracy);
// XTRA Support
@@ -1211,4 +1349,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
private native void native_agps_data_conn_closed();
private native void native_agps_data_conn_failed();
private native void native_set_agps_server(int type, String hostname, int port);
+
+ // Network-initiated (NI) Support
+ private native void native_send_ni_response(int notificationId, int userResponse);
}
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
new file mode 100755
index 0000000..a5466d1
--- /dev/null
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -0,0 +1,457 @@
+/*
+ * 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.UnsupportedEncodingException;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A GPS Network-initiated Handler class used by LocationManager.
+ *
+ * {@hide}
+ */
+public class GpsNetInitiatedHandler {
+
+ private static final String TAG = "GpsNetInitiatedHandler";
+
+ private static final boolean DEBUG = true;
+ private static final boolean VERBOSE = false;
+
+ // NI verify activity for bringing up UI (not used yet)
+ public static final String ACTION_NI_VERIFY = "android.intent.action.NETWORK_INITIATED_VERIFY";
+
+ // string constants for defining data fields in NI Intent
+ public static final String NI_INTENT_KEY_NOTIF_ID = "notif_id";
+ public static final String NI_INTENT_KEY_TITLE = "title";
+ public static final String NI_INTENT_KEY_MESSAGE = "message";
+ public static final String NI_INTENT_KEY_TIMEOUT = "timeout";
+ public static final String NI_INTENT_KEY_DEFAULT_RESPONSE = "default_resp";
+
+ // the extra command to send NI response to GpsLocationProvider
+ public static final String NI_RESPONSE_EXTRA_CMD = "send_ni_response";
+
+ // the extra command parameter names in the Bundle
+ public static final String NI_EXTRA_CMD_NOTIF_ID = "notif_id";
+ public static final String NI_EXTRA_CMD_RESPONSE = "response";
+
+ // these need to match GpsNiType constants in gps_ni.h
+ public static final int GPS_NI_TYPE_VOICE = 1;
+ public static final int GPS_NI_TYPE_UMTS_SUPL = 2;
+ public static final int GPS_NI_TYPE_UMTS_CTRL_PLANE = 3;
+
+ // these need to match GpsUserResponseType constants in gps_ni.h
+ public static final int GPS_NI_RESPONSE_ACCEPT = 1;
+ public static final int GPS_NI_RESPONSE_DENY = 2;
+ public static final int GPS_NI_RESPONSE_NORESP = 3;
+
+ // these need to match GpsNiNotifyFlags constants in gps_ni.h
+ public static final int GPS_NI_NEED_NOTIFY = 0x0001;
+ public static final int GPS_NI_NEED_VERIFY = 0x0002;
+ public static final int GPS_NI_PRIVACY_OVERRIDE = 0x0004;
+
+ // these need to match GpsNiEncodingType in gps_ni.h
+ public static final int GPS_ENC_NONE = 0;
+ public static final int GPS_ENC_SUPL_GSM_DEFAULT = 1;
+ public static final int GPS_ENC_SUPL_UTF8 = 2;
+ public static final int GPS_ENC_SUPL_UCS2 = 3;
+ public static final int GPS_ENC_UNKNOWN = -1;
+
+ private final Context mContext;
+
+ // parent gps location provider
+ private final GpsLocationProvider mGpsLocationProvider;
+
+ // configuration of notificaiton behavior
+ private boolean mPlaySounds = false;
+ private boolean visible = true;
+ private boolean mPopupImmediately = true;
+
+ // Set to true if string from HAL is encoded as Hex, e.g., "3F0039"
+ static private boolean mIsHexInput = true;
+
+ public static class GpsNiNotification
+ {
+ int notificationId;
+ int niType;
+ boolean needNotify;
+ boolean needVerify;
+ boolean privacyOverride;
+ int timeout;
+ int defaultResponse;
+ String requestorId;
+ String text;
+ int requestorIdEncoding;
+ int textEncoding;
+ Bundle extras;
+ };
+
+ public static class GpsNiResponse {
+ /* User reponse, one of the values in GpsUserResponseType */
+ int userResponse;
+ /* Optional extra data to pass with the user response */
+ Bundle extras;
+ };
+
+ /**
+ * The notification that is shown when a network-initiated notification
+ * (and verification) event is received.
+ * <p>
+ * This is lazily created, so use {@link #setNINotification()}.
+ */
+ private Notification mNiNotification;
+
+ public GpsNetInitiatedHandler(Context context, GpsLocationProvider gpsLocationProvider) {
+ mContext = context;
+ mGpsLocationProvider = gpsLocationProvider;
+ }
+
+ // Handles NI events from HAL
+ public void handleNiNotification(GpsNiNotification notif)
+ {
+ if (DEBUG) Log.d(TAG, "handleNiNotification" + " notificationId: " + notif.notificationId
+ + " requestorId: " + notif.requestorId + " text: " + notif.text);
+
+ // Notify and verify with immediate pop-up
+ if (notif.needNotify && notif.needVerify && mPopupImmediately)
+ {
+ // Popup the dialog box now
+ openNiDialog(notif);
+ }
+
+ // Notify only, or delayed pop-up (change mPopupImmediately to FALSE)
+ if (notif.needNotify && !notif.needVerify ||
+ notif.needNotify && notif.needVerify && !mPopupImmediately)
+ {
+ // Show the notification
+
+ // if mPopupImmediately == FALSE and needVerify == TRUE, a dialog will be opened
+ // when the user opens the notification message
+
+ setNiNotification(notif);
+ }
+
+ // ACCEPT cases: 1. Notify, no verify; 2. no notify, no verify; 3. privacy override.
+ if ( notif.needNotify && !notif.needVerify ||
+ !notif.needNotify && !notif.needVerify ||
+ notif.privacyOverride)
+ {
+ try {
+ mGpsLocationProvider.getNetInitiatedListener().sendNiResponse(notif.notificationId, GPS_NI_RESPONSE_ACCEPT);
+ }
+ catch (RemoteException e)
+ {
+ Log.e(TAG, e.getMessage());
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // A note about timeout
+ // According to the protocol, in the need_notify and need_verify case,
+ // a default response should be sent when time out.
+ //
+ // In some GPS hardware, the GPS driver (under HAL) can handle the timeout case
+ // and this class GpsNetInitiatedHandler does not need to do anything.
+ //
+ // However, the UI should at least close the dialog when timeout. Further,
+ // for more general handling, timeout response should be added to the Handler here.
+ //
+ }
+
+ // Sets the NI notification.
+ private synchronized void setNiNotification(GpsNiNotification notif) {
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ if (notificationManager == null) {
+ return;
+ }
+
+ String title = getNotifTitle(notif);
+ String message = getNotifMessage(notif);
+
+ if (DEBUG) Log.d(TAG, "setNiNotification, notifyId: " + notif.notificationId +
+ ", title: " + title +
+ ", message: " + message);
+
+ // Construct Notification
+ if (mNiNotification == null) {
+ mNiNotification = new Notification();
+ mNiNotification.icon = com.android.internal.R.drawable.stat_sys_gps_on; /* Change notification icon here */
+ mNiNotification.when = 0;
+ }
+
+ if (mPlaySounds) {
+ mNiNotification.defaults |= Notification.DEFAULT_SOUND;
+ } else {
+ mNiNotification.defaults &= ~Notification.DEFAULT_SOUND;
+ }
+
+ mNiNotification.flags = Notification.FLAG_ONGOING_EVENT;
+ mNiNotification.tickerText = getNotifTicker(notif);
+
+ // if not to popup dialog immediately, pending intent will open the dialog
+ Intent intent = !mPopupImmediately ? getDlgIntent(notif) : new Intent();
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ mNiNotification.setLatestEventInfo(mContext, title, message, pi);
+
+ if (visible) {
+ notificationManager.notify(notif.notificationId, mNiNotification);
+ } else {
+ notificationManager.cancel(notif.notificationId);
+ }
+ }
+
+ // Opens the notification dialog and waits for user input
+ private void openNiDialog(GpsNiNotification notif)
+ {
+ Intent intent = getDlgIntent(notif);
+
+ if (DEBUG) Log.d(TAG, "openNiDialog, notifyId: " + notif.notificationId +
+ ", requestorId: " + notif.requestorId +
+ ", text: " + notif.text);
+
+ mContext.startActivity(intent);
+ }
+
+ // Construct the intent for bringing up the dialog activity, which shows the
+ // notification and takes user input
+ private Intent getDlgIntent(GpsNiNotification notif)
+ {
+ Intent intent = new Intent();
+ String title = getDialogTitle(notif);
+ String message = getDialogMessage(notif);
+
+ // directly bring up the NI activity
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClass(mContext, com.android.internal.app.NetInitiatedActivity.class);
+
+ // put data in the intent
+ intent.putExtra(NI_INTENT_KEY_NOTIF_ID, notif.notificationId);
+ intent.putExtra(NI_INTENT_KEY_TITLE, title);
+ intent.putExtra(NI_INTENT_KEY_MESSAGE, message);
+ intent.putExtra(NI_INTENT_KEY_TIMEOUT, notif.timeout);
+ intent.putExtra(NI_INTENT_KEY_DEFAULT_RESPONSE, notif.defaultResponse);
+
+ if (DEBUG) Log.d(TAG, "generateIntent, title: " + title + ", message: " + message +
+ ", timeout: " + notif.timeout);
+
+ return intent;
+ }
+
+ // Converts a string (or Hex string) to a char array
+ static byte[] stringToByteArray(String original, boolean isHex)
+ {
+ int length = isHex ? original.length() / 2 : original.length();
+ byte[] output = new byte[length];
+ int i;
+
+ if (isHex)
+ {
+ for (i = 0; i < length; i++)
+ {
+ output[i] = (byte) Integer.parseInt(original.substring(i*2, i*2+2), 16);
+ }
+ }
+ else {
+ for (i = 0; i < length; i++)
+ {
+ output[i] = (byte) original.charAt(i);
+ }
+ }
+
+ return output;
+ }
+
+ /**
+ * Unpacks an byte array containing 7-bit packed characters into a String.
+ *
+ * @param input a 7-bit packed char array
+ * @return the unpacked String
+ */
+ static String decodeGSMPackedString(byte[] input)
+ {
+ final char CHAR_CR = 0x0D;
+ int nStridx = 0;
+ int nPckidx = 0;
+ int num_bytes = input.length;
+ int cPrev = 0;
+ int cCurr = 0;
+ byte nShift;
+ byte nextChar;
+ byte[] stringBuf = new byte[input.length * 2];
+ String result = "";
+
+ while(nPckidx < num_bytes)
+ {
+ nShift = (byte) (nStridx & 0x07);
+ cCurr = input[nPckidx++];
+ if (cCurr < 0) cCurr += 256;
+
+ /* A 7-bit character can be split at the most between two bytes of packed
+ ** data.
+ */
+ nextChar = (byte) (( (cCurr << nShift) | (cPrev >> (8-nShift)) ) & 0x7F);
+ stringBuf[nStridx++] = nextChar;
+
+ /* Special case where the whole of the next 7-bit character fits inside
+ ** the current byte of packed data.
+ */
+ if(nShift == 6)
+ {
+ /* If the next 7-bit character is a CR (0x0D) and it is the last
+ ** character, then it indicates a padding character. Drop it.
+ */
+ if (nPckidx == num_bytes || (cCurr >> 1) == CHAR_CR)
+ {
+ break;
+ }
+
+ nextChar = (byte) (cCurr >> 1);
+ stringBuf[nStridx++] = nextChar;
+ }
+
+ cPrev = cCurr;
+ }
+
+ try{
+ result = new String(stringBuf, 0, nStridx, "US-ASCII");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ Log.e(TAG, e.getMessage());
+ }
+
+ return result;
+ }
+
+ static String decodeUTF8String(byte[] input)
+ {
+ String decoded = "";
+ try {
+ decoded = new String(input, "UTF-8");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ Log.e(TAG, e.getMessage());
+ }
+ return decoded;
+ }
+
+ static String decodeUCS2String(byte[] input)
+ {
+ String decoded = "";
+ try {
+ decoded = new String(input, "UTF-16");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ Log.e(TAG, e.getMessage());
+ }
+ return decoded;
+ }
+
+ /** Decode NI string
+ *
+ * @param original The text string to be decoded
+ * @param isHex Specifies whether the content of the string has been encoded as a Hex string. Encoding
+ * a string as Hex can allow zeros inside the coded text.
+ * @param coding Specifies the coding scheme of the string, such as GSM, UTF8, UCS2, etc. This coding scheme
+ * needs to match those used passed to HAL from the native GPS driver. Decoding is done according
+ * to the <code> coding </code>, after a Hex string is decoded. Generally, if the
+ * notification strings don't need further decoding, <code> coding </code> encoding can be
+ * set to -1, and <code> isHex </code> can be false.
+ * @return the decoded string
+ */
+ static private String decodeString(String original, boolean isHex, int coding)
+ {
+ String decoded = original;
+ byte[] input = stringToByteArray(original, isHex);
+
+ switch (coding) {
+ case GPS_ENC_NONE:
+ decoded = original;
+ break;
+
+ case GPS_ENC_SUPL_GSM_DEFAULT:
+ decoded = decodeGSMPackedString(input);
+ break;
+
+ case GPS_ENC_SUPL_UTF8:
+ decoded = decodeUTF8String(input);
+ break;
+
+ case GPS_ENC_SUPL_UCS2:
+ decoded = decodeUCS2String(input);
+ break;
+
+ case GPS_ENC_UNKNOWN:
+ decoded = original;
+ break;
+
+ default:
+ Log.e(TAG, "Unknown encoding " + coding + " for NI text " + original);
+ break;
+ }
+ return decoded;
+ }
+
+ // change this to configure notification display
+ static private String getNotifTicker(GpsNiNotification notif)
+ {
+ String ticker = String.format("Position request! ReqId: [%s] ClientName: [%s]",
+ decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
+ decodeString(notif.text, mIsHexInput, notif.textEncoding));
+ return ticker;
+ }
+
+ // change this to configure notification display
+ static private String getNotifTitle(GpsNiNotification notif)
+ {
+ String title = String.format("Position Request");
+ return title;
+ }
+
+ // change this to configure notification display
+ static private String getNotifMessage(GpsNiNotification notif)
+ {
+ String message = String.format(
+ "NI Request received from [%s] for client [%s]!",
+ decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
+ decodeString(notif.text, mIsHexInput, notif.textEncoding));
+ return message;
+ }
+
+ // change this to configure dialog display (for verification)
+ static public String getDialogTitle(GpsNiNotification notif)
+ {
+ return getNotifTitle(notif);
+ }
+
+ // change this to configure dialog display (for verification)
+ static private String getDialogMessage(GpsNiNotification notif)
+ {
+ return getNotifMessage(notif);
+ }
+
+}
diff --git a/location/java/com/android/internal/location/GpsXtraDownloader.java b/location/java/com/android/internal/location/GpsXtraDownloader.java
index 2a8be57..33ebce7 100644
--- a/location/java/com/android/internal/location/GpsXtraDownloader.java
+++ b/location/java/com/android/internal/location/GpsXtraDownloader.java
@@ -64,6 +64,7 @@ public class GpsXtraDownloader {
if (count == 0) {
Log.e(TAG, "No XTRA servers were specified in the GPS configuration");
+ return;
} else {
mXtraServers = new String[count];
count = 0;
diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/location/java/com/android/internal/location/LocationProviderProxy.java
index 4ae424a..89337b3 100644
--- a/location/java/com/android/internal/location/LocationProviderProxy.java
+++ b/location/java/com/android/internal/location/LocationProviderProxy.java
@@ -20,6 +20,7 @@ import android.location.Address;
import android.location.ILocationProvider;
import android.location.Location;
import android.location.LocationManager;
+import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -217,9 +218,9 @@ public class LocationProviderProxy implements IBinder.DeathRecipient {
}
}
- public void updateNetworkState(int state) {
+ public void updateNetworkState(int state, NetworkInfo info) {
try {
- mProvider.updateNetworkState(state);
+ mProvider.updateNetworkState(state, info);
} catch (RemoteException e) {
Log.e(TAG, "updateNetworkState failed", e);
}
diff --git a/location/java/com/android/internal/location/MockProvider.java b/location/java/com/android/internal/location/MockProvider.java
index e2e0562..2614f82 100644
--- a/location/java/com/android/internal/location/MockProvider.java
+++ b/location/java/com/android/internal/location/MockProvider.java
@@ -20,6 +20,7 @@ import android.location.ILocationManager;
import android.location.ILocationProvider;
import android.location.Location;
import android.location.LocationProvider;
+import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
@@ -169,7 +170,7 @@ public class MockProvider extends ILocationProvider.Stub {
public void setMinTime(long minTime) {
}
- public void updateNetworkState(int state) {
+ public void updateNetworkState(int state, NetworkInfo info) {
}
public void updateLocation(Location location) {