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(); } }