protected String getValidationBodyXml(String phoneNumber, String validationCode) {

    XmlSerializer serializer = Xml.newSerializer();
    StringWriter writer = new StringWriter();
    try {
      serializer.setOutput(writer);
      serializer.startDocument("UTF-8", true);
      String ns = RegistrationUtil.NS;
      serializer.startTag(ns, "validation");

      RegistrationUtil.serializeProperty(serializer, ns, "phoneNumber", phoneNumber);
      RegistrationUtil.serializeProperty(serializer, ns, "validationCode", validationCode);

      serializer.endTag(ns, "validation");
      serializer.endDocument();
      return writer.toString();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  protected String getValidationEncryptedXml(Context context, String s) {
    XmlSerializer serializer = Xml.newSerializer();
    StringWriter writer = new StringWriter();
    try {
      serializer.setOutput(writer);
      serializer.startDocument("UTF-8", true);
      serializer.startTag("", "encryptedValidation");
      // serializer.attribute("", "regNum", getRegNum());

      String encryptedText = RegistrationUtil.getEncryptedStringAsBase64(context, s);
      serializer.text(RegistrationUtil.getEncryptedStringAsBase64(context, s));

      serializer.endTag("", "encryptedValidation");
      serializer.endDocument();
      // return writer.toString();
      return encryptedText;
    } catch (Exception e) {
      Log.e(TAG, e.toString());
      return null;
    }
  }
  protected String getEnrolmentBodyXml(SIP5060ProvisioningRequest req) {

    SharedPreferences settings =
        getSharedPreferences(RegisterAccount.PREFS_FILE, Context.MODE_PRIVATE);

    String enrolmentNum = settings.getString(RegisterAccount.PREF_PHONE_NUMBER, "");

    XmlSerializer serializer = Xml.newSerializer();
    StringWriter writer = new StringWriter();
    try {
      serializer.setOutput(writer);
      serializer.startDocument("UTF-8", true);
      String ns = RegistrationUtil.NS;
      serializer.startTag(ns, "enrolment");

      RegistrationUtil.serializeProperty(serializer, ns, "phoneNumber", enrolmentNum);
      RegistrationUtil.serializeProperty(serializer, ns, "secret", req.getAuthPassword());
      RegistrationUtil.serializeProperty(serializer, ns, "firstName", "");
      RegistrationUtil.serializeProperty(serializer, ns, "lastName", "");
      RegistrationUtil.serializeProperty(serializer, ns, "emailAddress", "");
      RegistrationUtil.serializeProperty(serializer, ns, "language", getLanguage());

      serializer.endTag(ns, "enrolment");
      serializer.endDocument();
      return writer.toString();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  protected String getEnrolmentEncryptedXml(SIP5060ProvisioningRequest req) {
    XmlSerializer serializer = Xml.newSerializer();
    StringWriter writer = new StringWriter();
    try {
      serializer.setOutput(writer);
      serializer.startDocument("UTF-8", true);
      serializer.startTag("", "encryptedEnrolment");
      // serializer.attribute("", "regNum", getRegNum());

      String fullBody = getEnrolmentBodyXml(req);

      String encryptedBody = RegistrationUtil.getEncryptedStringAsBase64(this, fullBody);
      serializer.text(encryptedBody);

      serializer.endTag("", "encryptedEnrolment");
      serializer.endDocument();
      // return writer.toString();
      return encryptedBody;
    } catch (Exception e) {
      Log.e(TAG, e.toString());
      return null;
    }
  }
  protected void handleValidationResponseSMS(String smsText) {

    try {
      Pattern p = Pattern.compile(PHASE2_PATTERN);
      Matcher m = p.matcher(smsText);
      if (!m.matches()) {
        // some other SMS, not for us
        logger.info("ignoring SMS (not for us), body = " + smsText);
        return;
      }
      String validationCode2 = m.group(1);
      String validatedNumber = m.group(2);

      LumicallDataSource ds = new LumicallDataSource(this);
      ds.open();
      List<SIP5060ProvisioningRequest> reqs = ds.getSIP5060ProvisioningRequests();
      if (reqs.size() < 1) {
        logger.severe("no SIP5060ProvisioningRequest");
        throw new RegistrationFailedException("no SIP5060ProvisioningRequest");
      }
      SIP5060ProvisioningRequest req = reqs.get(0);

      req.setPhoneNumber(validatedNumber);
      req.setValidationCode2(validationCode2);

      ds.persistSIP5060ProvisioningRequest(req);
      logger.info(
          "validation reply SMS, code2 = " + validationCode2 + ", my number = " + validatedNumber);

      AppProperties props;
      try {
        props = new AppProperties(this);
      } catch (IOException e) {
        throw new RegistrationFailedException(
            e.getClass().getCanonicalName() + ": " + e.getMessage());
      }

      notification =
          new Notification(
              R.drawable.icon22, getText(R.string.enrolment_submitting_code), new Date().getTime());
      Intent notificationIntent = new Intent(this, RegisterAccount.class);
      PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
      notification.setLatestEventInfo(
          this,
          getText(R.string.enrolment_request_title),
          getText(R.string.enrolment_submitting_code),
          contentIntent);
      nm.notify(10, notification);

      String s = getValidationBodyXml(validatedNumber, validationCode2);
      String responseText =
          RegistrationUtil.submitMessage(
              props, "validate", getValidationEncryptedXml(this, s), null);

      if (!responseText.startsWith("DONE")) {
        logger.severe(
            "Bad response from validation server when sending phase2 code, text = " + responseText);
        return;
      }
      notification =
          new Notification(
              R.drawable.icon22, getText(R.string.enrolment_submitted_code), new Date().getTime());
      notificationIntent = new Intent(this, ActivateAccount.class);
      contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
      notification.setLatestEventInfo(
          this,
          getText(R.string.enrolment_request_title),
          getText(R.string.enrolment_submitted_code),
          contentIntent);
      nm.notify(10, notification);

      // This only gets updated if no exception occurred
      validationDone(this, req);

      ds.close();

    } catch (RegistrationFailedException e) {
      // TODO: display error to user
      Log.e(TAG, e.toString());

      notification =
          new Notification(
              R.drawable.icon22,
              getText(R.string.enrolment_submission_failed),
              new Date().getTime());
      Intent notificationIntent = new Intent(this, RegisterAccount.class);
      PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
      notification.setLatestEventInfo(
          this,
          getText(R.string.enrolment_request_title),
          getText(R.string.enrolment_submission_failed),
          contentIntent);
      nm.notify(10, notification);
    }
  }
  protected void doEnrolmentBySMS(Context context) {
    try {
      AppProperties props;
      try {
        props = new AppProperties(context);
      } catch (IOException e) {
        throw new RegistrationFailedException(
            e.getClass().getCanonicalName() + ": " + e.getMessage());
      }

      notification =
          new Notification(
              R.drawable.icon22, getText(R.string.enrolment_request_detail), new Date().getTime());
      Intent notificationIntent = new Intent(this, RegisterAccount.class);
      PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
      notification.setLatestEventInfo(
          this,
          getText(R.string.enrolment_request_title),
          getText(R.string.enrolment_request_detail),
          contentIntent);
      nm.notify(10, notification);

      LumicallDataSource ds = new LumicallDataSource(this);
      ds.open();
      List<SIP5060ProvisioningRequest> reqs = ds.getSIP5060ProvisioningRequests();
      if (reqs.size() < 1) {
        logger.severe("no SIP5060ProvisioningRequest");
        throw new RegistrationFailedException("no SIP5060ProvisioningRequest");
      }
      SIP5060ProvisioningRequest req = reqs.get(0);
      String enrolmentMessage = getEnrolmentEncryptedXml(req);
      String numberToDial = RegistrationUtil.submitMessage(props, "enrol", enrolmentMessage, req);
      ds.persistSIP5060ProvisioningRequest(req);

      notification =
          new Notification(
              R.drawable.icon22,
              getText(R.string.enrolment_requested_detail),
              new Date().getTime());
      notificationIntent = new Intent(this, ActivateAccount.class);
      contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
      notification.setLatestEventInfo(
          this,
          getText(R.string.enrolment_request_title),
          getText(R.string.enrolment_requested_detail),
          contentIntent);
      nm.notify(10, notification);

      logger.info("HTTP response: " + numberToDial);
      if (numberToDial.charAt(0) == '+') {
        sendValidationSMS(context, numberToDial, req.getValidationCode1(), DEFAULT_RETRY_COUNT);
      }
      ds.close();

    } catch (RegistrationFailedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();

      notification =
          new Notification(
              R.drawable.icon22, getText(R.string.enrolment_request_failed), new Date().getTime());
      Intent notificationIntent = new Intent(this, RegisterAccount.class);
      PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
      notification.setLatestEventInfo(
          this,
          getText(R.string.enrolment_request_title),
          getText(R.string.enrolment_request_failed),
          contentIntent);
      nm.notify(10, notification);
    }
  }