/* * Copyright 2013-2016 Amazon.com, * Inc. or its affiliates. All Rights Reserved. * * Licensed under the Amazon Software License (the "License"). * You may not use this file except in compliance with the * License. A copy of the License is located at * * http://aws.amazon.com/asl/ * * or in the "license" file accompanying this file. This file is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR * CONDITIONS OF ANY KIND, express or implied. See the License * for the specific language governing permissions and * limitations under the License. */ package com.amazonaws.mobileconnectors.cognitoidentityprovider; import android.content.Context; import android.os.Handler; import com.amazonaws.mobileconnectors.cognitoidentityprovider.exceptions.CognitoInternalErrorException; import com.amazonaws.mobileconnectors.cognitoidentityprovider.exceptions.CognitoNotAuthorizedException; import com.amazonaws.mobileconnectors.cognitoidentityprovider.exceptions.CognitoParameterInvalidException; import com.amazonaws.mobileconnectors.cognitoidentityprovider.handlers.GenericHandler; import com.amazonaws.services.cognitoidentityprovider.model.DeviceType; import com.amazonaws.services.cognitoidentityprovider.model.ForgetDeviceRequest; import com.amazonaws.services.cognitoidentityprovider.model.GetDeviceRequest; import com.amazonaws.services.cognitoidentityprovider.model.GetDeviceResult; import com.amazonaws.services.cognitoidentityprovider.model.UpdateDeviceStatusRequest; import com.amazonaws.services.cognitoidentityprovider.model.UpdateDeviceStatusResult; import java.util.Date; /** * This class is an abstraction for a tracked device. A instance of this class represents one * tracked device and encapsulates all details for that device. This class provides methods to access * the device details. The device attributes are stored as an object of {@link CognitoUserAttributes}. */ public class CognitoDevice { final private String DEVICE_NAME_ATTRIBUTE = "device_name"; final private String DEVICE_TYPE_REMEMBERED = "remembered"; final private String DEVICE_TYPE_NOT_REMEMBERED = "not_remembered"; /** * Service generated device-key. */ final private String deviceKey; /** * Device attributes, will be null if no device attributes are available. */ private CognitoUserAttributes deviceAttributes; /** * The date on which the user first authenticated on this device. */ private final Date createDate; /** * The date on which device attribute/s last modified. */ private Date lastModifiedDate; /** * Last logged in on this device. */ private Date lastAccessedDate; /** * The {@link CognitoUser} this device is linked to. */ final private CognitoUser user; /** * Required to access Android OS resources. */ final private Context context; /** * Constructs an object of type {@link CognitoDevice} with device details. * * @param deviceKey REQUIRED: The device key. * @param deviceAttributes REQUIRED: All devices attributes, stored as a {@link CognitoUserAttributes} object. * @param createDate REQUIRED: The date when the device tracking last began. * @param lastModifiedDate REQUIRED: The date on which the device attributes were last modified. * @param lastAccessedDate REQUIRED: The date this device details were last read. * @param user REQUIRED: The {@link CognitoUser} this device is linked to. * @param context REQUIRED: App context. */ public CognitoDevice(String deviceKey, CognitoUserAttributes deviceAttributes, Date createDate, Date lastModifiedDate, Date lastAccessedDate, CognitoUser user, Context context) { this.deviceKey = deviceKey; this.deviceAttributes = deviceAttributes; this.createDate = createDate; this.lastModifiedDate = lastModifiedDate; this.lastAccessedDate = lastAccessedDate; this.user = user; this.context = context; } /** * Constructs a {@link CognitoDevice} object with {@link DeviceType} object. * * @param device REQUIRED: A {@link DeviceType} object. * @param user REQUIRED: The {@link CognitoUser} this device is linked to. * @param context REQUIRED: App context. */ public CognitoDevice(DeviceType device, CognitoUser user, Context context) { this.deviceKey = device.getDeviceKey(); this.deviceAttributes = new CognitoUserAttributes(device.getDeviceAttributes()); this.createDate = device.getDeviceCreateDate(); this.lastModifiedDate = device.getDeviceLastModifiedDate(); this.lastAccessedDate = device.getDeviceLastModifiedDate(); this.user = user; this.context = context; } /** * Returns the key of this device. * * @return device key. */ public String getDeviceKey() { return deviceKey; } /** * Returns all device attributes as an {@link CognitoUserAttributes} object. * * @return device attributes as an {@link CognitoUserAttributes} object. */ public CognitoUserAttributes getDeviceAttributes() { return deviceAttributes; } /** * Returns the value assigned for a specific attribute for this device. The attribute name is * passed as a string. Returns null if this attribute is not set for this device. * * @param attributeName REQUIRED: The name of the attribute whose value is needed. * @return value associated with the passed attribute name as a string, returns null if the attribute does not have any value set. */ public String getDeviceAttribute(String attributeName) { try { return deviceAttributes.getAttributes().get(attributeName); } catch (final Exception e) { return null; } } /** * Returns the name of this device. * * @return Name of the device. */ public String getDeviceName() { return getDeviceAttribute(DEVICE_NAME_ATTRIBUTE); } /** * Returns the date this device was created in the Cognito User Pools, that is the date when the * service started tracking this device. * * @return the date when the device tracking began. */ public Date getCreateDate() { return createDate; } /** * Returns the date when attributes for this device were last modified. * * @return the date when the device attributes were last modified. */ public Date getLastModifiedDate() { return lastModifiedDate; } /** * The date when the device details were last read. * * @return the date when the device details were last read. */ public Date getLastAccessedDate() { return lastAccessedDate; } /** * Fetches device properties. Call this method to ensure the device properties are current. * Reading device properties from this object can return null values. * * @param callback REQUIRED: {@link GenericHandler} callback. */ public void getDeviceInBackground(final GenericHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); } new Thread(new Runnable() { @Override public void run() { final Handler handler = new Handler(context.getMainLooper()); Runnable returnCallback; try { final GetDeviceResult getDeviceResult = getDeviceInternal(user.getCachedSession()); updateThis(getDeviceResult.getDevice()); returnCallback = new Runnable() { @Override public void run() { callback.onSuccess(); } }; } catch (final Exception e) { returnCallback = new Runnable() { @Override public void run() { callback.onFailure(e); } }; } handler.post(returnCallback); } }).start(); } /** * Fetches device properties, in the current thread. Call this method to ensure the device properties are current. * Reading device properties from this object can return null values. * * <p> * <b>Note:</b> This method will perform network operations. Calling this method in * applications' main thread will cause Android to throw NetworkOnMainThreadException. * </p> * * @param callback REQUIRED: {@link GenericHandler} callback. */ public void getDevice(GenericHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); } try { final GetDeviceResult getDeviceResult = getDeviceInternal(user.getCachedSession()); updateThis(getDeviceResult.getDevice()); callback.onSuccess(); } catch (final Exception e) { callback.onFailure(e); } } /** * Stops tracking this device, runs in a background thread. * * @param callback REQUIRED: {@link GenericHandler} callback. */ public void forgetDeviceInBackground(final GenericHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); } new Thread(new Runnable() { @Override public void run() { final Handler handler = new Handler(context.getMainLooper()); Runnable returnCallback; try { forgetDeviceInternal(user.getCachedSession()); returnCallback = new Runnable() { @Override public void run() { callback.onSuccess(); } }; } catch (final Exception e) { returnCallback = new Runnable() { @Override public void run() { callback.onFailure(e); } }; } handler.post(returnCallback); } }).start(); } /** * Stops tracking this device, runs in the current thread. * <p> * <b>Note:</b> This method will perform network operations. Calling this method in * applications' main thread will cause Android to throw NetworkOnMainThreadException. * </p> * * @param callback REQUIRED: {@link GenericHandler} callback. */ public void forgetDevice(GenericHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); } try { forgetDeviceInternal(user.getCachedSession()); callback.onSuccess(); } catch (final Exception e) { callback.onFailure(e); } } /** * Marks this device as trusted, runs in a background. * * @param callback REQUIRED: {@link GenericHandler} callback. */ public void rememberThisDeviceInBackground(final GenericHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); } new Thread(new Runnable() { @Override public void run() { final Handler handler = new Handler(context.getMainLooper()); Runnable returnCallback; try { updateDeviceStatusInternal(user.getCachedSession(), DEVICE_TYPE_REMEMBERED); returnCallback = new Runnable() { @Override public void run() { callback.onSuccess(); } }; } catch (final Exception e) { returnCallback = new Runnable() { @Override public void run() { callback.onFailure(e); } }; } handler.post(returnCallback); } }).start(); } /** * Marks this device as trusted, runs in the current thread. * <p> * <b>Note:</b> This method will perform network operations. Calling this method in * applications' main thread will cause Android to throw NetworkOnMainThreadException. * </p> * * @param callback REQUIRED: {@link GenericHandler} callback. */ public void rememberThisDevice(GenericHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); } try { updateDeviceStatusInternal(user.getCachedSession(), DEVICE_TYPE_REMEMBERED); } catch (final Exception e) { callback.onFailure(e); } } /** * Marks this device as <b>not</b> trusted, runs in a background thread. * * @param callback REQUIRED: {@link GenericHandler} callback. */ public void doNotRememberThisDeviceInBackground(final GenericHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); } new Thread(new Runnable() { @Override public void run() { final Handler handler = new Handler(context.getMainLooper()); Runnable returnCallback; try { updateDeviceStatusInternal(user.getCachedSession(), DEVICE_TYPE_NOT_REMEMBERED); returnCallback = new Runnable() { @Override public void run() { callback.onSuccess(); } }; } catch (final Exception e) { returnCallback = new Runnable() { @Override public void run() { callback.onFailure(e); } }; } handler.post(returnCallback); } }).start(); } /** * Marks this device as <b>not</b> trusted, runs in the current thread. * <p> * <b>Note:</b> This method will perform network operations. Calling this method in * applications' main thread will cause Android to throw NetworkOnMainThreadException. * </p> * * @param callback REQUIRED: {@link GenericHandler} callback. */ public void doNotRememberThisDevice(GenericHandler callback) { if (callback == null) { throw new CognitoParameterInvalidException("callback is null"); } try { updateDeviceStatusInternal(user.getCachedSession(), DEVICE_TYPE_NOT_REMEMBERED); } catch (final Exception e) { callback.onFailure(e); } } /** * Internal method to forget this device. * * @param session REQUIRED: A valid {@link CognitoUserSession}. */ private void forgetDeviceInternal(CognitoUserSession session) { if (session != null && session.isValid()) { if (this.deviceKey != null) { final CognitoDevice currentDeviceKey = user.thisDevice(); if (this.deviceKey.equals(currentDeviceKey.getDeviceKey())) { // CognitoDeviceHelper.clearCachedDevice(user.context); } final ForgetDeviceRequest forgetDeviceRequest = new ForgetDeviceRequest(); forgetDeviceRequest.setAccessToken(session.getAccessToken().getJWTToken()); forgetDeviceRequest.setDeviceKey(this.deviceKey); user.getCognitoIdentityProviderClient().forgetDevice(forgetDeviceRequest); } else { throw new CognitoParameterInvalidException("Device key is null"); } } else { throw new CognitoNotAuthorizedException("User is not authorized"); } } /** * Internal method to fetch device details. * * @param session REQUIRED: A valid {@link CognitoUserSession}. * @return */ private GetDeviceResult getDeviceInternal(CognitoUserSession session) { if (session != null && session.isValid()) { if (this.deviceKey != null) { final GetDeviceRequest getDeviceRequest = new GetDeviceRequest(); getDeviceRequest.setAccessToken(session.getAccessToken().getJWTToken()); getDeviceRequest.setDeviceKey(this.deviceKey); return user.getCognitoIdentityProviderClient().getDevice(getDeviceRequest); } else { throw new CognitoParameterInvalidException("Device key is null"); } } else { throw new CognitoNotAuthorizedException("User is not authorized"); } } /** * Internal method to set device status. * * @param session REQUIRED: A valid {@link CognitoUserSession}. * @param deviceTrustState REQUIRED: New status of the device. * @return */ private UpdateDeviceStatusResult updateDeviceStatusInternal(CognitoUserSession session, String deviceTrustState) { if (session != null && session.isValid()) { if (this.deviceKey != null) { final UpdateDeviceStatusRequest updateDeviceStatusRequest = new UpdateDeviceStatusRequest(); updateDeviceStatusRequest.setAccessToken(session.getAccessToken().getJWTToken()); updateDeviceStatusRequest.setDeviceKey(this.deviceKey); updateDeviceStatusRequest.setDeviceRememberedStatus(deviceTrustState); return user.getCognitoIdentityProviderClient().updateDeviceStatus(updateDeviceStatusRequest); } else { throw new CognitoParameterInvalidException("Device key is invalid"); } } else { throw new CognitoNotAuthorizedException("User is not authorized"); } } /** * Internal method to update this object. * * @param device REQUIRED: {@link DeviceType} returned from the service. */ private void updateThis(DeviceType device) { if (device == null) { throw new CognitoInternalErrorException("Service returned null object, this object was not updated"); } if (!device.getDeviceKey().equals(this.deviceKey)) { throw new CognitoInternalErrorException("Service error, this object was not updated"); } this.deviceAttributes = new CognitoUserAttributes(device.getDeviceAttributes()); this.lastModifiedDate = device.getDeviceLastModifiedDate(); this.lastAccessedDate = device.getDeviceLastModifiedDate(); } }