internal DeviceDetails(DeviceDetails source)
		{
			Type = source.Type;
			DeviceName = source.DeviceName;
			DeviceId = source.DeviceId;
			Model = source.Model;
			OperatingSystem = source.OperatingSystem;
			OperatingSystemVersion = source.OperatingSystemVersion;
			AdditionalProperties = source.AdditionalProperties == null
				? null
				: source.AdditionalProperties.ToDictionary(
					s => s.Key,
					s => s.Value);
		}
        public async Task EnsureRegisteredAsync(UserDetails user, DeviceDetails device, string appVersion,
            bool isReplacement)
        {
            // store app version for ReplaceRegistration calls
            _appVersion = appVersion;

	        try
			{
				await _synchroniseLock.WaitAsync();
				await EnsureRegisteredWithoutLockAsync(user, device, appVersion, isReplacement);
			}
			finally
	        {
		        _synchroniseLock.Release();
	        }
        }
 private async Task UpdateDeviceInContext(DeviceDetails device)
 {
     var deviceContext = device ?? new DeviceDetails();
     deviceContext.DeviceId = _environmentInformation.DeviceId;
     deviceContext.OperatingSystem = _environmentInformation.OperatingSystem;
     deviceContext.OperatingSystemVersion = _environmentInformation.OperatingSystemVersion;
     deviceContext.Model = _environmentInformation.Model;
     await _registrationContext.SetDevice(deviceContext);
 }
 private async Task<Device> CreateServiceDevice(DeviceDetails device)
 {
     return new Device
     {
         AdditionalProperties = device == null ? null : device.AdditionalProperties,
         Id = _environmentInformation.DeviceId,
         Model = _environmentInformation.Model,
         Name = device == null ? null : device.DeviceName,
         OperatingSystem = _environmentInformation.OperatingSystem,
         OperatingSystemVersion = _environmentInformation.OperatingSystemVersion,
         PushConfiguration = await CreatePushConfiguration(),
         Secret = GetOrGenerateDeviceSecret(),
         Type = device == null ? null : device.Type
     };
 }
		private async Task RegisterAsync(UserDetails user, DeviceDetails device, string appVersion, bool alreadyLocked = false)
        {
	        try
	        {
		        if (!alreadyLocked)
		        {
			        await _synchroniseLock.WaitAsync();
		        }

		        var isAnonymous = user == null || string.IsNullOrEmpty(user.UserId);

		        _logger.LogInformation("Starting registration for user: {0}",
			        isAnonymous ? "(anonymous)" : user.UserId);

		        var request = new RegistrationDetail
		        {
			        Client = CreateClientDetail(appVersion),
			        Device = await CreateServiceDevice(device),
			        User = user == null
				        ? null
				        : CreateServiceUser(user)
		        };

		        var response = await _publicRegistrationService.RegisterAsync(request);
		        await UpdateUserInContext(user, response.UserId, isAnonymous);

		        await UpdateDeviceInContext(device);
		        _registrationContext.NetworkId = response.NetworkId;

		        _serviceContext.UpdateFromAccessDetail(response.AccessDetails);
		        await _configurationManager.UpdateConfigurationAsync(
			        response.AccessDetails.Configuration.ConfigurationItems,
			        response.AccessDetails.Configuration.ConfigurationSets);

		        _logger.LogInformation("Registered successfully.  NetworkId: {0}",
			        response.NetworkId);

				PublishRegistrationChanged();

		        _logger.LogDebug("New token expiry time: {0}", response.AccessDetails.ExpiresOn);

	        }
	        finally
	        {
		        if (!alreadyLocked)
		        {
					_synchroniseLock.Release();
				}
			}
		}
		private async Task EnsureRegisteredWithoutLockAsync(UserDetails user, DeviceDetails device, string appVersion, bool isReplacement)
		{
			var existingUser = await _registrationContext.GetUser();
			if (existingUser == null || isReplacement)
			{
				// New registration or ReplaceRegistration
				await RegisterAsync(user, device, appVersion, true);
			}

			if (isReplacement)
			{
				// Refresh token
				await _tokenRefresher.RefreshTokenAsync();
				_registrationContext.DeviceSecret = Guid.NewGuid().ToString();
			}

			PublishRegistrationChanged();
		}
        public async Task<ApiResult> UpdateRegistrationDetailsAsync(UserDetails user = null, DeviceDetails device = null)
        {
            return await ApiResult.ForOperationAsync(async () =>
            {
	            try
	            {
					await _synchroniseLock.WaitAsync();

					if (user == null && device == null)
					{
						throw new ArgumentException("user or device must be specified");
					}

					var existingUser = await _registrationContext.GetUser();
					var wasAnonymous = existingUser.IsAnonymous;
					var oldUserId = existingUser.UserId;

					if (user != null && device != null)
					{
						// Composite update
						var isAnonymous = wasAnonymous && oldUserId == user.UserId;

						try
						{
							await _secureRegistrationService.UpdateRegistrationAsync(new RegistrationDetail
							{
								Client = CreateClientDetail(),
								Device = await CreateServiceDevice(device),
								User = CreateServiceUser(user)
							});
						}
						catch (ValidationException ex)
						{
							if (ex.ValidationFailures.Count == 1 && ex.ValidationFailures.Single().FailureKey == "UserIdAlreadyTaken")
							{
								Logger.Instance.LogInformation("UserId {0} is already taken.  Starting ReplaceRegistration flow.", user.UserId);
								await EnsureRegisteredWithoutLockAsync(user, device, _appVersion, true);
								return;
							}
							else
							{
								throw;
							}
						}
						await UpdateUserInContext(user, user.UserId, isAnonymous);
						await UpdateDeviceInContext(device);
					}
					else if (user != null)
					{
						var isAnonymous = wasAnonymous && oldUserId == user.UserId;

						try
						{
							// User only update
							await _secureRegistrationService.UpdateUserAsync(CreateServiceUser(user));
						}
						catch (ValidationException ex)
						{
							if (ex.ValidationFailures.Count == 1 && ex.ValidationFailures.Single().FailureKey == "UserIdAlreadyTaken")
							{
								Logger.Instance.LogInformation("UserId {0} is already taken.  Starting ReplaceRegistration flow.", user.UserId);
								await EnsureRegisteredWithoutLockAsync(user, null, _appVersion, true);
								return;
							}
							else
							{
								throw;
							}
						}

						await UpdateUserInContext(user, user.UserId, isAnonymous);
					}
					else
					{
						// Device only update
						await _secureRegistrationService.UpdateDeviceAsync(await CreateServiceDevice(device));

						await UpdateDeviceInContext(device);
					}

					PublishRegistrationChanged();
				}
	            finally
	            {
	                _synchroniseLock.Release();
	            }
            }, "UpdateRegistrationDetailsAsync");
        }
 public async Task ReplaceRegistrationAsync(UserDetails user, DeviceDetails device)
 {
     await EnsureRegisteredAsync(user, device, _appVersion, true);
 }
		public async Task SetDevice(DeviceDetails device)
		{
			_device = device;
			await SetValueAsync(device, "Device");
		}
		public async Task<DeviceDetails> GetDevice()
		{
			return _device ?? (_device = await GetValueAsync<DeviceDetails>("Device"));

		}