FBDeviceControl/Management/FBManagedConfigClient.m (141 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 "FBManagedConfigClient.h" #import "FBAMDServiceConnection.h" NSString *const FBManagedConfigService = @"com.apple.mobile.MCInstall"; @interface FBManagedConfigClient () @property (nonatomic, strong, readonly) FBAMDServiceConnection *connection; @property (nonatomic, strong, readonly) dispatch_queue_t queue; @property (nonatomic, strong, readonly) id<FBControlCoreLogger> logger; @end @implementation FBManagedConfigClient #pragma mark Initializers + (instancetype)managedConfigClientWithConnection:(FBAMDServiceConnection *)connection logger:(id<FBControlCoreLogger>)logger { dispatch_queue_t queue = dispatch_queue_create("com.facebook.FBDeviceControl.managed_config", DISPATCH_QUEUE_SERIAL); return [[self alloc] initWithConnection:connection queue:queue logger:logger]; } - (instancetype)initWithConnection:(FBAMDServiceConnection *)connection queue:(dispatch_queue_t)queue logger:(id<FBControlCoreLogger>)logger { self = [super init]; if (!self) { return nil; } _connection = connection; _queue = queue; _logger = logger; return self; } #pragma mark Public Methods - (FBFuture<NSDictionary<NSString *, id> *> *)getCloudConfiguration { return [FBFuture onQueue:self.queue resolveValue:^ NSDictionary<NSString *, id> * (NSError **error) { NSDictionary<NSString *, id> *result = [self.connection sendAndReceiveMessage:@{@"RequestType": @"GetCloudConfiguration"} error:error]; if (!result) { return nil; } return [FBCollectionOperations recursiveFilteredJSONSerializableRepresentationOfDictionary:result]; }]; } - (FBFuture<NSNull *> *)changeWallpaperWithName:(FBWallpaperName)name data:(NSData *)data { NSNumber *whereNumber = FBManagedConfigClient.wallpaperWhereForName[name]; if (!whereNumber) { return [[FBControlCoreError describeFormat:@"%@ is not a valid Wallpaper Name", name] failFuture]; } return [self changeSettings:@[ @{@"Item": @"Wallpaper", @"Image": data, @"Where": whereNumber} ]]; } static NSString * const OrderedIdentifiers = @"OrderedIdentifiers"; static NSString * const ProfileMetadata = @"ProfileMetadata"; static NSString * const PayloadUUID = @"PayloadUUID"; static NSString * const PayloadVersion = @"PayloadVersion"; - (FBFuture<NSArray<NSString *> *> *)getProfileList { return [FBFuture onQueue:self.queue resolveValue:^ NSArray<NSString *> * (NSError **error) { NSDictionary<NSString *, id> *result = [self.connection sendAndReceiveMessage:@{@"RequestType": @"GetProfileList"} error:error]; if (!result) { return nil; } NSArray<NSString *> *orderedIdentifiers = result[OrderedIdentifiers]; if (![FBCollectionInformation isArrayHeterogeneous:orderedIdentifiers withClass:NSString.class]) { return [[FBControlCoreError describeFormat:@"%@ is not an Array<String>", OrderedIdentifiers] fail:error]; } return orderedIdentifiers; }]; } - (FBFuture<NSNull *> *)installProfile:(NSData *)payload { return [FBFuture onQueue:self.queue resolveValue:^ NSDictionary<NSString *, id> * (NSError **error) { NSDictionary<NSString *, id> *result = [self.connection sendAndReceiveMessage:@{@"RequestType": @"InstallProfile", @"Payload": payload} error:error]; if (!result) { return nil; } return [FBCollectionOperations recursiveFilteredJSONSerializableRepresentationOfDictionary:result]; }]; } - (FBFuture<NSNull *> *)removeProfile:(NSString *)profileName { return [FBFuture onQueue:self.queue resolveValue:^ NSNull * (NSError **error) { NSDictionary<NSString *, id> *result = [self.connection sendAndReceiveMessage:@{@"RequestType": @"GetProfileList"} error:error]; if (!result) { return nil; } NSDictionary<NSString *, id> *profileMetadata = result[ProfileMetadata][profileName]; if (!profileMetadata) { return [[FBControlCoreError describeFormat:@"%@ is not one of %@", profileName, [FBCollectionInformation oneLineDescriptionFromArray:result[OrderedIdentifiers]]] fail:error]; } NSDictionary<NSString *, id> *profileIdentifier = @{ @"PayloadType": @"Configuration", @"PayloadIdentifier": profileName, PayloadUUID: profileMetadata[PayloadUUID], PayloadVersion: profileMetadata[PayloadVersion] }; NSData *payload = [NSPropertyListSerialization dataWithPropertyList:profileIdentifier format:0xc8 options:0 error:error]; if (!payload) { return nil; } result = [self.connection sendAndReceiveMessage:@{@"RequestType": @"RemoveProfile", @"ProfileIdentifier": payload} error:error]; if (!result) { return nil; } NSString *status = result[@"Status"]; if ([status isEqualToString:@"Error"]) { return [[FBControlCoreError describeFormat:@"Status is Error: %@", result] fail:error]; } return NSNull.null; }]; } #pragma mark Private Methods - (FBFuture<NSNull *> *)changeSettings:(NSArray<NSDictionary<NSString *, id> *> *)settings { return [FBFuture onQueue:self.queue resolveValue:^ NSNull * (NSError **error) { NSDictionary<NSString *, id> *result = [self.connection sendAndReceiveMessage:@{@"RequestType": @"Settings", @"Settings": settings} error:error]; if (!result) { return nil; } return NSNull.null; }]; } + (NSDictionary<FBWallpaperName, NSNumber *> *)wallpaperWhereForName { static dispatch_once_t onceToken; static NSDictionary<FBWallpaperName, NSNumber *> *value; dispatch_once(&onceToken, ^{ value = @{FBWallpaperNameHomescreen: @0, FBWallpaperNameLockscreen: @1}; }); return value; } @end