Functional API

API List

  • Hummer

FunctionFunction Name
HMRStatestate
voidstartWithAppId:appVersion:eventObserver:
HMRRequestIdloginWithUid:region:token:completion:
HMRRequestIdlogout
HMRRequestIdrefreshToken:completion:
NSString *sdkVersion
BOOLsetLoggerFilePath:
BOOLsetLogLevel:
BOOLsetLogCallback:
BOOLuploadLogsManually:
  • HMRPeerService

FunctionFunction Name
instancetypeinstance
HMRRequestIdqueryUserOnlineStatus:completion:
HMRRequestIdsendMessage:withOptions:toUser:completion:
HMRRequestIdaddObserver:
HMRRequestIdremoveObserver:
  • HMRRoomService

FunctionFunction Name
instancetypeinstance
HMRRequestIdjoinRoom:withAppExtras:options:completion:
HMRRequestIdleaveRoom:completion:
HMRRequestIdqueryMemberCounts:roomRegion:completion:
HMRRequestIdqueryMembers:completion:
HMRRequestIdsetRoomAttributes:withAttributes:options:completion:
HMRRequestIdaddOrUpdateRoomAttributes:withAttributes:options:completion:
HMRRequestIddeleteRoomAttributes:byKeys:options:completion:
HMRRequestIdclearRoomAttributes:withOptions:completion:
HMRRequestIdqueryRoomAttributes:byKeys:completion:
HMRRequestIdqueryRoomAttributes:completion:
HMRRequestIdsetMemberAttributes:inRoom:withAttributes:options:completion:
HMRRequestIdaddOrUpdateMemberAttributes:inRoom:withAttributes:options:completion:
HMRRequestIddeleteMemberAttributes:inRoom:byKeys:options:completion:
HMRRequestIdclearMemberAttributes:inRoom:options:completion:
HMRRequestIdqueryMemberAttributtes:inRoom:byKeys:completion:
HMRRequestIdqueryMemberAttributtes:inRoom:completion:
HMRRequestIdsendMessage:withOptions:inRoom:completion:
HMRRequestIdaddMemberObserver:
HMRRequestIdremoveMemberObserver:
HMRRequestIdaddRoomObserver:
HMRRequestIdremoveRoomObserver:

Detailed Description

Hummer::state

+ (HMRState)state;

Get the SDK current status.

Return
  • Get the SDK current status. See details in HMRState..

Hummer::startWithAppId:appVersion:eventObserver:

+ (void)startWithAppId:(uint64_t)appId
            appVersion:(NSString *)appVer
         eventObserver:(id<HMREventObserver>)observer;

Initialize Hummer and start listening to Hummer status change.

Notes:

  • Currently, the SDK only supports one Hummer instance and each application only creates one Hummer object.
  • Unless otherwise specified, all Hummer interface functions are called asynchronously, and the API is called in the same thread.
Parameters
ParameterDescription
appIdappId used for identifying specific services. See details inAPPID application for details..
appVerCurrent application version data statistics and fault locating
observerEvent listener HMREventObserver

Hummer::loginWithUid:region:token:completion:

+ (HMRRequestId)loginWithUid:(uint64_t)uid
                      region:(NSString *)region
                       token:(NSString *)token
                  completion:(HMRRequestCompletion)completion;

Log in an SDK.

Notes:

  • Execute this method after operation of startWithAppId.
  • This method is executed after a user logs in for upper-layer services.
Parameters
ParameterDescription
uidUser ID, which cannot be 0
regionRegion parameters for starting user context. For details, contact SDK developers.
tokenA valid user token for SDK authentication
completionCallback of processing completion
Return

Hummer::logout

+ (HMRRequestId)logout;

Log out an SDK.

Notes:

  • This operation must be executed before a user deregisters (logs out of) with a service.
Return

Hummer::refreshToken:completion:

+ (HMRRequestId)refreshToken:(NSString *)token
                  completion:(HMRRequestCompletion)completion;

Refresh a user token.

Parameters
ParameterDescription
tokenA user token to be refreshed
completionCallback of completion. Errors in the callback result should be processed.
Return

Hummer::sdkVersion

+ (NSString *)sdkVersion;

Get SDK version information.

Return
  • SDK version information

Hummer::setLoggerFilePath:

+ (BOOL)setLoggerFilePath:(NSString *)path;

Set a log saving path. The caller should ensure that the path is valid.

Notes:

  • Do not set it as the root directory of a system, such as Documents and Caches!
Parameters
ParameterDescription
pathSet a log saving path.
Return
  • Operation result

Hummer::setLogLevel:

+ (BOOL)setLogLevel:(HMRLogLevel)logLevel;

Set log output level

Parameter
ParameterDescription
logLevelLog level, see details in HMRLogLevel

#####Return

  • Operation result

Hummer::setLogCallback:

+ (BOOL)setLogCallback:(HMRLogCallback _Nullable)callback;

Set log output callback.

Parameter
ParameterDescription
callbackLog output callback, see details in HMRLogCallback

#####Return

  • Operation result

Hummer::uploadLogsManually:

+ (BOOL)uploadLogsManually:(NSString *)remark;

Report log manually.

Parameter
ParameterDescription
remarkRemark

#####Return

  • Operation result

HMRPeerService

HMRPeerService::instance

+ (instancetype)instance;

Create and initialize an HMRChatService instance.

Return
  • Returned HMRPeerService instance

HMRPeerService::queryUserOnlineStatus:completion:

- (HMRRequestId)queryUserOnlineStatus:(NSSet<HMRUserId *> *)users
                           completion:(nullable void (^)(HMRRequestId requestId,
                                                         NSDictionary<HMRUserId *, NSNumber *> *onlineUsers,
                                                         NSError *error))completion;

Batch search of online users can search the status of a specific user.

Notes:

  • 200 users can be searched in batch at maximum.
Parameters
ParameterDescription
usersSearch of list of online users' IDs. See details in HMRUserId.
completionAsynchronous callback of this operation
Return

HMRPeerService::sendMessage:withOptions:toUser:completion:

- (HMRRequestId)sendMessage:(HMRMessage *)message
                withOptions:(HMRMessagingOptions *_Nullable)options
                     toUser:(HMRUserId *)user
                 completion:(nullable HMRRequestCompletion)completion;

Send P2P signaling messages.

Notes:

  • The size of a single message can be 32 KB at most.
Parameters
ParameterDescription
messageSignaling message to be sent. See details in HMRMessage.
optionsConfiguration for sending the message, reserved field. See details in HMRMessagingOptions.
userID of a signaling message receiver. See details in HMRUserId.
completionAsynchronous callback of this operation
Return

HMRPeerService::addObserver:

- (HMRRequestId)addObserver:(id<HMRPeerServiceObserver>)observer;

Add listeners for signaling messages.

Parameters
ParameterDescription
observerListener object to be added. See details in HMRPeerServiceObserver.
Return

HMRPeerService::removeObserver:

- (HMRRequestId)removeObserver:(id<HMRPeerServiceObserver>)observer;

Remove listeners for signaling messages.

Parameters
ParameterDescription
observerListener objects to be removed. See details in HMRPeerServiceObserver.
Return

HMRRoomService

HMRRoomService::instance

+ (instancetype)instance;

Create and initialize an HMRChatRoomService instance.

Return
  • Returned HMRRoomService instance

HMRRoomService::joinRoom:withAppExtras:options:completion:

- (HMRRequestId)joinRoom:(HMRRoomId *)roomId
           withAppExtras:(nullable NSDictionary<NSString *, NSString *> *)extras
                 options:(nullable HMRRoomJoiningOptions *)options
              completion:(nullable HMRRequestCompletion)completion;

APIs for joining a room.

Parameters
ParameterDescription
roomIdRoom ID specified by operations. See details in HMRRoomId.
extrasExtension information for joining a room
optionsOptional parameters for joining a room. See details in HMRRoomJoiningOptions.
completionAsynchronous callback of this operation
Return

HMRRoomService::leaveRoom:completion:

- (HMRRequestId)leaveRoom:(HMRRoomId *)roomId
               completion:(nullable HMRRequestCompletion)completion;

APIs for exiting a room.

Parameters
ParameterDescription
roomIdRoom ID specified by operations. See details in HMRRoomId.
completionAsynchronous callback of this operation
Return

HMRRoomService::queryMemberCounts:roomRegion:completion:

- (HMRRequestId)queryMemberCounts:(NSSet<NSString *> *)roomIds
                       roomRegion:(NSString *)roomRegion
                       completion:(nullable void(^)(HMRRequestId requestId,
                               NSDictionary<HMRRoomId *, NSNumber *> *memberCounts,
                               NSError *error))completion;

Batch search for number of room members.

Notes:

  • Support concurrent search of 20 rooms at maximum.
  • Do not support cross-region room search.
Parameters
ParameterDescription
roomIdsRoom ID specified by operations. See details in HMRRoomId.ID.
roomRegionRoom region. See details in HMRRoomId.region.
completionAsynchronous callback of this operation.
Return

HMRRoomService::queryMembers:completion:

- (HMRRequestId)queryMembers:(HMRRoomId *)roomId
                  completion:(nullable void(^)(HMRRequestId requestId
                      , NSArray<HMRUserId *> *members
                      , NSError *error))completion;

Get a list of members in the specific room.

Notes:

  • When over 500 members have joined a room, 500 randomly selected members are returned.
Parameters
ParameterDescription
roomIdRoom ID specified by operations. See details in HMRRoomId.
completionAsynchronous callback of this operation
Return

HMRRoomService::setRoomAttributes:withAttributes:options:completion:

- (HMRRequestId)setRoomAttributes:(HMRRoomId *)roomId
                   withAttributes:(NSDictionary <NSString *, NSString *> *)attributes
                          options:(nullable HMRRoomAttributeOptions *)options
                       completion:(nullable HMRRequestCompletion)completion;

Set room profiles.

Notes:

  • Support full configuration of room profiles for joining and exiting a room.
  • Clear room profiles when this room keeps empty for 10 minutes.
  • Size of each profile can be up to 8 KB. Each room contains 32 profiles at maximum.
  • You are not advised to set the maximum number of profiles and maximum capacity of each profile. Otherwise, you may fail to get profile value when all of them reach peak value.
  • When configuring room profiles, you should fill in parameter Key (otherwise, an error is returned), whereas parameter Value can be null.
Parameters
ParameterDescription
roomIdRoom ID specified by operations. See details in HMRRoomId.
attributesProfiles
optionsReserved fields. See details in HMRRoomAttributeOptions.
completionAsynchronous callback of this operation
Return

HMRRoomService::addOrUpdateRoomAttributes:withAttributes:options:completion:

- (HMRRequestId)addOrUpdateRoomAttributes:(HMRRoomId *)roomId
                           withAttributes:(NSDictionary <NSString *, NSString *> *)attributes
                                  options:(nullable HMRRoomAttributeOptions *)options
                               completion:(nullable HMRRequestCompletion)completion;

Add or update profiles of specific rooms.

Notes:

  • Update while there are profiles; otherwise, add profiles.
  • Clear room profiles when this room keeps empty for 10 minutes.
  • Size of each profile can be up to 8 KB. Each room contains 32 profiles at maximum.
  • You are not advised to set the maximum number of profiles and maximum capacity of each profile. Otherwise, you may fail to get profile value when all of them reach peak value.
  • When configuring room profiles, you should fill in parameter Key (otherwise, an error is returned), whereas parameter Value can be null.
Parameters
ParameterDescription
roomIdRoom ID specified by operations. See details in HMRRoomId.
attributesProfiles
optionsReserved fields. See details in HMRRoomAttributeOptions.
completionAsynchronous callback of this operation
Return

HMRRoomService::deleteRoomAttributes:byKeys:options:completion:

- (HMRRequestId)deleteRoomAttributes:(HMRRoomId *)roomId
                              byKeys:(NSSet <NSString *> *)keys
                             options:(nullable HMRRoomAttributeOptions *)options
                          completion:(nullable HMRRequestCompletion)completion;

Delete specific profiles of specific rooms.

Notes:

  • Return success if specific room profiles exist and are deleted.
  • Return success if specific room profiles do not exist.
  • Return success if some specific room profiles exist.
Parameters
ParameterDescription
roomIdRoom ID specified by operations. See details in HMRRoomId.
keysProfile keys
optionsReserved fields. See details in HMRRoomAttributeOptions.
completionAsynchronous callback of this operation
Return

HMRRoomService::clearRoomAttributes:withOptions:completion:

- (HMRRequestId)clearRoomAttributes:(HMRRoomId *)roomId
                        withOptions:(nullable HMRRoomAttributeOptions *)options
                         completion:(nullable HMRRequestCompletion)completion;

Clear profiles of specific rooms.

Notes:

  • Clear room profiles when this room keeps empty for 10 minutes.
  • Return success if profiles are not configured for a specific room or do not exist.
Parameters
ParameterDescription
roomIdRoom ID specified by operations. See details in HMRRoomId.
optionsReserved field. See details in HMRRoomAttributeOptions.
completionAsynchronous callback of this operation
Return

HMRRoomService::queryRoomAttributes:byKeys:completion:

- (HMRRequestId)queryRoomAttributes:(HMRRoomId *)roomId
                             byKeys:(NSSet <NSString *> *)keys
                         completion:(nullable void (^)(HMRRequestId requestId,
                                 NSDictionary<NSString *, NSString *> *attributes,
                                 NSError *error))completion;

Search specific profiles of specific rooms.

Notes:

  • Profile keys: A single key cannot be empty.
  • Return success if the searched profile does not exist, but the returned result is empty.
  • Only return existing profiles if some room profiles exist.
  • Return success if specific room profiles have never been configured, or have been cleared due to timeout but are not configured again. The returned result is empty.
Parameters
ParameterDescription
roomIdRoom ID specified by operations. See details in HMRRoomId.
keysProfile keys
completionAsynchronous callback of this operation
Return

HMRRoomService::queryRoomAttributes:completion:

- (HMRRequestId)queryRoomAttributes:(HMRRoomId *)roomId
                         completion:(nullable void (^)(HMRRequestId requestId,
                                 NSDictionary<NSString *, NSString *> *attributes,
                                 NSError *error))completion;

Search all profiles of specific rooms.

Notes:

-Return success if specific room profiles have never been configured, or have been cleared due to timeout but are not configured again. The returned result is empty.

Parameters
ParameterDescription
roomIdRoom ID specified by operations. See details in HMRRoomId.
completionAsynchronous callback of this operation
Return

HMRRoomService::setMemberAttributes:inRoom:withAttributes:options:completion:

- (HMRRequestId)setMemberAttributes:(HMRUserId *)member
                             inRoom:(HMRRoomId *)roomId
                     withAttributes:(NSDictionary<NSString *, NSString *> *)attributes
                            options:(nullable HMRMemberAttributeOptions *)options
                         completion:(nullable HMRRequestCompletion)completion;

Set user profiles in the current room.

Notes:

  • You can only set your own profiles at present.
  • Size of a user's profile can be up to 8 KB. 32 profiles are supported at maximum.
  • You are not advised to set the maximum number of profiles and maximum capacity of each profile. Otherwise, you may fail to get profile value when all the profiles reach peak value.
Parameters
ParameterDescription
memberMember ID specified by operations. See details in HMRUserId.
roomIdRoom ID specified by operations. See details in HMRRoomId.
attributesProfiles
optionsProfile options, reserved fields. See details in HMRMemberAttributeOptions.
completionAsynchronous callback of this operation
Return

HMRRoomService::addOrUpdateMemberAttributes:inRoom:withAttributes:options:completion:

- (HMRRequestId)addOrUpdateMemberAttributes:(HMRUserId *)member
                                     inRoom:(HMRRoomId *)roomId
                             withAttributes:(NSDictionary<NSString *, NSString *> *)attributes
                                    options:(nullable HMRMemberAttributeOptions *)options
                                 completion:(nullable HMRRequestCompletion)completion;

Add or update user profiles in the current room.

Notes:

  • Update while there are profiles; otherwise, add profiles.
  • You can only set your own profiles at present.
  • Size of a user's profile can be up to 8 KB. 32 profiles are supported at maximum.
  • You are not advised to set the maximum number of profiles and maximum capacity of each profile. Otherwise, you may fail to get profile value when all the profiles reach peak value.
  • Add or update local user profiles in batches.
Parameters
ParameterDescription
memberUser ID. See details in HMRUserId.
roomIdRoom ID specified by operations. See details in HMRRoomId.
attributesProfiles
optionsProfile options, reserved fields. See details in HMRMemberAttributeOptions.
completionAsynchronous callback of this operation
Return

HMRRoomService::deleteMemberAttributes:inRoom:byKeys:options:completion:

- (HMRRequestId)deleteMemberAttributes:(HMRUserId *)member
                                inRoom:(HMRRoomId *)roomId
                                byKeys:(NSSet<NSString *> *)keys
                               options:(nullable HMRMemberAttributeOptions *)options
                            completion:(nullable HMRRequestCompletion)completion;

Delete specific user profiles in the current room.

Notes:

  • You can only set your own profiles at present.
  • Return success if some user profiles are deleted while some user profiles do not exist.
Parameters
ParameterDescription
memberUser ID. See details in HMRUserId.
roomIdRoom ID specified by operations. See details in HMRRoomId.
keysProfile keys
optionsProfile options, reserved fields. See details in HMRMemberAttributeOptions.
completionAsynchronous callback of this operation
Return

HMRRoomService::clearMemberAttributes:inRoom:options:completion:

- (HMRRequestId)clearMemberAttributes:(HMRUserId *)member
                               inRoom:(HMRRoomId *)roomId
                              options:(nullable HMRMemberAttributeOptions *)options
                           completion:(nullable HMRRequestCompletion)completion;

Clear all user profiles in the current room.

Notes:

  • You can only set your own profiles at present.
  • Return success if no profiles are configured for the specific user.
Parameters
ParameterDescription
memberUser ID. See details in HMRUserId.
roomIdRoom ID specified by operations. See details in HMRRoomId.
optionsProfile options, reserved fields. See details in HMRMemberAttributeOptions.
completionAsynchronous callback of this operation
Return

HMRRoomService::queryMemberAttributtes:inRoom:byKeys:completion:

- (HMRRequestId)queryMemberAttributtes:(HMRUserId *)member
                                inRoom:(HMRRoomId *)roomId
                                byKeys:(NSSet<NSString *> *)keys
                            completion:(nullable void (^)(HMRRequestId requestId,
                                NSDictionary<NSString *, NSString *> *attributtes,
                                NSError *error))completion;

Search specific user profiles in the current room.

Notes:

  • Return success if the searched profile does not exist, but the returned result is empty.
  • If some profiles exist, return success only for existing profiles with these profiles being the result.
Parameters
ParameterDescription
memberUser ID. See details in HMRUserId.
roomIdRoom ID specified by operations. See details in HMRRoomId.
keysProfile keys
completionAsynchronous callback of this operation
Return

HMRRoomService::queryMemberAttributtes:inRoom:completion:

- (HMRRequestId)queryMemberAttributtes:(HMRUserId *)member
                                inRoom:(HMRRoomId *)roomId
                            completion:(nullable void (^)(HMRRequestId requestId,
                                NSDictionary<NSString *, NSString *> *attributtes,
                                NSError *error))completion;

Search all user profiles in the current room.

Notes:

  • Return success if no profiles are configured for a user. The result is empty.
Parameters
ParameterDescription
memberUser ID. See details in HMRUserId.
roomIdRoom ID specified by operations. See details in HMRRoomId.
completionAsynchronous callback of this operation
Return

HMRRoomService::sendMessage:withOptions:inRoom:completion:

- (HMRRequestId)sendMessage:(HMRMessage *)message
                withOptions:(HMRMessagingOptions *)options
                     inRoom:(HMRRoomId *)roomId
                 completion:(nullable HMRRequestCompletion)completion;

Send room messages

Parameters
ParameterDescription
messageMessage to be sent. See details in HMRMessage..
optionsConfiguration for sending the message. See details in HMRMessagingOptions.
roomIdRoom ID. See details in HMRRoomId.
completionAsynchronous callback of this operation
Return

HMRRoomService::addMemberObserver:

- (HMRRequestId)addMemberObserver:(id <HMRRoomMemberObserver>)observer;

Add listeners for members

Parameters
ParameterDescription
observerListener object to be added. See details in HMRRoomMemberObserver.
Return

HMRRoomService::removeMemberObserver:

- (HMRRequestId)removeMemberObserver:(id <HMRRoomMemberObserver>)observer;

Remove listeners for members

Parameters
ParameterDescription
observerListener objects to be removed. See details in HMRRoomMemberObserver.
Return

HMRRoomService::addRoomObserver:

- (HMRRequestId)addRoomObserver:(id <HMRRoomObserver>)observer;

Add room listeners

Parameters
ParameterDescription
observerListener object to be added. See details in HMRRoomObserver.
Return

HMRRoomService::removeRoomObserver:

- (HMRRequestId)removeRoomObserver:(id <HMRRoomObserver>)observer;

Remove room listeners

Parameters
ParameterDescription
observerListener objects to be removed. See details in HMRRoomObserver.
Return

Enumerated values & structure definition

Hummer

HMRState

typedef NS_ENUM(NSUInteger, HMRState) 

SDK current status

Enumeration ValueMeaning
HMRStateDisconnected(0)Disconnected
HMRStateConnecting(1)Connecting
HMRStateReconnecting(2)Reconnecting
HMRStateConnected(3)Connected

HMRRequestId

Unique request identifier.

typedef NSString * HMRRequestId;

HMRUserId

用于表示一个用户的对象

@interface HMRUserId : NSObject <HMRIdentifiable>

HMR_UNAVAILABLE_CONSTRUCTOR;

/**
 Create an object by user ID

 @param ID User ID
 @return Return an object; an HMRMe object is returned if it is the current user
 */
+ (instancetype)userWithID:(UInt64)ID;

/**
Get the user who is currently logged into the SDK


 @return The user who is currently logged into the SDK
 */
+ (nullable HMRMe *)getMe;

@end

HMRLogLevel

Log level

typedef NS_ENUM(NSUInteger, HMRLogLevel) {
    HMR_LOG_LEVEL_TRACE = 0, // Output logs of all levels
    HMR_LOG_LEVEL_DEBUG = 1, // Output logs of DEBUG level
    HMR_LOG_LEVEL_INFO = 2, // Output key information and logs of DEBUG level
    HMR_LOG_LEVEL_WARNING = 3, // Output warning inforamtion and logs of DEBUG and WARNING level
    HMR_LOG_LEVEL_ERROR = 4, // Output errors and logs of DEBUG, WARNING and ERROR level
};

HMRLogCallback

Log callback

typedef void (^HMRLogCallback)(HMRLogLevel level, NSString * _Nullable message);

### HMRPeerService

#### HMRMessage

Signaling message structure of a module.
​```objc
@interface HMRMessage : NSObject

/**
 Message type for identifying the type of a message, based on which the service can serialize or deserialize content data
 */
@property (nonatomic, copy, readonly) NSString *type;
/**
 Serialized message content. the SDK and background are unaware of it. It is used for pass-through.
 */
@property (nonatomic, copy, readonly) NSData *content;
/**
 Besides serialized data, the service can pass through specific extended information.
 This filed SDK and background are also unaware of and pass through the data.
 */
@property (nonatomic, copy, readonly, nullable) NSDictionary<NSString *, NSString *> *extras;

HMR_UNAVAILABLE_CONSTRUCTOR;

/**
Method for creating a message
 
 @param type Message type
 @param content Message type
 @param extras Extended information
 @return Message instance
 */
+ (instancetype)messageWithType:(NSString *)type
                        content:(NSData *)content
                      appExtras:(nullable NSDictionary<NSString *, NSString *> *)extras;

@end

HMRMessagingOptions

Message configuration information.

@interface HMRMessagingOptions : NSObject <NSCopying>

@end

HMRRoomService

HMRRoomId

Room ID. The value can contain only letters and digits selected from [a-zA-Z0-9_-], with 64 bytes at maximum.[]

@interface HMRRoomId : NSObject <NSCopying>

/**
 Room ID. The value can contain only letters and digits selected from [a-zA-Z0-9_-], with 64 bytes at maximum.
 */
@property (nonatomic, copy, readonly) NSString *ID;

/**
  Region
  * If no specific region is displayed, user login region filled in Hummer.login will be used. 
   I.e.:, specific region prioritizes over the region specified during login.
 */
@property (nonatomic, copy, readonly) NSString *region;

+ (instancetype)roomWithID:(NSString *)ID;
+ (instancetype)roomWithID:(NSString *)ID region:(NSString *)region;

@end

HMRRoomJoiningOptions

Optional parameters for joining a room.

@interface HMRRoomJoiningOptions : NSObject

@end

HMRRoomAttributeOptions

Reserved parameters for room profiles.

@interface HMRRoomAttributeOptions : NSObject

@end

HMRMemberAttributeOptions

Reserved parameters for room user profiles.

@interface HMRMemberAttributeOptions : NSObject

@end

Was this page helpful?

Helpful Not helpful
Submitted! Your feedback would help us improve the website.
Feedback
Top