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