summaryrefslogtreecommitdiffstats
path: root/location/java/com
diff options
context:
space:
mode:
authorDanke Xie <dankex@qualcomm.com>2009-08-18 18:28:45 -0400
committerMike Lockwood <lockwood@android.com>2009-08-27 14:18:30 -0700
commit22d1f9fb23015471de6af1a70e40fb5c82ecb665 (patch)
tree0952ea04b5b0059aa48812f76e26599946e605c2 /location/java/com
parent0409cde3601131caf14c88d132c65d74774bbd79 (diff)
downloadframeworks_base-22d1f9fb23015471de6af1a70e40fb5c82ecb665.zip
frameworks_base-22d1f9fb23015471de6af1a70e40fb5c82ecb665.tar.gz
frameworks_base-22d1f9fb23015471de6af1a70e40fb5c82ecb665.tar.bz2
gps: Network initiated SUPL
Initial contribution from Qualcomm. Signed-off-by: Mike Lockwood <lockwood@android.com>
Diffstat (limited to 'location/java/com')
-rwxr-xr-xlocation/java/com/android/internal/location/GpsLocationProvider.java100
-rwxr-xr-xlocation/java/com/android/internal/location/GpsNetInitiatedHandler.java457
2 files changed, 557 insertions, 0 deletions
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 0b4fb88..bfa0671 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -27,6 +27,7 @@ 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;
@@ -46,14 +47,18 @@ 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.
@@ -214,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";
@@ -324,6 +330,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);
@@ -1047,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() {
@@ -1252,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);
+ }
+
+}