Added AT+CPBR support
Change-Id: Ice860d8f97350d805a5478e94a27ae19cf42dafd
diff --git a/jni/com_android_bluetooth_hfp.cpp b/jni/com_android_bluetooth_hfp.cpp
index e618152..f730382 100644
--- a/jni/com_android_bluetooth_hfp.cpp
+++ b/jni/com_android_bluetooth_hfp.cpp
@@ -474,11 +474,11 @@
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
-static jboolean atResponseCodeNative(JNIEnv *env, jobject object, jint response_code) {
+static jboolean atResponseCodeNative(JNIEnv *env, jobject object, jint response_code, jint cmee_code) {
bt_status_t status;
if (!sBluetoothHfpInterface) return JNI_FALSE;
- if ( (status = sBluetoothHfpInterface->at_response((bthf_at_response_t) response_code)) !=
+ if ( (status = sBluetoothHfpInterface->at_response((bthf_at_response_t) response_code, cmee_code)) !=
BT_STATUS_SUCCESS) {
LOGE("Failed AT response, status: %d", status);
}
@@ -538,7 +538,7 @@
{"copsResponseNative", "(Ljava/lang/String;)Z", (void *) copsResponseNative},
{"cindResponseNative", "(IIIIIII)Z", (void *) cindResponseNative},
{"atResponseStringNative", "(Ljava/lang/String;)Z", (void *) atResponseStringNative},
- {"atResponseCodeNative", "(I)Z", (void *)atResponseCodeNative},
+ {"atResponseCodeNative", "(II)Z", (void *)atResponseCodeNative},
{"clccResponseNative", "(IIIIZLjava/lang/String;I)Z", (void *) clccResponseNative},
{"phoneStateChangeNative", "(IIILjava/lang/String;I)Z", (void *) phoneStateChangeNative},
};
diff --git a/src/com/android/bluetooth/hfp/AtPhonebook.java b/src/com/android/bluetooth/hfp/AtPhonebook.java
index 555465c..ef76dd7 100755
--- a/src/com/android/bluetooth/hfp/AtPhonebook.java
+++ b/src/com/android/bluetooth/hfp/AtPhonebook.java
@@ -16,6 +16,8 @@
package com.android.bluetooth.hfp;
+import com.android.bluetooth.R;
+
import com.android.internal.telephony.GsmAlphabet;
import android.bluetooth.BluetoothDevice;
@@ -70,6 +72,8 @@
public int nameColumn;
};
+ private HeadsetStateMachine mStateMachine;
+
private final Context mContext;
private String mCurrentPhonebook;
@@ -87,8 +91,14 @@
private final HashMap<String, PhonebookResult> mPhonebooks =
new HashMap<String, PhonebookResult>(4);
- public AtPhonebook(Context context) {
+ final int TYPE_UNKNOWN = -1;
+ final int TYPE_READ = 0;
+ final int TYPE_SET = 1;
+ final int TYPE_TEST = 2;
+
+ public AtPhonebook(Context context, HeadsetStateMachine headsetState) {
mContext = context;
+ mStateMachine = headsetState;
mPhonebooks.put("DC", new PhonebookResult()); // dialled calls
mPhonebooks.put("RC", new PhonebookResult()); // received calls
mPhonebooks.put("MC", new PhonebookResult()); // missed calls
@@ -119,36 +129,217 @@
return number;
}
- /* package */ void handleAccessPermissionResult(Intent intent) {
- if (!mCheckingAccessPermission) {
- return;
- }
+ public boolean getCheckingAccessPermission() {
+ return mCheckingAccessPermission;
+ }
- //HeadsetBase headset = mHandsfree.getHeadset();
- // ASSERT: (headset != null) && headSet.isConnected()
- // REASON: mCheckingAccessPermission is true, otherwise resetAtState
- // has set mCheckingAccessPermission to false
+ public void setCheckingAccessPermission(boolean checkAccessPermission) {
+ mCheckingAccessPermission = checkAccessPermission;
+ }
- if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
+ public void setCpbrIndex(int cpbrIndex) {
+ mCpbrIndex1 = mCpbrIndex2 = cpbrIndex;
+ }
- if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
- BluetoothDevice.CONNECTION_ACCESS_NO) ==
- BluetoothDevice.CONNECTION_ACCESS_YES) {
- // BluetoothDevice remoteDevice = headset.getRemoteDevice();
- // TODO(BT) when we do CPBR, fix this NullPointerException
- BluetoothDevice remoteDevice = null;
- if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
- remoteDevice.setTrust(true);
+ public void handleCscsCommand(String atString, int type)
+ {
+ log("handleCscsCommand - atString = " +atString);
+ // Select Character Set
+ int atCommandResult = HeadsetHalConstants.AT_RESPONSE_ERROR;
+ int atCommandErrorCode = -1;
+ String atCommandResponse = null;
+ switch (type) {
+ case TYPE_READ: // Read
+ log("handleCscsCommand - Read Command");
+ atCommandResponse = "+CSCS: \"" + mCharacterSet + "\"";
+ atCommandResult = HeadsetHalConstants.AT_RESPONSE_OK;
+ break;
+ case TYPE_TEST: // Test
+ log("handleCscsCommand - Test Command");
+ atCommandResponse = ( "+CSCS: (\"UTF-8\",\"IRA\",\"GSM\")");
+ atCommandResult = HeadsetHalConstants.AT_RESPONSE_OK;
+ break;
+ case TYPE_SET: // Set
+ log("handleCscsCommand - Set Command");
+ String[] args = atString.split("=");
+ if (args.length < 2 || !(args[1] instanceof String)) {
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ break;
}
-
- // AtCommandResult cpbrResult = processCpbrCommand();
- // headset.sendURC(cpbrResult.toString());
- } else {
- // headset.sendURC("ERROR");
- }
+ String characterSet = ((atString.split("="))[1]);
+ characterSet = characterSet.replace("\"", "");
+ if (characterSet.equals("GSM") || characterSet.equals("IRA") ||
+ characterSet.equals("UTF-8") || characterSet.equals("UTF8")) {
+ mCharacterSet = characterSet;
+ atCommandResult = HeadsetHalConstants.AT_RESPONSE_OK;
+ } else {
+ atCommandErrorCode = BluetoothCmeError.OPERATION_NOT_SUPPORTED;
+ }
+ break;
+ case TYPE_UNKNOWN:
+ default:
+ log("handleCscsCommand - Invalid chars");
+ atCommandErrorCode = BluetoothCmeError.TEXT_HAS_INVALID_CHARS;
}
- mCpbrIndex1 = mCpbrIndex2 = -1;
- mCheckingAccessPermission = false;
+ if (atCommandResponse != null)
+ mStateMachine.atResponseStringNative(atCommandResponse);
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ }
+
+ public void handleCpbsCommand(String atString, int type) {
+ // Select PhoneBook memory Storage
+ log("handleCpbsCommand - atString = " +atString);
+ int atCommandResult = HeadsetHalConstants.AT_RESPONSE_ERROR;
+ int atCommandErrorCode = -1;
+ String atCommandResponse = null;
+ switch (type) {
+ case TYPE_READ: // Read
+ log("handleCpbsCommand - read command");
+ // Return current size and max size
+ if ("SM".equals(mCurrentPhonebook)) {
+ atCommandResponse = "+CPBS: \"SM\",0," + getMaxPhoneBookSize(0);
+ atCommandResult = HeadsetHalConstants.AT_RESPONSE_OK;
+ if (atCommandResponse != null)
+ mStateMachine.atResponseStringNative(atCommandResponse);
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ break;
+ }
+ PhonebookResult pbr = getPhonebookResult(mCurrentPhonebook, true);
+ if (pbr == null) {
+ atCommandErrorCode = BluetoothCmeError.OPERATION_NOT_SUPPORTED;
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ break;
+ }
+ int size = pbr.cursor.getCount();
+ atCommandResponse = "+CPBS: \"" + mCurrentPhonebook + "\"," + size + "," + getMaxPhoneBookSize(size);
+ atCommandResult = HeadsetHalConstants.AT_RESPONSE_OK;
+ break;
+ case TYPE_TEST: // Test
+ log("handleCpbsCommand - test command");
+ atCommandResponse = ("+CPBS: (\"ME\",\"SM\",\"DC\",\"RC\",\"MC\")");
+ atCommandResult = HeadsetHalConstants.AT_RESPONSE_OK;
+ break;
+ case TYPE_SET: // Set
+ log("handleCpbsCommand - set command");
+ String[] args = atString.split("=");
+ // Select phonebook memory
+ if (args.length < 2 || !(args[1] instanceof String)) {
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ break;
+ }
+ String pb = ((String)args[1]).trim();
+ while (pb.endsWith("\"")) pb = pb.substring(0, pb.length() - 1);
+ while (pb.startsWith("\"")) pb = pb.substring(1, pb.length());
+ if (getPhonebookResult(pb, false) == null && !"SM".equals(pb)) {
+ if (DBG) log("Dont know phonebook: '" + pb + "'");
+ atCommandErrorCode = BluetoothCmeError.OPERATION_NOT_ALLOWED;
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ break;
+ }
+ mCurrentPhonebook = pb;
+ atCommandResult = HeadsetHalConstants.AT_RESPONSE_OK;
+ break;
+ case TYPE_UNKNOWN:
+ default:
+ log("handleCpbsCommand - invalid chars");
+ atCommandErrorCode = BluetoothCmeError.TEXT_HAS_INVALID_CHARS;
+ }
+ if (atCommandResponse != null)
+ mStateMachine.atResponseStringNative(atCommandResponse);
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ }
+
+ public void handleCpbrCommand(String atString, int type, BluetoothDevice remoteDevice) {
+ log("handleCpbrCommand - atString = " +atString);
+ int atCommandResult = HeadsetHalConstants.AT_RESPONSE_ERROR;
+ int atCommandErrorCode = -1;
+ String atCommandResponse = null;
+ switch (type) {
+ case TYPE_TEST: // Test
+ /* Ideally we should return the maximum range of valid index's
+ * for the selected phone book, but this causes problems for the
+ * Parrot CK3300. So instead send just the range of currently
+ * valid index's.
+ */
+ log("handleCpbrCommand - test command");
+ int size;
+ if ("SM".equals(mCurrentPhonebook)) {
+ size = 0;
+ } else {
+ PhonebookResult pbr = getPhonebookResult(mCurrentPhonebook, true); //false);
+ if (pbr == null) {
+ atCommandErrorCode = BluetoothCmeError.OPERATION_NOT_ALLOWED;
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ break;
+ }
+ size = pbr.cursor.getCount();
+ log("handleCpbrCommand - size = "+size);
+ }
+ if (size == 0) {
+ /* Sending "+CPBR: (1-0)" can confused some carkits, send "1-1" * instead */
+ size = 1;
+ }
+ atCommandResponse = "+CPBR: (1-" + size + "),30,30";
+ atCommandResult = HeadsetHalConstants.AT_RESPONSE_OK;
+ if (atCommandResponse != null)
+ mStateMachine.atResponseStringNative(atCommandResponse);
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ break;
+ // Read PhoneBook Entries
+ case TYPE_READ:
+ case TYPE_SET: // Set & read
+ // Phone Book Read Request
+ // AT+CPBR=<index1>[,<index2>]
+ log("handleCpbrCommand - set/read command");
+ if (mCpbrIndex1 != -1) {
+ /* handling a CPBR at the moment, reject this CPBR command */
+ atCommandErrorCode = BluetoothCmeError.OPERATION_NOT_ALLOWED;
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ break;
+ }
+ // Parse indexes
+ int index1;
+ int index2;
+ if ((atString.split("=")).length < 2) {
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ break;
+ }
+ String atCommand = (atString.split("="))[1];
+ String[] indices = atCommand.split(",");
+ for(int i = 0; i < indices.length; i++)
+ indices[i] = indices[i].trim();
+ try {
+ index1 = Integer.parseInt(indices[0]);
+ if (indices.length == 1)
+ index2 = index1;
+ else
+ index2 = Integer.parseInt(indices[1]);
+ }
+ catch (Exception e) {
+ log("handleCpbrCommand - exception - invalid chars");
+ atCommandErrorCode = BluetoothCmeError.TEXT_HAS_INVALID_CHARS;
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ break;
+ }
+ mCpbrIndex1 = index1;
+ mCpbrIndex2 = index2;
+ mCheckingAccessPermission = true;
+
+ if (checkAccessPermission(remoteDevice)) {
+ mCheckingAccessPermission = false;
+ atCommandResult = processCpbrCommand();
+ mCpbrIndex1 = mCpbrIndex2 = -1;
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ break;
+ }
+ // no reponse here, will continue the process in handleAccessPermissionResult
+ break;
+ case TYPE_UNKNOWN:
+ default:
+ log("handleCpbrCommand - invalid chars");
+ atCommandErrorCode = BluetoothCmeError.TEXT_HAS_INVALID_CHARS;
+ mStateMachine.atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ }
}
/** Get the most recent result for the given phone book,
@@ -246,18 +437,26 @@
}
// process CPBR command after permission check
- private String processCpbrCommand()
+ /*package*/ int processCpbrCommand()
{
+ log("processCpbrCommand");
+ int atCommandResult = HeadsetHalConstants.AT_RESPONSE_ERROR;
+ int atCommandErrorCode = -1;
+ String atCommandResponse = null;
+ StringBuilder response = new StringBuilder();
+ String record;
+
// Shortcut SM phonebook
if ("SM".equals(mCurrentPhonebook)) {
- // return new AtCommandResult(AtCommandResult.OK);
+ atCommandResult = HeadsetHalConstants.AT_RESPONSE_OK;
+ return atCommandResult;
}
// Check phonebook
- PhonebookResult pbr = getPhonebookResult(mCurrentPhonebook, false);
+ PhonebookResult pbr = getPhonebookResult(mCurrentPhonebook, true); //false);
if (pbr == null) {
- // return mHandsfree.reportCmeError(BluetoothCmeError.OPERATION_NOT_ALLOWED);
- return null;
+ atCommandErrorCode = BluetoothCmeError.OPERATION_NOT_ALLOWED;
+ return atCommandResult;
}
// More sanity checks
@@ -266,14 +465,15 @@
// Handsfree connection.
if (pbr.cursor.getCount() == 0 || mCpbrIndex1 <= 0 || mCpbrIndex2 < mCpbrIndex1 ||
mCpbrIndex2 > pbr.cursor.getCount() || mCpbrIndex1 > pbr.cursor.getCount()) {
- // return new AtCommandResult(AtCommandResult.OK);
- return null;
+ atCommandResult = HeadsetHalConstants.AT_RESPONSE_OK;
+ return atCommandResult;
}
// Process
- //AtCommandResult result = new AtCommandResult(AtCommandResult.OK);
+ atCommandResult = HeadsetHalConstants.AT_RESPONSE_OK;
int errorDetected = -1; // no error
pbr.cursor.moveToPosition(mCpbrIndex1 - 1);
+ log("mCpbrIndex1 = "+mCpbrIndex1+ " and mCpbrIndex2 = "+mCpbrIndex2);
for (int index = mCpbrIndex1; index <= mCpbrIndex2; index++) {
String number = pbr.cursor.getString(pbr.numberColumn);
String name = null;
@@ -316,7 +516,7 @@
if (number.equals("-1")) {
// unknown numbers are stored as -1 in our database
number = "";
- // name = mContext.getString(R.string.unknown);
+ name = mContext.getString(R.string.unknownNumber);
}
// TODO(): Handle IRA commands. It's basically
@@ -324,20 +524,45 @@
if (!name.equals("") && mCharacterSet.equals("GSM")) {
byte[] nameByte = GsmAlphabet.stringToGsm8BitPacked(name);
if (nameByte == null) {
- // name = mContext.getString(R.string.unknown);
+ name = mContext.getString(R.string.unknownNumber);
} else {
name = new String(nameByte);
}
}
- // result.addResponse("+CPBR: " + index + ",\"" + number + "\"," +
- // regionType + ",\"" + name + "\"");
+ record = "+CPBR: " + index + ",\"" + number + "\"," + regionType + ",\"" + name + "\"";
+ record = record + "\r\n\r\n";
+ atCommandResponse = record;
+ log("processCpbrCommand - atCommandResponse = "+atCommandResponse);
+ mStateMachine.atResponseStringNative(atCommandResponse);
if (!pbr.cursor.moveToNext()) {
break;
}
}
- // return result;
- return null;
+ return atCommandResult;
+ }
+
+ // Check if the remote device has premission to read our phone book
+ // Return true if it has the permission
+ // false if not known and we have sent our Intent to check
+ private boolean checkAccessPermission(BluetoothDevice remoteDevice) {
+ log("checkAccessPermission");
+ boolean trust = remoteDevice.getTrustState();
+
+ if (trust) {
+ return true;
+ }
+
+ log("checkAccessPermission - ACTION_CONNECTION_ACCESS_REQUEST");
+ Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
+ intent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
+ intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
+ BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, remoteDevice);
+ // Leave EXTRA_PACKAGE_NAME and EXTRA_CLASS_NAME field empty
+ // BluetoothHandsfree's broadcast receiver is anonymous, cannot be targeted
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ return false;
}
private static String getPhoneType(int type) {
diff --git a/src/com/android/bluetooth/hfp/BluetoothCmeError.java b/src/com/android/bluetooth/hfp/BluetoothCmeError.java
new file mode 100644
index 0000000..1f2a45d
--- /dev/null
+++ b/src/com/android/bluetooth/hfp/BluetoothCmeError.java
@@ -0,0 +1,44 @@
+/*
+ * 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.bluetooth.hfp;
+
+/* Constants for extended AT error codes specified by the Handsfree profile. */
+public class BluetoothCmeError {
+ public static final int AG_FAILURE = 0;
+ public static final int NO_CONNECTION_TO_PHONE = 1;
+ public static final int OPERATION_NOT_ALLOWED = 3;
+ public static final int OPERATION_NOT_SUPPORTED = 4;
+ public static final int PIN_REQUIRED = 5;
+ public static final int SIM_MISSING = 10;
+ public static final int SIM_PIN_REQUIRED = 11;
+ public static final int SIM_PUK_REQUIRED = 12;
+ public static final int SIM_FAILURE = 13;
+ public static final int SIM_BUSY = 14;
+ public static final int WRONG_PASSWORD = 16;
+ public static final int SIM_PIN2_REQUIRED = 17;
+ public static final int SIM_PUK2_REQUIRED = 18;
+ public static final int MEMORY_FULL = 20;
+ public static final int INVALID_INDEX = 21;
+ public static final int MEMORY_FAILURE = 23;
+ public static final int TEXT_TOO_LONG = 24;
+ public static final int TEXT_HAS_INVALID_CHARS = 25;
+ public static final int DIAL_STRING_TOO_LONG = 26;
+ public static final int DIAL_STRING_HAS_INVALID_CHARS = 27;
+ public static final int NO_SERVICE = 30;
+ public static final int ONLY_911_ALLOWED = 32;
+}
+
diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java
index 698c1af..817f053 100755
--- a/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -54,6 +54,8 @@
mStateMachine.start();
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
+ filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
+
registerReceiver(mHeadsetReceiver, filter);
mReceiverRegistered=true;
return true;
@@ -92,6 +94,10 @@
intent);
}
}
+ else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
+ Log.v(TAG, "HeadsetService - Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY");
+ mStateMachine.handleAccessPermissionResult(intent);
+ }
}
};
diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index c16b676..5eb9753 100755
--- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -96,7 +96,6 @@
private AudioOn mAudioOn;
private Context mContext;
-
private PowerManager mPowerManager;
private boolean mVoiceRecognitionStarted = false;
@@ -158,7 +157,7 @@
mDialingOut = false;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mPhonebook = new AtPhonebook(mContext);
+ mPhonebook = new AtPhonebook(mContext, this);
mPhoneState = new HeadsetPhoneState(context, this);
mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -208,6 +207,7 @@
@Override
public void enter() {
log("Enter Disconnected: " + getCurrentMessage().what);
+ mPhonebook.resetAtState();
mPhoneState.listenForPhoneState(false);
}
@@ -597,14 +597,14 @@
case DIALING_OUT_TIMEOUT:
if (mDialingOut) {
mDialingOut= false;
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
}
break;
case START_VR_TIMEOUT:
if (mWaitingForVoiceRecognition) {
mWaitingForVoiceRecognition = false;
Log.e(TAG, "Timeout waiting for voice recognition to start");
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
}
break;
case STACK_EVENT:
@@ -798,14 +798,14 @@
case DIALING_OUT_TIMEOUT:
if (mDialingOut) {
mDialingOut= false;
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
}
break;
case START_VR_TIMEOUT:
if (mWaitingForVoiceRecognition) {
mWaitingForVoiceRecognition = false;
Log.e(TAG, "Timeout waiting for voice recognition to start");
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
}
break;
case STACK_EVENT:
@@ -1020,7 +1020,7 @@
try {
mContext.startActivity(sVoiceCommandIntent);
} catch (ActivityNotFoundException e) {
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
return;
}
expectVoiceRecognition();
@@ -1028,7 +1028,7 @@
} else if (state == HeadsetHalConstants.VR_STATE_STOPPED) {
if (mVoiceRecognitionStarted || mWaitingForVoiceRecognition)
{
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
mVoiceRecognitionStarted = false;
mWaitingForVoiceRecognition = false;
if (!isInCall())
@@ -1036,7 +1036,7 @@
}
else
{
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
}
} else {
Log.e(TAG, "Bad Voice Recognition state: " + state);
@@ -1060,7 +1060,7 @@
{
Log.d(TAG, "Voice recognition started successfully");
mWaitingForVoiceRecognition = false;
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
removeMessages(START_VR_TIMEOUT);
}
else
@@ -1153,6 +1153,51 @@
HEADSET_NREC + "=on");
}
+ private String parseUnknownAt(String atString)
+ {
+ StringBuilder atCommand = new StringBuilder(atString.length());
+ String result = null;
+
+ for (int i = 0; i < atString.length(); i++) {
+ char c = atString.charAt(i);
+ if (c == '"') {
+ int j = atString.indexOf('"', i + 1 ); // search for closing "
+ if (j == -1) { // unmatched ", insert one.
+ atCommand.append(atString.substring(i, atString.length()));
+ atCommand.append('"');
+ break;
+ }
+ atCommand.append(atString.substring(i, j + 1));
+ i = j;
+ } else if (c != ' ') {
+ atCommand.append(Character.toUpperCase(c));
+ }
+ }
+ result = atCommand.toString();
+ return result;
+ }
+
+ private int getAtCommandType(String atCommand)
+ {
+ int commandType = mPhonebook.TYPE_UNKNOWN;
+ String atString = null;
+ atCommand = atCommand.trim();
+ if (atCommand.length() > 5)
+ {
+ atString = atCommand.substring(5);
+ if (atString.startsWith("?")) // Read
+ commandType = mPhonebook.TYPE_READ;
+ else if (atString.startsWith("=?")) // Test
+ commandType = mPhonebook.TYPE_TEST;
+ else if (atString.startsWith("=")) // Set
+ commandType = mPhonebook.TYPE_SET;
+ else
+ commandType = mPhonebook.TYPE_UNKNOWN;
+ }
+ return commandType;
+ }
+
+
private void processAnswerCall() {
if (mPhoneProxy != null) {
try {
@@ -1183,21 +1228,21 @@
dialNumber = mPhonebook.getLastDialledNumber();
if (dialNumber == null) {
if (DBG) log("processDialCall, last dial number null");
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
return;
}
} else if (number.charAt(0) == '>') {
// Yuck - memory dialling requested.
// Just dial last number for now
if (number.startsWith(">9999")) { // for PTS test
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
return;
}
if (DBG) log("processDialCall, memory dial do last dial for now");
dialNumber = mPhonebook.getLastDialledNumber();
if (dialNumber == null) {
if (DBG) log("processDialCall, last dial number null");
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
return;
}
} else {
@@ -1250,7 +1295,7 @@
mPhoneState.setNumHeldCall(callState.mNumHeld);
mPhoneState.setCallState(callState.mCallState);
if (mDialingOut && callState.mCallState == HeadsetHalConstants.CALL_STATE_DIALING) {
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
removeMessages(DIALING_OUT_TIMEOUT);
mDialingOut = false;
}
@@ -1277,17 +1322,17 @@
if (mPhoneProxy != null) {
try {
if (mPhoneProxy.processChld(chld)) {
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
} else {
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
}
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
}
} else {
Log.e(TAG, "Handsfree phone proxy null for At+Chld");
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
}
}
@@ -1298,11 +1343,11 @@
if (number != null) {
atResponseStringNative("+CNUM: ,\"" + number + "\"," +
PhoneNumberUtils.toaFromString(number) + ",,4");
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0);
}
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
}
} else {
Log.e(TAG, "Handsfree phone proxy null for At+CNUM");
@@ -1350,9 +1395,52 @@
}
}
+ private void processAtCscs(String atString, int type) {
+ log("processAtCscs - atString = "+ atString);
+ if(mPhonebook != null) {
+ mPhonebook.handleCscsCommand(atString, type);
+ }
+ else {
+ Log.e(TAG, "Phonebook handle null for At+CSCS");
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
+ }
+ }
+
+ private void processAtCpbs(String atString, int type) {
+ log("processAtCpbs - atString = "+ atString);
+ if(mPhonebook != null) {
+ mPhonebook.handleCpbsCommand(atString, type);
+ }
+ else {
+ Log.e(TAG, "Phonebook handle null for At+CPBS");
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
+ }
+ }
+
+ private void processAtCpbr(String atString, int type, BluetoothDevice mCurrentDevice) {
+ log("processAtCpbr - atString = "+ atString);
+ if(mPhonebook != null) {
+ mPhonebook.handleCpbrCommand(atString, type, mCurrentDevice);
+ }
+ else {
+ Log.e(TAG, "Phonebook handle null for At+CPBR");
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
+ }
+ }
+
private void processUnknownAt(String atString) {
// TODO (BT)
- atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR);
+ log("processUnknownAt - atString = "+ atString);
+ String atCommand = parseUnknownAt(atString);
+ int commandType = getAtCommandType(atCommand);
+ if (atCommand.startsWith("+CSCS"))
+ processAtCscs(atCommand.substring(5), commandType);
+ else if (atCommand.startsWith("+CPBS"))
+ processAtCpbs(atCommand.substring(5), commandType);
+ else if (atCommand.startsWith("+CPBR"))
+ processAtCpbr(atCommand.substring(5), commandType, mCurrentDevice);
+ else
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
}
private void processKeyPressed() {
@@ -1550,6 +1638,43 @@
}
}
+ public void handleAccessPermissionResult(Intent intent) {
+ log("handleAccessPermissionResult");
+ if(mPhonebook != null) {
+ if (!mPhonebook.getCheckingAccessPermission()) {
+ return;
+ }
+ int atCommandResult = 0;
+ int atCommandErrorCode = 0;
+ //HeadsetBase headset = mHandsfree.getHeadset();
+ // ASSERT: (headset != null) && headSet.isConnected()
+ // REASON: mCheckingAccessPermission is true, otherwise resetAtState
+ // has set mCheckingAccessPermission to false
+ if (intent.getAction().equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
+ if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
+ BluetoothDevice.CONNECTION_ACCESS_NO) ==
+ BluetoothDevice.CONNECTION_ACCESS_YES) {
+ if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
+ mCurrentDevice.setTrust(true);
+ }
+ atCommandResult = mPhonebook.processCpbrCommand();
+ }
+ }
+ mPhonebook.setCpbrIndex(-1);
+ mPhonebook.setCheckingAccessPermission(false);
+
+ if (atCommandResult >= 0) {
+ atResponseCodeNative(atCommandResult, atCommandErrorCode);
+ }
+ else
+ log("handleAccessPermissionResult - RESULT_NONE");
+ }
+ else {
+ Log.e(TAG, "Phonebook handle null");
+ atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
+ }
+ }
+
private static final String SCHEME_TEL = "tel";
// Event types for STACK_EVENT message
@@ -1583,6 +1708,9 @@
}
}
+ /*package*/native boolean atResponseCodeNative(int responseCode, int errorCode);
+ /*package*/ native boolean atResponseStringNative(String responseString);
+
private native static void classInitNative();
private native void initializeNative();
private native void cleanupNative();
@@ -1598,11 +1726,11 @@
int batteryCharge);
private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal,
int batteryCharge);
- private native boolean atResponseCodeNative(int responseCode);
+
private native boolean clccResponseNative(int index, int dir, int status, int mode,
boolean mpty, String number, int type);
private native boolean copsResponseNative(String operatorName);
- private native boolean atResponseStringNative(String responseString);
+
private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState,
String number, int type);
}