These log statements were dead code. That isn't much of a problem, except that the 'e.getMessage()' that was being logged could be null, and that would cause a real problem. Change-Id: I8573bc687a7eda73782bd028e8ddc048a1954dc5
429 lines
15 KiB
Java
Executable File
429 lines
15 KiB
Java
Executable File
/*
|
|
* 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.location.LocationManager;
|
|
import android.os.Bundle;
|
|
import android.os.RemoteException;
|
|
import android.util.Log;
|
|
|
|
import com.android.internal.R;
|
|
import com.android.internal.telephony.GsmAlphabet;
|
|
|
|
/**
|
|
* 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 LocationManager mLocationManager;
|
|
|
|
// 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
|
|
{
|
|
public int notificationId;
|
|
public int niType;
|
|
public boolean needNotify;
|
|
public boolean needVerify;
|
|
public boolean privacyOverride;
|
|
public int timeout;
|
|
public int defaultResponse;
|
|
public String requestorId;
|
|
public String text;
|
|
public int requestorIdEncoding;
|
|
public int textEncoding;
|
|
public 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) {
|
|
mContext = context;
|
|
mLocationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
mLocationManager.sendNiResponse(notif.notificationId, GPS_NI_RESPONSE_ACCEPT);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// 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, mContext);
|
|
String message = getNotifMessage(notif, mContext);
|
|
|
|
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 | Notification.FLAG_AUTO_CANCEL;
|
|
mNiNotification.tickerText = getNotifTicker(notif, mContext);
|
|
|
|
// 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, mContext);
|
|
String message = getDialogMessage(notif, mContext);
|
|
|
|
// 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 PADDING_CHAR = 0x00;
|
|
int lengthBytes = input.length;
|
|
int lengthSeptets = (lengthBytes * 8) / 7;
|
|
String decoded;
|
|
|
|
/* Special case where the last 7 bits in the last byte could hold a valid
|
|
* 7-bit character or a padding character. Drop the last 7-bit character
|
|
* if it is a padding character.
|
|
*/
|
|
if (lengthBytes % 7 == 0) {
|
|
if (lengthBytes > 0) {
|
|
if ((input[lengthBytes - 1] >> 1) == PADDING_CHAR) {
|
|
lengthSeptets = lengthSeptets - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
decoded = GsmAlphabet.gsm7BitPackedToString(input, 0, lengthSeptets);
|
|
|
|
// Return "" if decoding of GSM packed string fails
|
|
if (null == decoded) {
|
|
Log.e(TAG, "Decoding of GSM packed string failed");
|
|
decoded = "";
|
|
}
|
|
|
|
return decoded;
|
|
}
|
|
|
|
static String decodeUTF8String(byte[] input)
|
|
{
|
|
String decoded = "";
|
|
try {
|
|
decoded = new String(input, "UTF-8");
|
|
}
|
|
catch (UnsupportedEncodingException e)
|
|
{
|
|
throw new AssertionError();
|
|
}
|
|
return decoded;
|
|
}
|
|
|
|
static String decodeUCS2String(byte[] input)
|
|
{
|
|
String decoded = "";
|
|
try {
|
|
decoded = new String(input, "UTF-16");
|
|
}
|
|
catch (UnsupportedEncodingException e)
|
|
{
|
|
throw new AssertionError();
|
|
}
|
|
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, Context context)
|
|
{
|
|
String ticker = String.format(context.getString(R.string.gpsNotifTicker),
|
|
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, Context context)
|
|
{
|
|
String title = String.format(context.getString(R.string.gpsNotifTitle));
|
|
return title;
|
|
}
|
|
|
|
// change this to configure notification display
|
|
static private String getNotifMessage(GpsNiNotification notif, Context context)
|
|
{
|
|
String message = String.format(context.getString(R.string.gpsNotifMessage),
|
|
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, Context context)
|
|
{
|
|
return getNotifTitle(notif, context);
|
|
}
|
|
|
|
// change this to configure dialog display (for verification)
|
|
static private String getDialogMessage(GpsNiNotification notif, Context context)
|
|
{
|
|
return getNotifMessage(notif, context);
|
|
}
|
|
|
|
}
|