From bf2a4f1a2ec12bcfe6f819c42f4a080e581ec78f Mon Sep 17 00:00:00 2001 From: Lawrence Lomax Date: Fri, 11 Dec 2020 07:01:36 -0800 Subject: [PATCH] Permit discovery of booted but unpaired devices Summary: There is a circumstance where we will ignore the `AMDevice` value for a booted device that is only ever then represented by an `AMRestorableDevice`. This occurs because "pairing" is always validated when obtaining `AMDevice` values. This is undesirable as it prevents us from being able to pull out values like the UDID for a device that we otherwise could do. Instead, it makes more sense to fetch the values here and to flag the device as unpaired. Starting pairing with a session is still preferred as this provides more values than just a simple "Connect" call so we attempt to do so if possible. Performing a session w/ pairing means that we obtain information such as the backup info, amongst others Because of the lack of pairing, operations later on will fail but in an informative way. There will be pairing failure messages in these eventualities which is vastly preferrable to the device "dissapearing" Reviewed By: abesto Differential Revision: D25494639 fbshipit-source-id: c21e20cc570170a889d45ef8dfde2edfd2757d35 --- .../Management/FBAMDeviceManager.h | 7 +- .../Management/FBAMDeviceManager.m | 171 ++++++++++++------ 2 files changed, 118 insertions(+), 60 deletions(-) diff --git a/FBDeviceControl/Management/FBAMDeviceManager.h b/FBDeviceControl/Management/FBAMDeviceManager.h index 9dd54ac7c..a595c603c 100644 --- a/FBDeviceControl/Management/FBAMDeviceManager.h +++ b/FBDeviceControl/Management/FBAMDeviceManager.h @@ -32,7 +32,10 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithCalls:(AMDCalls)calls workQueue:(dispatch_queue_t)workQueue asyncQueue:(dispatch_queue_t)asyncQueue ecidFilter:(NSString *)ecidFilter logger:(id)logger; /** - Starts using the AMDeviceRef via Connections. + Starts using the AMDeviceRef by doing the following: + - Starts a Connection with the device + - Performing and validating pairing. + - Starts a session. @param device the device to use. @param calls the calls to use. @@ -44,6 +47,8 @@ NS_ASSUME_NONNULL_BEGIN /** Stops using the AMDeviceRef connections. + - Stops the session with the device. + - Stops the connection with the device. @param device the device to use. @param calls the calls to use. diff --git a/FBDeviceControl/Management/FBAMDeviceManager.m b/FBDeviceControl/Management/FBAMDeviceManager.m index d36b16aa7..a37a972b9 100644 --- a/FBDeviceControl/Management/FBAMDeviceManager.m +++ b/FBDeviceControl/Management/FBAMDeviceManager.m @@ -16,6 +16,12 @@ @interface FBAMDeviceManager () ++ (BOOL)startConnectionToDevice:(AMDeviceRef)device calls:(AMDCalls)calls logger:(id)logger error:(NSError **)error; ++ (BOOL)startSessionByPairingWithDevice:(AMDeviceRef)device calls:(AMDCalls)calls logger:(id)logger error:(NSError **)error; ++ (BOOL)stopConnectionToDevice:(AMDeviceRef)device calls:(AMDCalls)calls logger:(id)logger error:(NSError **)error; ++ (BOOL)stopSessionWithDevice:(AMDeviceRef)device calls:(AMDCalls)calls logger:(id)logger error:(NSError **)error; + + @property (nonatomic, strong, readonly) dispatch_queue_t workQueue; @property (nonatomic, strong, readonly) dispatch_queue_t asyncQueue; @property (nonatomic, assign, readonly) AMDCalls calls; @@ -31,27 +37,42 @@ static BOOL FB_AMDeviceConnected(AMDeviceRef device, FBAMDeviceManager *manager) NSError *error = nil; id logger = manager.logger; AMDCalls calls = manager.calls; - if (![FBAMDeviceManager startUsing:device calls:calls logger:logger error:&error]) { - [logger.error logFormat:@"Cannot start session with device, ignoring device %@", error]; + + // Start with a basic connection. This should always succeed, even if the device is not paired. + if (![FBAMDeviceManager startConnectionToDevice:device calls:calls logger:logger error:&error]) { + [logger.error logFormat:@"Cannot connect to device, ignoring device %@", error]; return NO; } NSString *uniqueChipID = [CFBridgingRelease(calls.CopyValue(device, NULL, (__bridge CFStringRef)(FBDeviceKeyUniqueChipID))) stringValue]; if (!uniqueChipID) { - [FBAMDeviceManager stopUsing:device calls:calls logger:logger error:nil]; + [FBAMDeviceManager stopConnectionToDevice:device calls:calls logger:logger error:nil]; [logger.error logFormat:@"Ignoring device as cannot obtain ECID for it"]; return NO; } if (manager.ecidFilter && ![uniqueChipID isEqualToString:manager.ecidFilter]) { - [FBAMDeviceManager stopUsing:device calls:calls logger:logger error:nil]; + [FBAMDeviceManager stopConnectionToDevice:device calls:calls logger:logger error:nil]; [logger.error logFormat:@"Ignoring device as ECID %@ does not match filter %@", uniqueChipID, manager.ecidFilter]; return NO; } - // Get the values from the default domain. + + NSError *pairingError = nil; + BOOL pairedWithSession = [FBAMDeviceManager startSessionByPairingWithDevice:device calls:calls logger:logger error:&pairingError]; + if (!pairedWithSession) { + [logger logFormat:@"Device is not paired, degraded device information will be provied %@", pairingError]; + } + + // Get the values from the default domain, this will obtain information regardless of whether pairing was successful or not. NSMutableDictionary *info = [CFBridgingRelease(calls.CopyValue(device, NULL, NULL)) mutableCopy]; - // Get values from mobile backup. + // Get values from mobile backup, this will only return meaningful information if paired. NSDictionary * backupInfo = [CFBridgingRelease(calls.CopyValue(device, (__bridge CFStringRef)(MobileBackupDomain), NULL)) copy] ?: @{}; - // We're done with getting the device values. - [FBAMDeviceManager stopUsing:device calls:calls logger:logger error:nil]; + + // Stop the session if one was created. + if (pairedWithSession) { + [FBAMDeviceManager stopSessionWithDevice:device calls:calls logger:logger error:nil]; + } + // Always disconnect, regardless of whether there was a session or not. + [FBAMDeviceManager stopConnectionToDevice:device calls:calls logger:logger error:nil]; + if (!info) { [logger.error log:@"Ignoring device as no values were returned for it"]; return NO; @@ -119,66 +140,25 @@ - (instancetype)initWithCalls:(AMDCalls)calls workQueue:(dispatch_queue_t)workQu + (BOOL)startUsing:(AMDeviceRef)device calls:(AMDCalls)calls logger:(id)logger error:(NSError **)error { - if (device == NULL) { - return [[FBDeviceControlError - describe:@"Cannot utilize a non existent AMDeviceRef"] - failBool:error]; - } - - [logger logFormat:@"Connecting to %@", device]; - int status = calls.Connect(device); - if (status != 0) { - NSString *errorDescription = CFBridgingRelease(calls.CopyErrorText(status)); - return [[FBDeviceControlError - describeFormat:@"Failed to connect to %@. (%@)", device, errorDescription] - failBool:error]; - } - - [logger logFormat:@"Checking whether %@ is paired", device]; - if (!calls.IsPaired(device)) { - [logger logFormat:@"%@ is not paired, attempting to pair", device]; - status = calls.Pair(device); - if (status != 0) { - NSString *errorDescription = CFBridgingRelease(calls.CopyErrorText(status)); - return [[FBDeviceControlError - describeFormat:@"%@ is not paired with this host %@", device, errorDescription] - failBool:error]; - } - [logger logFormat:@"%@ succeeded pairing request", device]; - } - - [logger logFormat:@"Validating Pairing to %@", device]; - status = calls.ValidatePairing(device); - if (status != 0) { - NSString *errorDescription = CFBridgingRelease(calls.CopyErrorText(status)); - return [[FBDeviceControlError - describeFormat:@"Failed to validate pairing for %@. (%@)", device, errorDescription] - failBool:error]; + // Connect first + if (![self startConnectionToDevice:device calls:calls logger:logger error:error]) { + return NO; } - - [logger logFormat:@"Starting Session on %@", device]; - status = calls.StartSession(device); - if (status != 0) { - calls.Disconnect(device); - NSString *errorDescription = CFBridgingRelease(calls.CopyErrorText(status)); - return [[FBDeviceControlError - describeFormat:@"Failed to start session with device. (%@)", errorDescription] - failBool:error]; + // Confirm pairing and start a session + if (![self startSessionByPairingWithDevice:device calls:calls logger:logger error:error]) { + return NO; } - [logger logFormat:@"%@ ready for use", device]; return YES; } + (BOOL)stopUsing:(AMDeviceRef)device calls:(AMDCalls)calls logger:(id)logger error:(NSError **)error { - [logger logFormat:@"Stopping Session on %@", device]; - calls.StopSession(device); + // Stop the session first. + [self stopSessionWithDevice:device calls:calls logger:logger error:nil]; - [logger logFormat:@"Disconnecting from %@", device]; - calls.Disconnect(device); - - [logger logFormat:@"Disconnected from %@", device]; + // Then the connection. + [self stopConnectionToDevice:device calls:calls logger:logger error:nil]; return YES; } @@ -268,4 +248,77 @@ - (NSString *)identifierForDevice:(AMDeviceRef)amDevice return nil; } ++ (BOOL)startConnectionToDevice:(AMDeviceRef)device calls:(AMDCalls)calls logger:(id)logger error:(NSError **)error +{ + if (device == NULL) { + return [[FBDeviceControlError + describe:@"Cannot utilize a non existent AMDeviceRef"] + failBool:error]; + } + + [logger logFormat:@"Connecting to %@", device]; + int status = calls.Connect(device); + if (status != 0) { + NSString *errorDescription = CFBridgingRelease(calls.CopyErrorText(status)); + return [[FBDeviceControlError + describeFormat:@"Failed to connect to %@. (%@)", device, errorDescription] + failBool:error]; + } + return YES; +} + ++ (BOOL)startSessionByPairingWithDevice:(AMDeviceRef)device calls:(AMDCalls)calls logger:(id)logger error:(NSError **)error +{ + // Then confirm the pairing. + [logger logFormat:@"Checking whether %@ is paired", device]; + if (!calls.IsPaired(device)) { + [logger logFormat:@"%@ is not paired, attempting to pair", device]; + int status = calls.Pair(device); + if (status != 0) { + NSString *errorDescription = CFBridgingRelease(calls.CopyErrorText(status)); + return [[FBDeviceControlError + describeFormat:@"%@ is not paired with this host %@", device, errorDescription] + failBool:error]; + } + [logger logFormat:@"%@ succeeded pairing request", device]; + } + + [logger logFormat:@"Validating Pairing to %@", device]; + int status = calls.ValidatePairing(device); + if (status != 0) { + NSString *errorDescription = CFBridgingRelease(calls.CopyErrorText(status)); + return [[FBDeviceControlError + describeFormat:@"Failed to validate pairing for %@. (%@)", device, errorDescription] + failBool:error]; + } + + // A session may also be required. + [logger logFormat:@"Starting Session on %@", device]; + status = calls.StartSession(device); + if (status != 0) { + calls.Disconnect(device); + NSString *errorDescription = CFBridgingRelease(calls.CopyErrorText(status)); + return [[FBDeviceControlError + describeFormat:@"Failed to start session with device. (%@)", errorDescription] + failBool:error]; + } + + return YES; +} + ++ (BOOL)stopSessionWithDevice:(AMDeviceRef)device calls:(AMDCalls)calls logger:(id)logger error:(NSError **)error +{ + [logger logFormat:@"Stopping Session on %@", device]; + calls.StopSession(device); + return YES; +} + ++ (BOOL)stopConnectionToDevice:(AMDeviceRef)device calls:(AMDCalls)calls logger:(id)logger error:(NSError **)error +{ + [logger logFormat:@"Disconnecting from %@", device]; + calls.Disconnect(device); + [logger logFormat:@"Disconnected from %@", device]; + return YES; +} + @end