FBDeviceControl/Management/FBAMRestorableDeviceManager.m (119 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #import "FBAMRestorableDeviceManager.h" #import "FBDeviceControlError.h" #import "FBDeviceControlFrameworkLoader.h" #import "FBAMRestorableDevice.h" @interface FBAMRestorableDeviceManager () @property (nonatomic, strong, readonly) dispatch_queue_t workQueue; @property (nonatomic, strong, readonly) dispatch_queue_t asyncQueue; @property (nonatomic, assign, readonly) AMDCalls calls; @property (nonatomic, assign, readwrite) int registrationID; @property (nonatomic, copy, readonly) NSString *ecidFilter; - (NSDictionary<NSString *, id> *)infoForRestorableDevice:(AMRestorableDeviceRef)device; @end static NSString *NotificationTypeToString(AMRestorableDeviceNotificationType status) { switch (status) { case AMRestorableDeviceNotificationTypeConnected: return @"connected"; case AMRestorableDeviceNotificationTypeDisconnected: return @"disconnected"; default: return @"unknown"; } } static void FB_AMRestorableDeviceListenerCallback(AMRestorableDeviceRef device, AMRestorableDeviceNotificationType status, void *context) { FBAMRestorableDeviceManager *manager = (__bridge FBAMRestorableDeviceManager *)context; id<FBControlCoreLogger> logger = manager.logger; AMRestorableDeviceState deviceState = manager.calls.RestorableDeviceGetState(device); FBiOSTargetState targetState = [FBAMRestorableDevice targetStateForDeviceState:deviceState]; NSString *identifier = [@(manager.calls.RestorableDeviceGetECID(device)) stringValue]; [logger logFormat:@"%@ %@ in state %@", device, NotificationTypeToString(status), FBiOSTargetStateStringFromState(targetState)]; if (manager.ecidFilter && ![identifier isEqualToString:manager.ecidFilter]) { [logger logFormat:@"Ignoring %@ as it does not match filter of %@", device, manager.ecidFilter]; return; } switch (status) { case AMRestorableDeviceNotificationTypeConnected: { NSDictionary<NSString *, id> *info = [manager infoForRestorableDevice:device]; [logger logFormat:@"Caching restorable device values %@", info]; [manager deviceConnected:device identifier:identifier info:info]; return; } case AMRestorableDeviceNotificationTypeDisconnected: [manager deviceDisconnected:device identifier:identifier]; return; default: [logger logFormat:@"Unknown Restorable Notification %d", status]; return; } } @implementation FBAMRestorableDeviceManager #pragma mark Initializers - (instancetype)initWithCalls:(AMDCalls)calls workQueue:(dispatch_queue_t)workQueue asyncQueue:(dispatch_queue_t)asyncQueue ecidFilter:(NSString *)ecidFilter logger:(id<FBControlCoreLogger>)logger { self = [super initWithLogger:logger]; if (!self) { return nil; } _calls = calls; _workQueue = workQueue; _asyncQueue = asyncQueue; _ecidFilter = ecidFilter; return self; } #pragma mark Abstract Implementation - (BOOL)startListeningWithError:(NSError **)error { int registrationID = self.calls.RestorableDeviceRegisterForNotifications( FB_AMRestorableDeviceListenerCallback, (void *) CFBridgingRetain(self), 0, 0 ); if (registrationID < 1) { return [[FBDeviceControlError describeFormat:@"AMRestorableDeviceRegisterForNotifications failed with %d", registrationID] failBool:error]; } self.registrationID = registrationID; return YES; } - (BOOL)stopListeningWithError:(NSError **)error { int registrationID = self.registrationID; self.registrationID = 0; if (registrationID < 1) { return [[FBDeviceControlError describe:@"Cannot unregister from AMRestorableDevice notifications, no subscription"] failBool:error]; } // Return of AMRestorableDeviceUnregisterForNotifications seems to be some random number. // However, giving invalid registrationID is fine and we still get logging. self.calls.RestorableDeviceUnregisterForNotifications(registrationID); return YES; } - (FBAMRestorableDevice *)constructPublic:(AMRestorableDeviceRef)privateDevice identifier:(NSString *)identifier info:(NSDictionary<NSString *,id> *)info { return [[FBAMRestorableDevice alloc] initWithCalls:self.calls restorableDevice:privateDevice allValues:info workQueue:self.workQueue asyncQueue:self.asyncQueue logger:[self.logger withName:identifier]]; } + (void)updatePublicReference:(FBAMRestorableDevice *)publicDevice privateDevice:(AMRestorableDeviceRef)privateDevice identifier:(NSString *)identifier info:(NSDictionary<NSString *,id> *)info { publicDevice.restorableDevice = privateDevice; publicDevice.allValues = info; } + (AMRestorableDeviceRef)extractPrivateReference:(FBAMRestorableDevice *)publicDevice { return publicDevice.restorableDevice; } - (NSDictionary<NSString *, id> *)infoForRestorableDevice:(AMRestorableDeviceRef)device { return @{ FBDeviceKeyChipID: @(self.calls.RestorableDeviceGetChipID(device)), FBDeviceKeyDeviceClass: @(self.calls.RestorableDeviceGetDeviceClass(device)), FBDeviceKeyLocationID: @(self.calls.RestorableDeviceGetLocationID(device)), FBDeviceKeySerialNumber: CFBridgingRelease(self.calls.RestorableDeviceCopySerialNumber(device)) ?: NSNull.null, FBDeviceKeyDeviceName: CFBridgingRelease(self.calls.RestorableDeviceCopyUserFriendlyName(device)) ?: NSNull.null, FBDeviceKeyProductType: CFBridgingRelease(self.calls.RestorableDeviceCopyProductString(device)) ?: NSNull.null, FBDeviceKeyUniqueChipID: @(self.calls.RestorableDeviceGetECID(device)), }; } @end