/**
   * 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;
  }
Example #10
0
  // 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;
  }