@Override
 public void notifyDevices(
     byte[] sessionToken,
     List<String> deviceIds,
     ExtendedJSONObject payload,
     Long TTL,
     RequestDelegate<ExtendedJSONObject> requestDelegate) {
   requestDelegate.handleSuccess(new ExtendedJSONObject());
 }
 @Override
 public void accountStatus(String uid, RequestDelegate<AccountStatusResponse> requestDelegate) {
   boolean userFound = false;
   for (User user : users.values()) {
     if (user.uid.equals(uid)) {
       userFound = true;
       break;
     }
   }
   requestDelegate.handleSuccess(new AccountStatusResponse(userFound));
 }
 @Override
 public void recoveryEmailStatus(
     byte[] sessionToken, RequestDelegate<RecoveryEmailStatusResponse> requestDelegate) {
   String email = sessionTokens.get(Utils.byte2Hex(sessionToken));
   User user = users.get(email);
   if (email == null || user == null) {
     handleFailure(
         requestDelegate,
         HttpStatus.SC_UNAUTHORIZED,
         FxAccountRemoteError.INVALID_AUTHENTICATION_TOKEN,
         "invalid sessionToken");
     return;
   }
   requestDelegate.handleSuccess(new RecoveryEmailStatusResponse(email, user.verified));
 }
 @Override
 public void keys(byte[] keyFetchToken, RequestDelegate<TwoKeys> requestDelegate) {
   String email = keyFetchTokens.get(Utils.byte2Hex(keyFetchToken));
   User user = users.get(email);
   if (email == null || user == null) {
     handleFailure(
         requestDelegate,
         HttpStatus.SC_UNAUTHORIZED,
         FxAccountRemoteError.INVALID_AUTHENTICATION_TOKEN,
         "invalid keyFetchToken");
     return;
   }
   if (!user.verified) {
     handleFailure(
         requestDelegate,
         HttpStatus.SC_BAD_REQUEST,
         FxAccountRemoteError.ATTEMPT_TO_OPERATE_ON_AN_UNVERIFIED_ACCOUNT,
         "user is unverified");
     return;
   }
   requestDelegate.handleSuccess(new TwoKeys(user.kA, user.wrapkB));
 }
 @Override
 public void deviceList(byte[] sessionToken, RequestDelegate<FxAccountDevice[]> requestDelegate) {
   String email = sessionTokens.get(Utils.byte2Hex(sessionToken));
   User user = users.get(email);
   if (email == null || user == null) {
     handleFailure(
         requestDelegate,
         HttpStatus.SC_UNAUTHORIZED,
         FxAccountRemoteError.INVALID_AUTHENTICATION_TOKEN,
         "invalid sessionToken");
     return;
   }
   if (!user.verified) {
     handleFailure(
         requestDelegate,
         HttpStatus.SC_BAD_REQUEST,
         FxAccountRemoteError.ATTEMPT_TO_OPERATE_ON_AN_UNVERIFIED_ACCOUNT,
         "user is unverified");
     return;
   }
   Collection<FxAccountDevice> devices = user.devices.values();
   FxAccountDevice[] devicesArray = devices.toArray(new FxAccountDevice[devices.size()]);
   requestDelegate.handleSuccess(devicesArray);
 }
 @Override
 public void sign(
     byte[] sessionToken,
     ExtendedJSONObject publicKey,
     long certificateDurationInMilliseconds,
     RequestDelegate<String> requestDelegate) {
   String email = sessionTokens.get(Utils.byte2Hex(sessionToken));
   User user = users.get(email);
   if (email == null || user == null) {
     handleFailure(
         requestDelegate,
         HttpStatus.SC_UNAUTHORIZED,
         FxAccountRemoteError.INVALID_AUTHENTICATION_TOKEN,
         "invalid sessionToken");
     return;
   }
   if (!user.verified) {
     handleFailure(
         requestDelegate,
         HttpStatus.SC_BAD_REQUEST,
         FxAccountRemoteError.ATTEMPT_TO_OPERATE_ON_AN_UNVERIFIED_ACCOUNT,
         "user is unverified");
     return;
   }
   try {
     final long iat = System.currentTimeMillis();
     final long dur = certificateDurationInMilliseconds;
     final long exp = iat + dur;
     String certificate =
         mockMyIdTokenFactory.createMockMyIDCertificate(
             RSACryptoImplementation.createPublicKey(publicKey), "test", iat, exp);
     requestDelegate.handleSuccess(certificate);
   } catch (Exception e) {
     requestDelegate.handleError(e);
   }
 }
 @Override
 public void registerOrUpdateDevice(
     byte[] sessionToken,
     FxAccountDevice deviceToRegister,
     RequestDelegate<FxAccountDevice> requestDelegate) {
   String email = sessionTokens.get(Utils.byte2Hex(sessionToken));
   User user = users.get(email);
   if (email == null || user == null) {
     handleFailure(
         requestDelegate,
         HttpStatus.SC_UNAUTHORIZED,
         FxAccountRemoteError.INVALID_AUTHENTICATION_TOKEN,
         "invalid sessionToken");
     return;
   }
   if (!user.verified) {
     handleFailure(
         requestDelegate,
         HttpStatus.SC_BAD_REQUEST,
         FxAccountRemoteError.ATTEMPT_TO_OPERATE_ON_AN_UNVERIFIED_ACCOUNT,
         "user is unverified");
     return;
   }
   try {
     String deviceId = deviceToRegister.id;
     if (TextUtils.isEmpty(deviceId)) { // Create
       deviceId = UUID.randomUUID().toString();
       FxAccountDevice device =
           new FxAccountDevice(
               deviceToRegister.name, deviceId, deviceToRegister.type, null, null, null, null);
       requestDelegate.handleSuccess(device);
     } else { // Update
       FxAccountDevice existingDevice = user.devices.get(deviceId);
       if (existingDevice != null) {
         String deviceName = existingDevice.name;
         if (!TextUtils.isEmpty(deviceToRegister.name)) {
           deviceName = deviceToRegister.name;
         } // We could also update the other fields..
         FxAccountDevice device =
             new FxAccountDevice(
                 deviceName,
                 existingDevice.id,
                 existingDevice.type,
                 existingDevice.isCurrentDevice,
                 existingDevice.pushCallback,
                 existingDevice.pushPublicKey,
                 existingDevice.pushAuthKey);
         requestDelegate.handleSuccess(device);
       } else { // Device unknown
         handleFailure(
             requestDelegate,
             HttpStatus.SC_BAD_REQUEST,
             FxAccountRemoteError.UNKNOWN_DEVICE,
             "device is unknown");
         return;
       }
     }
   } catch (Exception e) {
     requestDelegate.handleError(e);
   }
 }