/**
   * 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;
  }
 /**
  * Search for the next COMPREHENSION-TLV object with the given tag from a list iterated by {@code
  * iter}. {@code iter} points to the object next to the found object when this method returns.
  * Used for searching the same list for similar tags, usually item id.
  *
  * @param tag A tag to search for
  * @param iter Iterator for ComprehensionTlv objects used for search
  * @return A ComprehensionTlv object that has the tag value of {@code tag}. If no object is found
  *     with the tag, null is returned.
  */
 private ComprehensionTlv searchForNextTag(
     ComprehensionTlvTag tag, Iterator<ComprehensionTlv> iter) {
   int tagValue = tag.value();
   while (iter.hasNext()) {
     ComprehensionTlv ctlv = iter.next();
     if (ctlv.getTag() == tagValue) {
       return ctlv;
     }
   }
   return null;
 }
  /**
   * Processes PLAY_TONE 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.t
   * @throws ResultException
   */
  private boolean processPlayTone(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)
      throws ResultException {

    CatLog.d(this, "process PlayTone");

    Tone tone = null;
    TextMessage textMsg = new TextMessage();
    Duration duration = null;
    IconId iconId = null;

    ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TONE, ctlvs);
    if (ctlv != null) {
      // Nothing to do for null objects.
      if (ctlv.getLength() > 0) {
        try {
          byte[] rawValue = ctlv.getRawValue();
          int valueIndex = ctlv.getValueIndex();
          int toneVal = rawValue[valueIndex];
          tone = Tone.fromInt(toneVal);
        } catch (IndexOutOfBoundsException e) {
          throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
        }
      }
    }
    // parse alpha identifier
    ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs);
    if (ctlv != null) {
      textMsg.text = ValueParser.retrieveAlphaId(ctlv);
    }
    // parse tone duration
    ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs);
    if (ctlv != null) {
      duration = ValueParser.retrieveDuration(ctlv);
    }
    // parse icon identifier
    ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
    if (ctlv != null) {
      iconId = ValueParser.retrieveIconId(ctlv);
      textMsg.iconSelfExplanatory = iconId.selfExplanatory;
    }

    boolean vibrate = (cmdDet.commandQualifier & 0x01) != 0x00;

    textMsg.responseNeeded = false;
    mCmdParams = new PlayToneParams(cmdDet, textMsg, tone, duration, vibrate);

    if (iconId != null) {
      mIconLoadState = LOAD_SINGLE_ICON;
      mIconLoader.loadIcon(iconId.recordNumber, this.obtainMessage(MSG_ID_LOAD_ICON_DONE));
      return true;
    }
    return false;
  }
  /**
   * Processes SET_UP_EVENT_LIST proactive command from the SIM card.
   *
   * @param cmdDet Command Details object retrieved.
   * @param ctlvs List of ComprehensionTlv objects following Command Details object and Device
   *     Identities object within the proactive command
   * @return false. This function always returns false meaning that the command processing is not
   *     pending and additional asynchronous processing is not required.
   */
  private boolean processSetUpEventList(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) {

    CatLog.d(this, "process SetUpEventList");
    ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.EVENT_LIST, ctlvs);
    if (ctlv != null) {
      try {
        byte[] rawValue = ctlv.getRawValue();
        int valueIndex = ctlv.getValueIndex();
        int valueLen = ctlv.getLength();
        int[] eventList = new int[valueLen];
        int eventValue = -1;
        int i = 0;
        while (valueLen > 0) {
          eventValue = rawValue[valueIndex] & 0xff;
          valueIndex++;
          valueLen--;

          switch (eventValue) {
            case USER_ACTIVITY_EVENT:
            case IDLE_SCREEN_AVAILABLE_EVENT:
            case LANGUAGE_SELECTION_EVENT:
            case BROWSER_TERMINATION_EVENT:
            case BROWSING_STATUS_EVENT:
              eventList[i] = eventValue;
              i++;
              break;
            default:
              break;
          }
        }
        mCmdParams = new SetEventListParams(cmdDet, eventList);
      } catch (IndexOutOfBoundsException e) {
        CatLog.d(this, " IndexOutofBoundException in processSetUpEventList");
      }
    }
    return false;
  }
  /**
   * Processes GET_INPUT 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 processGetInput(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs)
      throws ResultException {

    CatLog.d(this, "process GetInput");

    Input input = new Input();
    IconId iconId = null;

    ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, ctlvs);
    if (ctlv != null) {
      input.text = ValueParser.retrieveTextString(ctlv);
    } else {
      throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING);
    }

    ctlv = searchForTag(ComprehensionTlvTag.RESPONSE_LENGTH, ctlvs);
    if (ctlv != null) {
      try {
        byte[] rawValue = ctlv.getRawValue();
        int valueIndex = ctlv.getValueIndex();
        input.minLen = rawValue[valueIndex] & 0xff;
        input.maxLen = rawValue[valueIndex + 1] & 0xff;
      } catch (IndexOutOfBoundsException e) {
        throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD);
      }
    } else {
      throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING);
    }

    ctlv = searchForTag(ComprehensionTlvTag.DEFAULT_TEXT, ctlvs);
    if (ctlv != null) {
      input.defaultText = ValueParser.retrieveTextString(ctlv);
    }
    // parse icon identifier
    ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs);
    if (ctlv != null) {
      iconId = ValueParser.retrieveIconId(ctlv);
    }

    input.digitOnly = (cmdDet.commandQualifier & 0x01) == 0;
    input.ucs2 = (cmdDet.commandQualifier & 0x02) != 0;
    input.echo = (cmdDet.commandQualifier & 0x04) == 0;
    input.packed = (cmdDet.commandQualifier & 0x08) != 0;
    input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0;

    // Truncate the maxLen if it exceeds the max number of chars that can
    // be encoded. Limit depends on DCS in Command Qualifier.
    if (input.ucs2 && input.maxLen > MAX_UCS2_CHARS) {
      CatLog.d(
          this, "UCS2: received maxLen = " + input.maxLen + ", truncating to " + MAX_UCS2_CHARS);
      input.maxLen = MAX_UCS2_CHARS;
    } else if (!input.packed && input.maxLen > MAX_GSM7_DEFAULT_CHARS) {
      CatLog.d(
          this,
          "GSM 7Bit Default: received maxLen = "
              + input.maxLen
              + ", truncating to "
              + MAX_GSM7_DEFAULT_CHARS);
      input.maxLen = MAX_GSM7_DEFAULT_CHARS;
    }

    mCmdParams = new GetInputParams(cmdDet, input);

    if (iconId != null) {
      mloadIcon = true;
      mIconLoadState = LOAD_SINGLE_ICON;
      mIconLoader.loadIcon(iconId.recordNumber, this.obtainMessage(MSG_ID_LOAD_ICON_DONE));
      return true;
    }
    return false;
  }