diff --git a/tools_webrtc/apple/generate_privacy_manifest.py b/tools_webrtc/apple/generate_privacy_manifest.py new file mode 100644 index 0000000000..8d4dad9cdc --- /dev/null +++ b/tools_webrtc/apple/generate_privacy_manifest.py @@ -0,0 +1,81 @@ +#!/usr/bin/env vpython3 + +# Copyright (c) 2024 The WebRTC Project Authors. All rights reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +__doc__ = """Generate privacy manifest of WebRTC iOS framework.""" + +import argparse +import plistlib +import sys + + +def generate_privacy_manifest(out_file): + privacy_manifest = { + "NSPrivacyTracking": + False, + "NSPrivacyCollectedDataTypes": [], + "NSPrivacyTrackingDomains": [], + "NSPrivacyAccessedAPITypes": [ + # For mach_absolute_time usage in rtc_base/system_time.cc + { + "NSPrivacyAccessedAPIType": + "NSPrivacyAccessedAPICategorySystemBootTime", + "NSPrivacyAccessedAPITypeReasons": [ + # Declare this reason to access the system boot time + # in order to measure the amount of time that has elapsed + # between events that occurred within the app or to perform + # calculations to enable timers. + "35F9.1", + # Declare this reason to access the system boot time to + # calculate absolute timestamps for events that occurred + # within your app, such as events related to the UIKit or + # AVFAudio frameworks. + "8FFB.1", + ] + }, + # For stat usage in rtc_base/file_rotating_stream.cc + # TODO: bugs.webrtc.org/337909152 - Make this optional since this + # is only used for RTCFileLogger, which is not used by default and + # not considered as a core feature. + { + "NSPrivacyAccessedAPIType": + "NSPrivacyAccessedAPICategoryFileTimestamp", + "NSPrivacyAccessedAPITypeReasons": [ + # Declare this reason to access the timestamps, size, or + # other metadata of files inside the app container, app + # group container, or the app’s CloudKit container. + "C617.1" + ] + } + ] + } + + with open(out_file, 'wb') as file: + plistlib.dump(privacy_manifest, file, fmt=plistlib.FMT_XML) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("-o", "--output", type=str, help="Output file.") + # TODO: bugs.webrtc.org/337909152 - Add an option to not to emit privacy + # manifest entries for NSPrivacyAccessedAPICategoryFileTimestamp + + args = parser.parse_args() + + if not args.output: + print("Output file is required") + return 1 + + generate_privacy_manifest(args.output) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/webrtc.gni b/webrtc.gni index 447aae4096..c2989e4db4 100644 --- a/webrtc.gni +++ b/webrtc.gni @@ -1021,6 +1021,7 @@ if (is_mac || is_ios) { umbrella_header_path = "$target_gen_dir/$output_name.framework/WebRTC/$output_name.h" modulemap_path = "$target_gen_dir/Modules/module.modulemap" + privacy_manifest_path = "$target_gen_dir/$target_name/PrivacyInfo.xcprivacy" action_foreach("create_bracket_include_headers_$target_name") { script = "//tools_webrtc/apple/copy_framework_header.py" @@ -1061,6 +1062,7 @@ if (is_mac || is_ios) { deps += [ ":copy_framework_headers_$this_target_name", ":copy_modulemap_$this_target_name", + ":copy_privacy_manifest_$this_target_name", ":copy_umbrella_header_$this_target_name", ":create_bracket_include_headers_$this_target_name", ":modulemap_$this_target_name", @@ -1083,6 +1085,7 @@ if (is_mac || is_ios) { ":create_bracket_include_headers_$this_target_name") deps += [ + ":copy_privacy_manifest_$this_target_name", ":copy_umbrella_header_$this_target_name", ":create_bracket_include_headers_$this_target_name", ] @@ -1092,8 +1095,13 @@ if (is_mac || is_ios) { if (is_mac || target_environment == "catalyst") { # Catalyst frameworks use the same layout as regular Mac frameworks. headers_dir = "Versions/A/Headers" + + # The path to the privacy manifest file differs between Mac and iOS. + # https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/adding_a_privacy_manifest_to_your_app_or_third-party_sdk + privacy_manifest_out_path = "Versions/A/Resources/PrivacyInfo.xcprivacy" } else { headers_dir = "Headers" + privacy_manifest_out_path = "PrivacyInfo.xcprivacy" } bundle_data("copy_framework_headers_$this_target_name") { @@ -1143,6 +1151,25 @@ if (is_mac || is_ios) { deps = [ ":umbrella_header_$target_name" ] } + + action("create_privacy_manifest_$target_name") { + script = "//tools_webrtc/apple/generate_privacy_manifest.py" + + args = [ + "--output", + rebase_path(privacy_manifest_path), + ] + + outputs = [ privacy_manifest_path ] + } + + copy("copy_privacy_manifest_$target_name") { + sources = [ privacy_manifest_path ] + outputs = + [ "$root_out_dir/$output_name.framework/$privacy_manifest_out_path" ] + + deps = [ ":create_privacy_manifest_$target_name" ] + } } }