Search in sources :

Example 6 with ServiceResponse

use of org.whispersystems.signalservice.internal.ServiceResponse in project Signal-Android by WhisperSystems.

the class RefreshOwnProfileJob method setProfileBadges.

private void setProfileBadges(@Nullable List<SignalServiceProfile.Badge> badges) {
    if (badges == null) {
        return;
    }
    Set<String> localDonorBadgeIds = Recipient.self().getBadges().stream().filter(badge -> badge.getCategory() == Badge.Category.Donor).map(Badge::getId).collect(Collectors.toSet());
    Set<String> remoteDonorBadgeIds = badges.stream().filter(badge -> Objects.equals(badge.getCategory(), Badge.Category.Donor.getCode())).map(SignalServiceProfile.Badge::getId).collect(Collectors.toSet());
    boolean remoteHasSubscriptionBadges = remoteDonorBadgeIds.stream().anyMatch(RefreshOwnProfileJob::isSubscription);
    boolean localHasSubscriptionBadges = localDonorBadgeIds.stream().anyMatch(RefreshOwnProfileJob::isSubscription);
    boolean remoteHasBoostBadges = remoteDonorBadgeIds.stream().anyMatch(RefreshOwnProfileJob::isBoost);
    boolean localHasBoostBadges = localDonorBadgeIds.stream().anyMatch(RefreshOwnProfileJob::isBoost);
    if (!remoteHasSubscriptionBadges && localHasSubscriptionBadges) {
        Badge mostRecentExpiration = Recipient.self().getBadges().stream().filter(badge -> badge.getCategory() == Badge.Category.Donor).filter(badge -> isSubscription(badge.getId())).max(Comparator.comparingLong(Badge::getExpirationTimestamp)).get();
        Log.d(TAG, "Marking subscription badge as expired, should notify next time the conversation list is open.", true);
        SignalStore.donationsValues().setExpiredBadge(mostRecentExpiration);
        if (!SignalStore.donationsValues().isUserManuallyCancelled()) {
            Log.d(TAG, "Detected an unexpected subscription expiry.", true);
            Subscriber subscriber = SignalStore.donationsValues().getSubscriber();
            boolean isDueToPaymentFailure = false;
            if (subscriber != null) {
                ServiceResponse<ActiveSubscription> response = ApplicationDependencies.getDonationsService().getSubscription(subscriber.getSubscriberId()).blockingGet();
                if (response.getResult().isPresent()) {
                    ActiveSubscription activeSubscription = response.getResult().get();
                    if (activeSubscription.isFailedPayment()) {
                        Log.d(TAG, "Unexpected expiry due to payment failure.", true);
                        SignalStore.donationsValues().setUnexpectedSubscriptionCancelationReason(activeSubscription.getActiveSubscription().getStatus());
                        isDueToPaymentFailure = true;
                    }
                }
            }
            if (!isDueToPaymentFailure) {
                Log.d(TAG, "Unexpected expiry due to inactivity.", true);
                SignalStore.donationsValues().setUnexpectedSubscriptionCancelationReason(UnexpectedSubscriptionCancellation.INACTIVE.getStatus());
            }
            MultiDeviceSubscriptionSyncRequestJob.enqueue();
            SignalStore.donationsValues().setShouldCancelSubscriptionBeforeNextSubscribeAttempt(true);
        }
    } else if (!remoteHasBoostBadges && localHasBoostBadges) {
        Badge mostRecentExpiration = Recipient.self().getBadges().stream().filter(badge -> badge.getCategory() == Badge.Category.Donor).filter(badge -> isBoost(badge.getId())).max(Comparator.comparingLong(Badge::getExpirationTimestamp)).get();
        Log.d(TAG, "Marking boost badge as expired, should notify next time the conversation list is open.", true);
        SignalStore.donationsValues().setExpiredBadge(mostRecentExpiration);
    }
    boolean userHasVisibleBadges = badges.stream().anyMatch(SignalServiceProfile.Badge::isVisible);
    boolean userHasInvisibleBadges = badges.stream().anyMatch(b -> !b.isVisible());
    List<Badge> appBadges = badges.stream().map(Badges::fromServiceBadge).collect(Collectors.toList());
    if (userHasVisibleBadges && userHasInvisibleBadges) {
        boolean displayBadgesOnProfile = SignalStore.donationsValues().getDisplayBadgesOnProfile();
        Log.d(TAG, "Detected mixed visibility of badges. Telling the server to mark them all " + (displayBadgesOnProfile ? "" : "not") + " visible.", true);
        BadgeRepository badgeRepository = new BadgeRepository(context);
        badgeRepository.setVisibilityForAllBadges(displayBadgesOnProfile, appBadges).blockingSubscribe();
    } else {
        SignalDatabase.recipients().setBadges(Recipient.self().getId(), appBadges);
    }
}
Also used : SignalStore(org.thoughtcrime.securesms.keyvalue.SignalStore) SignalDatabase(org.thoughtcrime.securesms.database.SignalDatabase) Util(org.thoughtcrime.securesms.util.Util) NonNull(androidx.annotation.NonNull) Data(org.thoughtcrime.securesms.jobmanager.Data) ProfileName(org.thoughtcrime.securesms.profiles.ProfileName) RecipientDatabase(org.thoughtcrime.securesms.database.RecipientDatabase) ProfileKey(org.signal.zkgroup.profiles.ProfileKey) SignalServiceProfile(org.whispersystems.signalservice.api.profiles.SignalServiceProfile) ProfileAndCredential(org.whispersystems.signalservice.api.profiles.ProfileAndCredential) ProfileUtil(org.thoughtcrime.securesms.util.ProfileUtil) Badges(org.thoughtcrime.securesms.badges.Badges) Badge(org.thoughtcrime.securesms.badges.models.Badge) Recipient(org.thoughtcrime.securesms.recipients.Recipient) InvalidCiphertextException(org.whispersystems.signalservice.api.crypto.InvalidCiphertextException) ProfileKeyCredential(org.signal.zkgroup.profiles.ProfileKeyCredential) PushNetworkException(org.whispersystems.signalservice.api.push.exceptions.PushNetworkException) ApplicationDependencies(org.thoughtcrime.securesms.dependencies.ApplicationDependencies) ActiveSubscription(org.whispersystems.signalservice.api.subscriptions.ActiveSubscription) ProfileKeyUtil(org.thoughtcrime.securesms.crypto.ProfileKeyUtil) Subscriber(org.thoughtcrime.securesms.subscription.Subscriber) Set(java.util.Set) TextUtils(android.text.TextUtils) NetworkConstraint(org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint) IOException(java.io.IOException) ServiceResponse(org.whispersystems.signalservice.internal.ServiceResponse) Collectors(java.util.stream.Collectors) BadgeRepository(org.thoughtcrime.securesms.badges.BadgeRepository) Optional(org.whispersystems.libsignal.util.guava.Optional) Objects(java.util.Objects) Log(org.signal.core.util.logging.Log) List(java.util.List) Nullable(androidx.annotation.Nullable) UnexpectedSubscriptionCancellation(org.thoughtcrime.securesms.components.settings.app.subscription.errors.UnexpectedSubscriptionCancellation) Job(org.thoughtcrime.securesms.jobmanager.Job) Comparator(java.util.Comparator) ActiveSubscription(org.whispersystems.signalservice.api.subscriptions.ActiveSubscription) Subscriber(org.thoughtcrime.securesms.subscription.Subscriber) BadgeRepository(org.thoughtcrime.securesms.badges.BadgeRepository) Badge(org.thoughtcrime.securesms.badges.models.Badge) SignalServiceProfile(org.whispersystems.signalservice.api.profiles.SignalServiceProfile)

Example 7 with ServiceResponse

use of org.whispersystems.signalservice.internal.ServiceResponse in project Signal-Android by WhisperSystems.

the class CdshService method getRegisteredUsers.

public Single<ServiceResponse<Map<String, ACI>>> getRegisteredUsers(String username, String password, Set<String> e164Numbers) {
    return Single.create(emitter -> {
        AtomicReference<Stage> stage = new AtomicReference<>(Stage.WAITING_TO_INITIALIZE);
        List<String> addressBook = e164Numbers.stream().map(e -> e.substring(1)).collect(Collectors.toList());
        String url = String.format("%s/discovery/%s/%s", baseUrl, hexPublicKey, hexCodeHash);
        Request request = new Request.Builder().url(url).build();
        WebSocket webSocket = client.newWebSocket(request, new WebSocketListener() {

            @Override
            public void onMessage(WebSocket webSocket, ByteString bytes) {
                switch(stage.get()) {
                    case WAITING_TO_INITIALIZE:
                        enclave.completeHandshake(bytes.toByteArray());
                        byte[] request = enclave.establishedSend(buildPlaintextRequest(username, password, addressBook));
                        stage.set(Stage.WAITING_FOR_RESPONSE);
                        webSocket.send(ByteString.of(request));
                        break;
                    case WAITING_FOR_RESPONSE:
                        byte[] response = enclave.establishedRecv(bytes.toByteArray());
                        try {
                            Map<String, ACI> out = parseResponse(addressBook, response);
                            emitter.onSuccess(ServiceResponse.forResult(out, 200, null));
                        } catch (IOException e) {
                            emitter.onSuccess(ServiceResponse.forUnknownError(e));
                        } finally {
                            webSocket.close(1000, "OK");
                        }
                        break;
                    case FAILURE:
                        Log.w(TAG, "Received a message after we entered the failure state! Ignoring.");
                        webSocket.close(1000, "OK");
                        break;
                }
            }

            @Override
            public void onClosing(WebSocket webSocket, int code, String reason) {
                if (code != 1000) {
                    Log.w(TAG, "Remote side is closing with non-normal code " + code);
                    webSocket.close(1000, "Remote closed with code " + code);
                    stage.set(Stage.FAILURE);
                    emitter.onSuccess(ServiceResponse.forApplicationError(new NonSuccessfulResponseCodeException(code), code, null));
                }
            }

            @Override
            public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                emitter.onSuccess(ServiceResponse.forApplicationError(t, response != null ? response.code() : 0, null));
                stage.set(Stage.FAILURE);
                webSocket.close(1000, "OK");
            }
        });
        webSocket.send(ByteString.of(enclave.initialRequest()));
        emitter.setCancellable(() -> webSocket.close(1000, "OK"));
    });
}
Also used : DataInputStream(java.io.DataInputStream) SSLContext(javax.net.ssl.SSLContext) Single(io.reactivex.rxjava3.core.Single) ByteArrayOutputStream(java.io.ByteArrayOutputStream) HsmEnclaveClient(org.signal.libsignal.hsmenclave.HsmEnclaveClient) Util(org.whispersystems.signalservice.internal.util.Util) TrustManager(javax.net.ssl.TrustManager) HashMap(java.util.HashMap) WebSocketListener(okhttp3.WebSocketListener) TrustStore(org.whispersystems.signalservice.api.push.TrustStore) AtomicReference(java.util.concurrent.atomic.AtomicReference) Tls12SocketFactory(org.whispersystems.signalservice.api.util.Tls12SocketFactory) Pair(org.whispersystems.libsignal.util.Pair) ByteArrayInputStream(java.io.ByteArrayInputStream) NonSuccessfulResponseCodeException(org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException) Map(java.util.Map) ByteString(okio.ByteString) ConnectionSpec(okhttp3.ConnectionSpec) Response(okhttp3.Response) Log(org.whispersystems.libsignal.logging.Log) Hex(org.whispersystems.signalservice.internal.util.Hex) Request(okhttp3.Request) ACI(org.whispersystems.signalservice.api.push.ACI) Set(java.util.Set) IOException(java.io.IOException) WebSocket(okhttp3.WebSocket) KeyManagementException(java.security.KeyManagementException) UUID(java.util.UUID) ServiceResponse(org.whispersystems.signalservice.internal.ServiceResponse) SignalServiceConfiguration(org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration) ByteUtil(org.whispersystems.libsignal.util.ByteUtil) Collectors(java.util.stream.Collectors) StandardCharsets(java.nio.charset.StandardCharsets) SSLSocketFactory(javax.net.ssl.SSLSocketFactory) TimeUnit(java.util.concurrent.TimeUnit) List(java.util.List) OkHttpClient(okhttp3.OkHttpClient) X509TrustManager(javax.net.ssl.X509TrustManager) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) BlacklistingTrustManager(org.whispersystems.signalservice.internal.util.BlacklistingTrustManager) Collections(java.util.Collections) WebSocketListener(okhttp3.WebSocketListener) ByteString(okio.ByteString) Request(okhttp3.Request) NonSuccessfulResponseCodeException(org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException) AtomicReference(java.util.concurrent.atomic.AtomicReference) ByteString(okio.ByteString) IOException(java.io.IOException) WebSocket(okhttp3.WebSocket) Response(okhttp3.Response) ServiceResponse(org.whispersystems.signalservice.internal.ServiceResponse) HashMap(java.util.HashMap) Map(java.util.Map)

Example 8 with ServiceResponse

use of org.whispersystems.signalservice.internal.ServiceResponse in project Signal-Android by WhisperSystems.

the class MessagingService method sendToGroup.

public Single<ServiceResponse<SendGroupMessageResponse>> sendToGroup(byte[] body, byte[] joinedUnidentifiedAccess, long timestamp, boolean online) {
    List<String> headers = new LinkedList<String>() {

        {
            add("content-type:application/vnd.signal-messenger.mrm");
            add("Unidentified-Access-Key:" + Base64.encodeBytes(joinedUnidentifiedAccess));
        }
    };
    String path = String.format(Locale.US, "/v1/messages/multi_recipient?ts=%s&online=%s", timestamp, online);
    WebSocketRequestMessage requestMessage = WebSocketRequestMessage.newBuilder().setId(new SecureRandom().nextLong()).setVerb("PUT").setPath(path).addAllHeaders(headers).setBody(ByteString.copyFrom(body)).build();
    return signalWebSocket.request(requestMessage).map(DefaultResponseMapper.extend(SendGroupMessageResponse.class).withCustomError(401, (status, errorBody, getHeader) -> new InvalidUnidentifiedAccessHeaderException()).withCustomError(404, (status, errorBody, getHeader) -> new NotFoundException("At least one unregistered user in message send.")).withCustomError(409, (status, errorBody, getHeader) -> {
        GroupMismatchedDevices[] mismatchedDevices = JsonUtil.fromJsonResponse(errorBody, GroupMismatchedDevices[].class);
        return new GroupMismatchedDevicesException(mismatchedDevices);
    }).withCustomError(410, (status, errorBody, getHeader) -> {
        GroupStaleDevices[] staleDevices = JsonUtil.fromJsonResponse(errorBody, GroupStaleDevices[].class);
        return new GroupStaleDevicesException(staleDevices);
    }).build()::map).onErrorReturn(ServiceResponse::forUnknownError);
}
Also used : Single(io.reactivex.rxjava3.core.Single) SignalWebSocket(org.whispersystems.signalservice.api.SignalWebSocket) Util(org.whispersystems.signalservice.internal.util.Util) SecureRandom(java.security.SecureRandom) UnidentifiedAccess(org.whispersystems.signalservice.api.crypto.UnidentifiedAccess) NotFoundException(org.whispersystems.signalservice.api.push.exceptions.NotFoundException) GroupMismatchedDevicesException(org.whispersystems.signalservice.internal.push.exceptions.GroupMismatchedDevicesException) Locale(java.util.Locale) GroupStaleDevicesException(org.whispersystems.signalservice.internal.push.exceptions.GroupStaleDevicesException) LinkedList(java.util.LinkedList) GroupMismatchedDevices(org.whispersystems.signalservice.internal.push.GroupMismatchedDevices) DefaultResponseMapper(org.whispersystems.signalservice.internal.websocket.DefaultResponseMapper) SendMessageResponse(org.whispersystems.signalservice.internal.push.SendMessageResponse) InvalidUnidentifiedAccessHeaderException(org.whispersystems.signalservice.internal.push.exceptions.InvalidUnidentifiedAccessHeaderException) WebSocketRequestMessage(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage) ServiceResponse(org.whispersystems.signalservice.internal.ServiceResponse) JsonUtil(org.whispersystems.signalservice.internal.util.JsonUtil) OutgoingPushMessageList(org.whispersystems.signalservice.internal.push.OutgoingPushMessageList) Optional(org.whispersystems.libsignal.util.guava.Optional) UnregisteredUserException(org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException) ByteString(com.google.protobuf.ByteString) List(java.util.List) GroupStaleDevices(org.whispersystems.signalservice.internal.push.GroupStaleDevices) Base64(org.whispersystems.util.Base64) SendGroupMessageResponse(org.whispersystems.signalservice.internal.push.SendGroupMessageResponse) ResponseMapper(org.whispersystems.signalservice.internal.websocket.ResponseMapper) ServiceResponseProcessor(org.whispersystems.signalservice.internal.ServiceResponseProcessor) GroupMismatchedDevicesException(org.whispersystems.signalservice.internal.push.exceptions.GroupMismatchedDevicesException) WebSocketRequestMessage(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage) SecureRandom(java.security.SecureRandom) NotFoundException(org.whispersystems.signalservice.api.push.exceptions.NotFoundException) ByteString(com.google.protobuf.ByteString) LinkedList(java.util.LinkedList) GroupStaleDevicesException(org.whispersystems.signalservice.internal.push.exceptions.GroupStaleDevicesException) ServiceResponse(org.whispersystems.signalservice.internal.ServiceResponse) GroupStaleDevices(org.whispersystems.signalservice.internal.push.GroupStaleDevices) InvalidUnidentifiedAccessHeaderException(org.whispersystems.signalservice.internal.push.exceptions.InvalidUnidentifiedAccessHeaderException)

Example 9 with ServiceResponse

use of org.whispersystems.signalservice.internal.ServiceResponse in project Signal-Android by WhisperSystems.

the class MessagingService method send.

public Single<ServiceResponse<SendMessageResponse>> send(OutgoingPushMessageList list, Optional<UnidentifiedAccess> unidentifiedAccess) {
    List<String> headers = new LinkedList<String>() {

        {
            add("content-type:application/json");
        }
    };
    WebSocketRequestMessage requestMessage = WebSocketRequestMessage.newBuilder().setId(new SecureRandom().nextLong()).setVerb("PUT").setPath(String.format("/v1/messages/%s", list.getDestination())).addAllHeaders(headers).setBody(ByteString.copyFrom(JsonUtil.toJson(list).getBytes())).build();
    ResponseMapper<SendMessageResponse> responseMapper = DefaultResponseMapper.extend(SendMessageResponse.class).withResponseMapper((status, body, getHeader, unidentified) -> {
        SendMessageResponse sendMessageResponse = Util.isEmpty(body) ? new SendMessageResponse(false, unidentified) : JsonUtil.fromJsonResponse(body, SendMessageResponse.class);
        sendMessageResponse.setSentUnidentfied(unidentified);
        return ServiceResponse.forResult(sendMessageResponse, status, body);
    }).withCustomError(404, (status, body, getHeader) -> new UnregisteredUserException(list.getDestination(), new NotFoundException("not found"))).build();
    return signalWebSocket.request(requestMessage, unidentifiedAccess).map(responseMapper::map).onErrorReturn(ServiceResponse::forUnknownError);
}
Also used : Single(io.reactivex.rxjava3.core.Single) SignalWebSocket(org.whispersystems.signalservice.api.SignalWebSocket) Util(org.whispersystems.signalservice.internal.util.Util) SecureRandom(java.security.SecureRandom) UnidentifiedAccess(org.whispersystems.signalservice.api.crypto.UnidentifiedAccess) NotFoundException(org.whispersystems.signalservice.api.push.exceptions.NotFoundException) GroupMismatchedDevicesException(org.whispersystems.signalservice.internal.push.exceptions.GroupMismatchedDevicesException) Locale(java.util.Locale) GroupStaleDevicesException(org.whispersystems.signalservice.internal.push.exceptions.GroupStaleDevicesException) LinkedList(java.util.LinkedList) GroupMismatchedDevices(org.whispersystems.signalservice.internal.push.GroupMismatchedDevices) DefaultResponseMapper(org.whispersystems.signalservice.internal.websocket.DefaultResponseMapper) SendMessageResponse(org.whispersystems.signalservice.internal.push.SendMessageResponse) InvalidUnidentifiedAccessHeaderException(org.whispersystems.signalservice.internal.push.exceptions.InvalidUnidentifiedAccessHeaderException) WebSocketRequestMessage(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage) ServiceResponse(org.whispersystems.signalservice.internal.ServiceResponse) JsonUtil(org.whispersystems.signalservice.internal.util.JsonUtil) OutgoingPushMessageList(org.whispersystems.signalservice.internal.push.OutgoingPushMessageList) Optional(org.whispersystems.libsignal.util.guava.Optional) UnregisteredUserException(org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException) ByteString(com.google.protobuf.ByteString) List(java.util.List) GroupStaleDevices(org.whispersystems.signalservice.internal.push.GroupStaleDevices) Base64(org.whispersystems.util.Base64) SendGroupMessageResponse(org.whispersystems.signalservice.internal.push.SendGroupMessageResponse) ResponseMapper(org.whispersystems.signalservice.internal.websocket.ResponseMapper) ServiceResponseProcessor(org.whispersystems.signalservice.internal.ServiceResponseProcessor) SendMessageResponse(org.whispersystems.signalservice.internal.push.SendMessageResponse) ServiceResponse(org.whispersystems.signalservice.internal.ServiceResponse) UnregisteredUserException(org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException) WebSocketRequestMessage(org.whispersystems.signalservice.internal.websocket.WebSocketProtos.WebSocketRequestMessage) SecureRandom(java.security.SecureRandom) NotFoundException(org.whispersystems.signalservice.api.push.exceptions.NotFoundException) ByteString(com.google.protobuf.ByteString) LinkedList(java.util.LinkedList)

Example 10 with ServiceResponse

use of org.whispersystems.signalservice.internal.ServiceResponse in project Signal-Android by WhisperSystems.

the class DonationsServiceTest method givenASubscriberId_whenIGetASuccessfulResponse_thenItIsMappedWithTheCorrectStatusCodeAndNonEmptyObject.

@Test
public void givenASubscriberId_whenIGetASuccessfulResponse_thenItIsMappedWithTheCorrectStatusCodeAndNonEmptyObject() throws Exception {
    // GIVEN
    SubscriberId subscriberId = SubscriberId.generate();
    when(pushServiceSocket.getSubscription(subscriberId.serialize())).thenReturn(getActiveSubscription());
    // WHEN
    TestObserver<ServiceResponse<ActiveSubscription>> testObserver = testSubject.getSubscription(subscriberId).test();
    // THEN
    TEST_SCHEDULER.triggerActions();
    verify(pushServiceSocket).getSubscription(subscriberId.serialize());
    testObserver.assertComplete().assertValue(value -> value.getStatus() == 200 && value.getResult().isPresent());
}
Also used : ServiceResponse(org.whispersystems.signalservice.internal.ServiceResponse) SubscriberId(org.whispersystems.signalservice.api.subscriptions.SubscriberId) Test(org.junit.Test)

Aggregations

ServiceResponse (org.whispersystems.signalservice.internal.ServiceResponse)11 List (java.util.List)6 Optional (org.whispersystems.libsignal.util.guava.Optional)6 IOException (java.io.IOException)5 NonNull (androidx.annotation.NonNull)4 Single (io.reactivex.rxjava3.core.Single)4 Locale (java.util.Locale)4 Map (java.util.Map)4 Set (java.util.Set)4 TimeUnit (java.util.concurrent.TimeUnit)4 Pair (org.whispersystems.libsignal.util.Pair)4 ProfileAndCredential (org.whispersystems.signalservice.api.profiles.ProfileAndCredential)4 SignalServiceProfile (org.whispersystems.signalservice.api.profiles.SignalServiceProfile)4 ACI (org.whispersystems.signalservice.api.push.ACI)4 TextUtils (android.text.TextUtils)3 Nullable (androidx.annotation.Nullable)3 WorkerThread (androidx.annotation.WorkerThread)3 ByteString (com.google.protobuf.ByteString)3 SecureRandom (java.security.SecureRandom)3 Collections (java.util.Collections)3