// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "internal/platform/implementation/platform.h" #import #include #include #include #include "absl/strings/string_view.h" #include "internal/base/files.h" #import "internal/platform/implementation/apple/Log/GNCLogger.h" #include "internal/platform/implementation/apple/app_lifecycle_monitor.h" #include "internal/platform/implementation/apple/atomic_boolean.h" #include "internal/platform/implementation/apple/atomic_uint32.h" #include "internal/platform/implementation/apple/awdl.h" #include "internal/platform/implementation/apple/ble.h" #include "internal/platform/implementation/apple/condition_variable.h" #include "internal/platform/implementation/apple/count_down_latch.h" #include "internal/platform/implementation/apple/device_info.h" #import "internal/platform/implementation/apple/multi_thread_executor.h" #include "internal/platform/implementation/apple/mutex.h" #include "internal/platform/implementation/apple/preferences_manager.h" #import "internal/platform/implementation/apple/scheduled_executor.h" #import "internal/platform/implementation/apple/single_thread_executor.h" #include "internal/platform/implementation/apple/timer.h" #import "internal/platform/implementation/apple/utils.h" #include "internal/platform/implementation/apple/wifi.h" #include "internal/platform/implementation/apple/wifi_hotspot.h" #include "internal/platform/implementation/apple/wifi_lan.h" #include "internal/platform/implementation/mutex.h" #include "internal/platform/implementation/shared/file.h" #include "internal/platform/payload_id.h" #ifndef NO_WEBRTC #import "internal/platform/implementation/apple/webrtc.h" #endif namespace nearby { namespace api { std::string ImplementationPlatform::GetCustomSavePath(const std::string& parent_folder, const std::string& file_name) { // Collapse any path escaping characters. NSString* parentFolder = [@(parent_folder.c_str()) stringByReplacingOccurrencesOfString:@"../" withString:@""]; NSURL* parentFolderURL = [NSURL fileURLWithPath:parentFolder]; // The only reserved character in a file name on macOS is the forward-slash. It's unclear if iOS // has any additional restrictions. // // """ // In the Finder, filenames containing `/` can be created, but `/` is stored as a colon (:) in the // filesystem, and is shown as such on the command line. Filenames containing `:` created from the // command line are shown with `/` instead of `:` in the Finder, so that it is impossible to // create a file that the Finder shows as having a `:` in its filename. // """ // // See: https://en.wikipedia.org/wiki/Filename NSString* fileName = [@(file_name.c_str()) stringByReplacingOccurrencesOfString:@"/" withString:@":"]; NSString* baseName = [fileName stringByDeletingPathExtension]; NSString* extension = [fileName pathExtension]; NSURL* url = [parentFolderURL URLByAppendingPathComponent:fileName]; NSInteger index = 1; while ([NSFileManager.defaultManager fileExistsAtPath:url.path]) { index++; NSString* fileName = [NSString stringWithFormat:@"%@ %@.%@", baseName, [@(index) stringValue], extension]; url = [parentFolderURL URLByAppendingPathComponent:fileName]; } return url.path.UTF8String; } std::string ImplementationPlatform::GetDownloadPath(const std::string& parent_folder, const std::string& file_name) { NSString* customSavePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@(parent_folder.c_str())]; return GetCustomSavePath(customSavePath.UTF8String, file_name); } OSName ImplementationPlatform::GetCurrentOS() { return OSName::kApple; } // Atomics: std::unique_ptr ImplementationPlatform::CreateAtomicBoolean(bool initial_value) { return std::make_unique(initial_value); } std::unique_ptr ImplementationPlatform::CreateAtomicUint32( std::uint32_t initial_value) { return std::make_unique(initial_value); } std::unique_ptr ImplementationPlatform::CreateCountDownLatch(std::int32_t count) { return std::make_unique(count); } std::unique_ptr ImplementationPlatform::CreateMutex(Mutex::Mode mode) { // iOS does not support unchecked Mutex in debug mode, therefore // apple::Mutex is used for both kRegular and kRegularNoCheck. if (mode == Mutex::Mode::kRecursive) { return std::make_unique(); } else { return std::make_unique(); } } std::unique_ptr ImplementationPlatform::CreateConditionVariable(Mutex* mutex) { return std::make_unique(static_cast(mutex)); } ABSL_DEPRECATED("This interface will be deleted in the near future.") std::unique_ptr ImplementationPlatform::CreateInputFile(PayloadId payload_id) { return nullptr; } std::unique_ptr ImplementationPlatform::CreateInputFile(const std::string& file_path) { return shared::IOFile::CreateInputFile(file_path); } ABSL_DEPRECATED("This interface will be deleted in the near future.") std::unique_ptr ImplementationPlatform::CreateOutputFile(PayloadId payload_id) { return nullptr; } std::unique_ptr ImplementationPlatform::CreateOutputFile(const std::string& file_path) { FilePath path{file_path}; FilePath folder_path = path.GetParentPath(); // Verifies that a path is a valid directory. if (!Files::DirectoryExists(folder_path)) { if (!Files::CreateDirectories(folder_path)) { GNCLoggerError(@"Failed to create directory: %@", @(folder_path.ToString().c_str())); return nullptr; } } return shared::IOFile::CreateOutputFile(file_path); } // Java-like Executors std::unique_ptr ImplementationPlatform::CreateSingleThreadExecutor() { return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreateMultiThreadExecutor( int max_concurrency) { return std::make_unique(max_concurrency); } std::unique_ptr ImplementationPlatform::CreateScheduledExecutor() { return std::make_unique(); } // Mediums std::unique_ptr ImplementationPlatform::CreateBluetoothAdapter() { return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreateBluetoothClassicMedium( api::BluetoothAdapter& adapter) { return nullptr; } std::unique_ptr ImplementationPlatform::CreateBleMedium( api::BluetoothAdapter& adapter) { return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreateWifiMedium() { return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreateWifiLanMedium() { return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreateAwdlMedium() { return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreateWifiHotspotMedium() { #if TARGET_OS_IOS return std::make_unique(); #else return nullptr; #endif } std::unique_ptr ImplementationPlatform::CreateWifiDirectMedium() { return nullptr; } #ifndef NO_WEBRTC std::unique_ptr ImplementationPlatform::CreateWebRtcMedium() { return std::make_unique(); } #endif std::unique_ptr ImplementationPlatform::CreateAppLifecycleMonitor( std::function state_updated_callback) { #if TARGET_OS_IPHONE return std::make_unique(std::move(state_updated_callback)); #else return nullptr; #endif } absl::StatusOr ImplementationPlatform::SendRequest(const WebRequest& requestInfo) { NSURL* url = [NSURL URLWithString:@(requestInfo.url.c_str())]; NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; [request setHTTPMethod:@(requestInfo.method.c_str())]; for (const auto& header : requestInfo.headers) { [request setValue:@(header.second.c_str()) forHTTPHeaderField:@(header.first.c_str())]; } [request setHTTPBody:[@(requestInfo.body.c_str()) dataUsingEncoding:NSUTF8StringEncoding]]; NSCondition* condition = [[NSCondition alloc] init]; [condition lock]; __block NSData* blockData = nil; __block NSHTTPURLResponse* blockResponse = nil; __block NSError* blockError = nil; NSURLSessionDataTask* task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData* data, NSURLResponse* response, NSError* error) { [condition lock]; blockData = data; if ([response isKindOfClass:[NSHTTPURLResponse class]]) { blockResponse = (NSHTTPURLResponse*)response; } blockError = error; [condition signal]; [condition unlock]; }]; [task resume]; [condition wait]; [condition unlock]; if (blockResponse == nil) { return absl::FailedPreconditionError([[blockError localizedDescription] UTF8String]); } WebResponse webResponse; webResponse.status_code = blockResponse.statusCode; webResponse.status_text = [[NSHTTPURLResponse localizedStringForStatusCode:blockResponse.statusCode] UTF8String]; NSDictionary* headers = blockResponse.allHeaderFields; for (NSString* key in headers) { NSString* value = [headers objectForKey:key]; webResponse.headers.insert({[key UTF8String], [value UTF8String]}); } if (blockData != nil) { // Body is not a UTF-8 encoded string and is just using `std::string` as a container for data. webResponse.body = std::string((char*)blockData.bytes, blockData.length); } return webResponse; } std::unique_ptr ImplementationPlatform::CreateTimer() { return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreateDeviceInfo() { return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreatePreferencesManager( absl::string_view path) { return std::make_unique(path); } } // namespace api } // namespace nearby