Commit 17912578 authored by Olivier Robin's avatar Olivier Robin Committed by Commit Bot

Enable MetricKit payload collection

Add a flag to save MetricKit payloads.
Saving the payload will require
- a compilation flag that will be enabled only on canary.
- setting an experimental setting

JSON representation of MetricKit payload will be saved in Documents
directory.

Collecting this data will help developing MetricKit
support in Chrome.

Bug: 1017434
Change-Id: I5db5d7112859eb873ee68e8f7159f7ab9fe3dbfb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2156905
Commit-Queue: Olivier Robin <olivierrobin@chromium.org>
Reviewed-by: default avatarSylvain Defresne <sdefresne@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#760994}
parent 42fa51d7
...@@ -2,6 +2,14 @@ ...@@ -2,6 +2,14 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
import("//build/buildflag_header.gni")
import("//ios/chrome/features.gni")
buildflag_header("ios_enable_metrickit_buildflags") {
header = "ios_enable_metrickit_buildflags.h"
flags = [ "IOS_ENABLE_METRICKIT=$ios_enable_metrickit" ]
}
source_set("application_delegate") { source_set("application_delegate") {
sources = [ sources = [
"memory_warning_helper.h", "memory_warning_helper.h",
...@@ -23,6 +31,7 @@ source_set("unit_tests") { ...@@ -23,6 +31,7 @@ source_set("unit_tests") {
sources = [ sources = [
"app_state_unittest.mm", "app_state_unittest.mm",
"memory_warning_helper_unittest.mm", "memory_warning_helper_unittest.mm",
"metric_kit_subscriber_unittest.mm",
"metrics_mediator_unittest.mm", "metrics_mediator_unittest.mm",
"url_opener_unittest.mm", "url_opener_unittest.mm",
"user_activity_handler_unittest.mm", "user_activity_handler_unittest.mm",
...@@ -30,6 +39,7 @@ source_set("unit_tests") { ...@@ -30,6 +39,7 @@ source_set("unit_tests") {
deps = [ deps = [
":application_delegate", ":application_delegate",
":application_delegate_internal", ":application_delegate_internal",
":metric_kit_subscriber",
":tab_opening", ":tab_opening",
":test_support", ":test_support",
"//base", "//base",
...@@ -104,6 +114,21 @@ source_set("tab_opening") { ...@@ -104,6 +114,21 @@ source_set("tab_opening") {
] ]
} }
source_set("metric_kit_subscriber") {
configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"metric_kit_subscriber.h",
"metric_kit_subscriber.mm",
"metric_kit_subscribing_util.h",
"metric_kit_subscribing_util.mm",
]
libs = [ "MetricKit.framework" ]
deps = [
":ios_enable_metrickit_buildflags",
"//base",
]
}
source_set("application_delegate_internal") { source_set("application_delegate_internal") {
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
sources = [ sources = [
...@@ -121,6 +146,7 @@ source_set("application_delegate_internal") { ...@@ -121,6 +146,7 @@ source_set("application_delegate_internal") {
] ]
deps = [ deps = [
":application_delegate", ":application_delegate",
":ios_enable_metrickit_buildflags",
":tab_opening", ":tab_opening",
"//base", "//base",
"//build:branding_buildflags", "//build:branding_buildflags",
...@@ -171,6 +197,9 @@ source_set("application_delegate_internal") { ...@@ -171,6 +197,9 @@ source_set("application_delegate_internal") {
"//ui/base", "//ui/base",
"//url", "//url",
] ]
if (ios_enable_metrickit) {
deps += [ ":metric_kit_subscriber" ]
}
libs = [ libs = [
"CoreSpotlight.framework", "CoreSpotlight.framework",
"UIKit.framework", "UIKit.framework",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_APP_APPLICATION_DELEGATE_METRIC_KIT_SUBSCRIBER_H_
#define IOS_CHROME_APP_APPLICATION_DELEGATE_METRIC_KIT_SUBSCRIBER_H_
#import <Foundation/Foundation.h>
#import <MetricKit/MetricKit.h>
extern NSString* const kChromeMetricKitPayloadsDirectory;
// A subscriber that save MetricKit reports to the application document
// directory.
@interface MetricKitSubscriber : NSObject <MXMetricManagerSubscriber>
+ (instancetype)sharedInstance;
@end
#endif // IOS_CHROME_APP_APPLICATION_DELEGATE_METRIC_KIT_SUBSCRIBER_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/app/application_delegate/metric_kit_subscriber.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
NSString* const kChromeMetricKitPayloadsDirectory = @"ChromeMetricKitPayloads";
namespace {
void WriteMetricPayloads(NSArray<MXMetricPayload*>* payloads)
API_AVAILABLE(ios(13.0)) {
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString* documents_directory = [paths objectAtIndex:0];
NSString* metric_kit_report_directory = [documents_directory
stringByAppendingPathComponent:kChromeMetricKitPayloadsDirectory];
base::FilePath metric_kit_report_path(
base::SysNSStringToUTF8(metric_kit_report_directory));
if (!base::CreateDirectory(metric_kit_report_path)) {
return;
}
NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyyMMdd_HHmmss"];
[formatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
for (MXMetricPayload* payload : payloads) {
NSDate* end_date = payload.timeStampEnd;
NSString* file_name = [NSString
stringWithFormat:@"%@.json", [formatter stringFromDate:end_date]];
base::FilePath file_path(
base::SysNSStringToUTF8([metric_kit_report_directory
stringByAppendingPathComponent:file_name]));
NSData* file_data = payload.JSONRepresentation;
base::WriteFile(file_path, static_cast<const char*>(file_data.bytes),
file_data.length);
}
}
} // namespace
@implementation MetricKitSubscriber
+ (instancetype)sharedInstance {
static MetricKitSubscriber* instance = [[MetricKitSubscriber alloc] init];
return instance;
}
- (void)didReceiveMetricPayloads:(NSArray<MXMetricPayload*>*)payloads
API_AVAILABLE(ios(13.0)) {
base::ThreadPool::PostTask(
FROM_HERE,
{base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN,
base::ThreadPolicy::PREFER_BACKGROUND, base::MayBlock()},
base::BindOnce(WriteMetricPayloads, payloads));
}
@end
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/app/application_delegate/metric_kit_subscriber.h"
#import <Foundation/Foundation.h>
#import <MetricKit/MetricKit.h>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/run_loop.h"
#include "base/strings/sys_string_conversions.h"
#include "base/test/ios/wait_util.h"
#include "base/test/task_environment.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#include "third_party/ocmock/gtest_support.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
base::FilePath MetricKitReportDirectory() {
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString* documents_directory = [paths objectAtIndex:0];
NSString* metric_kit_report_directory = [documents_directory
stringByAppendingPathComponent:kChromeMetricKitPayloadsDirectory];
return base::FilePath(base::SysNSStringToUTF8(metric_kit_report_directory));
}
}
class MetricKitSubscriberTest : public PlatformTest {
public:
MetricKitSubscriberTest() {
base::DeleteFile(MetricKitReportDirectory(), true);
}
~MetricKitSubscriberTest() override {
base::DeleteFile(MetricKitReportDirectory(), true);
}
private:
base::test::TaskEnvironment task_environment_;
};
// Tests that Metrickit reports are correctly saved in the document directory.
TEST_F(MetricKitSubscriberTest, SaveReport) {
if (@available(iOS 13, *)) {
id mock_report = OCMClassMock([MXMetricPayload class]);
NSDate* date = [NSDate date];
std::string file_data("report content");
NSData* data = [NSData dataWithBytes:file_data.c_str()
length:file_data.size()];
NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyyMMdd_HHmmss"];
[formatter setTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]];
NSString* file_name =
[NSString stringWithFormat:@"%@.json", [formatter stringFromDate:date]];
base::FilePath file_path =
MetricKitReportDirectory().Append(base::SysNSStringToUTF8(file_name));
OCMStub([mock_report timeStampEnd]).andReturn(date);
OCMStub([mock_report JSONRepresentation]).andReturn(data);
NSArray* array = @[ mock_report ];
[[MetricKitSubscriber sharedInstance] didReceiveMetricPayloads:array];
EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
base::test::ios::kWaitForFileOperationTimeout, ^bool() {
base::RunLoop().RunUntilIdle();
return base::PathExists(file_path);
}));
std::string content;
base::ReadFileToString(file_path, &content);
EXPECT_EQ(content, file_data);
}
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_APP_APPLICATION_DELEGATE_METRIC_KIT_SUBSCRIBING_UTIL_H_
#define IOS_CHROME_APP_APPLICATION_DELEGATE_METRIC_KIT_SUBSCRIBING_UTIL_H_
// Enables MetricKit payloads collection and saving.
// Payloads will be saved in JSON files in application Documents directory.
//
// Checks if the collection is enabled in experimental settings.
// Changing the value of the setting will require restarting Chrome.
void EnableMetricKitReportCollection();
#endif // IOS_CHROME_APP_APPLICATION_DELEGATE_METRIC_KIT_SUBSCRIBING_UTIL_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/app/application_delegate/metric_kit_subscribing_util.h"
#import <Foundation/Foundation.h>
#import <MetricKit/MetricKit.h>
#import "ios/chrome/app/application_delegate/metric_kit_subscriber.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// The Experimental setting that enables the collection of MetricKit reports.
NSString* const kEnableMetricKit = @"EnableMetricKit";
} // namespace
void EnableMetricKitReportCollection() {
static dispatch_once_t once_token;
dispatch_once(&once_token, ^{
if (@available(iOS 13, *)) {
NSUserDefaults* standard_defaults = [NSUserDefaults standardUserDefaults];
if ([standard_defaults boolForKey:kEnableMetricKit]) {
[[MXMetricManager sharedManager]
addSubscriber:[MetricKitSubscriber sharedInstance]];
}
}
});
}
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "components/metrics/metrics_service.h" #include "components/metrics/metrics_service.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "components/ukm/ios/features.h" #include "components/ukm/ios/features.h"
#import "ios/chrome/app/application_delegate/ios_enable_metrickit_buildflags.h"
#import "ios/chrome/app/application_delegate/startup_information.h" #import "ios/chrome/app/application_delegate/startup_information.h"
#include "ios/chrome/browser/application_context.h" #include "ios/chrome/browser/application_context.h"
#include "ios/chrome/browser/chrome_url_constants.h" #include "ios/chrome/browser/chrome_url_constants.h"
...@@ -39,6 +40,10 @@ ...@@ -39,6 +40,10 @@
#import "ios/web/public/web_state.h" #import "ios/web/public/web_state.h"
#include "url/gurl.h" #include "url/gurl.h"
#if BUILDFLAG(IOS_ENABLE_METRICKIT)
#import "ios/chrome/app/application_delegate/metric_kit_subscribing_util.h" // nogncheck
#endif // BUILDFLAG(IOS_ENABLE_METRICKIT)
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
...@@ -222,6 +227,9 @@ using metrics_mediator::kAppEnteredBackgroundDateKey; ...@@ -222,6 +227,9 @@ using metrics_mediator::kAppEnteredBackgroundDateKey;
[self setBreakpadEnabled:optIn withUploading:allowUploading]; [self setBreakpadEnabled:optIn withUploading:allowUploading];
[self setWatchWWANEnabled:optIn]; [self setWatchWWANEnabled:optIn];
[self setAppGroupMetricsEnabled:optIn]; [self setAppGroupMetricsEnabled:optIn];
#if BUILDFLAG(IOS_ENABLE_METRICKIT)
EnableMetricKitReportCollection();
#endif
} }
- (BOOL)areMetricsEnabled { - (BOOL)areMetricsEnabled {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import("//build/config/features.gni") import("//build/config/features.gni")
import("//build/config/ios/rules.gni") import("//build/config/ios/rules.gni")
import("//build/mac/tweak_info_plist.gni") import("//build/mac/tweak_info_plist.gni")
import("//ios/chrome/features.gni")
import("//rlz/buildflags/buildflags.gni") import("//rlz/buildflags/buildflags.gni")
import("//third_party/protobuf/proto_library.gni") import("//third_party/protobuf/proto_library.gni")
...@@ -257,8 +258,11 @@ source_set("browser_impl") { ...@@ -257,8 +258,11 @@ source_set("browser_impl") {
} }
tweak_info_plist("experimental_info_plist") { tweak_info_plist("experimental_info_plist") {
info_plists = [ info_plists = [ "resources/Settings.bundle/Experimental.plist" ]
"resources/Settings.bundle/Experimental.plist", if (ios_enable_metrickit) {
info_plists += [ "resources/Settings.bundle/ExperimentalMetricKit.plist" ]
}
info_plists += [
"resources/Settings.bundle/ExperimentalFlags.plist", "resources/Settings.bundle/ExperimentalFlags.plist",
"resources/Settings.bundle/ExperimentalBlank.plist", "resources/Settings.bundle/ExperimentalBlank.plist",
] ]
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>Title</key>
<string>MetricKit</string>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Enable MetricKit collection</string>
<key>Key</key>
<string>EnableMetricKit</string>
<key>DefaultValue</key>
<false/>
</dict>
</array>
</dict>
</plist>
...@@ -7,4 +7,7 @@ declare_args() { ...@@ -7,4 +7,7 @@ declare_args() {
# gets the implementation from its downstream provider. Ignored if # gets the implementation from its downstream provider. Ignored if
# MaterialComponents is not build as a framework. # MaterialComponents is not build as a framework.
ios_chrome_links_with_material_components_framework = true ios_chrome_links_with_material_components_framework = true
# Enable MetricKit in Chrome to collect runtime data.
ios_enable_metrickit = false
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment