SessionStartEvent.swift 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. //
  2. // Copyright 2022 Google LLC
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. import Foundation
  16. @_implementationOnly import GoogleDataTransport
  17. #if SWIFT_PACKAGE
  18. import FirebaseSessionsObjC
  19. #endif // SWIFT_PACKAGE
  20. #if SWIFT_PACKAGE
  21. @_implementationOnly import GoogleUtilities_Environment
  22. #else
  23. @_implementationOnly import GoogleUtilities
  24. #endif // SWIFT_PACKAGE
  25. ///
  26. /// SessionStartEvent is responsible for:
  27. /// 1) Writing fields to the Session proto
  28. /// 2) Synthesizing itself for persisting to disk and logging to GoogleDataTransport
  29. ///
  30. class SessionStartEvent: NSObject, GDTCOREventDataObject {
  31. var proto: firebase_appquality_sessions_SessionEvent
  32. init(identifiers: IdentifierProvider, appInfo: ApplicationInfoProtocol,
  33. time: TimeProvider = Time()) {
  34. proto = firebase_appquality_sessions_SessionEvent()
  35. super.init()
  36. proto.event_type = firebase_appquality_sessions_EventType_SESSION_START
  37. proto.session_data.session_id = makeProtoString(identifiers.sessionID)
  38. proto.session_data.previous_session_id = makeProtoStringOrNil(identifiers.previousSessionID)
  39. proto.session_data.event_timestamp_us = time.timestampUS
  40. proto.application_info.app_id = makeProtoString(appInfo.appID)
  41. proto.application_info.session_sdk_version = makeProtoString(appInfo.sdkVersion)
  42. proto.application_info.log_environment = convertLogEnvironment(environment: appInfo.environment)
  43. proto.application_info.device_model = makeProtoString(appInfo.deviceModel)
  44. // proto.application_info.development_platform_name;
  45. // proto.application_info.development_platform_version;
  46. // `which_platform_info` tells nanopb which oneof we're choosing to fill in for our proto
  47. proto.application_info.which_platform_info = FIRSESGetAppleApplicationInfoTag()
  48. proto.application_info.apple_app_info
  49. .bundle_short_version = makeProtoString(appInfo.appDisplayVersion)
  50. proto.application_info.apple_app_info.network_connection_info
  51. .network_type = convertNetworkType(networkType: appInfo.networkInfo.networkType)
  52. proto.application_info.apple_app_info.network_connection_info
  53. .mobile_subtype = convertMobileSubtype(mobileSubtype: appInfo.networkInfo.mobileSubtype)
  54. proto.application_info.apple_app_info.os_name = convertOSName(osName: appInfo.osName)
  55. proto.application_info.apple_app_info.mcc_mnc = makeProtoString(appInfo.mccMNC)
  56. proto.session_data.data_collection_status
  57. .crashlytics = firebase_appquality_sessions_DataCollectionState_COLLECTION_UNKNOWN
  58. proto.session_data.data_collection_status
  59. .performance = firebase_appquality_sessions_DataCollectionState_COLLECTION_UNKNOWN
  60. }
  61. func setInstallationID(installationId: String) {
  62. proto.session_data.firebase_installation_id = makeProtoString(installationId)
  63. }
  64. func setSamplingRate(samplingRate: Double) {
  65. proto.session_data.data_collection_status.session_sampling_rate = samplingRate
  66. }
  67. // MARK: - GDTCOREventDataObject
  68. func transportBytes() -> Data {
  69. var fields = firebase_appquality_sessions_SessionEvent_fields
  70. var error: NSError?
  71. let data = FIRSESEncodeProto(&fields.0, &proto, &error)
  72. if error != nil {
  73. Logger.logError(error.debugDescription)
  74. }
  75. guard let data = data else {
  76. Logger.logError("Session event generated nil transportBytes. Returning empty data.")
  77. return Data()
  78. }
  79. return data
  80. }
  81. // MARK: - Data Conversion
  82. private func makeProtoStringOrNil(_ string: String?) -> UnsafeMutablePointer<pb_bytes_array_t>? {
  83. guard let string = string else {
  84. return nil
  85. }
  86. return FIRSESEncodeString(string)
  87. }
  88. private func makeProtoString(_ string: String) -> UnsafeMutablePointer<pb_bytes_array_t>? {
  89. return FIRSESEncodeString(string)
  90. }
  91. private func convertOSName(osName: String) -> firebase_appquality_sessions_OsName {
  92. switch osName.lowercased() {
  93. case "macos":
  94. return firebase_appquality_sessions_OsName_MACOS
  95. case "maccatalyst":
  96. return firebase_appquality_sessions_OsName_MACCATALYST
  97. case "ios_on_mac":
  98. return firebase_appquality_sessions_OsName_IOS_ON_MAC
  99. case "ios":
  100. return firebase_appquality_sessions_OsName_IOS
  101. case "tvos":
  102. return firebase_appquality_sessions_OsName_TVOS
  103. case "watchos":
  104. return firebase_appquality_sessions_OsName_WATCHOS
  105. case "ipados":
  106. return firebase_appquality_sessions_OsName_IPADOS
  107. default:
  108. Logger.logWarning("Found unknown OSName: \"\(osName)\" while converting.")
  109. return firebase_appquality_sessions_OsName_UNKNOWN_OSNAME
  110. }
  111. }
  112. /// Encodes the proto in this SessionStartEvent to Data, and then decodes the Data back into
  113. /// the proto object and returns the decoded proto. This is used for validating encoding works
  114. /// and should not be used in production code.
  115. func encodeDecodeEvent() -> firebase_appquality_sessions_SessionEvent {
  116. let transportBytes = self.transportBytes()
  117. var proto = firebase_appquality_sessions_SessionEvent()
  118. var fields = firebase_appquality_sessions_SessionEvent_fields
  119. let bytes = (transportBytes as NSData).bytes
  120. var istream: pb_istream_t = pb_istream_from_buffer(bytes, transportBytes.count)
  121. if !pb_decode(&istream, &fields.0, &proto) {
  122. let errorMessage = FIRSESPBGetError(istream)
  123. if errorMessage.count > 0 {
  124. Logger.logInfo("Failed to decode transportBytes: \(errorMessage)")
  125. }
  126. }
  127. return proto
  128. }
  129. /// Converts the provided log environment to its Proto format.
  130. private func convertLogEnvironment(environment: DevEnvironment)
  131. -> firebase_appquality_sessions_LogEnvironment {
  132. switch environment {
  133. case .prod:
  134. return firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_PROD
  135. case .staging:
  136. return firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_STAGING
  137. case .autopush:
  138. return firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_AUTOPUSH
  139. }
  140. }
  141. private func convertNetworkType(networkType: GULNetworkType)
  142. -> firebase_appquality_sessions_NetworkConnectionInfo_NetworkType {
  143. switch networkType {
  144. case .WIFI:
  145. return firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_WIFI
  146. case .mobile:
  147. return firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_MOBILE
  148. case .none:
  149. return firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_DUMMY
  150. @unknown default:
  151. return firebase_appquality_sessions_NetworkConnectionInfo_NetworkType_DUMMY
  152. }
  153. }
  154. private func convertMobileSubtype(mobileSubtype: String)
  155. -> firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype {
  156. var subtype: firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype
  157. #if os(iOS) && !targetEnvironment(macCatalyst)
  158. switch mobileSubtype {
  159. case CTRadioAccessTechnologyGPRS:
  160. subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_GPRS
  161. case CTRadioAccessTechnologyEdge:
  162. subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EDGE
  163. case CTRadioAccessTechnologyWCDMA:
  164. subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_CDMA
  165. case CTRadioAccessTechnologyCDMA1x:
  166. subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_CDMA
  167. case CTRadioAccessTechnologyHSDPA:
  168. subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_HSDPA
  169. case CTRadioAccessTechnologyHSUPA:
  170. subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_HSUPA
  171. case CTRadioAccessTechnologyCDMAEVDORev0:
  172. subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EVDO_0
  173. case CTRadioAccessTechnologyCDMAEVDORevA:
  174. subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EVDO_A
  175. case CTRadioAccessTechnologyCDMAEVDORevB:
  176. subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EVDO_B
  177. case CTRadioAccessTechnologyeHRPD:
  178. subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_EHRPD
  179. case CTRadioAccessTechnologyLTE:
  180. subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_LTE
  181. default:
  182. subtype =
  183. firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_UNKNOWN_MOBILE_SUBTYPE
  184. }
  185. if #available(iOS 14.1, *) {
  186. if mobileSubtype == CTRadioAccessTechnologyNRNSA || mobileSubtype ==
  187. CTRadioAccessTechnologyNR {
  188. subtype = firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_NR
  189. }
  190. }
  191. #else
  192. subtype =
  193. firebase_appquality_sessions_NetworkConnectionInfo_MobileSubtype_UNKNOWN_MOBILE_SUBTYPE
  194. #endif
  195. return subtype
  196. }
  197. }