/** * Processes LAUNCH_BROWSER proactive command from the SIM card. * * @param cmdDet Command Details container object. * @param ctlvs List of ComprehensionTlv objects following Command Details object and Device * Identities object within the proactive command * @return true if the command is processing is pending and additional asynchronous processing is * required. * @throws ResultException */ private boolean processLaunchBrowser(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { CatLog.d(this, "process LaunchBrowser"); TextMessage confirmMsg = new TextMessage(); IconId iconId = null; String url = null; ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.URL, ctlvs); if (ctlv != null) { try { byte[] rawValue = ctlv.getRawValue(); int valueIndex = ctlv.getValueIndex(); int valueLen = ctlv.getLength(); if (valueLen > 0) { url = GsmAlphabet.gsm8BitUnpackedToString(rawValue, valueIndex, valueLen); } else { url = null; } } catch (IndexOutOfBoundsException e) { throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); } } // parse alpha identifier. ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs); confirmMsg.text = ValueParser.retrieveAlphaId(ctlv); // parse icon identifier ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); if (ctlv != null) { iconId = ValueParser.retrieveIconId(ctlv); confirmMsg.iconSelfExplanatory = iconId.selfExplanatory; } // parse command qualifier value. LaunchBrowserMode mode; switch (cmdDet.commandQualifier) { case 0x00: default: mode = LaunchBrowserMode.LAUNCH_IF_NOT_ALREADY_LAUNCHED; break; case 0x02: mode = LaunchBrowserMode.USE_EXISTING_BROWSER; break; case 0x03: mode = LaunchBrowserMode.LAUNCH_NEW_BROWSER; break; } mCmdParams = new LaunchBrowserParams(cmdDet, confirmMsg, url, mode); if (iconId != null) { mIconLoadState = LOAD_SINGLE_ICON; mIconLoader.loadIcon(iconId.recordNumber, this.obtainMessage(MSG_ID_LOAD_ICON_DONE)); return true; } return false; }
/** * Interprets the user data payload as pack GSM 8-bit (a GSM alphabet string that's stored in * 8-bit unpacked format) characters, and decodes them into a String. * * @param byteCount the number of byest in the user data payload * @return a String with the decoded characters */ String getUserDataGSM8bit(int byteCount) { String ret; ret = GsmAlphabet.gsm8BitUnpackedToString(mPdu, mCur, byteCount); mCur += byteCount; return ret; }
/** * Interprets the user data payload as packed GSM 7bit characters, and decodes them into a * String. * * @param septetCount the number of septets in the user data payload * @return a String with the decoded characters */ String getUserDataGSM7Bit(int septetCount, int languageTable, int languageShiftTable) { String ret; ret = GsmAlphabet.gsm7BitPackedToString( mPdu, mCur, septetCount, mUserDataSeptetPadding, languageTable, languageShiftTable); mCur += (septetCount * 7) / 8; return ret; }
/** * Calculates the number of SMS's required to encode the message body and the number of characters * remaining until the next message. * * @param msgBody the message to encode * @param use7bitOnly ignore (but still count) illegal characters if true * @return TextEncodingDetails */ public static TextEncodingDetails calculateLength(CharSequence msgBody, boolean use7bitOnly) { CharSequence newMsgBody = null; Resources r = Resources.getSystem(); if (r.getBoolean(au.com.wallaceit.voicemail.R.bool.config_sms_force_7bit_encoding)) { newMsgBody = Sms7BitEncodingTranslator.translate(msgBody); } if (TextUtils.isEmpty(newMsgBody)) { newMsgBody = msgBody; } TextEncodingDetails ted = GsmAlphabet.countGsmSeptets(newMsgBody, use7bitOnly); if (ted == null) { return SmsMessageBase.calcUnicodeEncodingDetails(newMsgBody); } return ted; }
private byte[] buildEmailData(int length, int adnRecIndex, String email) { byte[] data = new byte[length]; for (int i = 0; i < length; i++) { data[i] = (byte) 0xff; } if (TextUtils.isEmpty(email)) { Log.w(LOG_TAG, "[buildEmailData] Empty email record"); return data; // return the empty record (for delete) } byte[] byteEmail = GsmAlphabet.stringToGsm8BitPacked(email); System.arraycopy(byteEmail, 0, data, 0, byteEmail.length); int pbrIndex = getPbrIndexBy(adnRecIndex); int recordIndex = adnRecIndex - getInitIndexBy(pbrIndex); if (mEmailPresentInIap) { data[length - 1] = (byte) (recordIndex + 1); } Log.w(LOG_TAG, " buildEmailData: data is" + IccUtils.bytesToHexString(data)); return data; }
@Override public void format(ByteArrayOutputStream buf) { if (buf == null) { return; } // Text string object int tag = 0x80 | ComprehensionTlvTag.LANGUAGE.value(); buf.write(tag); // tag byte[] data; if (mLang != null && mLang.length() > 0) { data = GsmAlphabet.stringToGsm8BitPacked(mLang); } else { data = new byte[0]; } buf.write(data.length); for (byte b : data) { buf.write(b); } }
// process CPBR command after permission check private AtCommandResult processCpbrCommand() { // Shortcut SM phonebook if ("SM".equals(mCurrentPhonebook)) { return new AtCommandResult(AtCommandResult.OK); } // Check phonebook PhonebookResult pbr = getPhonebookResult(mCurrentPhonebook, false); if (pbr == null) { return mHandsfree.reportCmeError(BluetoothCmeError.OPERATION_NOT_ALLOWED); } // More sanity checks // Send OK instead of ERROR if these checks fail. // When we send error, certain kits like BMW disconnect the // Handsfree connection. if (SystemProperties.BLUETI_ENHANCEMENT) { if (pbr.cursor.getCount() == 0 || mCpbrIndex1 <= 0 || mCpbrIndex2 < mCpbrIndex1 || mCpbrIndex1 > pbr.cursor.getCount()) { return new AtCommandResult(AtCommandResult.OK); } } else { if (pbr.cursor.getCount() == 0 || mCpbrIndex1 <= 0 || mCpbrIndex2 < mCpbrIndex1 || mCpbrIndex2 > pbr.cursor.getCount() || mCpbrIndex1 > pbr.cursor.getCount()) { return new AtCommandResult(AtCommandResult.OK); } } if (SystemProperties.BLUETI_ENHANCEMENT) { /* If index2 is more then what we currentely have in our device * We will return only the ones that we have and not just AT->OK */ if (mCpbrIndex2 > pbr.cursor.getCount()) { Log.d( TAG, "mCpbrIndex2 is: " + mCpbrIndex2 + " its more then we actually got : " + pbr.cursor.getCount()); Log.d(TAG, "Returning only what we got : " + pbr.cursor.getCount()); mCpbrIndex2 = pbr.cursor.getCount(); } } // Process AtCommandResult result = new AtCommandResult(AtCommandResult.OK); int errorDetected = -1; // no error pbr.cursor.moveToPosition(mCpbrIndex1 - 1); for (int index = mCpbrIndex1; index <= mCpbrIndex2; index++) { String number = pbr.cursor.getString(pbr.numberColumn); String name = null; int type = -1; if (pbr.nameColumn == -1) { // try caller id lookup // TODO: This code is horribly inefficient. I saw it // take 7 seconds to process 100 missed calls. Cursor c = mContext .getContentResolver() .query( Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number), new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.TYPE}, null, null, null); if (c != null) { if (c.moveToFirst()) { name = c.getString(0); type = c.getInt(1); } c.close(); } if (DBG && name == null) log("Caller ID lookup failed for " + number); } else { name = pbr.cursor.getString(pbr.nameColumn); } if (name == null) name = ""; name = name.trim(); if (name.length() > 28) name = name.substring(0, 28); if (pbr.typeColumn != -1) { type = pbr.cursor.getInt(pbr.typeColumn); name = name + "/" + getPhoneType(type); } if (number == null) number = ""; int regionType = PhoneNumberUtils.toaFromString(number); number = number.trim(); number = PhoneNumberUtils.stripSeparators(number); if (number.length() > 30) number = number.substring(0, 30); if (number.equals("-1")) { // unknown numbers are stored as -1 in our database number = ""; name = mContext.getString(R.string.unknown); } // TODO(): Handle IRA commands. It's basically // a 7 bit ASCII character set. if (!name.equals("") && mCharacterSet.equals("GSM")) { byte[] nameByte = GsmAlphabet.stringToGsm8BitPacked(name); if (nameByte == null) { name = mContext.getString(R.string.unknown); } else { name = new String(nameByte); } } result.addResponse( "+CPBR: " + index + ",\"" + number + "\"," + regionType + ",\"" + name + "\""); if (!pbr.cursor.moveToNext()) { break; } } return result; }
@Override public void format(ByteArrayOutputStream buf) { if (buf == null) { return; } // Text string object int tag = 0x80 | ComprehensionTlvTag.TEXT_STRING.value(); buf.write(tag); // tag byte[] data; if (mIsYesNo) { data = new byte[1]; data[0] = mYesNoResponse ? GET_INKEY_YES : GET_INKEY_NO; } else if (mInData != null && mInData.length() > 0) { try { // ETSI TS 102 223 8.15, should use the same format as in SMS messages // on the network. if (mIsUcs2) { // ucs2 is by definition big endian. data = mInData.getBytes("UTF-16BE"); } else if (mIsPacked) { byte[] tempData = GsmAlphabet.stringToGsm7BitPacked(mInData, 0, 0); // The size of the new buffer will be smaller than the original buffer // since 7-bit GSM packed only requires ((mInData.length * 7) + 7) / 8 bytes. // And we don't need to copy/store the first byte from the returned array // because it is used to store the count of septets used. data = new byte[tempData.length - 1]; System.arraycopy(tempData, 1, data, 0, tempData.length - 1); } else { data = GsmAlphabet.stringToGsm8BitPacked(mInData); } } catch (UnsupportedEncodingException e) { data = new byte[0]; } catch (EncodeException e) { data = new byte[0]; } } else { data = new byte[0]; } // length - one more for data coding scheme. // ETSI TS 102 223 Annex C (normative): Structure of CAT communications // Any length within the APDU limits (up to 255 bytes) can thus be encoded on two bytes. // This coding is chosen to remain compatible with TS 101.220. // Note that we need to reserve one more byte for coding scheme thus the maximum APDU // size would be 254 bytes. if (data.length + 1 <= 255) { writeLength(buf, data.length + 1); } else { data = new byte[0]; } // data coding scheme if (mIsUcs2) { buf.write(0x08); // UCS2 } else if (mIsPacked) { buf.write(0x00); // 7 bit packed } else { buf.write(0x04); // 8 bit unpacked } for (byte b : data) { buf.write(b); } }
/** * Get an SMS-SUBMIT PDU for a destination address and a message using the specified encoding. * * @param scAddress Service Centre address. Null means use default. * @param encoding Encoding defined by constants in * com.android.internal.telephony.SmsConstants.ENCODING_* * @param languageTable * @param languageShiftTable * @return a <code>SubmitPdu</code> containing the encoded SC address, if applicable, and the * encoded message. Returns null on encode error. * @hide */ public static SubmitPdu getSubmitPdu( String scAddress, String destinationAddress, String message, boolean statusReportRequested, byte[] header, int encoding, int languageTable, int languageShiftTable) { // Perform null parameter checks. if (message == null || destinationAddress == null) { return null; } if (encoding == ENCODING_UNKNOWN) { // Find the best encoding to use TextEncodingDetails ted = calculateLength(message, false); encoding = ted.codeUnitSize; languageTable = ted.languageTable; languageShiftTable = ted.languageShiftTable; if (encoding == ENCODING_7BIT && (languageTable != 0 || languageShiftTable != 0)) { if (header != null) { SmsHeader smsHeader = SmsHeader.fromByteArray(header); if (smsHeader.languageTable != languageTable || smsHeader.languageShiftTable != languageShiftTable) { Rlog.w( LOG_TAG, "Updating language table in SMS header: " + smsHeader.languageTable + " -> " + languageTable + ", " + smsHeader.languageShiftTable + " -> " + languageShiftTable); smsHeader.languageTable = languageTable; smsHeader.languageShiftTable = languageShiftTable; header = SmsHeader.toByteArray(smsHeader); } } else { SmsHeader smsHeader = new SmsHeader(); smsHeader.languageTable = languageTable; smsHeader.languageShiftTable = languageShiftTable; header = SmsHeader.toByteArray(smsHeader); } } } SubmitPdu ret = new SubmitPdu(); // MTI = SMS-SUBMIT, UDHI = header != null byte mtiByte = (byte) (0x01 | (header != null ? 0x40 : 0x00)); ByteArrayOutputStream bo = getSubmitPduHead(scAddress, destinationAddress, mtiByte, statusReportRequested, ret); // User Data (and length) byte[] userData; try { if (encoding == ENCODING_7BIT) { userData = GsmAlphabet.stringToGsm7BitPackedWithHeader( message, header, languageTable, languageShiftTable); } else { // assume UCS-2 try { userData = encodeUCS2(message, header); } catch (UnsupportedEncodingException uex) { Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex); return null; } } } catch (EncodeException ex) { // Encoding to the 7-bit alphabet failed. Let's see if we can // send it as a UCS-2 encoded message try { userData = encodeUCS2(message, header); encoding = ENCODING_16BIT; } catch (UnsupportedEncodingException uex) { Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex); return null; } } if (encoding == ENCODING_7BIT) { if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) { // Message too long Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " septets)"); return null; } // TP-Data-Coding-Scheme // Default encoding, uncompressed // To test writing messages to the SIM card, change this value 0x00 // to 0x12, which means "bits 1 and 0 contain message class, and the // class is 2". Note that this takes effect for the sender. In other // words, messages sent by the phone with this change will end up on // the receiver's SIM card. You can then send messages to yourself // (on a phone with this change) and they'll end up on the SIM card. bo.write(0x00); } else { // assume UCS-2 if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) { // Message too long Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " bytes)"); return null; } // TP-Data-Coding-Scheme // UCS-2 encoding, uncompressed bo.write(0x08); } // (no TP-Validity-Period) bo.write(userData, 0, userData.length); ret.encodedMessage = bo.toByteArray(); return ret; }
// process CPBR command after permission check /*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)) { atCommandResult = HeadsetHalConstants.AT_RESPONSE_OK; return atCommandResult; } // Check phonebook PhonebookResult pbr = getPhonebookResult(mCurrentPhonebook, true); // false); if (pbr == null) { atCommandErrorCode = BluetoothCmeError.OPERATION_NOT_ALLOWED; return atCommandResult; } // More sanity checks // Send OK instead of ERROR if these checks fail. // When we send error, certain kits like BMW disconnect the // Handsfree connection. if (pbr.cursor.getCount() == 0 || mCpbrIndex1 <= 0 || mCpbrIndex2 < mCpbrIndex1 || mCpbrIndex2 > pbr.cursor.getCount() || mCpbrIndex1 > pbr.cursor.getCount()) { atCommandResult = HeadsetHalConstants.AT_RESPONSE_OK; return atCommandResult; } // Process 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; int type = -1; if (pbr.nameColumn == -1 && number != null && number.length() > 0) { // try caller id lookup // TODO: This code is horribly inefficient. I saw it // take 7 seconds to process 100 missed calls. Cursor c = mContentResolver.query( Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number), new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.TYPE}, null, null, null); if (c != null) { if (c.moveToFirst()) { name = c.getString(0); type = c.getInt(1); } c.close(); } if (DBG && name == null) log("Caller ID lookup failed for " + number); } else if (pbr.nameColumn != -1) { name = pbr.cursor.getString(pbr.nameColumn); } else { log("processCpbrCommand: empty name and number"); } if (name == null) name = ""; name = name.trim(); if (name.length() > 28) name = name.substring(0, 28); if (pbr.typeColumn != -1) { type = pbr.cursor.getInt(pbr.typeColumn); name = name + "/" + getPhoneType(type); } if (number == null) number = ""; int regionType = PhoneNumberUtils.toaFromString(number); number = number.trim(); number = PhoneNumberUtils.stripSeparators(number); if (number.length() > 30) number = number.substring(0, 30); int numberPresentation = Calls.PRESENTATION_ALLOWED; if (pbr.numberPresentationColumn != -1) { numberPresentation = pbr.cursor.getInt(pbr.numberPresentationColumn); } if (numberPresentation != Calls.PRESENTATION_ALLOWED) { number = ""; // TODO: there are 3 types of numbers should have resource // strings for: unknown, private, and payphone name = mContext.getString(R.string.unknownNumber); } // TODO(): Handle IRA commands. It's basically // a 7 bit ASCII character set. if (!name.equals("") && mCharacterSet.equals("GSM")) { byte[] nameByte = GsmAlphabet.stringToGsm8BitPacked(name); if (nameByte == null) { name = mContext.getString(R.string.unknownNumber); } else { name = new String(nameByte); } } 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; } } if (pbr != null && pbr.cursor != null) { pbr.cursor.close(); pbr.cursor = null; } return atCommandResult; }