@Nullable
  private XmppAxolotlMessage buildHeader(Contact contact) {
    final XmppAxolotlMessage axolotlMessage =
        new XmppAxolotlMessage(contact.getJid().toBareJid(), getOwnDeviceId());

    Set<XmppAxolotlSession> contactSessions = findSessionsforContact(contact);
    Set<XmppAxolotlSession> ownSessions = findOwnSessions();
    if (contactSessions.isEmpty()) {
      return null;
    }
    Log.d(
        Config.LOGTAG,
        AxolotlService.getLogprefix(account) + "Building axolotl foreign keyElements...");
    for (XmppAxolotlSession session : contactSessions) {
      Log.v(
          Config.LOGTAG,
          AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString());
      axolotlMessage.addDevice(session);
    }
    Log.d(
        Config.LOGTAG,
        AxolotlService.getLogprefix(account) + "Building axolotl own keyElements...");
    for (XmppAxolotlSession session : ownSessions) {
      Log.v(
          Config.LOGTAG,
          AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString());
      axolotlMessage.addDevice(session);
    }

    return axolotlMessage;
  }
  public boolean createSessionsIfNeeded(final Conversation conversation) {
    Log.i(
        Config.LOGTAG,
        AxolotlService.getLogprefix(account) + "Creating axolotl sessions if needed...");
    boolean newSessions = false;
    Set<AxolotlAddress> addresses = findDevicesWithoutSession(conversation);
    for (AxolotlAddress address : addresses) {
      Log.d(
          Config.LOGTAG,
          AxolotlService.getLogprefix(account) + "Processing device: " + address.toString());
      FetchStatus status = fetchStatusMap.get(address);
      if (status == null || status == FetchStatus.ERROR) {
        fetchStatusMap.put(address, FetchStatus.PENDING);
        this.buildSessionFromPEP(address);
        newSessions = true;
      } else if (status == FetchStatus.PENDING) {
        newSessions = true;
      } else {
        Log.d(
            Config.LOGTAG,
            AxolotlService.getLogprefix(account)
                + "Already fetching bundle for "
                + address.toString());
      }
    }

    return newSessions;
  }
 public XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message) {
   XmppAxolotlMessage axolotlMessage = messageCache.get(message.getUuid());
   if (axolotlMessage != null) {
     Log.d(
         Config.LOGTAG, AxolotlService.getLogprefix(account) + "Cache hit: " + message.getUuid());
     messageCache.remove(message.getUuid());
   } else {
     Log.d(
         Config.LOGTAG, AxolotlService.getLogprefix(account) + "Cache miss: " + message.getUuid());
   }
   return axolotlMessage;
 }
 private void parseEvent(final Element event, final Jid from, final Account account) {
   Element items = event.findChild("items");
   String node = items == null ? null : items.getAttribute("node");
   if ("urn:xmpp:avatar:metadata".equals(node)) {
     Avatar avatar = Avatar.parseMetadata(items);
     if (avatar != null) {
       avatar.owner = from.toBareJid();
       if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) {
         if (account.getJid().toBareJid().equals(from)) {
           if (account.setAvatar(avatar.getFilename())) {
             mXmppConnectionService.databaseBackend.updateAccount(account);
           }
           mXmppConnectionService.getAvatarService().clear(account);
           mXmppConnectionService.updateConversationUi();
           mXmppConnectionService.updateAccountUi();
         } else {
           Contact contact = account.getRoster().getContact(from);
           contact.setAvatar(avatar);
           mXmppConnectionService.getAvatarService().clear(contact);
           mXmppConnectionService.updateConversationUi();
           mXmppConnectionService.updateRosterUi();
         }
       } else {
         mXmppConnectionService.fetchAvatar(account, avatar);
       }
     }
   } else if ("http://jabber.org/protocol/nick".equals(node)) {
     Element i = items.findChild("item");
     Element nick = i == null ? null : i.findChild("nick", "http://jabber.org/protocol/nick");
     if (nick != null && nick.getContent() != null) {
       Contact contact = account.getRoster().getContact(from);
       contact.setPresenceName(nick.getContent());
       mXmppConnectionService.getAvatarService().clear(account);
       mXmppConnectionService.updateConversationUi();
       mXmppConnectionService.updateAccountUi();
     }
   } else if (AxolotlService.PEP_DEVICE_LIST.equals(node)) {
     Log.d(
         Config.LOGTAG,
         AxolotlService.getLogprefix(account)
             + "Received PEP device list update from "
             + from
             + ", processing...");
     Element item = items.findChild("item");
     Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
     AxolotlService axolotlService = account.getAxolotlService();
     axolotlService.registerDevices(from, deviceIds);
     mXmppConnectionService.updateAccountUi();
   }
 }
 public void wipeOtherPepDevices() {
   Set<Integer> deviceIds = new HashSet<>();
   deviceIds.add(getOwnDeviceId());
   IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIds);
   Log.d(
       Config.LOGTAG,
       AxolotlService.getLogprefix(account) + "Wiping all other devices from Pep:" + publish);
   mXmppConnectionService.sendIqPacket(
       account,
       publish,
       new OnIqPacketReceived() {
         @Override
         public void onIqPacketReceived(Account account, IqPacket packet) {
           // TODO: implement this!
         }
       });
 }
 private XmppAxolotlSession getReceivingSession(XmppAxolotlMessage message) {
   AxolotlAddress senderAddress =
       new AxolotlAddress(message.getFrom().toString(), message.getSenderDeviceId());
   XmppAxolotlSession session = sessions.get(senderAddress);
   if (session == null) {
     Log.d(
         Config.LOGTAG,
         AxolotlService.getLogprefix(account)
             + "Account: "
             + account.getJid()
             + " No axolotl session found while parsing received message "
             + message);
     session = recreateUncachedSession(senderAddress);
     if (session == null) {
       session = new XmppAxolotlSession(account, axolotlStore, senderAddress);
     }
   }
   return session;
 }
 private void putDevicesForJid(
     String bareJid, List<Integer> deviceIds, SQLiteAxolotlStore store) {
   for (Integer deviceId : deviceIds) {
     AxolotlAddress axolotlAddress = new AxolotlAddress(bareJid, deviceId);
     Log.d(
         Config.LOGTAG,
         AxolotlService.getLogprefix(account)
             + "Building session for remote address: "
             + axolotlAddress.toString());
     String fingerprint =
         store
             .loadSession(axolotlAddress)
             .getSessionState()
             .getRemoteIdentityKey()
             .getFingerprint()
             .replaceAll("\\s", "");
     this.put(
         axolotlAddress, new XmppAxolotlSession(account, store, axolotlAddress, fingerprint));
   }
 }
  private Message parseAxolotlChat(
      Element axolotlMessage, Jid from, String id, Conversation conversation, int status) {
    Message finishedMessage = null;
    AxolotlService service = conversation.getAccount().getAxolotlService();
    XmppAxolotlMessage xmppAxolotlMessage =
        XmppAxolotlMessage.fromElement(axolotlMessage, from.toBareJid());
    XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage =
        service.processReceivingPayloadMessage(xmppAxolotlMessage);
    if (plaintextMessage != null) {
      finishedMessage =
          new Message(
              conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status);
      finishedMessage.setAxolotlFingerprint(plaintextMessage.getFingerprint());
      Log.d(
          Config.LOGTAG,
          AxolotlService.getLogprefix(finishedMessage.getConversation().getAccount())
              + " Received Message with session fingerprint: "
              + plaintextMessage.getFingerprint());
    }

    return finishedMessage;
  }
  public Set<AxolotlAddress> findDevicesWithoutSession(final Conversation conversation) {
    Log.d(
        Config.LOGTAG,
        AxolotlService.getLogprefix(account)
            + "Finding devices without session for "
            + conversation.getContact().getJid().toBareJid());
    Jid contactJid = conversation.getContact().getJid().toBareJid();
    Set<AxolotlAddress> addresses = new HashSet<>();
    if (deviceIds.get(contactJid) != null) {
      for (Integer foreignId : this.deviceIds.get(contactJid)) {
        AxolotlAddress address = new AxolotlAddress(contactJid.toString(), foreignId);
        if (sessions.get(address) == null) {
          IdentityKey identityKey =
              axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
          if (identityKey != null) {
            Log.d(
                Config.LOGTAG,
                AxolotlService.getLogprefix(account)
                    + "Already have session for "
                    + address.toString()
                    + ", adding to cache...");
            XmppAxolotlSession session =
                new XmppAxolotlSession(
                    account,
                    axolotlStore,
                    address,
                    identityKey.getFingerprint().replaceAll("\\s", ""));
            sessions.put(address, session);
          } else {
            Log.d(
                Config.LOGTAG,
                AxolotlService.getLogprefix(account)
                    + "Found device "
                    + account.getJid().toBareJid()
                    + ":"
                    + foreignId);
            addresses.add(new AxolotlAddress(contactJid.toString(), foreignId));
          }
        }
      }
    } else {
      Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Have no target devices in PEP!");
    }
    if (deviceIds.get(account.getJid().toBareJid()) != null) {
      for (Integer ownId : this.deviceIds.get(account.getJid().toBareJid())) {
        AxolotlAddress address = new AxolotlAddress(account.getJid().toBareJid().toString(), ownId);
        if (sessions.get(address) == null) {
          IdentityKey identityKey =
              axolotlStore.loadSession(address).getSessionState().getRemoteIdentityKey();
          if (identityKey != null) {
            Log.d(
                Config.LOGTAG,
                AxolotlService.getLogprefix(account)
                    + "Already have session for "
                    + address.toString()
                    + ", adding to cache...");
            XmppAxolotlSession session =
                new XmppAxolotlSession(
                    account,
                    axolotlStore,
                    address,
                    identityKey.getFingerprint().replaceAll("\\s", ""));
            sessions.put(address, session);
          } else {
            Log.d(
                Config.LOGTAG,
                AxolotlService.getLogprefix(account)
                    + "Found device "
                    + account.getJid().toBareJid()
                    + ":"
                    + ownId);
            addresses.add(new AxolotlAddress(account.getJid().toBareJid().toString(), ownId));
          }
        }
      }
    }

    return addresses;
  }
  private void buildSessionFromPEP(final AxolotlAddress address) {
    Log.i(
        Config.LOGTAG,
        AxolotlService.getLogprefix(account)
            + "Building new sesstion for "
            + address.getDeviceId());

    try {
      IqPacket bundlesPacket =
          mXmppConnectionService
              .getIqGenerator()
              .retrieveBundlesForDevice(Jid.fromString(address.getName()), address.getDeviceId());
      Log.d(
          Config.LOGTAG,
          AxolotlService.getLogprefix(account) + "Retrieving bundle: " + bundlesPacket);
      mXmppConnectionService.sendIqPacket(
          account,
          bundlesPacket,
          new OnIqPacketReceived() {
            private void finish() {
              AxolotlAddress ownAddress =
                  new AxolotlAddress(account.getJid().toBareJid().toString(), 0);
              if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)
                  && !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) {
                mXmppConnectionService.keyStatusUpdated();
              }
            }

            @Override
            public void onIqPacketReceived(Account account, IqPacket packet) {
              Log.d(
                  Config.LOGTAG,
                  AxolotlService.getLogprefix(account)
                      + "Received preKey IQ packet, processing...");
              final IqParser parser = mXmppConnectionService.getIqParser();
              final List<PreKeyBundle> preKeyBundleList = parser.preKeys(packet);
              final PreKeyBundle bundle = parser.bundle(packet);
              if (preKeyBundleList.isEmpty() || bundle == null) {
                Log.e(
                    Config.LOGTAG,
                    AxolotlService.getLogprefix(account) + "preKey IQ packet invalid: " + packet);
                fetchStatusMap.put(address, FetchStatus.ERROR);
                finish();
                return;
              }
              Random random = new Random();
              final PreKeyBundle preKey =
                  preKeyBundleList.get(random.nextInt(preKeyBundleList.size()));
              if (preKey == null) {
                // should never happen
                fetchStatusMap.put(address, FetchStatus.ERROR);
                finish();
                return;
              }

              final PreKeyBundle preKeyBundle =
                  new PreKeyBundle(
                      0,
                      address.getDeviceId(),
                      preKey.getPreKeyId(),
                      preKey.getPreKey(),
                      bundle.getSignedPreKeyId(),
                      bundle.getSignedPreKey(),
                      bundle.getSignedPreKeySignature(),
                      bundle.getIdentityKey());

              axolotlStore.saveIdentity(address.getName(), bundle.getIdentityKey());

              try {
                SessionBuilder builder = new SessionBuilder(axolotlStore, address);
                builder.process(preKeyBundle);
                XmppAxolotlSession session =
                    new XmppAxolotlSession(
                        account,
                        axolotlStore,
                        address,
                        bundle.getIdentityKey().getFingerprint().replaceAll("\\s", ""));
                sessions.put(address, session);
                fetchStatusMap.put(address, FetchStatus.SUCCESS);
              } catch (UntrustedIdentityException | InvalidKeyException e) {
                Log.e(
                    Config.LOGTAG,
                    AxolotlService.getLogprefix(account)
                        + "Error building session for "
                        + address
                        + ": "
                        + e.getClass().getName()
                        + ", "
                        + e.getMessage());
                fetchStatusMap.put(address, FetchStatus.ERROR);
              }

              finish();
            }
          });
    } catch (InvalidJidException e) {
      Log.e(
          Config.LOGTAG,
          AxolotlService.getLogprefix(account)
              + "Got address with invalid jid: "
              + address.getName());
    }
  }