1 /*
2  * Copyright (C) 2021 The Android Open Source Project
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  */
16 
17 package com.android.internal.telephony.metrics;
18 
19 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
20 
21 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CALL_COMPOSER;
22 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT;
23 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT_ROLE;
24 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT_STANDALONE;
25 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHAT_V1;
26 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHAT_V2;
27 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CUSTOM;
28 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_FT;
29 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_FT_OVER_SMS;
30 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_GEO_PUSH;
31 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_GEO_PUSH_VIA_SMS;
32 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_MMTEL;
33 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_POST_CALL;
34 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_SHARED_MAP;
35 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_SHARED_SKETCH;
36 import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_CUSTOM;
37 import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_DEACTIVATED;
38 import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_GIVEUP;
39 import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_NORESOURCE;
40 import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_PROBATION;
41 import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_REJECTED;
42 import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_TIMEOUT;
43 import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR;
44 import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PRE_PROVISIONING_XML;
45 import static com.android.internal.telephony.TelephonyStatsLog.UCE_EVENT_STATS__TYPE__INCOMING_OPTION;
46 import static com.android.internal.telephony.TelephonyStatsLog.UCE_EVENT_STATS__TYPE__OUTGOING_OPTION;
47 import static com.android.internal.telephony.TelephonyStatsLog.UCE_EVENT_STATS__TYPE__PUBLISH;
48 import static com.android.internal.telephony.TelephonyStatsLog.UCE_EVENT_STATS__TYPE__SUBSCRIBE;
49 
50 import android.annotation.NonNull;
51 import android.os.Binder;
52 import android.telephony.SubscriptionManager;
53 import android.telephony.TelephonyManager;
54 import android.telephony.TelephonyProtoEnums;
55 import android.telephony.ims.FeatureTagState;
56 import android.telephony.ims.RcsContactPresenceTuple;
57 import android.telephony.ims.RcsContactUceCapability;
58 import android.telephony.ims.aidl.IRcsConfigCallback;
59 import android.util.Base64;
60 import android.util.IndentingPrintWriter;
61 
62 import com.android.ims.rcs.uce.UceStatsWriter;
63 import com.android.ims.rcs.uce.UceStatsWriter.UceStatsCallback;
64 import com.android.ims.rcs.uce.util.FeatureTags;
65 import com.android.internal.annotations.VisibleForTesting;
66 import com.android.internal.telephony.Phone;
67 import com.android.internal.telephony.PhoneFactory;
68 import com.android.internal.telephony.nano.PersistAtomsProto;
69 import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent;
70 import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent;
71 import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerListenerEvent;
72 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationFeatureTagStats;
73 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationServiceDescStats;
74 import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
75 import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
76 import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats;
77 import com.android.internal.telephony.nano.PersistAtomsProto.SipDelegateStats;
78 import com.android.internal.telephony.nano.PersistAtomsProto.SipMessageResponse;
79 import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportFeatureTagStats;
80 import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportSession;
81 import com.android.internal.telephony.nano.PersistAtomsProto.UceEventStats;
82 import com.android.telephony.Rlog;
83 
84 import java.io.PrintWriter;
85 import java.util.ArrayList;
86 import java.util.Arrays;
87 import java.util.HashMap;
88 import java.util.HashSet;
89 import java.util.List;
90 import java.util.Map;
91 import java.util.Random;
92 import java.util.Set;
93 
94 /** Tracks RCS provisioning, sip transport, UCE metrics for phone. */
95 public class RcsStats {
96     private static final String TAG = RcsStats.class.getSimpleName();
97     private static final long MIN_DURATION_MILLIS = 1L * SECOND_IN_MILLIS;
98     private final PersistAtomsStorage mAtomsStorage =
99             PhoneFactory.getMetricsCollector().getAtomsStorage();
100     private static final Random RANDOM = new Random();
101 
102     private UceStatsWriterCallback mCallback;
103     private static RcsStats sInstance;
104 
105     public static final int NONE = -1;
106     public static final int STATE_REGISTERED = 0;
107     public static final int STATE_DEREGISTERED = 1;
108     public static final int STATE_DENIED = 2;
109 
110     private static final String SIP_REQUEST_MESSAGE_TYPE_INVITE = "INVITE";
111     private static final String SIP_REQUEST_MESSAGE_TYPE_ACK = "ACK";
112     private static final String SIP_REQUEST_MESSAGE_TYPE_OPTIONS = "OPTIONS";
113     private static final String SIP_REQUEST_MESSAGE_TYPE_BYE = "BYE";
114     private static final String SIP_REQUEST_MESSAGE_TYPE_CANCEL = "CANCEL";
115     private static final String SIP_REQUEST_MESSAGE_TYPE_REGISTER = "REGISTER";
116     private static final String SIP_REQUEST_MESSAGE_TYPE_PRACK = "PRACK";
117     private static final String SIP_REQUEST_MESSAGE_TYPE_SUBSCRIBE = "SUBSCRIBE";
118     private static final String SIP_REQUEST_MESSAGE_TYPE_NOTIFY = "NOTIFY";
119     private static final String SIP_REQUEST_MESSAGE_TYPE_PUBLISH = "PUBLISH";
120     private static final String SIP_REQUEST_MESSAGE_TYPE_INFO = "INFO";
121     private static final String SIP_REQUEST_MESSAGE_TYPE_REFER = "REFER";
122     private static final String SIP_REQUEST_MESSAGE_TYPE_MESSAGE = "MESSAGE";
123     private static final String SIP_REQUEST_MESSAGE_TYPE_UPDATE = "UPDATE";
124 
125     /**
126      * Describe Feature Tags
127      * See frameworks/opt/net/ims/src/java/com/android/ims/rcs/uce/util/FeatureTags.java
128      * and int value matching the Feature Tags
129      * See stats/enums/telephony/enums.proto
130      */
131     private static final Map<String, Integer> FEATURE_TAGS = new HashMap<>();
132 
133     static {
134         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_STANDALONE_MSG.trim().toLowerCase(),
135                 TelephonyProtoEnums.IMS_FEATURE_TAG_STANDALONE_MSG);
136         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHAT_IM.trim().toLowerCase(),
137                 TelephonyProtoEnums.IMS_FEATURE_TAG_CHAT_IM);
138         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHAT_SESSION.trim().toLowerCase(),
139                 TelephonyProtoEnums.IMS_FEATURE_TAG_CHAT_SESSION);
140         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_FILE_TRANSFER.trim().toLowerCase(),
141                 TelephonyProtoEnums.IMS_FEATURE_TAG_FILE_TRANSFER);
142         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_FILE_TRANSFER_VIA_SMS.trim().toLowerCase(),
143                 TelephonyProtoEnums.IMS_FEATURE_TAG_FILE_TRANSFER_VIA_SMS);
144         FEATURE_TAGS.put(
145                 FeatureTags.FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING.trim().toLowerCase(),
146                 TelephonyProtoEnums.IMS_FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING);
147         FEATURE_TAGS.put(
148                 FeatureTags.FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY.trim().toLowerCase(),
149                 TelephonyProtoEnums.IMS_FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY);
150         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_POST_CALL.trim().toLowerCase(),
151                 TelephonyProtoEnums.IMS_FEATURE_TAG_POST_CALL);
152         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_SHARED_MAP.trim().toLowerCase(),
153                 TelephonyProtoEnums.IMS_FEATURE_TAG_SHARED_MAP);
154         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_SHARED_SKETCH.trim().toLowerCase(),
155                 TelephonyProtoEnums.IMS_FEATURE_TAG_SHARED_SKETCH);
156         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_GEO_PUSH.trim().toLowerCase(),
157                 TelephonyProtoEnums.IMS_FEATURE_TAG_GEO_PUSH);
158         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_GEO_PUSH_VIA_SMS.trim().toLowerCase(),
159                 TelephonyProtoEnums.IMS_FEATURE_TAG_GEO_PUSH_VIA_SMS);
160         FEATURE_TAGS.put(
161                 FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION.trim().toLowerCase(),
162                 TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION);
163         String FeatureTag = FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG;
164         FEATURE_TAGS.put(FeatureTag.trim().toLowerCase(),
165                 TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG);
166         FEATURE_TAGS.put(
167                 FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED.trim().toLowerCase(),
168                 TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_VERSION_SUPPORTED);
169         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHATBOT_ROLE.trim().toLowerCase(),
170                 TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_ROLE);
171         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_MMTEL.trim().toLowerCase(),
172                 TelephonyProtoEnums.IMS_FEATURE_TAG_MMTEL);
173         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_VIDEO.trim().toLowerCase(),
174                 TelephonyProtoEnums.IMS_FEATURE_TAG_VIDEO);
175         FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_PRESENCE.trim().toLowerCase(),
176                 TelephonyProtoEnums.IMS_FEATURE_TAG_PRESENCE);
177     }
178 
179     /**
180      * Describe Service IDs
181      * See frameworks/base/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
182      * and int value matching the service IDs
183      * See frameworks/proto_logging/stats/atoms.proto
184      */
185     private static final Map<String, Integer> SERVICE_IDS = new HashMap<>();
186 
187     static {
188         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_MMTEL.trim().toLowerCase(),
189                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_MMTEL);
190         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHAT_V1.trim().toLowerCase(),
191                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHAT_V1);
192         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHAT_V2.trim().toLowerCase(),
193                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHAT_V2);
194         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_FT.trim().toLowerCase(),
195                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_FT);
196         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_FT_OVER_SMS.trim().toLowerCase(),
197                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_FT_OVER_SMS);
198         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH.trim().toLowerCase(),
199                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_GEO_PUSH);
200         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH_VIA_SMS.trim().toLowerCase(),
201                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_GEO_PUSH_VIA_SMS);
202         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CALL_COMPOSER.trim().toLowerCase(),
203                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CALL_COMPOSER);
204         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_POST_CALL.trim().toLowerCase(),
205                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_POST_CALL);
206         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_SHARED_MAP.trim().toLowerCase(),
207                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_SHARED_MAP);
208         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_SHARED_SKETCH.trim().toLowerCase(),
209                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_SHARED_SKETCH);
210         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT.trim().toLowerCase(),
211                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT);
212         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_STANDALONE.trim().toLowerCase(),
213                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT_STANDALONE
214         );
215         SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_ROLE.trim().toLowerCase(),
216                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT_ROLE);
217     }
218 
219     /**
220      * Describe Message Method Type
221      * See stats/enums/telephony/enums.proto
222      */
223     private static final Map<String, Integer> MESSAGE_TYPE = new HashMap<>();
224 
225     static {
226         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_INVITE.trim().toLowerCase(),
227                 TelephonyProtoEnums.SIP_REQUEST_INVITE);
228         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_ACK.trim().toLowerCase(),
229                 TelephonyProtoEnums.SIP_REQUEST_ACK);
230         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_OPTIONS.trim().toLowerCase(),
231                 TelephonyProtoEnums.SIP_REQUEST_OPTIONS);
232         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_BYE.trim().toLowerCase(),
233                 TelephonyProtoEnums.SIP_REQUEST_BYE);
234         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_CANCEL.trim().toLowerCase(),
235                 TelephonyProtoEnums.SIP_REQUEST_CANCEL);
236         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_REGISTER.trim().toLowerCase(),
237                 TelephonyProtoEnums.SIP_REQUEST_REGISTER);
238         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_PRACK.trim().toLowerCase(),
239                 TelephonyProtoEnums.SIP_REQUEST_PRACK);
240         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_SUBSCRIBE.trim().toLowerCase(),
241                 TelephonyProtoEnums.SIP_REQUEST_SUBSCRIBE);
242         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_NOTIFY.trim().toLowerCase(),
243                 TelephonyProtoEnums.SIP_REQUEST_NOTIFY);
244         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_PUBLISH.trim().toLowerCase(),
245                 TelephonyProtoEnums.SIP_REQUEST_PUBLISH);
246         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_INFO.trim().toLowerCase(),
247                 TelephonyProtoEnums.SIP_REQUEST_INFO);
248         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_REFER.trim().toLowerCase(),
249                 TelephonyProtoEnums.SIP_REQUEST_REFER);
250         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_MESSAGE.trim().toLowerCase(),
251                 TelephonyProtoEnums.SIP_REQUEST_MESSAGE);
252         MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_UPDATE.trim().toLowerCase(),
253                 TelephonyProtoEnums.SIP_REQUEST_UPDATE);
254     }
255 
256     /**
257      * Describe Reasons
258      * See frameworks/opt/net/ims/src/java/com/android/ims/rcs/uce/request/
259      * SubscriptionTerminatedHelper.java
260      * and int value matching the Reasons
261      * See frameworks/proto_logging/stats/atoms.proto
262      */
263     private static final Map<String, Integer> NOTIFY_REASONS = new HashMap<>();
264 
265     static {
266         NOTIFY_REASONS.put("deactivated", PRESENCE_NOTIFY_EVENT__REASON__REASON_DEACTIVATED);
267         NOTIFY_REASONS.put("probation", PRESENCE_NOTIFY_EVENT__REASON__REASON_PROBATION);
268         NOTIFY_REASONS.put("rejected", PRESENCE_NOTIFY_EVENT__REASON__REASON_REJECTED);
269         NOTIFY_REASONS.put("timeout", PRESENCE_NOTIFY_EVENT__REASON__REASON_TIMEOUT);
270         NOTIFY_REASONS.put("giveup", PRESENCE_NOTIFY_EVENT__REASON__REASON_GIVEUP);
271         NOTIFY_REASONS.put("noresource", PRESENCE_NOTIFY_EVENT__REASON__REASON_NORESOURCE);
272     }
273 
274     /**
275      * Describe Rcs Capability set
276      * See frameworks/base/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
277      */
278     private static final HashSet<String> RCS_SERVICE_ID_SET = new HashSet<>();
279     static {
280         RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_CHAT_V1);
281         RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_CHAT_V2);
282         RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_FT);
283         RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_FT_OVER_SMS);
284         RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH);
285         RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH_VIA_SMS);
286         RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_SHARED_MAP);
287         RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_SHARED_SKETCH);
288         RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_CHATBOT);
289         RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_STANDALONE);
290         RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_ROLE);
291     }
292 
293     /**
294      * Describe Mmtel Capability set
295      * See frameworks/base/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
296      */
297     private static final HashSet<String> MMTEL_SERVICE_ID_SET = new HashSet<>();
298     static {
299         MMTEL_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_MMTEL);
300         MMTEL_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_CALL_COMPOSER);
301         MMTEL_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_POST_CALL);
302     }
303 
304     private static final Map<Long, Integer> sSubscribeTaskIds = new HashMap<>();
305     private static final int SUBSCRIBE_SUCCESS = 1;
306     private static final int SUBSCRIBE_NOTIFY = 2;
307 
308     @VisibleForTesting
309     protected final Map<Integer, ImsDedicatedBearerListenerEvent> mDedicatedBearerListenerEventMap =
310             new HashMap<>();
311     @VisibleForTesting
312     protected final List<RcsAcsProvisioningStats> mRcsAcsProvisioningStatsList =
313             new ArrayList<RcsAcsProvisioningStats>();
314     @VisibleForTesting
315     protected final HashMap<Integer, RcsProvisioningCallback> mRcsProvisioningCallbackMap =
316             new HashMap<>();
317 
318     // Maps feature tag name -> ImsRegistrationFeatureTagStats.
319     private final List<ImsRegistrationFeatureTagStats> mImsRegistrationFeatureTagStatsList =
320             new ArrayList<>();
321 
322     // Maps service id -> ImsRegistrationServiceDescStats.
323     @VisibleForTesting
324     protected final List<ImsRegistrationServiceDescStats> mImsRegistrationServiceDescStatsList =
325             new ArrayList<>();
326 
327     private List<LastSipDelegateStat> mLastSipDelegateStatList = new ArrayList<>();
328     private HashMap<Integer, SipTransportFeatureTags> mLastFeatureTagStatMap = new HashMap<>();
329     private ArrayList<SipMessageArray> mSipMessageArray = new ArrayList<>();
330     private ArrayList<SipTransportSessionArray> mSipTransportSessionArray = new ArrayList<>();
331     private SipTransportSessionArray mSipTransportSession;
332     private SipMessageArray mSipMessage;
333 
334     private class LastSipDelegateStat {
335         public int mSubId;
336         public SipDelegateStats mLastStat;
337         private Set<String> mSupportedTags;
338 
LastSipDelegateStat(int subId, Set<String> supportedTags)339         LastSipDelegateStat(int subId, Set<String> supportedTags) {
340             mSubId = subId;
341             mSupportedTags = supportedTags;
342         }
343 
createSipDelegateStat(int subId)344         public void createSipDelegateStat(int subId) {
345             mLastStat = getDefaultSipDelegateStat(subId);
346             mLastStat.uptimeMillis = getWallTimeMillis();
347             mLastStat.destroyReason = NONE;
348         }
349 
setSipDelegateDestroyReason(int destroyReason)350         public void setSipDelegateDestroyReason(int destroyReason) {
351             mLastStat.destroyReason = destroyReason;
352         }
353 
isDestroyed()354         public boolean isDestroyed() {
355             return mLastStat.destroyReason > NONE;
356         }
357 
conclude(long now)358         public void conclude(long now) {
359             long duration = now - mLastStat.uptimeMillis;
360             if (duration < MIN_DURATION_MILLIS) {
361                 logd("concludeSipDelegateStat: discarding transient stats,"
362                         + " duration= " + duration);
363             } else {
364                 mLastStat.uptimeMillis = duration;
365                 mAtomsStorage.addSipDelegateStats(copyOf(mLastStat));
366             }
367             mLastStat.uptimeMillis = now;
368         }
369 
compare(int subId, Set<String> supportedTags)370         public boolean compare(int subId, Set<String> supportedTags) {
371             if (subId != mSubId || supportedTags == null || supportedTags.isEmpty()) {
372                 return false;
373             }
374             for (String tag : supportedTags) {
375                 if (!mSupportedTags.contains(tag)) {
376                     return false;
377                 }
378             }
379             return true;
380         }
381 
getDefaultSipDelegateStat(int subId)382         private SipDelegateStats getDefaultSipDelegateStat(int subId) {
383             SipDelegateStats stat = new SipDelegateStats();
384             stat.dimension = RANDOM.nextInt();
385             stat.carrierId = getCarrierId(subId);
386             stat.slotId = getSlotId(subId);
387             return stat;
388         }
389     }
390 
copyOf(@onNull SipDelegateStats source)391     private static SipDelegateStats copyOf(@NonNull SipDelegateStats source) {
392         SipDelegateStats newStat = new SipDelegateStats();
393 
394         newStat.dimension = source.dimension;
395         newStat.slotId = source.slotId;
396         newStat.carrierId = source.carrierId;
397         newStat.destroyReason = source.destroyReason;
398         newStat.uptimeMillis = source.uptimeMillis;
399 
400         return newStat;
401     }
402 
403     private class SipTransportFeatureTags {
404         private HashMap<String, LastFeatureTagState> mFeatureTagMap;
405         private int mSubId;
406 
407         private class LastFeatureTagState {
408             public long timeStamp;
409             public int carrierId;
410             public int slotId;
411             public int state;
412             public int reason;
413 
LastFeatureTagState(int carrierId, int slotId, int state, int reason, long timeStamp)414             LastFeatureTagState(int carrierId, int slotId, int state, int reason, long timeStamp) {
415                 this.carrierId = carrierId;
416                 this.slotId = slotId;
417                 this.state = state;
418                 this.reason = reason;
419                 this.timeStamp = timeStamp;
420             }
421 
update(int state, int reason, long timeStamp)422             public void update(int state, int reason, long timeStamp) {
423                 this.state = state;
424                 this.reason = reason;
425                 this.timeStamp = timeStamp;
426             }
427 
update(long timeStamp)428             public void update(long timeStamp) {
429                 this.timeStamp = timeStamp;
430             }
431         }
432 
SipTransportFeatureTags(int subId)433         SipTransportFeatureTags(int subId) {
434             mFeatureTagMap = new HashMap<>();
435             mSubId = subId;
436         }
437 
getLastTagStates()438         public HashMap<String, LastFeatureTagState> getLastTagStates() {
439             return mFeatureTagMap;
440         }
441 
442         /*** Create or update featureTags whenever feature Tag states are changed */
updateLastFeatureTagState(String tagName, int state, int reason, long timeStamp)443         public synchronized void updateLastFeatureTagState(String tagName, int state, int reason,
444                 long timeStamp) {
445             int carrierId = getCarrierId(mSubId);
446             int slotId = getSlotId(mSubId);
447             if (mFeatureTagMap.containsKey(tagName)) {
448                 LastFeatureTagState lastFeatureTagState = mFeatureTagMap.get(tagName);
449                 if (lastFeatureTagState != null) {
450                     addFeatureTagStat(tagName, lastFeatureTagState, timeStamp);
451                     lastFeatureTagState.update(state, reason, timeStamp);
452                 } else {
453                     create(tagName, carrierId, slotId, state, reason, timeStamp);
454                 }
455 
456             } else {
457                 create(tagName, carrierId, slotId, state, reason, timeStamp);
458             }
459         }
460 
461         /** Update current featureTags associated to active SipDelegates when metrics is pulled */
conclude(long timeStamp)462         public synchronized void conclude(long timeStamp) {
463             HashMap<String, LastFeatureTagState> featureTagsCopy = new HashMap<>();
464             featureTagsCopy.putAll(mFeatureTagMap);
465             for (Map.Entry<String, LastFeatureTagState> last : featureTagsCopy.entrySet()) {
466                 String tagName = last.getKey();
467                 LastFeatureTagState lastFeatureTagState = last.getValue();
468                 addFeatureTagStat(tagName, lastFeatureTagState, timeStamp);
469                 updateTimeStamp(mSubId, tagName, timeStamp);
470             }
471         }
472 
473         /** Finalizes the durations of the current featureTags associated to active SipDelegates */
addFeatureTagStat(@onNull String tagName, @NonNull LastFeatureTagState lastFeatureTagState, long now)474         private synchronized boolean addFeatureTagStat(@NonNull String tagName,
475                 @NonNull LastFeatureTagState lastFeatureTagState, long now) {
476             long duration = now - lastFeatureTagState.timeStamp;
477             if (duration < MIN_DURATION_MILLIS
478                     || !isValidCarrierId(lastFeatureTagState.carrierId)) {
479                 logd("conclude: discarding transient stats, duration= " + duration
480                         + ", carrierId = " + lastFeatureTagState.carrierId);
481             } else {
482                 SipTransportFeatureTagStats sipFeatureTagStat = new SipTransportFeatureTagStats();
483                 switch (lastFeatureTagState.state) {
484                     case STATE_DENIED:
485                         sipFeatureTagStat.sipTransportDeniedReason = lastFeatureTagState.reason;
486                         sipFeatureTagStat.sipTransportDeregisteredReason = NONE;
487                         break;
488                     case STATE_DEREGISTERED:
489                         sipFeatureTagStat.sipTransportDeniedReason = NONE;
490                         sipFeatureTagStat.sipTransportDeregisteredReason =
491                                 lastFeatureTagState.reason;
492                         break;
493                     default:
494                         sipFeatureTagStat.sipTransportDeniedReason = NONE;
495                         sipFeatureTagStat.sipTransportDeregisteredReason = NONE;
496                         break;
497                 }
498 
499                 sipFeatureTagStat.carrierId = lastFeatureTagState.carrierId;
500                 sipFeatureTagStat.slotId = lastFeatureTagState.slotId;
501                 sipFeatureTagStat.associatedMillis = duration;
502                 sipFeatureTagStat.featureTagName = convertTagNameToValue(tagName);
503                 mAtomsStorage.addSipTransportFeatureTagStats(sipFeatureTagStat);
504                 return true;
505             }
506             return false;
507         }
508 
updateTimeStamp(int subId, String tagName, long timeStamp)509         private void updateTimeStamp(int subId, String tagName, long timeStamp) {
510             SipTransportFeatureTags sipTransportFeatureTags = mLastFeatureTagStatMap.get(subId);
511             if (sipTransportFeatureTags != null) {
512                 HashMap<String, LastFeatureTagState> lastTagStates =
513                         sipTransportFeatureTags.getLastTagStates();
514                 if (lastTagStates != null && lastTagStates.containsKey(tagName)) {
515                     LastFeatureTagState lastFeatureTagState = lastTagStates.get(tagName);
516                     if (lastFeatureTagState != null) {
517                         lastFeatureTagState.update(timeStamp);
518                     }
519                 }
520             }
521         }
522 
create(String tagName, int carrierId, int slotId, int state, int reason, long timeStamp)523         private LastFeatureTagState create(String tagName, int carrierId, int slotId, int state,
524                 int reason, long timeStamp) {
525             LastFeatureTagState lastFeatureTagState = new LastFeatureTagState(carrierId, slotId,
526                     state, reason, timeStamp);
527             mFeatureTagMap.put(tagName, lastFeatureTagState);
528             return lastFeatureTagState;
529         }
530     }
531 
532     class UceStatsWriterCallback implements UceStatsCallback {
533         private RcsStats mRcsStats;
534 
UceStatsWriterCallback(RcsStats rcsStats)535         UceStatsWriterCallback(RcsStats rcsStats) {
536             logd("created Callback");
537             mRcsStats = rcsStats;
538         }
539 
onImsRegistrationFeatureTagStats(int subId, List<String> featureTagList, int registrationTech)540         public void onImsRegistrationFeatureTagStats(int subId, List<String> featureTagList,
541                 int registrationTech) {
542             mRcsStats.onImsRegistrationFeatureTagStats(subId, featureTagList, registrationTech);
543         }
544 
onStoreCompleteImsRegistrationFeatureTagStats(int subId)545         public void onStoreCompleteImsRegistrationFeatureTagStats(int subId) {
546             mRcsStats.onStoreCompleteImsRegistrationFeatureTagStats(subId);
547         }
548 
onImsRegistrationServiceDescStats(int subId, List<String> serviceIdList, List<String> serviceIdVersionList, int registrationTech)549         public void onImsRegistrationServiceDescStats(int subId, List<String> serviceIdList,
550                 List<String> serviceIdVersionList, int registrationTech) {
551             mRcsStats.onImsRegistrationServiceDescStats(subId, serviceIdList, serviceIdVersionList,
552                     registrationTech);
553         }
554 
onSubscribeResponse(int subId, long taskId, int networkResponse)555         public void onSubscribeResponse(int subId, long taskId, int networkResponse) {
556             if (networkResponse >= 200 && networkResponse <= 299) {
557                 if (!sSubscribeTaskIds.containsKey(taskId)) {
558                     sSubscribeTaskIds.put(taskId, SUBSCRIBE_SUCCESS);
559                 }
560             }
561             mRcsStats.onUceEventStats(subId, UCE_EVENT_STATS__TYPE__SUBSCRIBE,
562                     true, 0, networkResponse);
563         }
564 
onUceEvent(int subId, int type, boolean successful, int commandCode, int networkResponse)565         public void onUceEvent(int subId, int type, boolean successful, int commandCode,
566                 int networkResponse) {
567             int eventType = 0;
568             switch (type) {
569                 case UceStatsWriter.PUBLISH_EVENT:
570                     eventType = UCE_EVENT_STATS__TYPE__PUBLISH;
571                     break;
572                 case UceStatsWriter.SUBSCRIBE_EVENT:
573                     eventType = UCE_EVENT_STATS__TYPE__SUBSCRIBE;
574                     break;
575                 case UceStatsWriter.INCOMING_OPTION_EVENT:
576                     eventType = UCE_EVENT_STATS__TYPE__INCOMING_OPTION;
577                     break;
578                 case UceStatsWriter.OUTGOING_OPTION_EVENT:
579                     eventType = UCE_EVENT_STATS__TYPE__OUTGOING_OPTION;
580                     break;
581                 default:
582                     return;
583             }
584             mRcsStats.onUceEventStats(subId, eventType, successful, commandCode, networkResponse);
585         }
586 
onSubscribeTerminated(int subId, long taskId, String reason)587         public void onSubscribeTerminated(int subId, long taskId, String reason) {
588             if (sSubscribeTaskIds.containsKey(taskId)) {
589                 int previousSubscribeStatus = sSubscribeTaskIds.get(taskId);
590                 sSubscribeTaskIds.remove(taskId);
591                 // The device received a success response related to the subscription request.
592                 // However, PIDF was not received due to reason value.
593                 if (previousSubscribeStatus == SUBSCRIBE_SUCCESS) {
594                     mRcsStats.onPresenceNotifyEvent(subId, reason, false,
595                             false, false, false);
596                 }
597             }
598         }
599 
onPresenceNotifyEvent(int subId, long taskId, List<RcsContactUceCapability> updatedCapList)600         public void onPresenceNotifyEvent(int subId, long taskId,
601                 List<RcsContactUceCapability> updatedCapList) {
602             if (updatedCapList == null || updatedCapList.isEmpty()) {
603                 return;
604             }
605             if (sSubscribeTaskIds.containsKey(taskId)) {
606                 sSubscribeTaskIds.replace(taskId, SUBSCRIBE_NOTIFY);
607             }
608             for (RcsContactUceCapability capability : updatedCapList) {
609                 boolean rcsCap = false;
610                 boolean mmtelCap = false;
611                 boolean noCap = true;
612                 List<RcsContactPresenceTuple> tupleList = capability.getCapabilityTuples();
613                 if (tupleList.isEmpty()) {
614                     noCap = true;
615                     mRcsStats.onPresenceNotifyEvent(subId, "", true,
616                             rcsCap, mmtelCap, noCap);
617                     continue;
618                 }
619                 for (RcsContactPresenceTuple tuple : tupleList) {
620                     String serviceId = tuple.getServiceId();
621                     if (RCS_SERVICE_ID_SET.contains(serviceId)) {
622                         rcsCap = true;
623                         noCap = false;
624                     } else if (MMTEL_SERVICE_ID_SET.contains(serviceId)) {
625                         if (serviceId.equals(RcsContactPresenceTuple.SERVICE_ID_CALL_COMPOSER)) {
626                             if ("1.0".equals(tuple.getServiceVersion())) {
627                                 rcsCap = true;
628                                 noCap = false;
629                                 continue;
630                             }
631                         }
632                         mmtelCap = true;
633                         noCap = false;
634                     }
635                 }
636                 mRcsStats.onPresenceNotifyEvent(subId, "", true, rcsCap,
637                         mmtelCap, noCap);
638             }
639         }
640 
onStoreCompleteImsRegistrationServiceDescStats(int subId)641         public void onStoreCompleteImsRegistrationServiceDescStats(int subId) {
642             mRcsStats.onStoreCompleteImsRegistrationServiceDescStats(subId);
643         }
644     }
645 
646     /** Callback class to receive RCS ACS result and to store metrics. */
647     public class RcsProvisioningCallback extends IRcsConfigCallback.Stub {
648         private RcsStats mRcsStats;
649         private int mSubId;
650         private boolean mEnableSingleRegistration;
651         private boolean mRegistered;
652 
RcsProvisioningCallback(RcsStats rcsStats, int subId, boolean enableSingleRegistration)653         RcsProvisioningCallback(RcsStats rcsStats, int subId, boolean enableSingleRegistration) {
654             logd("created RcsProvisioningCallback");
655             mRcsStats = rcsStats;
656             mSubId = subId;
657             mEnableSingleRegistration = enableSingleRegistration;
658             mRegistered = false;
659         }
660 
setEnableSingleRegistration(boolean enableSingleRegistration)661         public synchronized void setEnableSingleRegistration(boolean enableSingleRegistration) {
662             mEnableSingleRegistration = enableSingleRegistration;
663         }
664 
getRegistered()665         public boolean getRegistered() {
666             return mRegistered;
667         }
668 
setRegistered(boolean registered)669         public void setRegistered(boolean registered) {
670             mRegistered = registered;
671         }
672 
673         @Override
onConfigurationChanged(byte[] config)674         public void onConfigurationChanged(byte[] config) {
675             // this callback will not be handled.
676         }
677 
678         @Override
onAutoConfigurationErrorReceived(int errorCode, String errorString)679         public void onAutoConfigurationErrorReceived(int errorCode, String errorString) {
680             final long callingIdentity = Binder.clearCallingIdentity();
681             try {
682                 mRcsStats.onRcsAcsProvisioningStats(mSubId, errorCode,
683                         RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR,
684                         mEnableSingleRegistration);
685             } finally {
686                 restoreCallingIdentity(callingIdentity);
687             }
688         }
689 
690         @Override
onConfigurationReset()691         public void onConfigurationReset() {
692             // this callback will not be handled.
693         }
694 
695         @Override
onRemoved()696         public void onRemoved() {
697             final long callingIdentity = Binder.clearCallingIdentity();
698             try {
699                 // store cached metrics
700                 mRcsStats.onStoreCompleteRcsAcsProvisioningStats(mSubId);
701                // remove this obj from Map
702                 mRcsStats.removeRcsProvisioningCallback(mSubId);
703             } finally {
704                 restoreCallingIdentity(callingIdentity);
705             }
706         }
707 
708         @Override
onPreProvisioningReceived(byte[] config)709         public void onPreProvisioningReceived(byte[] config) {
710             final long callingIdentity = Binder.clearCallingIdentity();
711             try {
712                 // Receiving pre provisioning means http 200 OK with body.
713                 mRcsStats.onRcsAcsProvisioningStats(mSubId, 200,
714                         RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PRE_PROVISIONING_XML,
715                         mEnableSingleRegistration);
716             } finally {
717                 restoreCallingIdentity(callingIdentity);
718             }
719         }
720     };
721 
722     private class SipMessageArray {
723         private String mMethod;
724         private String mCallId;
725         private int mDirection;
726 
SipMessageArray(String method, int direction, String callId)727         SipMessageArray(String method, int direction, String callId) {
728             this.mMethod = method;
729             this.mCallId = callId;
730             this.mDirection = direction;
731         }
732 
addSipMessageStat( @onNull int subId, @NonNull String sipMessageMethod, int sipMessageResponse, int sipMessageDirection, int messageError)733         private synchronized void addSipMessageStat(
734                 @NonNull int subId, @NonNull String sipMessageMethod,
735                 int sipMessageResponse, int sipMessageDirection, int messageError) {
736             int carrierId = getCarrierId(subId);
737             if (!isValidCarrierId(carrierId)) {
738                 return;
739             }
740             SipMessageResponse proto = new SipMessageResponse();
741             proto.carrierId = carrierId;
742             proto.slotId = getSlotId(subId);
743             proto.sipMessageMethod = convertMessageTypeToValue(sipMessageMethod);
744             proto.sipMessageResponse = sipMessageResponse;
745             proto.sipMessageDirection = sipMessageDirection;
746             proto.messageError = messageError;
747             proto.count = 1;
748             mAtomsStorage.addSipMessageResponse(proto);
749         }
750     }
751 
752     private class SipTransportSessionArray {
753         private String mMethod;
754         private String mCallId;
755         private int mDirection;
756         private int mSipResponse;
757 
SipTransportSessionArray(String method, int direction, String callId)758         SipTransportSessionArray(String method, int direction, String callId) {
759             this.mMethod = method;
760             this.mCallId = callId;
761             this.mDirection = direction;
762             this.mSipResponse = 0;
763         }
764 
addSipTransportSessionStat( @onNull int subId, @NonNull String sessionMethod, int sipMessageDirection, int sipResponse, boolean isEndedGracefully)765         private synchronized void addSipTransportSessionStat(
766                 @NonNull int subId, @NonNull String sessionMethod, int sipMessageDirection,
767                 int sipResponse, boolean isEndedGracefully) {
768             int carrierId = getCarrierId(subId);
769             if (!isValidCarrierId(carrierId)) {
770                 return;
771             }
772             SipTransportSession proto = new SipTransportSession();
773             proto.carrierId = carrierId;
774             proto.slotId = getSlotId(subId);
775             proto.sessionMethod = convertMessageTypeToValue(sessionMethod);
776             proto.sipMessageDirection = sipMessageDirection;
777             proto.sipResponse = sipResponse;
778             proto.sessionCount = 1;
779             proto.endedGracefullyCount = 1;
780             proto.isEndedGracefully = isEndedGracefully;
781             mAtomsStorage.addCompleteSipTransportSession(proto);
782         }
783     }
784 
785     @VisibleForTesting
RcsStats()786     protected RcsStats() {
787         mCallback = null;
788     }
789 
790     /** Gets a RcsStats instance. */
getInstance()791     public static RcsStats getInstance() {
792         synchronized (RcsStats.class) {
793             if (sInstance == null) {
794                 Rlog.d(TAG, "RcsStats created.");
795                 sInstance = new RcsStats();
796             }
797             return sInstance;
798         }
799     }
800 
801     /** register callback to UceStatsWriter. */
registerUceCallback()802     public void registerUceCallback() {
803         if (mCallback == null) {
804             mCallback = new UceStatsWriterCallback(sInstance);
805             Rlog.d(TAG, "UceStatsWriterCallback created.");
806             UceStatsWriter.init(mCallback);
807         }
808     }
809 
810     /** Update or create new atom when RCS service registered. */
onImsRegistrationFeatureTagStats(int subId, List<String> featureTagList, int registrationTech)811     public void onImsRegistrationFeatureTagStats(int subId, List<String> featureTagList,
812             int registrationTech) {
813         synchronized (mImsRegistrationFeatureTagStatsList) {
814             int carrierId = getCarrierId(subId);
815             if (!isValidCarrierId(carrierId)) {
816                 flushImsRegistrationFeatureTagStatsInvalid();
817                 return;
818             }
819 
820             // update cached atom if exists
821             onStoreCompleteImsRegistrationFeatureTagStats(subId);
822 
823             if (featureTagList == null) {
824                 Rlog.d(TAG, "featureTagNames is null or empty");
825                 return;
826             }
827 
828             for (String featureTag : featureTagList) {
829                 ImsRegistrationFeatureTagStats proto = new ImsRegistrationFeatureTagStats();
830                 proto.carrierId = carrierId;
831                 proto.slotId = getSlotId(subId);
832                 proto.featureTagName = convertTagNameToValue(featureTag);
833                 proto.registrationTech = registrationTech;
834                 proto.registeredMillis = getWallTimeMillis();
835                 mImsRegistrationFeatureTagStatsList.add(proto);
836             }
837         }
838     }
839 
840     /** Update duration, store and delete cached ImsRegistrationFeatureTagStats list to storage. */
onStoreCompleteImsRegistrationFeatureTagStats(int subId)841     public void onStoreCompleteImsRegistrationFeatureTagStats(int subId) {
842         synchronized (mImsRegistrationFeatureTagStatsList) {
843             int carrierId = getCarrierId(subId);
844             List<ImsRegistrationFeatureTagStats> deleteList = new ArrayList<>();
845             long now = getWallTimeMillis();
846             for (ImsRegistrationFeatureTagStats proto : mImsRegistrationFeatureTagStatsList) {
847                 if (proto.carrierId == carrierId) {
848                     proto.registeredMillis = now - proto.registeredMillis;
849                     mAtomsStorage.addImsRegistrationFeatureTagStats(proto);
850                     deleteList.add(proto);
851                 }
852             }
853             for (ImsRegistrationFeatureTagStats proto : deleteList) {
854                 mImsRegistrationFeatureTagStatsList.remove(proto);
855             }
856         }
857     }
858 
859     /** Update duration and store cached ImsRegistrationFeatureTagStats when metrics are pulled */
onFlushIncompleteImsRegistrationFeatureTagStats()860     public void onFlushIncompleteImsRegistrationFeatureTagStats() {
861         synchronized (mImsRegistrationFeatureTagStatsList) {
862             long now = getWallTimeMillis();
863             for (ImsRegistrationFeatureTagStats proto : mImsRegistrationFeatureTagStatsList) {
864                 ImsRegistrationFeatureTagStats newProto = copyImsRegistrationFeatureTagStats(proto);
865                 // the current time is a placeholder and total registered time will be
866                 // calculated when generating final atoms
867                 newProto.registeredMillis = now - proto.registeredMillis;
868                 mAtomsStorage.addImsRegistrationFeatureTagStats(newProto);
869                 proto.registeredMillis = now;
870             }
871         }
872     }
873 
874     /** Create a new atom when RCS client stat changed. */
onRcsClientProvisioningStats(int subId, int event)875     public synchronized void onRcsClientProvisioningStats(int subId, int event) {
876         int carrierId = getCarrierId(subId);
877 
878         if (!isValidCarrierId(carrierId)) {
879             return;
880         }
881 
882         RcsClientProvisioningStats proto = new RcsClientProvisioningStats();
883         proto.carrierId = carrierId;
884         proto.slotId = getSlotId(subId);
885         proto.event = event;
886         proto.count = 1;
887         mAtomsStorage.addRcsClientProvisioningStats(proto);
888     }
889 
890     /** Update or create new atom when RCS ACS stat changed. */
onRcsAcsProvisioningStats(int subId, int responseCode, int responseType, boolean enableSingleRegistration)891     public void onRcsAcsProvisioningStats(int subId, int responseCode, int responseType,
892             boolean enableSingleRegistration) {
893 
894         synchronized (mRcsAcsProvisioningStatsList) {
895             int carrierId = getCarrierId(subId);
896             if (!isValidCarrierId(carrierId)) {
897                 flushRcsAcsProvisioningStatsInvalid();
898                 return;
899             }
900 
901             // update cached atom if exists
902             onStoreCompleteRcsAcsProvisioningStats(subId);
903 
904             // create new stats to cache
905             RcsAcsProvisioningStats newStats = new RcsAcsProvisioningStats();
906             newStats.carrierId = carrierId;
907             newStats.slotId = getSlotId(subId);
908             newStats.responseCode = responseCode;
909             newStats.responseType = responseType;
910             newStats.isSingleRegistrationEnabled = enableSingleRegistration;
911             newStats.count = 1;
912             newStats.stateTimerMillis = getWallTimeMillis();
913 
914             // add new stats in list
915             mRcsAcsProvisioningStatsList.add(newStats);
916         }
917     }
918 
919     /** Update duration, store and delete cached RcsAcsProvisioningStats */
onStoreCompleteRcsAcsProvisioningStats(int subId)920     public void onStoreCompleteRcsAcsProvisioningStats(int subId) {
921         synchronized (mRcsAcsProvisioningStatsList) {
922             // find cached RcsAcsProvisioningStats based sub ID
923             RcsAcsProvisioningStats existingStats = getRcsAcsProvisioningStats(subId);
924             if (existingStats != null) {
925                 existingStats.stateTimerMillis =
926                         getWallTimeMillis() - existingStats.stateTimerMillis;
927                 mAtomsStorage.addRcsAcsProvisioningStats(existingStats);
928                 // remove cached atom from list
929                 mRcsAcsProvisioningStatsList.remove(existingStats);
930             }
931         }
932     }
933 
934     /** Update duration and store cached RcsAcsProvisioningStats when metrics are pulled */
onFlushIncompleteRcsAcsProvisioningStats()935     public void onFlushIncompleteRcsAcsProvisioningStats() {
936         synchronized (mRcsAcsProvisioningStatsList) {
937             long now = getWallTimeMillis();
938             for (RcsAcsProvisioningStats stats : mRcsAcsProvisioningStatsList) {
939                 // we store a copy into atoms storage
940                 // so that we can continue using the original object.
941                 RcsAcsProvisioningStats proto = copyRcsAcsProvisioningStats(stats);
942                 // the current time is a placeholder and total registered time will be
943                 // calculated when generating final atoms
944                 proto.stateTimerMillis = now - proto.stateTimerMillis;
945                 mAtomsStorage.addRcsAcsProvisioningStats(proto);
946                 // update cached atom's time
947                 stats.stateTimerMillis = now;
948             }
949         }
950     }
951 
952     /** Create SipDelegateStat when SipDelegate is created */
createSipDelegateStats(int subId, Set<String> supportedTags)953     public synchronized void createSipDelegateStats(int subId, Set<String> supportedTags) {
954         if (supportedTags != null && !supportedTags.isEmpty()) {
955             LastSipDelegateStat lastState = getLastSipDelegateStat(subId, supportedTags);
956             lastState.createSipDelegateStat(subId);
957         }
958     }
959 
960     /** Update destroyReason and duration of SipDelegateStat when SipDelegate is destroyed */
onSipDelegateStats(int subId, Set<String> supportedTags, int destroyReason)961     public synchronized void onSipDelegateStats(int subId, Set<String> supportedTags,
962             int destroyReason) {
963         if (supportedTags != null && !supportedTags.isEmpty()) {
964             LastSipDelegateStat lastState = getLastSipDelegateStat(subId, supportedTags);
965             lastState.setSipDelegateDestroyReason(destroyReason);
966             concludeSipDelegateStat();
967         }
968     }
969 
970     /** Create/Update atoms when states of sipTransportFeatureTags are changed */
onSipTransportFeatureTagStats( int subId, Set<FeatureTagState> deniedTags, Set<FeatureTagState> deRegiTags, Set<String> regiTags)971     public synchronized void onSipTransportFeatureTagStats(
972             int subId,
973             Set<FeatureTagState> deniedTags,
974             Set<FeatureTagState> deRegiTags,
975             Set<String> regiTags) {
976         long now = getWallTimeMillis();
977         SipTransportFeatureTags sipTransportFeatureTags = getLastFeatureTags(subId);
978         if (regiTags != null && !regiTags.isEmpty()) {
979             for (String tag : regiTags) {
980                 sipTransportFeatureTags.updateLastFeatureTagState(tag, STATE_REGISTERED,
981                         NONE, now);
982             }
983         }
984         if (deniedTags != null && !deniedTags.isEmpty()) {
985             for (FeatureTagState tag : deniedTags) {
986                 sipTransportFeatureTags.updateLastFeatureTagState(tag.getFeatureTag(), STATE_DENIED,
987                         tag.getState(), now);
988             }
989         }
990         if (deRegiTags != null && !deRegiTags.isEmpty()) {
991             for (FeatureTagState tag : deRegiTags) {
992                 sipTransportFeatureTags.updateLastFeatureTagState(
993                         tag.getFeatureTag(), STATE_DEREGISTERED, tag.getState(), now);
994             }
995         }
996     }
997 
998     /** Update duration of  sipTransportFeatureTags when metrics are pulled */
concludeSipTransportFeatureTagsStat()999     public synchronized void concludeSipTransportFeatureTagsStat() {
1000         if (mLastFeatureTagStatMap.isEmpty()) {
1001             return;
1002         }
1003 
1004         long now = getWallTimeMillis();
1005         HashMap<Integer, SipTransportFeatureTags> lastFeatureTagStatsCopy = new HashMap<>();
1006         lastFeatureTagStatsCopy.putAll(mLastFeatureTagStatMap);
1007         for (SipTransportFeatureTags sipTransportFeatureTags : lastFeatureTagStatsCopy.values()) {
1008             if (sipTransportFeatureTags != null) {
1009                 sipTransportFeatureTags.conclude(now);
1010             }
1011         }
1012     }
1013 
1014     /** Request Message */
onSipMessageRequest(String callId, String sipMessageMethod, int sipMessageDirection)1015     public synchronized void onSipMessageRequest(String callId, String sipMessageMethod,
1016             int sipMessageDirection) {
1017         mSipMessage = new SipMessageArray(sipMessageMethod, sipMessageDirection, callId);
1018         mSipMessageArray.add(mSipMessage);
1019     }
1020 
1021     /** invalidated result when Request message is sent */
invalidatedMessageResult(int subId, String sipMessageMethod, int sipMessageDirection, int messageError)1022     public synchronized void invalidatedMessageResult(int subId, String sipMessageMethod,
1023             int sipMessageDirection, int messageError) {
1024         mSipMessage.addSipMessageStat(subId, sipMessageMethod, 0,
1025                 sipMessageDirection, messageError);
1026     }
1027 
1028     /** Create a new atom when RCS SIP Message Response changed. */
onSipMessageResponse(int subId, String callId, int sipMessageResponse, int messageError)1029     public synchronized void onSipMessageResponse(int subId, String callId,
1030             int sipMessageResponse, int messageError) {
1031         SipMessageArray match = mSipMessageArray.stream()
1032                 .filter(d -> d.mCallId.equals(callId)).findFirst().orElse(null);
1033         if (match != null) {
1034             mSipMessage.addSipMessageStat(subId, match.mMethod, sipMessageResponse,
1035                     match.mDirection, messageError);
1036             mSipMessageArray.removeIf(d -> d.mCallId.equals(callId));
1037         }
1038     }
1039 
1040     /** Request SIP Method Message */
earlySipTransportSession(String sessionMethod, String callId, int sipMessageDirection)1041     public synchronized void earlySipTransportSession(String sessionMethod, String callId,
1042             int sipMessageDirection) {
1043         mSipTransportSession = new SipTransportSessionArray(sessionMethod,
1044                 sipMessageDirection, callId);
1045         mSipTransportSessionArray.add(mSipTransportSession);
1046     }
1047 
1048     /** Response Message */
confirmedSipTransportSession(String callId, int sipResponse)1049     public synchronized void confirmedSipTransportSession(String callId,
1050             int sipResponse) {
1051         SipTransportSessionArray match = mSipTransportSessionArray.stream()
1052                 .filter(d -> d.mCallId.equals(callId)).findFirst().orElse(null);
1053         if (match != null) {
1054             match.mSipResponse = sipResponse;
1055         }
1056     }
1057 
1058     /** Create a new atom when RCS SIP Transport Session changed. */
onSipTransportSessionClosed(int subId, String callId, int sipResponse, boolean isEndedGracefully)1059     public synchronized void onSipTransportSessionClosed(int subId, String callId,
1060             int sipResponse, boolean isEndedGracefully) {
1061         SipTransportSessionArray match = mSipTransportSessionArray.stream()
1062                 .filter(d -> d.mCallId.equals(callId)).findFirst().orElse(null);
1063         if (match != null) {
1064             if (sipResponse != 0) {
1065                 match.mSipResponse = sipResponse;
1066             }
1067             mSipTransportSession.addSipTransportSessionStat(subId, match.mMethod, match.mDirection,
1068                     sipResponse, isEndedGracefully);
1069             mSipTransportSessionArray.removeIf(d -> d.mCallId.equals(callId));
1070         }
1071     }
1072 
1073     /** Add a listener to the hashmap for waiting upcoming DedicatedBearer established event */
onImsDedicatedBearerListenerAdded(@onNull final int listenerId, @NonNull final int slotId, @NonNull final int ratAtEnd, @NonNull final int qci)1074     public synchronized void onImsDedicatedBearerListenerAdded(@NonNull final int listenerId,
1075             @NonNull final int slotId, @NonNull final int ratAtEnd, @NonNull final int qci) {
1076         int subId = getSubId(slotId);
1077         int carrierId = getCarrierId(subId);
1078 
1079         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
1080                 || !isValidCarrierId(carrierId)) {
1081             return;
1082         }
1083 
1084         if (mDedicatedBearerListenerEventMap.containsKey(listenerId)) {
1085             return;
1086         }
1087 
1088         ImsDedicatedBearerListenerEvent preProto = new ImsDedicatedBearerListenerEvent();
1089         preProto.carrierId = carrierId;
1090         preProto.slotId = slotId;
1091         preProto.ratAtEnd = ratAtEnd;
1092         preProto.qci = qci;
1093         preProto.dedicatedBearerEstablished = false;
1094         preProto.eventCount = 1;
1095 
1096         mDedicatedBearerListenerEventMap.put(listenerId, preProto);
1097     }
1098 
1099     /** update previously added atom with dedicatedBearerEstablished = true when
1100      *  DedicatedBearerListener Event changed. */
onImsDedicatedBearerListenerUpdateSession(final int listenerId, final int slotId, final int rat, final int qci, @NonNull final boolean dedicatedBearerEstablished)1101     public synchronized void onImsDedicatedBearerListenerUpdateSession(final int listenerId,
1102             final int slotId, final int rat, final int qci,
1103             @NonNull final boolean dedicatedBearerEstablished) {
1104         int subId = getSubId(slotId);
1105         int carrierId = getCarrierId(subId);
1106 
1107         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
1108                 || !isValidCarrierId(carrierId)) {
1109             return;
1110         }
1111 
1112         if (mDedicatedBearerListenerEventMap.containsKey(listenerId)) {
1113             ImsDedicatedBearerListenerEvent preProto =
1114                     mDedicatedBearerListenerEventMap.get(listenerId);
1115 
1116             preProto.ratAtEnd = rat;
1117             preProto.qci = qci;
1118             preProto.dedicatedBearerEstablished = dedicatedBearerEstablished;
1119 
1120             mDedicatedBearerListenerEventMap.replace(listenerId, preProto);
1121         } else {
1122             ImsDedicatedBearerListenerEvent preProto = new ImsDedicatedBearerListenerEvent();
1123             preProto.carrierId = carrierId;
1124             preProto.slotId = slotId;
1125             preProto.ratAtEnd = rat;
1126             preProto.qci = qci;
1127             preProto.dedicatedBearerEstablished = dedicatedBearerEstablished;
1128             preProto.eventCount = 1;
1129 
1130             mDedicatedBearerListenerEventMap.put(listenerId, preProto);
1131         }
1132     }
1133 
1134     /** add proto to atom when listener is removed, so that I can save the status of dedicatedbearer
1135      *  establishment per listener id */
onImsDedicatedBearerListenerRemoved(@onNull final int listenerId)1136     public synchronized void onImsDedicatedBearerListenerRemoved(@NonNull final int listenerId) {
1137         if (mDedicatedBearerListenerEventMap.containsKey(listenerId)) {
1138 
1139             ImsDedicatedBearerListenerEvent newProto =
1140                     mDedicatedBearerListenerEventMap.get(listenerId);
1141 
1142             mAtomsStorage.addImsDedicatedBearerListenerEvent(newProto);
1143             mDedicatedBearerListenerEventMap.remove(listenerId);
1144         }
1145     }
1146 
1147     /** Create a new atom when DedicatedBearer Event changed. */
onImsDedicatedBearerEvent(int slotId, int ratAtEnd, int qci, int bearerState, boolean localConnectionInfoReceived, boolean remoteConnectionInfoReceived, boolean hasListeners)1148     public synchronized void onImsDedicatedBearerEvent(int slotId, int ratAtEnd, int qci,
1149             int bearerState, boolean localConnectionInfoReceived,
1150             boolean remoteConnectionInfoReceived, boolean hasListeners) {
1151         int subId = getSubId(slotId);
1152         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1153             return;
1154         }
1155 
1156         ImsDedicatedBearerEvent proto = new ImsDedicatedBearerEvent();
1157         proto.carrierId = getCarrierId(subId);
1158         proto.slotId = getSlotId(subId);
1159         proto.ratAtEnd = ratAtEnd;
1160         proto.qci = qci;
1161         proto.bearerState = bearerState;
1162         proto.localConnectionInfoReceived = localConnectionInfoReceived;
1163         proto.remoteConnectionInfoReceived = remoteConnectionInfoReceived;
1164         proto.hasListeners = hasListeners;
1165         proto.count = 1;
1166         mAtomsStorage.addImsDedicatedBearerEvent(proto);
1167     }
1168 
1169     /**
1170      * Update or Create a new atom when Ims Registration Service Desc state changed.
1171      * Use-related parts are already converted from UseStatsWriter based on RcsContactPresenceTuple.
1172      */
onImsRegistrationServiceDescStats(int subId, List<String> serviceIdList, List<String> serviceIdVersionList, int registrationTech)1173     public void onImsRegistrationServiceDescStats(int subId, List<String> serviceIdList,
1174             List<String> serviceIdVersionList, int registrationTech) {
1175         synchronized (mImsRegistrationServiceDescStatsList) {
1176             int carrierId = getCarrierId(subId);
1177             if (!isValidCarrierId(carrierId)) {
1178                 handleImsRegistrationServiceDescStats();
1179                 return;
1180             }
1181             // update cached atom if exists
1182             onStoreCompleteImsRegistrationServiceDescStats(subId);
1183 
1184             if (serviceIdList == null) {
1185                 Rlog.d(TAG, "serviceIds is null or empty");
1186                 return;
1187             }
1188 
1189             int index = 0;
1190             for (String serviceId : serviceIdList) {
1191                 ImsRegistrationServiceDescStats mImsRegistrationServiceDescStats =
1192                         new ImsRegistrationServiceDescStats();
1193 
1194                 mImsRegistrationServiceDescStats.carrierId = carrierId;
1195                 mImsRegistrationServiceDescStats.slotId = getSlotId(subId);
1196                 mImsRegistrationServiceDescStats.serviceIdName = convertServiceIdToValue(serviceId);
1197                 mImsRegistrationServiceDescStats.serviceIdVersion =
1198                         Float.parseFloat(serviceIdVersionList.get(index++));
1199                 mImsRegistrationServiceDescStats.registrationTech = registrationTech;
1200                 mImsRegistrationServiceDescStatsList.add(mImsRegistrationServiceDescStats);
1201             }
1202         }
1203     }
1204 
1205     /** Update duration and cached of ImsRegistrationServiceDescStats when metrics are pulled */
onFlushIncompleteImsRegistrationServiceDescStats()1206     public void onFlushIncompleteImsRegistrationServiceDescStats() {
1207         synchronized (mImsRegistrationServiceDescStatsList) {
1208             for (ImsRegistrationServiceDescStats proto : mImsRegistrationServiceDescStatsList) {
1209                 ImsRegistrationServiceDescStats newProto =
1210                         copyImsRegistrationServiceDescStats(proto);
1211                 long now = getWallTimeMillis();
1212                 // the current time is a placeholder and total registered time will be
1213                 // calculated when generating final atoms
1214                 newProto.publishedMillis = now - proto.publishedMillis;
1215                 mAtomsStorage.addImsRegistrationServiceDescStats(newProto);
1216                 proto.publishedMillis = now;
1217             }
1218         }
1219     }
1220 
1221     /** Create a new atom when Uce Event Stats changed. */
onUceEventStats(int subId, int type, boolean successful, int commandCode, int networkResponse)1222     public synchronized void onUceEventStats(int subId, int type, boolean successful,
1223             int commandCode, int networkResponse) {
1224         UceEventStats proto = new UceEventStats();
1225 
1226         int carrierId = getCarrierId(subId);
1227         if (!isValidCarrierId(carrierId)) {
1228             handleImsRegistrationServiceDescStats();
1229             return;
1230         }
1231         proto.carrierId = carrierId;
1232         proto.slotId = getSlotId(subId);
1233         proto.type = type;
1234         proto.successful = successful;
1235         proto.commandCode = commandCode;
1236         proto.networkResponse = networkResponse;
1237         proto.count = 1;
1238         mAtomsStorage.addUceEventStats(proto);
1239 
1240         /**
1241          * The publishedMillis of ImsRegistrationServiceDescStat is the time gap between
1242          * Publish success and Un publish.
1243          * So, when the publish operation is successful, the corresponding time gap is set,
1244          * and in case of failure, the cached stat is deleted.
1245          */
1246         if (type == UCE_EVENT_STATS__TYPE__PUBLISH) {
1247             if (successful) {
1248                 setImsRegistrationServiceDescStatsTime(proto.carrierId);
1249             } else {
1250                 deleteImsRegistrationServiceDescStats(proto.carrierId);
1251             }
1252         }
1253     }
1254 
1255     /** Create a new atom when Presence Notify Event changed. */
onPresenceNotifyEvent(int subId, String reason, boolean contentBodyReceived, boolean rcsCaps, boolean mmtelCaps, boolean noCaps)1256     public synchronized void onPresenceNotifyEvent(int subId, String reason,
1257             boolean contentBodyReceived, boolean rcsCaps, boolean mmtelCaps, boolean noCaps) {
1258         PresenceNotifyEvent proto = new PresenceNotifyEvent();
1259 
1260         int carrierId = getCarrierId(subId);
1261         if (!isValidCarrierId(carrierId)) {
1262             handleImsRegistrationServiceDescStats();
1263             return;
1264         }
1265 
1266         proto.carrierId = carrierId;
1267         proto.slotId = getSlotId(subId);
1268         proto.reason = convertPresenceNotifyReason(reason);
1269         proto.contentBodyReceived = contentBodyReceived;
1270         proto.rcsCapsCount = rcsCaps ? 1 : 0;
1271         proto.mmtelCapsCount = mmtelCaps ? 1 : 0;
1272         proto.noCapsCount = noCaps ? 1 : 0;
1273         proto.count = 1;
1274         mAtomsStorage.addPresenceNotifyEvent(proto);
1275     }
1276 
1277     /** Update duration a created Ims Registration Desc Stat atom when Un publish event happened. */
onStoreCompleteImsRegistrationServiceDescStats(int subId)1278     public void onStoreCompleteImsRegistrationServiceDescStats(int subId) {
1279         synchronized (mImsRegistrationServiceDescStatsList) {
1280             int carrierId = getCarrierId(subId);
1281             List<ImsRegistrationServiceDescStats> deleteList = new ArrayList<>();
1282             for (ImsRegistrationServiceDescStats proto : mImsRegistrationServiceDescStatsList) {
1283                 if (proto.carrierId == carrierId) {
1284                     proto.publishedMillis = getWallTimeMillis() - proto.publishedMillis;
1285                     mAtomsStorage.addImsRegistrationServiceDescStats(proto);
1286                     deleteList.add(proto);
1287                 }
1288             }
1289             for (ImsRegistrationServiceDescStats proto : deleteList) {
1290                 mImsRegistrationServiceDescStatsList.remove(proto);
1291             }
1292         }
1293     }
1294 
1295     /** Create a new atom when GBA Success Event changed. */
onGbaSuccessEvent(int subId)1296     public synchronized void onGbaSuccessEvent(int subId) {
1297         int carrierId = getCarrierId(subId);
1298         if (!isValidCarrierId(carrierId)) {
1299             return;
1300         }
1301 
1302         GbaEvent proto = new GbaEvent();
1303         proto.carrierId = carrierId;
1304         proto.slotId = getSlotId(subId);
1305         proto.successful = true;
1306         proto.failedReason = -1;
1307         proto.count = 1;
1308         mAtomsStorage.addGbaEvent(proto);
1309     }
1310 
1311     /** Create a new atom when GBA Failure Event changed. */
onGbaFailureEvent(int subId, int reason)1312     public synchronized void onGbaFailureEvent(int subId, int reason) {
1313         int carrierId = getCarrierId(subId);
1314         if (!isValidCarrierId(carrierId)) {
1315             return;
1316         }
1317 
1318         GbaEvent proto = new GbaEvent();
1319         proto.carrierId = carrierId;
1320         proto.slotId = getSlotId(subId);
1321         proto.successful = false;
1322         proto.failedReason = reason;
1323         proto.count = 1;
1324         mAtomsStorage.addGbaEvent(proto);
1325     }
1326 
1327     /** Create or return exist RcsProvisioningCallback based on subId. */
getRcsProvisioningCallback(int subId, boolean enableSingleRegistration)1328     public synchronized RcsProvisioningCallback getRcsProvisioningCallback(int subId,
1329             boolean enableSingleRegistration) {
1330         // find exist obj in Map
1331         RcsProvisioningCallback rcsProvisioningCallback = mRcsProvisioningCallbackMap.get(subId);
1332         if (rcsProvisioningCallback != null) {
1333             return rcsProvisioningCallback;
1334         }
1335 
1336         // create new, add Map and return
1337         rcsProvisioningCallback = new RcsProvisioningCallback(this, subId,
1338                 enableSingleRegistration);
1339         mRcsProvisioningCallbackMap.put(subId, rcsProvisioningCallback);
1340         return rcsProvisioningCallback;
1341     }
1342 
1343     /** Set whether single registration is supported. */
setEnableSingleRegistration(int subId, boolean enableSingleRegistration)1344     public synchronized void setEnableSingleRegistration(int subId,
1345             boolean enableSingleRegistration) {
1346         // find exist obj and set
1347         RcsProvisioningCallback callbackBinder = mRcsProvisioningCallbackMap.get(subId);
1348         if (callbackBinder != null) {
1349             callbackBinder.setEnableSingleRegistration(enableSingleRegistration);
1350         }
1351     }
1352 
removeRcsProvisioningCallback(int subId)1353     private synchronized void removeRcsProvisioningCallback(int subId) {
1354         // remove obj from Map based on subId
1355         mRcsProvisioningCallbackMap.remove(subId);
1356     }
1357 
copyImsRegistrationFeatureTagStats( ImsRegistrationFeatureTagStats proto)1358     private ImsRegistrationFeatureTagStats copyImsRegistrationFeatureTagStats(
1359             ImsRegistrationFeatureTagStats proto) {
1360         ImsRegistrationFeatureTagStats newProto = new ImsRegistrationFeatureTagStats();
1361         newProto.carrierId = proto.carrierId;
1362         newProto.slotId = proto.slotId;
1363         newProto.featureTagName = proto.featureTagName;
1364         newProto.registrationTech = proto.registrationTech;
1365         newProto.registeredMillis = proto.registeredMillis;
1366 
1367         return newProto;
1368     }
1369 
copyRcsAcsProvisioningStats(RcsAcsProvisioningStats proto)1370     private RcsAcsProvisioningStats copyRcsAcsProvisioningStats(RcsAcsProvisioningStats proto) {
1371         RcsAcsProvisioningStats newProto = new RcsAcsProvisioningStats();
1372         newProto.carrierId = proto.carrierId;
1373         newProto.slotId = proto.slotId;
1374         newProto.responseCode = proto.responseCode;
1375         newProto.responseType = proto.responseType;
1376         newProto.isSingleRegistrationEnabled = proto.isSingleRegistrationEnabled;
1377         newProto.count = proto.count;
1378         newProto.stateTimerMillis = proto.stateTimerMillis;
1379 
1380         return newProto;
1381     }
1382 
copyImsRegistrationServiceDescStats( ImsRegistrationServiceDescStats proto)1383     private ImsRegistrationServiceDescStats copyImsRegistrationServiceDescStats(
1384             ImsRegistrationServiceDescStats proto) {
1385         ImsRegistrationServiceDescStats newProto = new ImsRegistrationServiceDescStats();
1386         newProto.carrierId = proto.carrierId;
1387         newProto.slotId = proto.slotId;
1388         newProto.serviceIdName = proto.serviceIdName;
1389         newProto.serviceIdVersion = proto.serviceIdVersion;
1390         newProto.registrationTech = proto.registrationTech;
1391         return newProto;
1392     }
1393 
setImsRegistrationServiceDescStatsTime(int carrierId)1394     private void setImsRegistrationServiceDescStatsTime(int carrierId) {
1395         synchronized (mImsRegistrationServiceDescStatsList) {
1396             for (ImsRegistrationServiceDescStats descStats : mImsRegistrationServiceDescStatsList) {
1397                 if (descStats.carrierId == carrierId) {
1398                     descStats.publishedMillis = getWallTimeMillis();
1399                 }
1400             }
1401         }
1402     }
1403 
deleteImsRegistrationServiceDescStats(int carrierId)1404     private void deleteImsRegistrationServiceDescStats(int carrierId) {
1405         synchronized (mImsRegistrationServiceDescStatsList) {
1406             List<ImsRegistrationServiceDescStats> deleteList = new ArrayList<>();
1407             for (ImsRegistrationServiceDescStats proto : mImsRegistrationServiceDescStatsList) {
1408                 if (proto.carrierId == carrierId) {
1409                     deleteList.add(proto);
1410                 }
1411             }
1412             for (ImsRegistrationServiceDescStats stats : deleteList) {
1413                 mImsRegistrationServiceDescStatsList.remove(stats);
1414             }
1415         }
1416     }
1417 
handleImsRegistrationServiceDescStats()1418     private void handleImsRegistrationServiceDescStats() {
1419         synchronized (mImsRegistrationServiceDescStatsList) {
1420             List<ImsRegistrationServiceDescStats> deleteList = new ArrayList<>();
1421             for (ImsRegistrationServiceDescStats proto : mImsRegistrationServiceDescStatsList) {
1422                 int subId = getSubId(proto.slotId);
1423                 int newCarrierId = getCarrierId(subId);
1424                 if (proto.carrierId != newCarrierId) {
1425                     deleteList.add(proto);
1426                     if (proto.publishedMillis != 0) {
1427                         proto.publishedMillis = getWallTimeMillis() - proto.publishedMillis;
1428                         mAtomsStorage.addImsRegistrationServiceDescStats(proto);
1429                     }
1430                 }
1431             }
1432             for (ImsRegistrationServiceDescStats stats : deleteList) {
1433                 mImsRegistrationServiceDescStatsList.remove(stats);
1434             }
1435         }
1436     }
1437 
getRcsAcsProvisioningStats(int subId)1438     private RcsAcsProvisioningStats getRcsAcsProvisioningStats(int subId) {
1439         int carrierId = getCarrierId(subId);
1440         int slotId = getSlotId(subId);
1441 
1442         for (RcsAcsProvisioningStats stats : mRcsAcsProvisioningStatsList) {
1443             if (stats == null) {
1444                 continue;
1445             }
1446             if (stats.carrierId == carrierId && stats.slotId == slotId) {
1447                 return stats;
1448             }
1449         }
1450         return null;
1451     }
1452 
flushRcsAcsProvisioningStatsInvalid()1453     private void flushRcsAcsProvisioningStatsInvalid() {
1454         List<RcsAcsProvisioningStats> inValidList = new ArrayList<RcsAcsProvisioningStats>();
1455 
1456         int subId;
1457         int newCarrierId;
1458 
1459         for (RcsAcsProvisioningStats stats : mRcsAcsProvisioningStatsList) {
1460             subId = getSubId(stats.slotId);
1461             newCarrierId = getCarrierId(subId);
1462             if (stats.carrierId != newCarrierId) {
1463                 inValidList.add(stats);
1464             }
1465         }
1466 
1467         for (RcsAcsProvisioningStats inValid : inValidList) {
1468             inValid.stateTimerMillis = getWallTimeMillis() - inValid.stateTimerMillis;
1469             mAtomsStorage.addRcsAcsProvisioningStats(inValid);
1470             mRcsAcsProvisioningStatsList.remove(inValid);
1471         }
1472         inValidList.clear();
1473     }
1474 
flushImsRegistrationFeatureTagStatsInvalid()1475     private void flushImsRegistrationFeatureTagStatsInvalid() {
1476         List<ImsRegistrationFeatureTagStats> inValidList =
1477                 new ArrayList<ImsRegistrationFeatureTagStats>();
1478 
1479         int subId;
1480         int newCarrierId;
1481 
1482         for (ImsRegistrationFeatureTagStats stats : mImsRegistrationFeatureTagStatsList) {
1483             subId = getSubId(stats.slotId);
1484             newCarrierId = getCarrierId(subId);
1485             if (stats.carrierId != newCarrierId) {
1486                 inValidList.add(stats);
1487             }
1488         }
1489 
1490         for (ImsRegistrationFeatureTagStats inValid : inValidList) {
1491             inValid.registeredMillis = getWallTimeMillis() - inValid.registeredMillis;
1492             mAtomsStorage.addImsRegistrationFeatureTagStats(inValid);
1493             mImsRegistrationFeatureTagStatsList.remove(inValid);
1494         }
1495         inValidList.clear();
1496     }
1497 
getLastSipDelegateStat(int subId, Set<String> supportedTags)1498     private LastSipDelegateStat getLastSipDelegateStat(int subId, Set<String> supportedTags) {
1499         LastSipDelegateStat stat = null;
1500         for (LastSipDelegateStat lastStat : mLastSipDelegateStatList) {
1501             if (lastStat.compare(subId, supportedTags)) {
1502                 stat = lastStat;
1503                 break;
1504             }
1505         }
1506 
1507         if (stat == null) {
1508             stat = new LastSipDelegateStat(subId, supportedTags);
1509             mLastSipDelegateStatList.add(stat);
1510         }
1511 
1512         return stat;
1513     }
1514 
concludeSipDelegateStat()1515     private void concludeSipDelegateStat() {
1516         if (mLastSipDelegateStatList.isEmpty()) {
1517             return;
1518         }
1519         long now = getWallTimeMillis();
1520         List<LastSipDelegateStat> sipDelegateStatsCopy = new ArrayList<>(mLastSipDelegateStatList);
1521         for (LastSipDelegateStat stat : sipDelegateStatsCopy) {
1522             if (stat.isDestroyed()) {
1523                 stat.conclude(now);
1524                 mLastSipDelegateStatList.remove(stat);
1525             }
1526         }
1527     }
1528 
getLastFeatureTags(int subId)1529     private SipTransportFeatureTags getLastFeatureTags(int subId) {
1530         SipTransportFeatureTags sipTransportFeatureTags;
1531         if (mLastFeatureTagStatMap.containsKey(subId)) {
1532             sipTransportFeatureTags = mLastFeatureTagStatMap.get(subId);
1533         } else {
1534             sipTransportFeatureTags = new SipTransportFeatureTags(subId);
1535             mLastFeatureTagStatMap.put(subId, sipTransportFeatureTags);
1536         }
1537         return sipTransportFeatureTags;
1538     }
1539     @VisibleForTesting
isValidCarrierId(int carrierId)1540     protected boolean isValidCarrierId(int carrierId) {
1541         return carrierId > TelephonyManager.UNKNOWN_CARRIER_ID;
1542     }
1543 
1544     @VisibleForTesting
getSlotId(int subId)1545     protected int getSlotId(int subId) {
1546         return SubscriptionManager.getPhoneId(subId);
1547     }
1548 
1549     @VisibleForTesting
getCarrierId(int subId)1550     protected int getCarrierId(int subId) {
1551         int phoneId = SubscriptionManager.getPhoneId(subId);
1552         Phone phone = PhoneFactory.getPhone(phoneId);
1553         return phone != null ? phone.getCarrierId() : TelephonyManager.UNKNOWN_CARRIER_ID;
1554     }
1555 
1556     @VisibleForTesting
getWallTimeMillis()1557     protected long getWallTimeMillis() {
1558         //time in UTC, preserved across reboots, but can be adjusted e.g. by the user or NTP
1559         return System.currentTimeMillis();
1560     }
1561 
1562     @VisibleForTesting
logd(String msg)1563     protected void logd(String msg) {
1564         Rlog.d(TAG, msg);
1565     }
1566 
1567     @VisibleForTesting
getSubId(int slotId)1568     protected int getSubId(int slotId) {
1569         final int[] subIds = SubscriptionManager.getSubId(slotId);
1570         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
1571         if (subIds != null && subIds.length >= 1) {
1572             subId = subIds[0];
1573         }
1574         return subId;
1575     }
1576 
1577     /** Get a enum value from pre-defined feature tag name list */
1578     @VisibleForTesting
convertTagNameToValue(@onNull String tagName)1579     public int convertTagNameToValue(@NonNull String tagName) {
1580         return FEATURE_TAGS.getOrDefault(tagName.trim().toLowerCase(),
1581                 TelephonyProtoEnums.IMS_FEATURE_TAG_CUSTOM);
1582     }
1583 
1584     /** Get a enum value from pre-defined service id list */
1585     @VisibleForTesting
convertServiceIdToValue(@onNull String serviceId)1586     public int convertServiceIdToValue(@NonNull String serviceId) {
1587         return SERVICE_IDS.getOrDefault(serviceId.trim().toLowerCase(),
1588                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CUSTOM);
1589     }
1590 
1591     /** Get a enum value from pre-defined message type list */
1592     @VisibleForTesting
convertMessageTypeToValue(@onNull String messageType)1593     public int convertMessageTypeToValue(@NonNull String messageType) {
1594         return MESSAGE_TYPE.getOrDefault(messageType.trim().toLowerCase(),
1595                 TelephonyProtoEnums.SIP_REQUEST_CUSTOM);
1596     }
1597 
1598     /** Get a enum value from pre-defined reason list */
1599     @VisibleForTesting
convertPresenceNotifyReason(@onNull String reason)1600     public int convertPresenceNotifyReason(@NonNull String reason) {
1601         return NOTIFY_REASONS.getOrDefault(reason.trim().toLowerCase(),
1602                 PRESENCE_NOTIFY_EVENT__REASON__REASON_CUSTOM);
1603     }
1604 
1605     /**
1606      * Print all metrics data for debugging purposes
1607      *
1608      * @param rawWriter Print writer
1609      */
printAllMetrics(PrintWriter rawWriter)1610     public synchronized void printAllMetrics(PrintWriter rawWriter) {
1611         if (mAtomsStorage == null || mAtomsStorage.mAtoms == null) {
1612             return;
1613         }
1614 
1615         final IndentingPrintWriter pw = new IndentingPrintWriter(rawWriter, "  ");
1616         PersistAtomsProto.PersistAtoms metricAtoms = mAtomsStorage.mAtoms;
1617 
1618         pw.println("RcsStats Metrics Proto: ");
1619         pw.println("------------------------------------------");
1620         pw.println("ImsRegistrationFeatureTagStats:");
1621         pw.increaseIndent();
1622         for (ImsRegistrationFeatureTagStats stat : metricAtoms.imsRegistrationFeatureTagStats) {
1623             pw.println("[" + stat.carrierId + "]"
1624                     + " [" + stat.slotId + "]"
1625                     + " Feature Tag Name = " + stat.featureTagName
1626                     + ", Registration Tech = " + stat.registrationTech
1627                     + ", Registered Duration (ms) = " + stat.registeredMillis);
1628         }
1629         pw.decreaseIndent();
1630 
1631         pw.println("RcsClientProvisioningStats:");
1632         pw.increaseIndent();
1633         for (RcsClientProvisioningStats stat : metricAtoms.rcsClientProvisioningStats) {
1634             pw.println("[" + stat.carrierId + "]"
1635                     + " [" + stat.slotId + "]"
1636                     + " Event = " + stat.event
1637                     + ", Count = " + stat.count);
1638         }
1639         pw.decreaseIndent();
1640 
1641         pw.println("RcsAcsProvisioningStats:");
1642         pw.increaseIndent();
1643         for (RcsAcsProvisioningStats stat : metricAtoms.rcsAcsProvisioningStats) {
1644             pw.println("[" + stat.carrierId + "]"
1645                     + " [" + stat.slotId + "]"
1646                     + " Response Code = " + stat.responseCode
1647                     + ", Response Type = " + stat.responseType
1648                     + ", Single Registration Enabled = " + stat.isSingleRegistrationEnabled
1649                     + ", Count = " + stat.count
1650                     + ", State Timer (ms) = " + stat.stateTimerMillis);
1651         }
1652         pw.decreaseIndent();
1653 
1654         pw.println("SipDelegateStats:");
1655         pw.increaseIndent();
1656         for (SipDelegateStats stat : metricAtoms.sipDelegateStats) {
1657             pw.println("[" + stat.carrierId + "]"
1658                     + " [" + stat.slotId + "]"
1659                     + " [" + stat.dimension + "]"
1660                     + " Destroy Reason = " + stat.destroyReason
1661                     + ", Uptime (ms) = " + stat.uptimeMillis);
1662         }
1663         pw.decreaseIndent();
1664 
1665         pw.println("SipTransportFeatureTagStats:");
1666         pw.increaseIndent();
1667         for (SipTransportFeatureTagStats stat : metricAtoms.sipTransportFeatureTagStats) {
1668             pw.println("[" + stat.carrierId + "]"
1669                     + " [" + stat.slotId + "]"
1670                     + " Feature Tag Name = " + stat.featureTagName
1671                     + ", Denied Reason = " + stat.sipTransportDeniedReason
1672                     + ", Deregistered Reason = " + stat.sipTransportDeregisteredReason
1673                     + ", Associated Time (ms) = " + stat.associatedMillis);
1674         }
1675         pw.decreaseIndent();
1676 
1677         pw.println("SipMessageResponse:");
1678         pw.increaseIndent();
1679         for (SipMessageResponse stat : metricAtoms.sipMessageResponse) {
1680             pw.println("[" + stat.carrierId + "]"
1681                     + " [" + stat.slotId + "]"
1682                     + " Message Method = " + stat.sipMessageMethod
1683                     + ", Response = " + stat.sipMessageResponse
1684                     + ", Direction = " + stat.sipMessageDirection
1685                     + ", Error = " + stat.messageError
1686                     + ", Count = " + stat.count);
1687         }
1688         pw.decreaseIndent();
1689 
1690         pw.println("SipTransportSession:");
1691         pw.increaseIndent();
1692         for (SipTransportSession stat : metricAtoms.sipTransportSession) {
1693             pw.println("[" + stat.carrierId + "]"
1694                     + " [" + stat.slotId + "]"
1695                     + " Session Method = " + stat.sessionMethod
1696                     + ", Direction = " + stat.sipMessageDirection
1697                     + ", Response = " + stat.sipResponse
1698                     + ", Count = " + stat.sessionCount
1699                     + ", GraceFully Count = " + stat.endedGracefullyCount);
1700         }
1701         pw.decreaseIndent();
1702 
1703         pw.println("ImsDedicatedBearerListenerEvent:");
1704         pw.increaseIndent();
1705         for (ImsDedicatedBearerListenerEvent stat : metricAtoms.imsDedicatedBearerListenerEvent) {
1706             pw.println("[" + stat.carrierId + "]"
1707                     + " [" + stat.slotId + "]"
1708                     + " RAT = " + stat.ratAtEnd
1709                     + ", QCI = " + stat.qci
1710                     + ", Dedicated Bearer Established = " + stat.dedicatedBearerEstablished
1711                     + ", Count = " + stat.eventCount);
1712         }
1713         pw.decreaseIndent();
1714 
1715         pw.println("ImsDedicatedBearerEvent:");
1716         pw.increaseIndent();
1717         for (ImsDedicatedBearerEvent stat : metricAtoms.imsDedicatedBearerEvent) {
1718             pw.println("[" + stat.carrierId + "]"
1719                     + " [" + stat.slotId + "]"
1720                     + " RAT = " + stat.ratAtEnd
1721                     + ", QCI = " + stat.qci
1722                     + ", Bearer State = " + stat.bearerState
1723                     + ", Local Connection Info = " + stat.localConnectionInfoReceived
1724                     + ", Remote Connection Info = " + stat.remoteConnectionInfoReceived
1725                     + ", Listener Existence = " + stat.hasListeners
1726                     + ", Count = " + stat.count);
1727         }
1728         pw.decreaseIndent();
1729 
1730         pw.println("ImsRegistrationServiceDescStats:");
1731         pw.increaseIndent();
1732         for (ImsRegistrationServiceDescStats stat : metricAtoms.imsRegistrationServiceDescStats) {
1733             pw.println("[" + stat.carrierId + "]"
1734                     + " [" + stat.slotId + "]"
1735                     + " Name = " + stat.serviceIdName
1736                     + ", Version = " + stat.serviceIdVersion
1737                     + ", Registration Tech = " + stat.registrationTech
1738                     + ", Published Time (ms) = " + stat.publishedMillis);
1739         }
1740         pw.decreaseIndent();
1741 
1742         pw.println("UceEventStats:");
1743         pw.increaseIndent();
1744         for (UceEventStats stat : metricAtoms.uceEventStats) {
1745             pw.println("[" + stat.carrierId + "]"
1746                     + " [" + stat.slotId + "]"
1747                     + " Type = " + stat.type
1748                     + ", Successful = " + stat.successful
1749                     + ", Code = " + stat.commandCode
1750                     + ", Response = " + stat.networkResponse
1751                     + ", Count = " + stat.count);
1752         }
1753         pw.decreaseIndent();
1754 
1755         pw.println("PresenceNotifyEvent:");
1756         pw.increaseIndent();
1757         for (PresenceNotifyEvent stat : metricAtoms.presenceNotifyEvent) {
1758             pw.println("[" + stat.carrierId + "]"
1759                     + " [" + stat.slotId + "]"
1760                     + " Reason = " + stat.reason
1761                     + ", Body = " + stat.contentBodyReceived
1762                     + ", RCS Count = " + stat.rcsCapsCount
1763                     + ", MMTEL Count = " + stat.mmtelCapsCount
1764                     + ", NoCaps Count = " + stat.noCapsCount
1765                     + ", Count = " + stat.count);
1766         }
1767         pw.decreaseIndent();
1768 
1769         pw.println("GbaEvent:");
1770         pw.increaseIndent();
1771         for (GbaEvent stat : metricAtoms.gbaEvent) {
1772             pw.println("[" + stat.carrierId + "]"
1773                     + " [" + stat.slotId + "]"
1774                     + " Successful = "  + stat.successful
1775                     + ", Fail Reason = " + stat.failedReason
1776                     + ", Count = " + stat.count);
1777         }
1778         pw.decreaseIndent();
1779     }
1780 
1781     /**
1782      * Reset all events
1783      */
reset()1784     public synchronized void reset() {
1785         if (mAtomsStorage == null || mAtomsStorage.mAtoms == null) {
1786             return;
1787         }
1788 
1789         PersistAtomsProto.PersistAtoms metricAtoms = mAtomsStorage.mAtoms;
1790 
1791         metricAtoms.imsRegistrationFeatureTagStats =
1792                 PersistAtomsProto.ImsRegistrationFeatureTagStats.emptyArray();
1793         metricAtoms.rcsClientProvisioningStats =
1794                 PersistAtomsProto.RcsClientProvisioningStats.emptyArray();
1795         metricAtoms.rcsAcsProvisioningStats =
1796                 PersistAtomsProto.RcsAcsProvisioningStats.emptyArray();
1797         metricAtoms.sipDelegateStats = PersistAtomsProto.SipDelegateStats.emptyArray();
1798         metricAtoms.sipTransportFeatureTagStats =
1799                 PersistAtomsProto.SipTransportFeatureTagStats.emptyArray();
1800         metricAtoms.sipMessageResponse = PersistAtomsProto.SipMessageResponse.emptyArray();
1801         metricAtoms.sipTransportSession = PersistAtomsProto.SipTransportSession.emptyArray();
1802         metricAtoms.imsDedicatedBearerListenerEvent =
1803                 PersistAtomsProto.ImsDedicatedBearerListenerEvent.emptyArray();
1804         metricAtoms.imsDedicatedBearerEvent =
1805                 PersistAtomsProto.ImsDedicatedBearerEvent.emptyArray();
1806         metricAtoms.imsRegistrationServiceDescStats =
1807                 PersistAtomsProto.ImsRegistrationServiceDescStats.emptyArray();
1808         metricAtoms.uceEventStats = PersistAtomsProto.UceEventStats.emptyArray();
1809         metricAtoms.presenceNotifyEvent = PersistAtomsProto.PresenceNotifyEvent.emptyArray();
1810         metricAtoms.gbaEvent = PersistAtomsProto.GbaEvent.emptyArray();
1811     }
1812 
1813     /**
1814      * Convert the PersistAtomsProto into Base-64 encoded string
1815      *
1816      * @return Encoded string
1817      */
buildLog()1818     public String buildLog() {
1819         PersistAtomsProto.PersistAtoms log = buildProto();
1820         return Base64.encodeToString(
1821                 PersistAtomsProto.PersistAtoms.toByteArray(log), Base64.DEFAULT);
1822     }
1823 
1824     /**
1825      * Build the PersistAtomsProto
1826      *
1827      * @return PersistAtomsProto.PersistAtoms
1828      */
buildProto()1829     public PersistAtomsProto.PersistAtoms buildProto() {
1830         PersistAtomsProto.PersistAtoms log = new PersistAtomsProto.PersistAtoms();
1831 
1832         PersistAtomsProto.PersistAtoms atoms = mAtomsStorage.mAtoms;
1833         log.imsRegistrationFeatureTagStats = Arrays.copyOf(atoms.imsRegistrationFeatureTagStats,
1834                 atoms.imsRegistrationFeatureTagStats.length);
1835         log.rcsClientProvisioningStats = Arrays.copyOf(atoms.rcsClientProvisioningStats,
1836                 atoms.rcsClientProvisioningStats.length);
1837         log.rcsAcsProvisioningStats = Arrays.copyOf(atoms.rcsAcsProvisioningStats,
1838                 atoms.rcsAcsProvisioningStats.length);
1839         log.sipDelegateStats = Arrays.copyOf(atoms.sipDelegateStats, atoms.sipDelegateStats.length);
1840         log.sipTransportFeatureTagStats = Arrays.copyOf(atoms.sipTransportFeatureTagStats,
1841                 atoms.sipTransportFeatureTagStats.length);
1842         log.sipMessageResponse = Arrays.copyOf(atoms.sipMessageResponse,
1843                 atoms.sipMessageResponse.length);
1844         log.sipTransportSession = Arrays.copyOf(atoms.sipTransportSession,
1845                 atoms.sipTransportSession.length);
1846         log.imsDedicatedBearerListenerEvent = Arrays.copyOf(atoms.imsDedicatedBearerListenerEvent,
1847                 atoms.imsDedicatedBearerListenerEvent.length);
1848         log.imsDedicatedBearerEvent = Arrays.copyOf(atoms.imsDedicatedBearerEvent,
1849                 atoms.imsDedicatedBearerEvent.length);
1850         log.imsRegistrationServiceDescStats = Arrays.copyOf(atoms.imsRegistrationServiceDescStats,
1851                 atoms.imsRegistrationServiceDescStats.length);
1852         log.uceEventStats = Arrays.copyOf(atoms.uceEventStats, atoms.uceEventStats.length);
1853         log.presenceNotifyEvent = Arrays.copyOf(atoms.presenceNotifyEvent,
1854                 atoms.presenceNotifyEvent.length);
1855         log.gbaEvent = Arrays.copyOf(atoms.gbaEvent, atoms.gbaEvent.length);
1856 
1857         return log;
1858     }
1859 
1860 }
1861