idb_companion/Server/FBIDBServiceHandler.mm (1,545 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 <string> #import <idbGRPC/idb.grpc.pb.h> #import <idbGRPC/idb.pb.h> #import <grpcpp/grpcpp.h> #import <FBSimulatorControl/FBSimulatorControl.h> #import "FBCodeCoverageRequest.h" #import "FBDataDownloadInput.h" #import "FBIDBCommandExecutor.h" #import "FBIDBError.h" #import "FBIDBPortsConfiguration.h" #import "FBIDBServiceHandler.h" #import "FBIDBStorageManager.h" #import "FBIDBTestOperation.h" #import "FBIDBXCTestReporter.h" #import "FBXCTestRunRequest.h" #import <FBControlCore/FBFuture.h> #import "FBXCTestDescriptor.h" #import "FBDsymInstallLinkToBundle.h" using grpc::Server; using grpc::ServerBuilder; using grpc::ServerContext; using grpc::Status; #pragma mark Private Functions static NSString *nsstring_from_c_string(const ::std::string& string) { return [NSString stringWithUTF8String:string.c_str()]; } static int BufferOutputSize = 16384; // # 16Kb static FBCompressionFormat read_compression_format(const idb::Payload_Compression comp) { switch (comp) { case idb::Payload_Compression::Payload_Compression_GZIP: return FBCompressionFormatGZIP; case idb::Payload_Compression::Payload_Compression_ZSTD: return FBCompressionFormatZSTD; default: return FBCompressionFormatGZIP; } } template <class T> static FBFuture<NSNull *> * resolve_next_read(grpc::internal::ReaderInterface<T> *reader) { FBMutableFuture<NSNull *> *future = FBMutableFuture.future; dispatch_queue_t queue = dispatch_queue_create("com.facebook.idb.grpc.reader_wait", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ T stop; reader->Read(&stop); [future resolveWithResult:NSNull.null]; }); return future; } template <class T> static id<FBDataConsumer> drain_consumer(grpc::internal::WriterInterface<T> *writer, FBFuture<NSNull *> *done) { return [FBBlockDataConsumer asynchronousDataConsumerWithBlock:^(NSData *data) { if (done.hasCompleted) { return; } T response; idb::Payload *payload = response.mutable_payload(); payload->set_data(data.bytes, data.length); writer->Write(response); }]; } template <class Write, class Read> static id<FBDataConsumer> consumer_from_request(grpc::ServerReaderWriter<Write, Read> *stream, Read& request, FBFuture<NSNull *> *done, NSError **error) { Read initial; stream->Read(&initial); request = initial; const std::string requestedFilePath = initial.start().file_path(); if (requestedFilePath.length() > 0) { return [FBFileWriter syncWriterForFilePath:nsstring_from_c_string(requestedFilePath.c_str()) error:error]; } return drain_consumer(stream, done); } template <class T> static Status drain_writer(FBFuture<FBProcess<NSNull *, NSInputStream *, id> *> *taskFuture, grpc::internal::WriterInterface<T> *stream) { NSError *error = nil; FBProcess<NSNull *, NSInputStream *, id> *task = [taskFuture block:&error]; if (!task) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } NSInputStream *inputStream = task.stdOut; [inputStream open]; while (true) { uintptr_t buffer[BufferOutputSize]; NSInteger size = [inputStream read:(uint8_t *)buffer maxLength:BufferOutputSize]; if (size == 0) { break; } if (size < 0) { return Status::OK; } T response; idb::Payload *payload = response.mutable_payload(); payload->set_data(reinterpret_cast<void *>(buffer), size); stream->Write(response); } [inputStream close]; NSNumber *exitCode = [task.exitCode block:&error]; if (exitCode.integerValue != 0) { NSString *errorString = [NSString stringWithFormat:@"Draining operation failed with exit code %ld", (long)exitCode.integerValue]; return Status(grpc::StatusCode::INTERNAL, errorString.UTF8String); } return Status::OK; } template <class T> static Status respond_file_path(NSURL *source, NSString *destination, grpc::internal::WriterInterface<T> *stream) { if (source) { NSError *error = nil; if (![NSFileManager.defaultManager moveItemAtPath:source.path toPath:destination error:&error]) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } } T response; idb::Payload payload = response.payload(); payload.set_file_path(destination.UTF8String); stream->Write(response); return Status::OK; } template <class T> static FBProcessInput<NSOutputStream *> *pipe_to_input(const idb::Payload initial, grpc::ServerReader<T> *reader) { const std::string initialData = initial.data(); FBProcessInput<NSOutputStream *> *input = [FBProcessInput inputFromStream]; NSOutputStream *stream = input.contents; dispatch_queue_t queue = dispatch_queue_create("com.facebook.idb.processinput", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ T request; [stream open]; [stream write:(const uint8_t *)initialData.c_str() maxLength:initialData.length()]; while (reader->Read(&request)) { const auto tarData = request.payload().data(); [stream write:(const uint8_t *)tarData.c_str() maxLength:tarData.length()]; } [stream close]; }); return input; } static FBProcessInput<NSOutputStream *> *pipe_to_input_output(const idb::Payload initial, grpc::ServerReaderWriter<idb::InstallResponse, idb::InstallRequest> *stream) { const std::string initialData = initial.data(); FBProcessInput<NSOutputStream *> *input = [FBProcessInput inputFromStream]; NSOutputStream *appStream = input.contents; dispatch_queue_t queue = dispatch_queue_create("com.facebook.idb.processinput", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ idb::InstallRequest request; [appStream open]; [appStream write:(const uint8_t *)initialData.c_str() maxLength:initialData.length()]; while (stream->Read(&request)) { const auto tarData = request.payload().data(); [appStream write:(const uint8_t *)tarData.c_str() maxLength:tarData.length()]; } [appStream close]; }); return input; } static id<FBDataConsumer, FBDataConsumerLifecycle> pipe_output(const idb::ProcessOutput_Interface interface, dispatch_queue_t queue, grpc::ServerReaderWriter<idb::LaunchResponse, idb::LaunchRequest> *stream) { id<FBDataConsumer, FBDataConsumerLifecycle> consumer = [FBBlockDataConsumer asynchronousDataConsumerOnQueue:queue consumer:^(NSData *data) { idb::LaunchResponse response; idb::ProcessOutput *output = response.mutable_output(); output->set_data(data.bytes, data.length); output->set_interface(interface); stream->Write(response); }]; return consumer; } template <class T> static FBFuture<NSArray<NSURL *> *> *filepaths_from_stream(const idb::Payload initial, grpc::ServerReader<T> *reader) { NSMutableArray<NSURL *> *filePaths = NSMutableArray.array; FBMutableFuture<NSArray<NSURL *> *> *future = FBMutableFuture.future; dispatch_queue_t queue = dispatch_queue_create("com.facebook.idb.processinput", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ T request; [filePaths addObject:[NSURL fileURLWithPath:nsstring_from_c_string(initial.file_path())]]; while (reader->Read(&request)) { [filePaths addObject:[NSURL fileURLWithPath:nsstring_from_c_string(request.payload().file_path())]]; } [future resolveWithResult:filePaths]; }); return future; } static FBFutureContext<NSArray<NSURL *> *> *filepaths_from_tar(FBTemporaryDirectory *temporaryDirectory, FBProcessInput<NSOutputStream *> *input, bool extract_from_subdir, FBCompressionFormat compression, id<FBControlCoreLogger> logger) { dispatch_queue_t queue = dispatch_queue_create("com.facebook.idb.processinput", DISPATCH_QUEUE_SERIAL); FBFutureContext<NSURL *> *tarContext = [temporaryDirectory withArchiveExtractedFromStream:input compression:compression]; if (extract_from_subdir) { // Extract from subdirectories return [temporaryDirectory filesFromSubdirs:tarContext]; } else { // Extract from the top level return [tarContext onQueue:queue pend:^FBFuture<NSArray<NSURL *> *> *(NSURL *extractionDir) { NSError *error; NSArray<NSURL *> *paths = [NSFileManager.defaultManager contentsOfDirectoryAtURL:extractionDir includingPropertiesForKeys:@[NSURLIsDirectoryKey] options:0 error:&error]; if (!paths) { return [FBFuture futureWithError:error]; } return [FBFuture futureWithResult:paths]; }]; } } template<class T> static FBFutureContext<NSArray<NSURL *> *> *filepaths_from_reader(FBTemporaryDirectory *temporaryDirectory, grpc::ServerReader<T> *reader, bool extract_from_subdir, id<FBControlCoreLogger> logger) { T request; reader->Read(&request); idb::Payload firstPayload = request.payload(); // The first item in the payload stream may be the compression format, if it's not assume the default. FBCompressionFormat compression = FBCompressionFormatGZIP; if (firstPayload.source_case() == idb::Payload::kCompression) { compression = read_compression_format(firstPayload.compression()); reader->Read(&request); firstPayload = request.payload(); } switch (firstPayload.source_case()) { case idb::Payload::kData: { FBProcessInput<NSOutputStream *> *input = pipe_to_input(firstPayload, reader); return filepaths_from_tar(temporaryDirectory, input, extract_from_subdir, compression, logger); } case idb::Payload::kFilePath: { return [FBFutureContext futureContextWithFuture:filepaths_from_stream(firstPayload, reader)]; } default: { return [FBFutureContext futureContextWithError:[FBControlCoreError errorForFormat:@"Unrecognised initial payload type %u", firstPayload.source_case()]]; } } } static NSDictionary<NSString *, NSString *> *extract_str_dict(const ::google::protobuf::Map<::std::string, ::std::string >& iterator) { NSMutableDictionary<NSString *, NSString *> *environment = NSMutableDictionary.dictionary; for (auto item : iterator) { environment[nsstring_from_c_string(item.first)] = nsstring_from_c_string(item.second); } return environment; } template <class T> static NSArray<NSString *> *extract_string_array(T &input) { NSMutableArray<NSString *> *arguments = NSMutableArray.array; for (auto value : input) { [arguments addObject:nsstring_from_c_string(value)]; } return arguments; } static FBCodeCoverageRequest *extract_code_coverage(const idb::XctestRunRequest *request) { if (request->has_code_coverage()) { const idb::XctestRunRequest::CodeCoverage codeCoverage = request->code_coverage(); FBCodeCoverageFormat format = FBCodeCoverageExported; switch (codeCoverage.format()) { case idb::XctestRunRequest_CodeCoverage_Format::XctestRunRequest_CodeCoverage_Format_RAW: format = FBCodeCoverageRaw; break; case idb::XctestRunRequest_CodeCoverage_Format::XctestRunRequest_CodeCoverage_Format_EXPORTED: default: format = FBCodeCoverageExported; break; } return [[FBCodeCoverageRequest alloc] initWithCollect:codeCoverage.collect() format:format]; } else { // fallback to deprecated request field for backwards compatibility return [[FBCodeCoverageRequest alloc] initWithCollect:request->collect_coverage() format:FBCodeCoverageExported]; } } static FBXCTestRunRequest *convert_xctest_request(const idb::XctestRunRequest *request) { NSNumber *testTimeout = @(request->timeout()); NSArray<NSString *> *arguments = extract_string_array(request->arguments()); NSDictionary<NSString *, NSString *> *environment = extract_str_dict(request->environment()); NSMutableSet<NSString *> *testsToRun = nil; NSMutableSet<NSString *> *testsToSkip = NSMutableSet.set; NSString *testBundleID = nsstring_from_c_string(request->test_bundle_id()); BOOL reportActivities = request->report_activities(); BOOL collectLogs = request->collect_logs(); BOOL waitForDebugger = request->wait_for_debugger(); BOOL reportAttachments = request->report_attachments(); FBCodeCoverageRequest *coverage = extract_code_coverage(request); if (request->tests_to_run_size() > 0) { testsToRun = NSMutableSet.set; for (int i = 0; i < request->tests_to_run_size(); i++) { const std::string value = request->tests_to_run(i); [testsToRun addObject:nsstring_from_c_string(value)]; } } for (int i = 0; i < request->tests_to_skip_size(); i++) { const std::string value = request->tests_to_skip(i); [testsToSkip addObject:nsstring_from_c_string(value)]; } switch (request->mode().mode_case()) { case idb::XctestRunRequest_Mode::kLogic: { return [FBXCTestRunRequest logicTestWithTestBundleID:testBundleID environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverage collectLogs:collectLogs waitForDebugger:waitForDebugger]; } case idb::XctestRunRequest_Mode::kApplication: { const idb::XctestRunRequest::Application application = request->mode().application(); NSString *appBundleID = nsstring_from_c_string(application.app_bundle_id()); return [FBXCTestRunRequest applicationTestWithTestBundleID:testBundleID appBundleID:appBundleID environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverage collectLogs:collectLogs waitForDebugger:waitForDebugger]; } case idb::XctestRunRequest_Mode::kUi: { const idb::XctestRunRequest::UI ui = request->mode().ui(); NSString *appBundleID = nsstring_from_c_string(ui.app_bundle_id()); NSString *testHostAppBundleID = nsstring_from_c_string(ui.test_host_app_bundle_id()); return [FBXCTestRunRequest uiTestWithTestBundleID:testBundleID appBundleID:appBundleID testHostAppBundleID:testHostAppBundleID environment:environment arguments:arguments testsToRun:testsToRun testsToSkip:testsToSkip testTimeout:testTimeout reportActivities:reportActivities reportAttachments:reportAttachments coverageRequest:coverage collectLogs:collectLogs]; } default: return nil; } } static NSPredicate *nspredicate_from_crash_log_query(const idb::CrashLogQuery *request) { NSMutableArray<NSPredicate *> *subpredicates = [NSMutableArray array]; if (request->since()) { [subpredicates addObject:[FBCrashLogInfo predicateNewerThanDate:[NSDate dateWithTimeIntervalSince1970:request->since()]]]; } if (request->before()) { [subpredicates addObject:[FBCrashLogInfo predicateOlderThanDate:[NSDate dateWithTimeIntervalSince1970:request->before()]]]; } NSString *bundle_id = nsstring_from_c_string(request->bundle_id()); if (bundle_id.length) { [subpredicates addObject:[FBCrashLogInfo predicateForIdentifier:bundle_id]]; } NSString *name = nsstring_from_c_string(request->name()); if (name.length) { [subpredicates addObject:[FBCrashLogInfo predicateForName:name]]; } if (subpredicates.count == 0) { return [NSPredicate predicateWithValue:YES]; } return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]; } static void fill_crash_log_info(idb::CrashLogInfo *info, const FBCrashLogInfo *crash) { info->set_name(crash.name.UTF8String); info->set_process_name(crash.processName.UTF8String); info->set_parent_process_name(crash.parentProcessName.UTF8String); info->set_process_identifier(crash.processIdentifier); info->set_parent_process_identifier(crash.parentProcessIdentifier); info->set_timestamp(crash.date.timeIntervalSince1970); } static void fill_crash_log_response(idb::CrashLogResponse *response, const NSArray<FBCrashLogInfo *> *crashes) { for (FBCrashLogInfo *crash in crashes) { idb::CrashLogInfo *info = response->add_list(); fill_crash_log_info(info, crash); } } static FBSimulatorHIDEvent *translate_event(idb::HIDEvent &event, NSError **error) { if (event.has_press()) { idb::HIDEvent_HIDDirection direction = event.press().direction(); idb::HIDEvent_HIDPressAction action = event.press().action(); if (action.has_key()) { int keycode = (int)action.key().keycode(); if (direction == idb::HIDEvent_HIDDirection_DOWN) { return [FBSimulatorHIDEvent keyDown:keycode]; } else if (direction == idb::HIDEvent_HIDDirection_UP) { return [FBSimulatorHIDEvent keyUp:keycode]; } } else if (action.has_button()) { // Need to convert between the objc enum that starts at 1 and the grpc enum that starts at 0 FBSimulatorHIDButton button = (FBSimulatorHIDButton)(action.button().button() + 1); if (direction == idb::HIDEvent_HIDDirection_DOWN) { return [FBSimulatorHIDEvent buttonDown:button]; } else if (direction == idb::HIDEvent_HIDDirection_UP) { return [FBSimulatorHIDEvent buttonUp:button]; } } else if (action.has_touch()) { int x = action.touch().point().x(); int y = action.touch().point().y(); if (direction == idb::HIDEvent_HIDDirection_DOWN) { return [FBSimulatorHIDEvent touchDownAtX:x y:y]; } else if (direction == idb::HIDEvent_HIDDirection_UP) { return [FBSimulatorHIDEvent touchUpAtX:x y:y]; } } } else if (event.has_swipe()) { return [FBSimulatorHIDEvent swipe:event.swipe().start().x() yStart:event.swipe().start().y() xEnd:event.swipe().end().x() yEnd:event.swipe().end().y() delta:event.swipe().delta() duration:event.swipe().duration()]; } else if (event.has_delay()) { return [FBSimulatorHIDEvent delay:event.delay().duration()]; } if (error) { *error = [FBControlCoreError errorForDescription:@"Can't decode event"]; } return nil; } static FBInstrumentsConfiguration *translate_instruments_configuration(idb::InstrumentsRunRequest_Start request, FBIDBStorageManager *storageManager) { return [FBInstrumentsConfiguration configurationWithTemplateName:nsstring_from_c_string(request.template_name()) targetApplication:nsstring_from_c_string(request.app_bundle_id()) appEnvironment:extract_str_dict(request.environment()) appArguments:extract_string_array(request.arguments()) toolArguments:[storageManager interpolateArgumentReplacements:extract_string_array(request.tool_arguments())] timings:[FBInstrumentsTimings timingsWithTerminateTimeout:request.timings().terminate_timeout() ?: DefaultInstrumentsTerminateTimeout launchRetryTimeout:request.timings().launch_retry_timeout() ?: DefaultInstrumentsLaunchRetryTimeout launchErrorTimeout:request.timings().launch_error_timeout() ?: DefaultInstrumentsLaunchErrorTimeout operationDuration:request.timings().operation_duration() ?: DefaultInstrumentsOperationDuration ] ]; } static FBXCTraceRecordConfiguration *translate_xctrace_record_configuration(idb::XctraceRecordRequest_Start request) { return [FBXCTraceRecordConfiguration RecordWithTemplateName:nsstring_from_c_string(request.template_name()) timeLimit:request.time_limit() ?: DefaultXCTraceRecordOperationTimeLimit package:nsstring_from_c_string(request.package()) allProcesses:request.target().all_processes() processToAttach:nsstring_from_c_string(request.target().process_to_attach()) processToLaunch:nsstring_from_c_string(request.target().launch_process().process_to_launch()) launchArgs:extract_string_array(request.target().launch_process().launch_args()) targetStdin:nsstring_from_c_string(request.target().launch_process().target_stdin()) targetStdout:nsstring_from_c_string(request.target().launch_process().target_stdout()) processEnv:extract_str_dict(request.target().launch_process().process_env()) shim:nil ]; } static idb::DebugServerResponse translate_debugserver_status(id<FBDebugServer> debugServer) { idb::DebugServerResponse response; idb::DebugServerResponse::Status *status = response.mutable_status(); if (debugServer) { for (NSString *command in debugServer.lldbBootstrapCommands) { status->add_lldb_bootstrap_commands(command.UTF8String); } } return response; } static NSString *file_container(idb::FileContainer container) { switch (container.kind()) { case idb::FileContainer_Kind_ROOT: return FBFileContainerKindRoot; case idb::FileContainer_Kind_MEDIA: return FBFileContainerKindMedia; case idb::FileContainer_Kind_CRASHES: return FBFileContainerKindCrashes; case idb::FileContainer_Kind_PROVISIONING_PROFILES: return FBFileContainerKindProvisioningProfiles; case idb::FileContainer_Kind_MDM_PROFILES: return FBFileContainerKindMDMProfiles; case idb::FileContainer_Kind_SPRINGBOARD_ICONS: return FBFileContainerKindSpringboardIcons; case idb::FileContainer_Kind_WALLPAPER: return FBFileContainerKindWallpaper; case idb::FileContainer_Kind_DISK_IMAGES: return FBFileContainerKindDiskImages; case idb::FileContainer_Kind_GROUP_CONTAINER: return FBFileContainerKindGroup; case idb::FileContainer_Kind_APPLICATION_CONTAINER: return FBFileContainerKindApplication; case idb::FileContainer_Kind_AUXILLARY: return FBFileContainerKindAuxillary; case idb::FileContainer_Kind_XCTEST: return FBFileContainerKindXctest; case idb::FileContainer_Kind_DYLIB: return FBFileContainerKindDylib; case idb::FileContainer_Kind_DSYM: return FBFileContainerKindDsym; case idb::FileContainer_Kind_FRAMEWORK: return FBFileContainerKindFramework; case idb::FileContainer_Kind_SYMBOLS: return FBFileContainerKindSymbols; case idb::FileContainer_Kind_APPLICATION: default: return nsstring_from_c_string(container.bundle_id()); } } static FBDsymBundleType bundle_type_link_to_dsym(idb::InstallRequest_LinkDsymToBundle_BundleType bundleType) { switch (bundleType) { case idb::InstallRequest_LinkDsymToBundle_BundleType_XCTEST: return FBDsymBundleTypeXCTest; case idb::InstallRequest_LinkDsymToBundle_BundleType_APP: return FBDsymBundleTypeApp; default: return FBDsymBundleTypeApp; } } static void populate_companion_info(idb::CompanionInfo *info, id<FBEventReporter> reporter, id<FBiOSTarget> target) { info->set_udid(target.udid.UTF8String); NSDictionary<NSString *, NSString *> *metadata = reporter.metadata ?: @{}; NSError *error = nil; NSData *data = [NSJSONSerialization dataWithJSONObject:metadata options:0 error:&error]; if (data) { info->set_metadata(data.bytes, data.length); } } #pragma mark Constructors FBIDBServiceHandler::FBIDBServiceHandler(FBIDBCommandExecutor *commandExecutor, id<FBiOSTarget> target, id<FBEventReporter> eventReporter) { _commandExecutor = commandExecutor; _target = target; _eventReporter = eventReporter; } FBIDBServiceHandler::FBIDBServiceHandler(const FBIDBServiceHandler &c) { _commandExecutor = c._commandExecutor; _target = c._target; _eventReporter = c._eventReporter; } #pragma mark Handled Methods FBFuture<FBInstalledArtifact *> *FBIDBServiceHandler::install_future(const idb::InstallRequest_Destination destination, grpc::ServerReaderWriter<idb::InstallResponse, idb::InstallRequest> *stream) {@autoreleasepool{ // Read the initial request idb::InstallRequest request; stream->Read(&request); // The name hint may be provided, if it is not then default to some UUID, then advance the stream. NSString *name = NSUUID.UUID.UUIDString; if (request.value_case() == idb::InstallRequest::ValueCase::kNameHint) { name = nsstring_from_c_string(request.name_hint()); stream->Read(&request); } // A debuggable flag may be provided, if it is, then obtain that value, then advance that stream. BOOL makeDebuggable = NO; if (request.value_case() == idb::InstallRequest::ValueCase::kMakeDebuggable) { makeDebuggable = (request.make_debuggable() == true); stream->Read(&request); } FBDsymInstallLinkToBundle *linkToBundle = nil; //(2022-03-02) REMOVE! Keeping only for retrocompatibility // A bundle id might be provided, if it is, then obtain the installed app if exists, then advance that stream. // It can be used to determine where debug symbols should be linked if (request.value_case() == idb::InstallRequest::ValueCase::kBundleId) { NSString *bundleID = nsstring_from_c_string(request.bundle_id()); linkToBundle = [[FBDsymInstallLinkToBundle alloc] initWith:bundleID bundle_type:FBDsymBundleTypeApp]; stream->Read(&request); } if (request.value_case() == idb::InstallRequest::ValueCase::kLinkDsymToBundle) { idb::InstallRequest_LinkDsymToBundle link_to_bundle = request.link_dsym_to_bundle(); FBDsymBundleType bundleType = bundle_type_link_to_dsym(link_to_bundle.bundle_type()); NSString *bundleID = nsstring_from_c_string(link_to_bundle.bundle_id()); linkToBundle = [[FBDsymInstallLinkToBundle alloc] initWith:bundleID bundle_type:bundleType]; stream->Read(&request); } // Now that we've read the header, the next item in the stream must be the payload. if (request.value_case() != idb::InstallRequest::ValueCase::kPayload) { return [[FBIDBError describeFormat:@"Expected the next item in the stream to be a payload"] failFuture]; } // The first item in the payload stream may be the compression format, if it's not assume the default. FBCompressionFormat compression = FBCompressionFormatGZIP; idb::Payload payload = request.payload(); if (payload.source_case() == idb::Payload::kCompression) { compression = read_compression_format(payload.compression()); stream->Read(&request); payload = request.payload(); } switch (payload.source_case()) { case idb::Payload::kData: { FBProcessInput<NSOutputStream *> *dataStream = pipe_to_input_output(payload, stream); switch (destination) { case idb::InstallRequest_Destination::InstallRequest_Destination_APP: return [_commandExecutor install_app_stream:dataStream compression:compression make_debuggable:makeDebuggable]; case idb::InstallRequest_Destination::InstallRequest_Destination_XCTEST: return [_commandExecutor install_xctest_app_stream:dataStream]; case idb::InstallRequest_Destination::InstallRequest_Destination_DSYM: return [_commandExecutor install_dsym_stream:dataStream compression:compression linkTo:linkToBundle]; case idb::InstallRequest_Destination::InstallRequest_Destination_DYLIB: return [_commandExecutor install_dylib_stream:dataStream name:name]; case idb::InstallRequest_Destination::InstallRequest_Destination_FRAMEWORK: return [_commandExecutor install_framework_stream:dataStream]; default: return nil; } } case idb::Payload::kUrl: { NSURL *url = [NSURL URLWithString:[NSString stringWithCString:payload.url().c_str() encoding:NSUTF8StringEncoding]]; FBDataDownloadInput *download = [FBDataDownloadInput dataDownloadWithURL:url logger:_target.logger]; switch (destination) { case idb::InstallRequest_Destination::InstallRequest_Destination_APP: return [_commandExecutor install_app_stream:download.input compression:compression make_debuggable:makeDebuggable]; case idb::InstallRequest_Destination::InstallRequest_Destination_XCTEST: return [_commandExecutor install_xctest_app_stream:download.input]; case idb::InstallRequest_Destination::InstallRequest_Destination_DSYM: return [_commandExecutor install_dsym_stream:download.input compression:compression linkTo:linkToBundle]; case idb::InstallRequest_Destination::InstallRequest_Destination_DYLIB: return [_commandExecutor install_dylib_stream:download.input name:name]; case idb::InstallRequest_Destination::InstallRequest_Destination_FRAMEWORK: return [_commandExecutor install_framework_stream:download.input]; default: return nil; } } case idb::Payload::kFilePath: { NSString *filePath = nsstring_from_c_string(payload.file_path()); switch (destination) { case idb::InstallRequest_Destination::InstallRequest_Destination_APP: return [_commandExecutor install_app_file_path:filePath make_debuggable:makeDebuggable]; case idb::InstallRequest_Destination::InstallRequest_Destination_XCTEST: return [_commandExecutor install_xctest_app_file_path:filePath]; case idb::InstallRequest_Destination::InstallRequest_Destination_DSYM: return [_commandExecutor install_dsym_file_path:filePath linkTo:linkToBundle]; case idb::InstallRequest_Destination::InstallRequest_Destination_DYLIB: return [_commandExecutor install_dylib_file_path:filePath]; case idb::InstallRequest_Destination::InstallRequest_Destination_FRAMEWORK: return [_commandExecutor install_framework_file_path:filePath]; default: return nil; } } default: return nil; } }} Status FBIDBServiceHandler::list_apps(ServerContext *context, const idb::ListAppsRequest *request, idb::ListAppsResponse *response) {@autoreleasepool{ NSError *error = nil; NSSet<NSString *> *persistedBundleIDs = _commandExecutor.storageManager.application.persistedBundleIDs; BOOL fetchAppProcessState = request->suppress_process_state() == false; NSDictionary<FBInstalledApplication *, id> *apps = [[_commandExecutor list_apps:fetchAppProcessState] block:&error]; if (!apps) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } for (FBInstalledApplication *app in apps.allKeys) { idb::InstalledAppInfo *appInfo = response->add_apps(); appInfo->set_bundle_id(app.bundle.identifier.UTF8String ?: ""); appInfo->set_name(app.bundle.name.UTF8String ?: ""); appInfo->set_install_type(app.installTypeString.UTF8String); for (NSString *architecture in app.bundle.binary.architectures) { appInfo->add_architectures(architecture.UTF8String); } id processState = apps[app]; if ([processState isKindOfClass:NSNumber.class]) { appInfo->set_process_state(idb::InstalledAppInfo_AppProcessState_RUNNING); appInfo->set_process_identifier([processState unsignedIntegerValue]); } else { appInfo->set_process_state(idb::InstalledAppInfo_AppProcessState_UNKNOWN); } appInfo->set_debuggable(app.installType == FBApplicationInstallTypeUserDevelopment && [persistedBundleIDs containsObject:app.bundle.identifier]); } return Status::OK; }} Status FBIDBServiceHandler::open_url(ServerContext *context, const idb::OpenUrlRequest *request, idb::OpenUrlRequest *response) {@autoreleasepool{ NSError *error = nil; [[_commandExecutor open_url:nsstring_from_c_string(request->url())] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, [error.localizedDescription UTF8String]); } return Status::OK; }} Status FBIDBServiceHandler::install(ServerContext *context, grpc::ServerReaderWriter<idb::InstallResponse, idb::InstallRequest> *stream) {@autoreleasepool{ idb::InstallRequest request; stream->Read(&request); idb::InstallRequest_Destination destination = request.destination(); NSError *error = nil; FBInstalledArtifact *artifact = [install_future(destination, stream) block:&error]; if (!artifact) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String ?: "An internal error occured when installing"); } idb::InstallResponse response; response.set_name(artifact.name.UTF8String); response.set_uuid(artifact.uuid.UUIDString.UTF8String ?: ""); stream->Write(response); return Status::OK; }} Status FBIDBServiceHandler::screenshot(ServerContext *context, const idb::ScreenshotRequest *request, idb::ScreenshotResponse *response) {@autoreleasepool{ NSError *error = nil; NSData *screenshot = [[_commandExecutor take_screenshot:FBScreenshotFormatPNG] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } response->set_image_data(screenshot.bytes, screenshot.length); return Status::OK; }} Status FBIDBServiceHandler::focus(ServerContext *context, const idb::FocusRequest *request, idb::FocusResponse *response) {@autoreleasepool{ NSError *error = nil; [[_commandExecutor focus] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, [error.localizedDescription UTF8String]); } return Status::OK; }} Status FBIDBServiceHandler::accessibility_info(ServerContext *context, const idb::AccessibilityInfoRequest *request, idb::AccessibilityInfoResponse *response) {@autoreleasepool{ NSError *error = nil; NSValue *point = nil; if (request->has_point()) { point = [NSValue valueWithPoint:CGPointMake(request->point().x(), request->point().y())]; } BOOL nestedFormat = request->format() == idb::AccessibilityInfoRequest_Format::AccessibilityInfoRequest_Format_NESTED; NSArray<NSDictionary<NSString *, id> *> *info = [[_commandExecutor accessibility_info_at_point:point nestedFormat:nestedFormat] block:&error]; if (!info) { return Status(grpc::StatusCode::INTERNAL, [error.localizedDescription UTF8String]); } NSData *data = [NSJSONSerialization dataWithJSONObject:info options:0 error:&error]; if (!data) { return Status(grpc::StatusCode::INTERNAL, [error.localizedDescription UTF8String]); } NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; response->set_json([json UTF8String]); return Status::OK; }} Status FBIDBServiceHandler::uninstall(ServerContext *context, const idb::UninstallRequest *request, idb::UninstallResponse *response) {@autoreleasepool{ NSError *error = nil; [[_commandExecutor uninstall_application:nsstring_from_c_string(request->bundle_id())] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, [error.localizedDescription UTF8String]); } return Status::OK; }} Status FBIDBServiceHandler::mkdir(grpc::ServerContext *context, const idb::MkdirRequest *request, idb::MkdirResponse *response) {@autoreleasepool{ NSError *error = nil; [[_commandExecutor create_directory:nsstring_from_c_string(request->path()) containerType:file_container(request->container())] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return Status::OK; }} Status FBIDBServiceHandler::mv(grpc::ServerContext *context, const idb::MvRequest *request, idb::MvResponse *response) {@autoreleasepool{ NSError *error = nil; NSMutableArray<NSString *> *originalPaths = NSMutableArray.array; for (int j = 0; j < request->src_paths_size(); j++) { [originalPaths addObject:nsstring_from_c_string(request->src_paths(j))]; } [[_commandExecutor move_paths:originalPaths to_path:nsstring_from_c_string(request->dst_path()) containerType:file_container(request->container())] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return Status::OK; }} Status FBIDBServiceHandler::rm(grpc::ServerContext *context, const idb::RmRequest *request, idb::RmResponse *response) {@autoreleasepool{ NSError *error = nil; NSMutableArray<NSString *> *paths = NSMutableArray.array; for (int j = 0; j < request->paths_size(); j++) { [paths addObject:nsstring_from_c_string(request->paths(j))]; } [[_commandExecutor remove_paths:paths containerType:file_container(request->container())] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return Status::OK; }} Status FBIDBServiceHandler::ls(grpc::ServerContext *context, const idb::LsRequest *request, idb::LsResponse *response) {@autoreleasepool{ NSError *error = nil; if (request->paths_size() > 0) { NSArray<NSString *> *inputPaths = extract_string_array(request->paths()); NSDictionary<NSString *, NSArray<NSString *> *> *pathsToPaths = [[_commandExecutor list_paths:inputPaths containerType:file_container(request->container())] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } for (NSString *containerPath in pathsToPaths.allKeys) { NSArray<NSString *> *paths = pathsToPaths[containerPath]; idb::FileListing *listing = response->add_listings(); idb::FileInfo *parent = listing->mutable_parent(); parent->set_path(containerPath.UTF8String); for (NSString *path in paths) { idb::FileInfo *info = listing->add_files(); info->set_path(path.UTF8String); } } } else { // Back-compat with single paths NSArray<NSString *> *paths = [[_commandExecutor list_path:nsstring_from_c_string(request->path()) containerType:file_container(request->container())] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } for (NSString *path in paths) { idb::FileInfo *info = response->add_files(); info->set_path(path.UTF8String); } } return Status::OK; }} Status FBIDBServiceHandler::approve(ServerContext *context, const idb::ApproveRequest *request, idb::ApproveResponse *response) {@autoreleasepool{ NSError *error = nil; NSDictionary<NSNumber *, FBSettingsApprovalService> *mapping = @{ @((int)idb::ApproveRequest_Permission::ApproveRequest_Permission_MICROPHONE): FBSettingsApprovalServiceMicrophone, @((int)idb::ApproveRequest_Permission::ApproveRequest_Permission_PHOTOS): FBSettingsApprovalServicePhotos, @((int)idb::ApproveRequest_Permission::ApproveRequest_Permission_CAMERA): FBSettingsApprovalServiceCamera, @((int)idb::ApproveRequest_Permission::ApproveRequest_Permission_CONTACTS): FBSettingsApprovalServiceContacts, @((int)idb::ApproveRequest_Permission::ApproveRequest_Permission_URL): FBSettingsApprovalServiceUrl, @((int)idb::ApproveRequest_Permission::ApproveRequest_Permission_LOCATION): FBSettingsApprovalServiceLocation, @((int)idb::ApproveRequest_Permission::ApproveRequest_Permission_NOTIFICATION): FBSettingsApprovalServiceNotification, }; NSMutableSet<FBSettingsApprovalService> *services = NSMutableSet.set; for (int j = 0; j < request->permissions_size(); j++) { idb::ApproveRequest_Permission permission = request->permissions(j); [services addObject:mapping[@(permission)]]; } if ([services containsObject:FBSettingsApprovalServiceUrl]) { [services removeObject:FBSettingsApprovalServiceUrl]; [[_commandExecutor approve_deeplink:nsstring_from_c_string(request->scheme()) for_application:nsstring_from_c_string(request->bundle_id())] block:&error]; } if ([services count] > 0 && !error) { [[_commandExecutor approve:services for_application:nsstring_from_c_string(request->bundle_id())] block:&error]; } if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return Status::OK; }} Status FBIDBServiceHandler::clear_keychain(ServerContext *context, const idb::ClearKeychainRequest *request, idb::ClearKeychainResponse *response) {@autoreleasepool{ NSError *error = nil; [[_commandExecutor clear_keychain] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, [error.localizedDescription UTF8String]); } return Status::OK; }} Status FBIDBServiceHandler::terminate(ServerContext *context, const idb::TerminateRequest *request, idb::TerminateResponse *response) {@autoreleasepool{ NSError *error = nil; [[_commandExecutor kill_application:nsstring_from_c_string(request->bundle_id())] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return Status::OK; }} Status FBIDBServiceHandler::hid(grpc::ServerContext *context, grpc::ServerReader<idb::HIDEvent> *reader, idb::HIDResponse *response) {@autoreleasepool{ NSError *error = nil; idb::HIDEvent grpcEvent; while (reader->Read(&grpcEvent)) { FBSimulatorHIDEvent *event = translate_event(grpcEvent, &error); if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } [[_commandExecutor hid:event] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } } return Status::OK; }} Status FBIDBServiceHandler::set_location(ServerContext *context, const idb::SetLocationRequest *request, idb::SetLocationResponse *response) {@autoreleasepool{ NSError *error = nil; [[_commandExecutor set_location:request->location().latitude() longitude:request->location().longitude()] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, [error.localizedDescription UTF8String]); } return Status::OK; }} Status FBIDBServiceHandler::setting(ServerContext* context, const idb::SettingRequest* request, idb::SettingResponse* response) {@autoreleasepool{ switch (request->setting_case()) { case idb::SettingRequest::SettingCase::kHardwareKeyboard: { NSError *error = nil; NSNull *result = [[_commandExecutor set_hardware_keyboard_enabled:request->hardwarekeyboard().enabled()] await:&error]; if (!result) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return Status::OK; } case idb::SettingRequest::SettingCase::kStringSetting: { idb::SettingRequest::StringSetting stringSetting = request->stringsetting(); switch (stringSetting.setting()) { case idb::Setting::LOCALE: { NSError *error = nil; NSNull *result = [[_commandExecutor set_locale_with_identifier:nsstring_from_c_string(stringSetting.value().c_str())] await:&error]; if (!result) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return Status::OK; } case idb::Setting::ANY: { NSError *error = nil; NSString *name = nsstring_from_c_string(stringSetting.name().c_str()); NSString *value = nsstring_from_c_string(stringSetting.value().c_str()); NSString *type = nil; if (stringSetting.value_type().length() > 0) { type = nsstring_from_c_string(stringSetting.value_type().c_str()); } NSString *domain = nil; if (stringSetting.domain().length() > 0) { domain = nsstring_from_c_string(stringSetting.domain().c_str()); } NSNull *result = [[_commandExecutor set_preference:name value:value type: type domain:domain] await:&error]; if (!result) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return Status::OK; } default: return Status(grpc::StatusCode::INTERNAL, "Unknown setting case"); } } default: return Status(grpc::StatusCode::INTERNAL, "Unknown setting case"); } }} Status FBIDBServiceHandler::get_setting(ServerContext* context, const idb::GetSettingRequest* request, idb::GetSettingResponse* response) {@autoreleasepool{ switch (request->setting()) { case idb::Setting::LOCALE: { NSError *error = nil; NSString *localeIdentifier = [[_commandExecutor get_current_locale_identifier] await:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } response->set_value(localeIdentifier.UTF8String); return Status::OK; } case idb::Setting::ANY: { NSError *error = nil; NSString *name = nsstring_from_c_string(request->name().c_str()); NSString *domain = nil; if (request->domain().length() > 0) { domain = nsstring_from_c_string(request->domain().c_str()); } NSString *value = [[_commandExecutor get_preference:name domain:domain] await:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } response->set_value(value.UTF8String); return Status::OK; } default: return Status(grpc::StatusCode::INTERNAL, "Unknown setting case"); } }} Status FBIDBServiceHandler::list_settings(ServerContext* context, const idb::ListSettingRequest* request, idb::ListSettingResponse* response) {@autoreleasepool{ switch (request->setting()) { case idb::Setting::LOCALE: { NSArray<NSString *> *localeIdentifiers = _commandExecutor.list_locale_identifiers; auto values = response->mutable_values(); for (NSString *localeIdentifier in localeIdentifiers) { values->Add(localeIdentifier.UTF8String); } return Status::OK; } default: return Status(grpc::StatusCode::INTERNAL, "Unknown setting case"); } }} Status FBIDBServiceHandler::contacts_update(ServerContext *context, const idb::ContactsUpdateRequest *request, idb::ContactsUpdateResponse *response) {@autoreleasepool{ NSError *error = nil; std::string data = request->payload().data(); [[_commandExecutor update_contacts:[NSData dataWithBytes:data.c_str() length:data.length()]] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, [error.localizedDescription UTF8String]); } return Status::OK; }} Status FBIDBServiceHandler::launch(grpc::ServerContext *context, grpc::ServerReaderWriter<idb::LaunchResponse, idb::LaunchRequest> *stream) {@autoreleasepool{ idb::LaunchRequest request; stream->Read(&request); idb::LaunchRequest_Start start = request.start(); NSError *error = nil; FBProcessOutput *stdOut = FBProcessOutput.outputForNullDevice; FBProcessOutput *stdErr = FBProcessOutput.outputForNullDevice; NSMutableArray<FBFuture *> *completions = NSMutableArray.array; if (start.wait_for()) { dispatch_queue_t writeQueue = dispatch_queue_create("com.facebook.idb.launch.write", DISPATCH_QUEUE_SERIAL); id<FBDataConsumer, FBDataConsumerLifecycle> consumer = pipe_output(idb::ProcessOutput_Interface_STDOUT, writeQueue, stream); [completions addObject:consumer.finishedConsuming]; stdOut = [FBProcessOutput outputForDataConsumer:consumer]; consumer = pipe_output(idb::ProcessOutput_Interface_STDERR, writeQueue, stream); [completions addObject:consumer.finishedConsuming]; stdErr = [FBProcessOutput outputForDataConsumer:consumer]; } FBProcessIO *io = [[FBProcessIO alloc] initWithStdIn:nil stdOut:stdOut stdErr:stdErr]; FBApplicationLaunchConfiguration *configuration = [[FBApplicationLaunchConfiguration alloc] initWithBundleID:nsstring_from_c_string(start.bundle_id()) bundleName:nil arguments:extract_string_array(start.app_args()) environment:extract_str_dict(start.env()) waitForDebugger:(start.wait_for_debugger() ? YES : NO) io:io launchMode:start.foreground_if_running() ? FBApplicationLaunchModeForegroundIfRunning : FBApplicationLaunchModeFailIfRunning]; id<FBLaunchedApplication> launchedApp = [[_commandExecutor launch_app:configuration] block:&error]; if (!launchedApp) { if (error.code != 0) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } else { return Status(grpc::StatusCode::FAILED_PRECONDITION, error.localizedDescription.UTF8String); } } // Respond with the pid of the launched process idb::LaunchResponse response; idb::DebuggerInfo *debugger_info = response.mutable_debugger(); debugger_info->set_pid(launchedApp.processIdentifier); stream->Write(response); // Return early if not waiting for output if (!start.wait_for()) { return Status::OK; } // Otherwise wait for the client to hang up. stream->Read(&request); [[launchedApp.applicationTerminated cancel] block:nil]; [[FBFuture futureWithFutures:completions] block:nil]; return Status::OK; }} Status FBIDBServiceHandler::crash_list(ServerContext *context, const idb::CrashLogQuery *request, idb::CrashLogResponse *response) {@autoreleasepool{ NSError *error = nil; NSPredicate *predicate = nspredicate_from_crash_log_query(request); NSArray<FBCrashLogInfo *> *crashes = [[_commandExecutor crash_list:predicate] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } fill_crash_log_response(response, crashes); return Status::OK; }} Status FBIDBServiceHandler::crash_show(ServerContext *context, const idb::CrashShowRequest *request, idb::CrashShowResponse *response) {@autoreleasepool{ NSError *error = nil; NSString *name = nsstring_from_c_string(request->name()); if (!name){ return Status(grpc::StatusCode::INTERNAL, @"Missing crash name".UTF8String); } NSPredicate *predicate = [FBCrashLogInfo predicateForName:name]; FBCrashLog *crash = [[_commandExecutor crash_show:predicate] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } idb::CrashLogInfo *info = response->mutable_info(); fill_crash_log_info(info, crash.info); response->set_contents(crash.contents.UTF8String); return Status::OK; }} Status FBIDBServiceHandler::crash_delete(ServerContext *context, const idb::CrashLogQuery *request, idb::CrashLogResponse *response) {@autoreleasepool{ NSError *error = nil; NSPredicate *predicate = nspredicate_from_crash_log_query(request); NSArray<FBCrashLogInfo *> *crashes = [[_commandExecutor crash_delete:predicate] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } fill_crash_log_response(response, crashes); return Status::OK; }} Status FBIDBServiceHandler::xctest_list_bundles(ServerContext *context, const idb::XctestListBundlesRequest *request, idb::XctestListBundlesResponse *response) {@autoreleasepool{ NSError *error = nil; NSSet<id<FBXCTestDescriptor>> *descriptors = [[_commandExecutor list_test_bundles] block:&error]; if (!descriptors) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } for (id<FBXCTestDescriptor> descriptor in descriptors) { idb::XctestListBundlesResponse_Bundles *bundle = response->add_bundles(); bundle->set_name(descriptor.name.UTF8String ?: ""); bundle->set_bundle_id(descriptor.testBundleID.UTF8String ?: ""); for (NSString *architecture in descriptor.architectures) { bundle->add_architectures(architecture.UTF8String); } } return Status::OK; }} Status FBIDBServiceHandler::xctest_list_tests(ServerContext *context, const idb::XctestListTestsRequest *request, idb::XctestListTestsResponse *response) {@autoreleasepool{ NSError *error = nil; NSArray<NSString *> *tests = [[_commandExecutor list_tests_in_bundle:nsstring_from_c_string(request->bundle_name()) with_app:nsstring_from_c_string(request->app_path())] block:&error]; if (!tests) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } for (NSString *test in tests) { response->add_names(test.UTF8String); } return Status::OK; }} Status FBIDBServiceHandler::xctest_run(ServerContext *context, const idb::XctestRunRequest *request, grpc::ServerWriter<idb::XctestRunResponse> *response) {@autoreleasepool{ FBXCTestRunRequest *xctestRunRequest = convert_xctest_request(request); if (xctestRunRequest == nil) { return Status(grpc::StatusCode::INTERNAL, "Failed to convert xctest request"); } // Once the reporter is created, only it will perform writing to the writer. NSError *error = nil; FBIDBXCTestReporter *reporter = [[FBIDBXCTestReporter alloc] initWithResponseWriter:response queue:_target.workQueue logger:_target.logger]; FBIDBTestOperation *operation = [[_commandExecutor xctest_run:xctestRunRequest reporter:reporter logger:[FBControlCoreLoggerFactory loggerToConsumer:reporter]] block:&error]; if (!operation) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } reporter.configuration = operation.reporterConfiguration; // First wait for the test operation to finish [operation.completed block:&error]; // Then make sure we've reported everything, otherwise we could write in the background (use-after-free) [reporter.reportingTerminated block:&error]; return Status::OK; }} Status FBIDBServiceHandler::log(ServerContext *context, const idb::LogRequest *request, grpc::ServerWriter<idb::LogResponse> *response) {@autoreleasepool{ // In the background, write out the log data. Prevent future writes if the client write fails. // This will happen asynchronously with the server thread. FBMutableFuture<NSNull *> *writingDone = FBMutableFuture.future; id<FBDataConsumer, FBDataConsumerLifecycle, FBDataConsumerSync> consumer = [FBBlockDataConsumer synchronousDataConsumerWithBlock:^(NSData *data) { idb::LogResponse item; item.set_output(data.bytes, data.length); if (writingDone.hasCompleted) { return; } bool success = response->Write(item); if (success) { return; } // The client write failed, the client has gone so don't write again. [writingDone resolveWithResult:NSNull.null]; }]; // Setup the log operation. NSError *error = nil; BOOL logFromCompanion = request->source() == idb::LogRequest::Source::LogRequest_Source_COMPANION; NSArray<NSString *> *arguments = extract_string_array(request->arguments()); id<FBLogOperation> operation = [(logFromCompanion ? [_commandExecutor tail_companion_logs:consumer] : [_target tailLog:arguments consumer:consumer]) block:&error]; if (!operation) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } // Poll on completion (logging happens asynchronously). This occurs when the stream is cancelled, the log operation is done, or the last write failed. FBFuture<NSNull *> *completed = [FBFuture race:@[writingDone, operation.completed]]; while (completed.hasCompleted == NO && context->IsCancelled() == false) { // Sleep for 200ms before polling again. usleep(1000 * 200); } // Signal that we're done writing due to the operation completion or the client going away. // This will also prevent the polling of the ClientContext. [writingDone resolveWithResult:NSNull.null]; // Teardown the log operation now that we're done with it FBFuture<NSNull *> *teardown = [operation.completed cancel]; [teardown block:nil]; return Status::OK; }} Status FBIDBServiceHandler::record(grpc::ServerContext *context, grpc::ServerReaderWriter<idb::RecordResponse, idb::RecordRequest> *stream) {@autoreleasepool{ idb::RecordRequest initial; stream->Read(&initial); NSError *error = nil; const std::string requestedFilePath = initial.start().file_path(); NSString *filePath = requestedFilePath.length() > 0 ? nsstring_from_c_string(requestedFilePath.c_str()) : [[_target.auxillaryDirectory stringByAppendingPathComponent:@"idb_encode"] stringByAppendingPathExtension:@"mp4"]; id<FBiOSTargetOperation> operation = [[_target startRecordingToFile:filePath] block:&error]; if (!operation) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } idb::RecordRequest stop; stream->Read(&stop); if (![[_target stopRecording] succeeds:&error]) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } if (requestedFilePath.length() > 0) { return respond_file_path(nil, filePath, stream); } else { return drain_writer([FBArchiveOperations createGzipForPath:filePath queue:dispatch_queue_create("com.facebook.idb.record", DISPATCH_QUEUE_SERIAL) logger:_target.logger], stream); } }} Status FBIDBServiceHandler::video_stream(ServerContext* context, grpc::ServerReaderWriter<idb::VideoStreamResponse, idb::VideoStreamRequest>* stream) {@autoreleasepool{ NSError *error = nil; idb::VideoStreamRequest request; FBMutableFuture<NSNull *> *done = FBMutableFuture.future; id<FBDataConsumer> consumer = consumer_from_request(stream, request, done, &error); if (!consumer) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } idb::VideoStreamRequest_Start start = request.start(); NSNumber *framesPerSecond = start.fps() > 0 ? @(start.fps()) : nil; FBVideoStreamEncoding encoding = @""; switch (start.format()) { case idb::VideoStreamRequest_Format_RBGA: encoding = FBVideoStreamEncodingBGRA; break; case idb::VideoStreamRequest_Format_H264: encoding = FBVideoStreamEncodingH264; break; case idb::VideoStreamRequest_Format_MJPEG: encoding = FBVideoStreamEncodingMJPEG; break; case idb::VideoStreamRequest_Format_MINICAP: encoding = FBVideoStreamEncodingMinicap; break; default: return Status(grpc::StatusCode::INTERNAL, "Invalid Video format provided"); } NSNumber *compressionQuality = @(start.compression_quality()); NSNumber *scaleFactor = @(start.scale_factor()); NSNumber *avgBitrate = start.avg_bitrate() > 0 ? @(start.avg_bitrate()) : nil; FBVideoStreamConfiguration *configuration = [[FBVideoStreamConfiguration alloc] initWithEncoding:encoding framesPerSecond:framesPerSecond compressionQuality:compressionQuality scaleFactor:scaleFactor avgBitrate:avgBitrate]; id<FBVideoStream> videoStream = [[_target createStreamWithConfiguration:configuration] block:&error]; if (!stream) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } BOOL success = [[videoStream startStreaming:consumer] block:&error] != nil; if (success == NO) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } // Wait for the client to hangup or stream to stop FBFuture<NSNull *> *clientStopped = resolve_next_read(stream); [[FBFuture race:@[clientStopped, videoStream.completed]] block:nil]; // Signal that we're done so we don't write to a dangling pointer. [done resolveWithResult:NSNull.null]; // Stop the streaming for real. It may have stopped already in which case this returns instantly. success = [[videoStream stopStreaming] block:&error] != nil; [_target.logger logFormat:@"The video stream is terminated"]; if (success == NO) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return Status::OK; }} Status FBIDBServiceHandler::push(grpc::ServerContext *context, grpc::ServerReader<idb::PushRequest> *reader, idb::PushResponse *response) {@autoreleasepool{ NSError *error = nil; idb::PushRequest request; reader->Read(&request); if (request.value_case() != idb::PushRequest::kInner) { return Status(grpc::StatusCode::INTERNAL, "First message must contain the commands information"); } const idb::PushRequest_Inner inner = request.inner(); [[filepaths_from_reader(_commandExecutor.temporaryDirectory, reader, false, _target.logger) onQueue:_target.asyncQueue pop:^FBFuture<NSNull *> *(NSArray<NSURL *> *files) { return [_commandExecutor push_files:files to_path:nsstring_from_c_string(inner.dst_path()) containerType:file_container(inner.container())]; }] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return Status::OK; }} Status FBIDBServiceHandler::pull(ServerContext *context, const ::idb::PullRequest *request, grpc::ServerWriter<::idb::PullResponse> *stream) {@autoreleasepool{ NSString *path = nsstring_from_c_string(request->src_path()); NSError *error = nil; if (request->dst_path().length() > 0) { NSString *filePath = [[_commandExecutor pull_file_path:path destination_path:nsstring_from_c_string(request->dst_path()) containerType:file_container(request->container())] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return respond_file_path(nil, filePath, stream); } else { NSURL *url = [_commandExecutor.temporaryDirectory temporaryDirectory]; NSString *tempPath = [url.path stringByAppendingPathComponent:path.lastPathComponent]; NSString *filePath = [[_commandExecutor pull_file_path:path destination_path:tempPath containerType:file_container(request->container())] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return drain_writer([FBArchiveOperations createGzippedTarForPath:filePath queue:dispatch_queue_create("com.facebook.idb.pull", DISPATCH_QUEUE_SERIAL) logger:_target.logger], stream); } }} Status FBIDBServiceHandler::tail(ServerContext* context, grpc::ServerReaderWriter<idb::TailResponse, idb::TailRequest>* stream) {@autoreleasepool{ idb::TailRequest request; stream->Read(&request); idb::TailRequest_Start start = request.start(); NSString *path = nsstring_from_c_string(start.path()); NSString *container = file_container(start.container()); FBMutableFuture<NSNull *> *finished = FBMutableFuture.future; id<FBDataConsumer, FBDataConsumerSync> consumer = [FBBlockDataConsumer synchronousDataConsumerWithBlock:^(NSData *data) { if (finished.hasCompleted) { return; } idb::TailResponse response; response.set_data(data.bytes, data.length); stream->Write(response); }]; NSError *error = nil; FBFuture<NSNull *> *tailOperation = [[_commandExecutor tail:path to_consumer:consumer in_container:container] block:&error]; if (!tailOperation) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } stream->Read(&request); [[tailOperation cancel] block:nil]; [finished resolveWithResult:NSNull.null]; return Status::OK; }} Status FBIDBServiceHandler::describe(ServerContext *context, const idb::TargetDescriptionRequest *request, idb::TargetDescriptionResponse *response) {@autoreleasepool{ // Populate the default values idb::TargetDescription *description = response->mutable_target_description(); FBiOSTargetScreenInfo *screenInfo = _target.screenInfo; if (screenInfo) { idb::ScreenDimensions *dimensions = description->mutable_screen_dimensions(); dimensions->set_width(screenInfo.widthPixels); dimensions->set_height(screenInfo.heightPixels); dimensions->set_height_points(screenInfo.heightPixels/screenInfo.scale); dimensions->set_width_points(screenInfo.widthPixels/screenInfo.scale); dimensions->set_density(screenInfo.scale); } description->set_udid(_target.udid.UTF8String); description->set_name(_target.name.UTF8String); description->set_state(FBiOSTargetStateStringFromState(_target.state).UTF8String); description->set_target_type(FBiOSTargetTypeStringFromTargetType(_target.targetType).lowercaseString.UTF8String); description->set_os_version(_target.osVersion.name.UTF8String); description->set_architecture(_target.architecture.UTF8String); // Add extended information NSDictionary<NSString *, id> *extendedInformation = _target.extendedInformation; NSError *error = nil; NSData *data = [NSJSONSerialization dataWithJSONObject:extendedInformation options:0 error:&error]; if (!data) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } description->set_extended(data.bytes, data.length); // Also attach the companion metadata populate_companion_info(response->mutable_companion(), _eventReporter, _target); // Only fetch diagnostic information when requested. if (!request->fetch_diagnostics()) { return Status::OK; } NSDictionary<NSString *, id> *diagnosticInformation = [[_commandExecutor diagnostic_information] block:&error]; if (!diagnosticInformation) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } data = [NSJSONSerialization dataWithJSONObject:diagnosticInformation options:0 error:&error]; if (!data) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } description->set_diagnostics(data.bytes, data.length); return Status::OK; }} Status FBIDBServiceHandler::add_media(grpc::ServerContext *context, grpc::ServerReader<idb::AddMediaRequest> *reader, idb::AddMediaResponse *response) {@autoreleasepool{ NSError *error = nil; [[filepaths_from_reader(_commandExecutor.temporaryDirectory, reader, true, _target.logger) onQueue:_target.asyncQueue pop:^(NSArray<NSURL *> *files) { return [_commandExecutor add_media:files]; }] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return Status::OK; }} Status FBIDBServiceHandler::instruments_run(grpc::ServerContext *context, grpc::ServerReaderWriter<idb::InstrumentsRunResponse, idb::InstrumentsRunRequest> *stream) {@autoreleasepool{ __block idb::InstrumentsRunRequest startRunRequest; __block pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL); __block bool finished_writing = NO; dispatch_queue_t queue = dispatch_queue_create("com.facebook.idb.instruments.server", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ idb::InstrumentsRunRequest request; stream->Read(&request); startRunRequest = request; }); FBInstrumentsConfiguration *configuration = translate_instruments_configuration(startRunRequest.start(), _commandExecutor.storageManager); NSError *error = nil; id<FBDataConsumer> consumer = [FBBlockDataConsumer asynchronousDataConsumerOnQueue:queue consumer:^(NSData *data) { idb::InstrumentsRunResponse response; response.set_log_output(data.bytes, data.length); pthread_mutex_lock(&mutex); if (!finished_writing) { stream->Write(response); } pthread_mutex_unlock(&mutex); }]; id<FBControlCoreLogger> logger = [FBControlCoreLoggerFactory compositeLoggerWithLoggers:@[ [FBControlCoreLoggerFactory loggerToConsumer:consumer], _target.logger, ]]; FBInstrumentsOperation *operation = [[_target startInstruments:configuration logger:logger] block:&error]; if (!operation) { pthread_mutex_lock(&mutex); finished_writing = YES; pthread_mutex_unlock(&mutex); return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } __block idb::InstrumentsRunRequest stopRunRequest; dispatch_sync(queue, ^{ idb::InstrumentsRunResponse response; response.set_state(idb::InstrumentsRunResponse::State::InstrumentsRunResponse_State_RUNNING_INSTRUMENTS); stream->Write(response); idb::InstrumentsRunRequest request; stream->Read(&request); stopRunRequest = request; }); if (![operation.stop succeeds:&error]) { pthread_mutex_lock(&mutex); finished_writing = YES; pthread_mutex_unlock(&mutex); return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } NSArray<NSString *> *postProcessArguments = [_commandExecutor.storageManager interpolateArgumentReplacements:extract_string_array(stopRunRequest.stop().post_process_arguments())]; dispatch_sync(queue, ^{ idb::InstrumentsRunResponse response; response.set_state(idb::InstrumentsRunResponse::State::InstrumentsRunResponse_State_POST_PROCESSING); stream->Write(response); }); NSURL *processed = [[FBInstrumentsOperation postProcess:postProcessArguments traceDir:operation.traceDir queue:queue logger:logger] block:&error]; pthread_mutex_lock(&mutex); finished_writing = YES; pthread_mutex_unlock(&mutex); if (!processed) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return drain_writer([FBArchiveOperations createGzippedTarForPath:processed.path queue:queue logger:_target.logger], stream); }} Status FBIDBServiceHandler::debugserver(grpc::ServerContext *context, grpc::ServerReaderWriter<idb::DebugServerResponse, idb::DebugServerRequest> *stream) {@autoreleasepool{ idb::DebugServerRequest request; stream->Read(&request); NSError *error = nil; switch (request.control_case()) { case idb::DebugServerRequest::ControlCase::kStart: { idb::DebugServerRequest::Start start = request.start(); id<FBDebugServer> debugServer = [[_commandExecutor debugserver_start:nsstring_from_c_string(start.bundle_id())] block:&error]; if (!debugServer) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } stream->Write(translate_debugserver_status(debugServer)); return Status::OK; } case idb::DebugServerRequest::ControlCase::kStatus: { id<FBDebugServer> debugServer = [[_commandExecutor debugserver_status] block:&error]; stream->Write(translate_debugserver_status(debugServer)); return Status::OK; } case idb::DebugServerRequest::ControlCase::kStop: { id<FBDebugServer> debugServer = [[_commandExecutor debugserver_stop] block:&error]; if (!debugServer) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } stream->Write(translate_debugserver_status(debugServer)); return Status::OK; } default: { return Status(grpc::StatusCode::UNIMPLEMENTED, NULL); } } }} Status FBIDBServiceHandler::dap(grpc::ServerContext *context, grpc::ServerReaderWriter<idb::DapResponse, idb::DapRequest> *stream) {@autoreleasepool{ idb::DapRequest initial_request; stream->Read(&initial_request); if (initial_request.control_case() != idb::DapRequest::ControlCase::kStart) { return Status(grpc::StatusCode::FAILED_PRECONDITION, "Dap command expected a Start messaged in the beginning of the Stream"); } idb::DapRequest_Start start = initial_request.start(); NSString *pkg_id = nsstring_from_c_string(start.debugger_pkg_id()); NSString *lldb_vscode = [@"dap" stringByAppendingPathComponent:[pkg_id stringByAppendingPathComponent: @"usr/bin/lldb-vscode"]]; id<FBDataConsumer> reader = [FBBlockDataConsumer synchronousDataConsumerWithBlock:^(NSData *data) { idb::DapResponse response; idb::DapResponse_Pipe *stdout = response.mutable_stdout(); stdout->set_data(data.bytes, data.length); stream->Write(response); [_target.logger.debug logFormat:@"Dap server stdout consumer: sent %lu bytes.", data.length]; }]; [_target.logger.debug logFormat:@"Starting dap server with path %@", lldb_vscode]; NSError *error = nil; FBProcessInput<id<FBDataConsumer>> *writer = [FBProcessInput inputFromConsumer]; FBProcess *process = [[_commandExecutor dapServerWithPath:lldb_vscode stdIn:writer stdOut:reader] awaitWithTimeout:600 error:&error]; if (error){ NSString *errorMsg = [NSString stringWithFormat:@"Failed to spaw DAP server. Error: %@", error.localizedDescription]; return Status(grpc::StatusCode::INTERNAL, errorMsg.UTF8String); } [_target.logger.debug logFormat:@"Dap server spawn with PID: %d", process.processIdentifier]; idb::DapResponse response; response.mutable_started(); stream->Write(response); dispatch_queue_t write_queue = dispatch_queue_create("com.facebook.idb.dap.write", DISPATCH_QUEUE_SERIAL); auto writeFuture = [FBFuture onQueue:write_queue resolveWhen:^BOOL { idb::DapRequest request; stream->Read(&request); if (request.control_case() == idb::DapRequest::ControlCase::kStop){ [_target.logger.debug logFormat:@"Received stop from Dap Request"]; [_target.logger.debug logFormat:@"Dap server with pid %d. Stderr: %@", process.processIdentifier, process.stdErr]; return YES; } idb::DapRequest_Pipe pipe = request.pipe(); auto raw_data = pipe.data(); NSData *data = [NSData dataWithBytes:raw_data.c_str() length:raw_data.length()]; if (data.length == 0) { [_target.logger.debug logFormat:@"Dap Request. Receiving empty messages. Transmission finished."]; return YES; } [_target.logger.debug logFormat:@"Dap Request. Received %lu bytes from client", data.length]; [writer.contents consumeData:data]; return NO; }]; // Debug session shouln't be longer than 10hours [writeFuture awaitWithTimeout:36000 error:&error]; if (error){ NSString *errorMsg = [NSString stringWithFormat:@"Error in writting to dap server stdout: %@", error.localizedDescription]; return Status(grpc::StatusCode::INTERNAL, errorMsg.UTF8String); } idb::DapResponse_Event *stopped = response.mutable_stopped(); stopped->set_desc(@"Dap server stopped.".UTF8String); stream->Write(response); return Status::OK; }} Status FBIDBServiceHandler::connect(grpc::ServerContext *context, const idb::ConnectRequest *request, idb::ConnectResponse *response) {@autoreleasepool{ // Add Meta to Reporter [_eventReporter addMetadata:extract_str_dict(request->metadata())]; // Get the local state BOOL isLocal = [NSFileManager.defaultManager fileExistsAtPath:nsstring_from_c_string(request->local_file_path())]; idb::CompanionInfo *info = response->mutable_companion(); info->set_is_local(isLocal); // Populate the other values. populate_companion_info(info, _eventReporter, _target); return Status::OK; }} Status FBIDBServiceHandler::xctrace_record(ServerContext *context,grpc::ServerReaderWriter<idb::XctraceRecordResponse, idb::XctraceRecordRequest> *stream) {@autoreleasepool{ __block idb::XctraceRecordRequest recordRequest; __block pthread_mutex_t mutex; pthread_mutex_init(&mutex, NULL); __block bool finished_writing = NO; dispatch_queue_t queue = dispatch_queue_create("com.facebook.idb.xctrace.record", DISPATCH_QUEUE_SERIAL); // @lint-ignore FBOBJCDISCOURAGEDFUNCTION dispatch_sync(queue, ^{ idb::XctraceRecordRequest request; stream->Read(&request); recordRequest = request; }); FBXCTraceRecordConfiguration *configuration = translate_xctrace_record_configuration(recordRequest.start()); NSError *error = nil; id<FBDataConsumer> consumer = [FBBlockDataConsumer asynchronousDataConsumerOnQueue:queue consumer:^(NSData *data) { idb::XctraceRecordResponse response; response.set_log(data.bytes, data.length); pthread_mutex_lock(&mutex); if (!finished_writing) { stream->Write(response); } pthread_mutex_unlock(&mutex); }]; id<FBControlCoreLogger> logger = [FBControlCoreLoggerFactory compositeLoggerWithLoggers:@[ [FBControlCoreLoggerFactory loggerToConsumer:consumer], _target.logger, ]]; FBXCTraceRecordOperation *operation = [[_target startXctraceRecord:configuration logger:logger] block:&error]; if (!operation) { pthread_mutex_lock(&mutex); finished_writing = YES; pthread_mutex_unlock(&mutex); return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } // @lint-ignore FBOBJCDISCOURAGEDFUNCTION dispatch_sync(queue, ^{ idb::XctraceRecordResponse response; response.set_state(idb::XctraceRecordResponse::State::XctraceRecordResponse_State_RUNNING); stream->Write(response); idb::XctraceRecordRequest request; stream->Read(&request); recordRequest = request; }); NSTimeInterval stopTimeout = recordRequest.stop().timeout() ?: DefaultXCTraceRecordStopTimeout; if (![[operation stopWithTimeout:stopTimeout] succeeds:&error]) { pthread_mutex_lock(&mutex); finished_writing = YES; pthread_mutex_unlock(&mutex); return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } NSArray<NSString *> *postProcessArgs = extract_string_array(recordRequest.stop().args()); // @lint-ignore FBOBJCDISCOURAGEDFUNCTION dispatch_sync(queue, ^{ idb::XctraceRecordResponse response; response.set_state(idb::XctraceRecordResponse::State::XctraceRecordResponse_State_PROCESSING); stream->Write(response); }); NSURL *processed = [[FBInstrumentsOperation postProcess:postProcessArgs traceDir:operation.traceDir queue:queue logger:logger] block:&error]; pthread_mutex_lock(&mutex); finished_writing = YES; pthread_mutex_unlock(&mutex); if (!processed) { return Status(grpc::StatusCode::INTERNAL, error.localizedDescription.UTF8String); } return drain_writer([FBArchiveOperations createGzippedTarForPath:processed.path queue:queue logger:_target.logger], stream); }} Status FBIDBServiceHandler::send_notification(grpc::ServerContext *context, const idb::SendNotificationRequest *request, idb::SendNotificationResponse *response) {@autoreleasepool{ NSError *error = nil; [[_commandExecutor sendPushNotificationForBundleID:nsstring_from_c_string(request->bundle_id()) jsonPayload:nsstring_from_c_string(request->json_payload())] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, [error.localizedDescription UTF8String]); } return Status::OK; }} Status FBIDBServiceHandler::simulate_memory_warning(grpc::ServerContext *context, const idb::SimulateMemoryWarningRequest *request, idb::SimulateMemoryWarningResponse *response) {@autoreleasepool{ NSError *error = nil; [[_commandExecutor simulateMemoryWarning] block:&error]; if (error) { return Status(grpc::StatusCode::INTERNAL, [error.localizedDescription UTF8String]); } return Status::OK; }}