]> git.saurik.com Git - apple/security.git/blob - keychain/analytics/SecEventMetric.m
Security-59754.41.1.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 if ((self = [super init])) {
58 NSMutableSet *attributeClasses = [[[self class] supportedAttributeClasses] mutableCopy];
59 [attributeClasses addObject:[NSDictionary class]];
60
61 _eventName = [coder decodeObjectOfClass:[NSString class] forKey:@"eventName"];
62 _attributes = [coder decodeObjectOfClasses:attributeClasses forKey:@"attributes"];
63
64 if (!_eventName || !_attributes) {
65 return NULL;
66 }
67 }
68 return self;
69 }
70
71 - (void)encodeWithCoder:(NSCoder *)coder {
72 [coder encodeObject:self.eventName forKey:@"eventName"];
73 [coder encodeObject:self.attributes forKey:@"attributes"];
74 }
75
76 + (NSSet *)supportedAttributeClasses {
77 static NSSet *supported = NULL;
78 static dispatch_once_t onceToken;
79 dispatch_once(&onceToken, ^{
80 NSArray *clss = @[
81 [NSString class],
82 [NSNumber class],
83 [NSDate class],
84 [NSError class],
85 ];
86 supported = [NSSet setWithArray:clss];
87 });
88 return supported;
89 }
90
91 - (void)setObject:(nullable id)object forKeyedSubscript:(NSString *)key {
92 bool found = false;
93 if (key == NULL) {
94 return;
95 }
96 if (object) {
97 for (Class cls in [[self class] supportedAttributeClasses]) {
98 if ([object isKindOfClass:cls]) {
99 found = true;
100 break;
101 }
102 }
103 if (!found) {
104 os_log(OS_LOG_DEFAULT, "genericMetric %{public}@ with unhandled metric type: %{public}@", key, NSStringFromClass([object class]));
105 return;
106 }
107 }
108 @synchronized(self) {
109 self.attributes[key] = object;
110 }
111 }
112 - (void)setMetricValue:(nullable id)metric forKey:(NSString *)key {
113 self[key] = metric;
114 }
115
116 - (uint64_t)convertTimeIntervalToServerTime:(NSTimeInterval)timeInterval
117 {
118 return (uint64_t)((timeInterval + NSTimeIntervalSince1970) * 1000.);
119 }
120
121 - (SECC2MPGenericEvent *)genericEvent {
122 SECC2MPGenericEvent* genericEvent = [[SECC2MPGenericEvent alloc] init];
123
124 genericEvent.name = self.eventName;
125 genericEvent.type = SECC2MPGenericEvent_Type_cloudkit_client;
126
127 genericEvent.timestampStart = 0;
128 genericEvent.timestampEnd = 0;
129 [self.attributes enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
130 SECC2MPGenericEventMetric* metric = [[SECC2MPGenericEventMetric alloc] init];
131 metric.key = key;
132
133 metric.value = [[SECC2MPGenericEventMetricValue alloc] init];
134 if ([obj isKindOfClass:[NSError class]]) {
135 metric.value.errorValue = [self generateError:obj];
136 } else if ([obj isKindOfClass:[NSDate class]]) {
137 metric.value.dateValue = [self convertTimeIntervalToServerTime:[obj timeIntervalSinceReferenceDate]];
138 } else if ([obj isKindOfClass:[NSNumber class]]) {
139 metric.value.doubleValue = [obj doubleValue];
140 } else if ([obj isKindOfClass:[NSString class]]) {
141 metric.value.stringValue = obj;
142 } else {
143 // should never happen since setObject:forKeyedSubscript: validates the input and rejects invalid input
144 return;
145 }
146 if (metric.value) {
147 [genericEvent addMetric:metric];
148 }
149 }];
150 return genericEvent;
151 }
152
153 - (SECC2MPError*) generateError:(NSError*)error
154 {
155 SECC2MPError* generatedError = [[SECC2MPError alloc] init];
156 generatedError.errorDomain = error.domain;
157 generatedError.errorCode = error.code;
158 if ([SecC2DeviceInfo isAppleInternal]) {
159 generatedError.errorDescription = error.userInfo[NSLocalizedDescriptionKey];
160 }
161 NSError* underlyingError = error.userInfo[NSUnderlyingErrorKey];
162 if (underlyingError) {
163 generatedError.underlyingError = [self generateError:underlyingError];
164 }
165 return generatedError;
166 }
167
168 @end