gps: Network initiated SUPL

Initial contribution from Qualcomm.

Signed-off-by: Mike Lockwood <lockwood@android.com>
This commit is contained in:
Danke Xie
2009-08-18 18:28:45 -04:00
committed by Mike Lockwood
parent 0409cde360
commit 22d1f9fb23
10 changed files with 836 additions and 11 deletions

View File

@@ -147,6 +147,7 @@ LOCAL_SRC_FILES += \
location/java/android/location/ILocationListener.aidl \
location/java/android/location/ILocationManager.aidl \
location/java/android/location/ILocationProvider.aidl \
location/java/android/location/INetInitiatedListener.aidl \
media/java/android/media/IAudioService.aidl \
media/java/android/media/IMediaScannerListener.aidl \
media/java/android/media/IMediaScannerService.aidl \

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2007 Google Inc.
*
* 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.app;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.IMountService;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.widget.Toast;
import android.util.Log;
import android.location.LocationManager;
import com.android.internal.location.GpsLocationProvider;
import com.android.internal.location.GpsNetInitiatedHandler;
/**
* This activity is shown to the user for him/her to accept or deny network-initiated
* requests. It uses the alert dialog style. It will be launched from a notification.
*/
public class NetInitiatedActivity extends AlertActivity implements DialogInterface.OnClickListener {
private static final String TAG = "NetInitiatedActivity";
private static final boolean DEBUG = true;
private static final boolean VERBOSE = false;
private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1;
private static final int NEGATIVE_BUTTON = AlertDialog.BUTTON2;
// Dialog button text
public static final String BUTTON_TEXT_ACCEPT = "Accept";
public static final String BUTTON_TEXT_DENY = "Deny";
// Received ID from intent, -1 when no notification is in progress
private int notificationId = -1;
/** Used to detect when NI request is received */
private BroadcastReceiver mNetInitiatedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.d(TAG, "NetInitiatedReceiver onReceive: " + intent.getAction());
if (intent.getAction() == GpsNetInitiatedHandler.ACTION_NI_VERIFY) {
handleNIVerify(intent);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set up the "dialog"
final Intent intent = getIntent();
final AlertController.AlertParams p = mAlertParams;
p.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
p.mTitle = intent.getStringExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_TITLE);
p.mMessage = intent.getStringExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_MESSAGE);
p.mPositiveButtonText = BUTTON_TEXT_ACCEPT;
p.mPositiveButtonListener = this;
p.mNegativeButtonText = BUTTON_TEXT_DENY;
p.mNegativeButtonListener = this;
notificationId = intent.getIntExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_NOTIF_ID, -1);
if (DEBUG) Log.d(TAG, "onCreate, notifId: " + notificationId);
setupAlert();
}
@Override
protected void onResume() {
super.onResume();
if (DEBUG) Log.d(TAG, "onResume");
registerReceiver(mNetInitiatedReceiver, new IntentFilter(GpsNetInitiatedHandler.ACTION_NI_VERIFY));
}
@Override
protected void onPause() {
super.onPause();
if (DEBUG) Log.d(TAG, "onPause");
unregisterReceiver(mNetInitiatedReceiver);
}
/**
* {@inheritDoc}
*/
public void onClick(DialogInterface dialog, int which) {
if (which == POSITIVE_BUTTON) {
sendUserResponse(GpsNetInitiatedHandler.GPS_NI_RESPONSE_ACCEPT);
}
if (which == NEGATIVE_BUTTON) {
sendUserResponse(GpsNetInitiatedHandler.GPS_NI_RESPONSE_DENY);
}
// No matter what, finish the activity
finish();
notificationId = -1;
}
// Respond to NI Handler under GpsLocationProvider, 1 = accept, 2 = deny
private void sendUserResponse(int response) {
if (DEBUG) Log.d(TAG, "sendUserResponse, response: " + response);
LocationManager locationManager = (LocationManager)
this.getSystemService(Context.LOCATION_SERVICE);
locationManager.sendNiResponse(notificationId, response);
}
private void handleNIVerify(Intent intent) {
int notifId = intent.getIntExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_NOTIF_ID, -1);
notificationId = notifId;
if (DEBUG) Log.d(TAG, "handleNIVerify action: " + intent.getAction());
}
private void showNIError() {
Toast.makeText(this, "NI error" /* com.android.internal.R.string.usb_storage_error_message */,
Toast.LENGTH_LONG).show();
}
}

View File

@@ -16,16 +16,18 @@
#define LOG_TAG "GpsLocationProvider"
//#define LOG_NDDEBUG 0
#include "JNIHelp.h"
#include "jni.h"
#include "hardware_legacy/gps.h"
#include "hardware_legacy/gps_ni.h"
#include "utils/Log.h"
#include "utils/misc.h"
#include <string.h>
#include <pthread.h>
static pthread_mutex_t sEventMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t sEventCond = PTHREAD_COND_INITIALIZER;
static jmethodID method_reportLocation;
@@ -34,16 +36,19 @@ static jmethodID method_reportSvStatus;
static jmethodID method_reportAGpsStatus;
static jmethodID method_reportNmea;
static jmethodID method_xtraDownloadRequest;
static jmethodID method_reportNiNotification;
static const GpsInterface* sGpsInterface = NULL;
static const GpsXtraInterface* sGpsXtraInterface = NULL;
static const AGpsInterface* sAGpsInterface = NULL;
static const GpsNiInterface* sGpsNiInterface = NULL;
// data written to by GPS callbacks
static GpsLocation sGpsLocation;
static GpsStatus sGpsStatus;
static GpsSvStatus sGpsSvStatus;
static AGpsStatus sAGpsStatus;
static GpsNiNotification sGpsNiNotification;
// buffer for NMEA data
#define NMEA_SENTENCE_LENGTH 100
@@ -62,6 +67,7 @@ static GpsStatus sGpsStatusCopy;
static GpsSvStatus sGpsSvStatusCopy;
static AGpsStatus sAGpsStatusCopy;
static NmeaSentence sNmeaBufferCopy[NMEA_SENTENCE_LENGTH];
static GpsNiNotification sGpsNiNotificationCopy;
enum CallbackType {
kLocation = 1,
@@ -71,6 +77,7 @@ enum CallbackType {
kXtraDownloadRequest = 16,
kDisableRequest = 32,
kNmeaAvailable = 64,
kNiNotification = 128,
};
static int sPendingCallbacks;
@@ -160,6 +167,20 @@ download_request_callback()
pthread_mutex_unlock(&sEventMutex);
}
static void
gps_ni_notify_callback(GpsNiNotification *notification)
{
LOGD("gps_ni_notify_callback: notif=%d", notification->notification_id);
pthread_mutex_lock(&sEventMutex);
sPendingCallbacks |= kNiNotification;
memcpy(&sGpsNiNotification, notification, sizeof(GpsNiNotification));
pthread_cond_signal(&sEventCond);
pthread_mutex_unlock(&sEventMutex);
}
GpsXtraCallbacks sGpsXtraCallbacks = {
download_request_callback,
};
@@ -168,6 +189,10 @@ AGpsCallbacks sAGpsCallbacks = {
agps_status_callback,
};
GpsNiCallbacks sGpsNiCallbacks = {
gps_ni_notify_callback,
};
static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");
method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
@@ -175,6 +200,7 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env,
method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II)V");
method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(IJ)V");
method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification", "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
}
static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) {
@@ -194,6 +220,12 @@ static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject o
sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
if (sAGpsInterface)
sAGpsInterface->init(&sAGpsCallbacks);
if (!sGpsNiInterface)
sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
if (sGpsNiInterface)
sGpsNiInterface->init(&sGpsNiCallbacks);
return true;
}
@@ -210,7 +242,7 @@ static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject ob
sGpsInterface->cleanup();
}
static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj, jint positionMode,
static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj, jint positionMode,
jboolean singleFix, jint fixFrequency)
{
int result = sGpsInterface->set_position_mode(positionMode, (singleFix ? 0 : fixFrequency));
@@ -235,7 +267,7 @@ static void android_location_GpsLocationProvider_wait_for_event(JNIEnv* env, job
{
pthread_mutex_lock(&sEventMutex);
pthread_cond_wait(&sEventCond, &sEventMutex);
// copy and clear the callback flags
int pendingCallbacks = sPendingCallbacks;
sPendingCallbacks = 0;
@@ -254,18 +286,20 @@ static void android_location_GpsLocationProvider_wait_for_event(JNIEnv* env, job
memcpy(&sAGpsStatusCopy, &sAGpsStatus, sizeof(sAGpsStatusCopy));
if (pendingCallbacks & kNmeaAvailable)
memcpy(&sNmeaBufferCopy, &sNmeaBuffer, nmeaSentenceCount * sizeof(sNmeaBuffer[0]));
if (pendingCallbacks & kNiNotification)
memcpy(&sGpsNiNotificationCopy, &sGpsNiNotification, sizeof(sGpsNiNotificationCopy));
pthread_mutex_unlock(&sEventMutex);
if (pendingCallbacks & kLocation) {
if (pendingCallbacks & kLocation) {
env->CallVoidMethod(obj, method_reportLocation, sGpsLocationCopy.flags,
(jdouble)sGpsLocationCopy.latitude, (jdouble)sGpsLocationCopy.longitude,
(jdouble)sGpsLocationCopy.altitude,
(jfloat)sGpsLocationCopy.speed, (jfloat)sGpsLocationCopy.bearing,
(jdouble)sGpsLocationCopy.altitude,
(jfloat)sGpsLocationCopy.speed, (jfloat)sGpsLocationCopy.bearing,
(jfloat)sGpsLocationCopy.accuracy, (jlong)sGpsLocationCopy.timestamp);
}
if (pendingCallbacks & kStatus) {
env->CallVoidMethod(obj, method_reportStatus, sGpsStatusCopy.status);
}
}
if (pendingCallbacks & kSvStatus) {
env->CallVoidMethod(obj, method_reportSvStatus);
}
@@ -277,16 +311,34 @@ static void android_location_GpsLocationProvider_wait_for_event(JNIEnv* env, job
env->CallVoidMethod(obj, method_reportNmea, i, sNmeaBuffer[i].timestamp);
}
}
if (pendingCallbacks & kXtraDownloadRequest) {
if (pendingCallbacks & kXtraDownloadRequest) {
env->CallVoidMethod(obj, method_xtraDownloadRequest);
}
if (pendingCallbacks & kDisableRequest) {
// don't need to do anything - we are just poking so wait_for_event will return.
}
if (pendingCallbacks & kNiNotification) {
LOGD("android_location_GpsLocationProvider_wait_for_event: sent notification callback.");
jstring reqId = env->NewStringUTF(sGpsNiNotificationCopy.requestor_id);
jstring text = env->NewStringUTF(sGpsNiNotificationCopy.text);
jstring extras = env->NewStringUTF(sGpsNiNotificationCopy.extras);
env->CallVoidMethod(obj, method_reportNiNotification,
sGpsNiNotificationCopy.notification_id,
sGpsNiNotificationCopy.ni_type,
sGpsNiNotificationCopy.notify_flags,
sGpsNiNotificationCopy.timeout,
sGpsNiNotificationCopy.default_response,
reqId,
text,
sGpsNiNotificationCopy.requestor_id_encoding,
sGpsNiNotificationCopy.text_encoding,
extras
);
}
}
static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj,
jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray,
static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj,
jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray,
jintArray maskArray)
{
// this should only be called from within a call to reportStatus, so we don't need to lock here
@@ -358,7 +410,7 @@ static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env,
return (sGpsXtraInterface != NULL);
}
static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj,
static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj,
jbyteArray data, jint length)
{
jbyte* bytes = env->GetByteArrayElements(data, 0);
@@ -415,6 +467,16 @@ static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jo
}
}
static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* env, jobject obj,
jint notifId, jint response)
{
if (!sGpsNiInterface)
sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
if (sGpsNiInterface) {
sGpsNiInterface->respond(notifId, response);
}
}
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
@@ -436,6 +498,7 @@ static JNINativeMethod sMethods[] = {
{"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed},
{"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed},
{"native_set_agps_server", "(ILjava/lang/String;I)V", (void*)android_location_GpsLocationProvider_set_agps_server},
{"native_send_ni_response", "(II)V", (void*)android_location_GpsLocationProvider_send_ni_response},
};
int register_android_location_GpsLocationProvider(JNIEnv* env)

View File

@@ -1163,6 +1163,10 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name="com.android.internal.app.NetInitiatedActivity"
android:theme="@style/Theme.Dialog.Alert"
android:excludeFromRecents="true">
</activity>
<service android:name="com.android.server.LoadAverageService"
android:exported="true" />

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -1417,4 +1417,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;
}
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -49,6 +49,7 @@ import android.location.IGpsStatusProvider;
import android.location.ILocationListener;
import android.location.ILocationManager;
import android.location.ILocationProvider;
import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
@@ -71,6 +72,7 @@ import android.util.PrintWriterPrinter;
import com.android.internal.location.GpsLocationProvider;
import com.android.internal.location.LocationProviderProxy;
import com.android.internal.location.MockProvider;
import com.android.internal.location.GpsNetInitiatedHandler;
/**
* The service class that manages LocationProviders and issues location
@@ -118,6 +120,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
private final Context mContext;
private IGeocodeProvider mGeocodeProvider;
private IGpsStatusProvider mGpsStatusProvider;
private INetInitiatedListener mNetInitiatedListener;
private LocationWorkerHandler mLocationHandler;
// Cache the real providers for use in addTestProvider() and removeTestProvider()
@@ -541,6 +544,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// Create a gps location provider
GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
mGpsStatusProvider = provider.getGpsStatusProvider();
mNetInitiatedListener = provider.getNetInitiatedListener();
LocationProviderProxy proxy = new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider);
addProvider(proxy);
mGpsLocationProvider = proxy;
@@ -1131,6 +1135,18 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
public boolean sendNiResponse(int notifId, int userResponse)
{
try {
return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
}
catch (RemoteException e)
{
Log.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
return false;
}
}
class ProximityAlert {
final int mUid;
final double mLatitude;