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 T get(AxolotlAddress address) {
   synchronized (MAP_LOCK) {
     Map<Integer, T> devices = map.get(address.getName());
     if (devices == null) {
       return null;
     }
     return devices.get(address.getDeviceId());
   }
 }
  private String getSessionName(AxolotlAddress axolotlAddress) {
    Recipient recipient =
        RecipientFactory.getRecipientsFromString(context, axolotlAddress.getName(), true)
            .getPrimaryRecipient();
    long recipientId = recipient.getRecipientId();
    int deviceId = axolotlAddress.getDeviceId();

    return recipientId + (deviceId == TextSecureAddress.DEFAULT_DEVICE_ID ? "" : "." + deviceId);
  }
 public void put(AxolotlAddress address, T value) {
   synchronized (MAP_LOCK) {
     Map<Integer, T> devices = map.get(address.getName());
     if (devices == null) {
       devices = new HashMap<>();
       map.put(address.getName(), devices);
     }
     devices.put(address.getDeviceId(), value);
   }
 }
 public Map<Integer, T> getAll(AxolotlAddress address) {
   synchronized (MAP_LOCK) {
     Map<Integer, T> devices = map.get(address.getName());
     if (devices == null) {
       return new HashMap<>();
     }
     return devices;
   }
 }
 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));
   }
 }
  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());
    }
  }
 public boolean hasAny(AxolotlAddress address) {
   synchronized (MAP_LOCK) {
     Map<Integer, T> devices = map.get(address.getName());
     return devices != null && !devices.isEmpty();
   }
 }