]> git.saurik.com Git - apple/security.git/blob - keychain/analytics/SecEventMetric.m
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / analytics / SecEventMetric.m
1 /*
2 * Copyright (c) 2018 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 #import "keychain/analytics/SecEventMetric.h"
26 #import "keychain/analytics/SecC2DeviceInfo.h"
27 #import "keychain/analytics/C2Metric/SECC2MPMetric.h"
28 #import "keychain/analytics/C2Metric/SECC2MPGenericEvent.h"
29 #import "keychain/analytics/C2Metric/SECC2MPGenericEventMetric.h"
30 #import "keychain/analytics/C2Metric/SECC2MPGenericEventMetricValue.h"
31 #import "keychain/analytics/C2Metric/SECC2MPError.h"
32
33 #import <os/log.h>
34
35 @interface SecEventMetric ()
36 @property NSString *eventName;
37 @property NSMutableDictionary *attributes;
38 @end
39
40 @implementation SecEventMetric
41
42 + (BOOL)supportsSecureCoding {
43 return YES;
44 }
45
46 - (instancetype)initWithEventName:(NSString *)name
47 {
48 if ((self = [super init]) == NULL) {
49 return self;
50 }
51 self.eventName = name;
52 self.attributes = [NSMutableDictionary dictionary];
53 return self;
54 }
55
56 - (instancetype)initWithCoder:(NSCoder *)coder {
57 self = [super init];
58 if (self) {
59 NSMutableSet *attributeClasses = [[[self class] supportedAttributeClasses] mutableCopy];
60 [attributeClasses addObject:[NSDictionary class]];
61
62 _eventName = [coder decodeObjectOfClass:[NSString class] forKey:@"eventName"];
63 _attributes = [coder decodeObjectOfClasses:attributeClasses forKey:@"attributes"];
64
65 if (!_eventName || !_attributes) {
66 return NULL;
67 }
68 }
69 return self;
70 }
71
72 - (void)encodeWithCoder:(NSCoder *)coder {
73 [coder encodeObject:self.eventName forKey:@"eventName"];
74 [coder encodeObject:self.attributes forKey:@"attributes"];
75 }
76
77 + (NSSet *)supportedAttributeClasses {
78 static NSSet *supported = NULL;
79 static dispatch_once_t onceToken;
80 dispatch_once(&onceToken, ^{
81 NSArray *clss = @[
82 [NSString class],
83 [NSNumber class],
84 [NSDate class],
85 [NSError class],
86 ];
87 supported = [NSSet setWithArray:clss];
88 });
89 return supported;
90 }
91
92 - (void)setObject:(nullable id)object forKeyedSubscript:(NSString *)key {
93 bool found = false;
94 if (key == NULL) {
95 return;
96 }
97 if (object) {
98 for (Class cls in [[self class] supportedAttributeClasses]) {
99 if ([object isKindOfClass:cls]) {
100 found = true;
101 break;
102 }
103 }
104 if (!found) {
105 os_log(OS_LOG_DEFAULT, "genericMetric %{public}@ with unhandled metric type: %{public}@", key, NSStringFromClass([object class]));
106 return;
107 }
108 }
109 @synchronized(self) {
110 self.attributes[key] = object;
111 }
112 }
113 - (void)setMetricValue:(nullable id)metric forKey:(NSString *)key {
114 self[key] = metric;
115 }
116
117 - (uint64_t)convertTimeIntervalToServerTime:(NSTimeInterval)timeInterval
118 {
119 return (uint64_t)((timeInterval + NSTimeIntervalSince1970) * 1000.);
120 }
121
122 - (SECC2MPGenericEvent *)genericEvent {
123 SECC2MPGenericEvent* genericEvent = [[SECC2MPGenericEvent alloc] init];
124
125 genericEvent.name = self.eventName;
126 genericEvent.type = SECC2MPGenericEvent_Type_cloudkit_client;
127
128 genericEvent.timestampStart = 0;
129 genericEvent.timestampEnd = 0;
130 [self.attributes enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
131 SECC2MPGenericEventMetric* metric = [[SECC2MPGenericEventMetric alloc] init];
132 metric.key = key;
133
134 metric.value = [[SECC2MPGenericEventMetricValue alloc] init];
135 if ([obj isKindOfClass:[NSError class]]) {
136 metric.value.errorValue = [self generateError:obj];
137 } else if ([obj isKindOfClass:[NSDate class]]) {
138 metric.value.dateValue = [self convertTimeIntervalToServerTime:[obj timeIntervalSinceReferenceDate]];
139 } else if ([obj isKindOfClass:[NSNumber class]]) {
140 metric.value.doubleValue = [obj doubleValue];
141 } else if ([obj isKindOfClass:[NSString class]]) {
142 metric.value.stringValue = obj;
143 } else {
144 // should never happen since setObject:forKeyedSubscript: validates the input and rejects invalid input
145 return;
146 }
147 if (metric.value) {
148 [genericEvent addMetric:metric];
149 }
150 }];
151 return genericEvent;
152 }
153
154 - (SECC2MPError*) generateError:(NSError*)error
155 {
156 SECC2MPError* generatedError = [[SECC2MPError alloc] init];
157 generatedError.errorDomain = error.domain;
158 generatedError.errorCode = error.code;
159 if ([SecC2DeviceInfo isAppleInternal]) {
160 generatedError.errorDescription = error.userInfo[NSLocalizedDescriptionKey];
161 }
162 NSError* underlyingError = error.userInfo[NSUnderlyingErrorKey];
163 if (underlyingError) {
164 generatedError.underlyingError = [self generateError:underlyingError];
165 }
166 return generatedError;
167 }
168
169 @end