SessionStartEvent.swift 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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. ///
  18. /// SessionStartEvent is responsible for:
  19. /// 1) Writing fields to the Session proto
  20. /// 2) Synthesizing itself for persisting to disk and logging to GoogleDataTransport
  21. ///
  22. class SessionStartEvent: NSObject, GDTCOREventDataObject {
  23. var proto: firebase_appquality_sessions_SessionEvent
  24. init(identifiers: IdentifierProvider, appInfo: ApplicationInfoProtocol,
  25. time: TimeProvider = Time()) {
  26. proto = firebase_appquality_sessions_SessionEvent()
  27. super.init()
  28. proto.event_type = firebase_appquality_sessions_EventType_SESSION_START
  29. proto.session_data.session_id = makeProtoString(identifiers.sessionID)
  30. proto.session_data.previous_session_id = makeProtoStringOrNil(identifiers.previousSessionID)
  31. proto.session_data.event_timestamp_us = time.timestampUS
  32. proto.application_info.app_id = makeProtoString(appInfo.appID)
  33. proto.application_info.session_sdk_version = makeProtoString(appInfo.sdkVersion)
  34. proto.application_info.log_environment = convertLogEnvironment(environment: appInfo.environment)
  35. proto.application_info.device_model = makeProtoString(appInfo.deviceModel)
  36. // proto.application_info.development_platform_name;
  37. // proto.application_info.development_platform_version;
  38. // `which_platform_info` tells nanopb which oneof we're choosing to fill in for our proto
  39. proto.application_info.which_platform_info = FIRSESGetAppleApplicationInfoTag()
  40. proto.application_info.apple_app_info.bundle_short_version = makeProtoString(appInfo.bundleID)
  41. // proto.application_info.apple_app_info.network_connection_info
  42. proto.application_info.apple_app_info.os_name = convertOSName(osName: appInfo.osName)
  43. proto.application_info.apple_app_info.mcc_mnc = makeProtoString(appInfo.mccMNC)
  44. proto.session_data.data_collection_status
  45. .crashlytics = firebase_appquality_sessions_DataCollectionState_COLLECTION_UNKNOWN
  46. proto.session_data.data_collection_status
  47. .performance = firebase_appquality_sessions_DataCollectionState_COLLECTION_UNKNOWN
  48. }
  49. func setInstallationID(identifiers: IdentifierProvider) {
  50. proto.session_data.firebase_installation_id = makeProtoString(identifiers.installationID)
  51. }
  52. func setSamplingRate(samplingRate: Double) {
  53. proto.session_data.data_collection_status.session_sampling_rate = samplingRate
  54. }
  55. // MARK: - GDTCOREventDataObject
  56. func transportBytes() -> Data {
  57. var fields = firebase_appquality_sessions_SessionEvent_fields
  58. var error: NSError?
  59. let data = FIRSESEncodeProto(&fields.0, &proto, &error)
  60. if error != nil {
  61. Logger.logError(error.debugDescription)
  62. }
  63. guard let data = data else {
  64. Logger.logError("Session event generated nil transportBytes. Returning empty data.")
  65. return Data()
  66. }
  67. return data
  68. }
  69. // MARK: - Data Conversion
  70. private func makeProtoStringOrNil(_ string: String?) -> UnsafeMutablePointer<pb_bytes_array_t>? {
  71. guard let string = string else {
  72. return nil
  73. }
  74. return FIRSESEncodeString(string)
  75. }
  76. private func makeProtoString(_ string: String) -> UnsafeMutablePointer<pb_bytes_array_t>? {
  77. return FIRSESEncodeString(string)
  78. }
  79. private func convertOSName(osName: String) -> firebase_appquality_sessions_OsName {
  80. switch osName.lowercased() {
  81. case "macos":
  82. return firebase_appquality_sessions_OsName_MACOS
  83. case "maccatalyst":
  84. return firebase_appquality_sessions_OsName_MACCATALYST
  85. case "ios_on_mac":
  86. return firebase_appquality_sessions_OsName_IOS_ON_MAC
  87. case "ios":
  88. return firebase_appquality_sessions_OsName_IOS
  89. case "tvos":
  90. return firebase_appquality_sessions_OsName_TVOS
  91. case "watchos":
  92. return firebase_appquality_sessions_OsName_WATCHOS
  93. case "ipados":
  94. return firebase_appquality_sessions_OsName_IPADOS
  95. default:
  96. Logger.logWarning("Found unknown OSName: \"\(osName)\" while converting.")
  97. return firebase_appquality_sessions_OsName_UNKNOWN_OSNAME
  98. }
  99. }
  100. /// Encodes the proto in this SessionStartEvent to Data, and then decodes the Data back into
  101. /// the proto object and returns the decoded proto. This is used for validating encoding works
  102. /// and should not be used in production code.
  103. func encodeDecodeEvent() -> firebase_appquality_sessions_SessionEvent {
  104. let transportBytes = self.transportBytes()
  105. var proto = firebase_appquality_sessions_SessionEvent()
  106. var fields = firebase_appquality_sessions_SessionEvent_fields
  107. let bytes = (transportBytes as NSData).bytes
  108. var istream: pb_istream_t = pb_istream_from_buffer(bytes, transportBytes.count)
  109. if !pb_decode(&istream, &fields.0, &proto) {
  110. let errorMessage = FIRSESPBGetError(istream)
  111. if errorMessage.count > 0 {
  112. Logger.logInfo("Failed to decode transportBytes: \(errorMessage)")
  113. }
  114. }
  115. return proto
  116. }
  117. /// Converts the provided log environment to its Proto format.
  118. private func convertLogEnvironment(environment: DevEnvironment)
  119. -> firebase_appquality_sessions_LogEnvironment {
  120. switch environment {
  121. case .prod:
  122. return firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_PROD
  123. case .staging:
  124. return firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_STAGING
  125. case .autopush:
  126. return firebase_appquality_sessions_LogEnvironment_LOG_ENVIRONMENT_AUTOPUSH
  127. }
  128. }
  129. }