• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(int subId, String sipMessageMethod, int sipMessageDirection, int messageError)1032     public synchronized void invalidatedMessageResult(int subId, String sipMessageMethod,
1033             int sipMessageDirection, int messageError) {
1034         mSipMessage.addSipMessageStat(subId, sipMessageMethod, 0,
1035                 sipMessageDirection, messageError);
1036     }
1037 
1038     /** Create a new atom when RCS SIP Message Response changed. */
onSipMessageResponse(int subId, String callId, int sipMessageResponse, int messageError)1039     public synchronized void onSipMessageResponse(int subId, String callId,
1040             int sipMessageResponse, int messageError) {
1041         SipMessageArray match = mSipMessageArray.stream()
1042                 .filter(d -> d.mCallId.equals(callId)).findFirst().orElse(null);
1043         if (match != null) {
1044             mSipMessage.addSipMessageStat(subId, match.mMethod, sipMessageResponse,
1045                     match.mDirection, messageError);
1046             mSipMessageArray.removeIf(d -> d.mCallId.equals(callId));
1047         }
1048     }
1049 
1050     /** Request SIP Method Message */
earlySipTransportSession(String sessionMethod, String callId, int sipMessageDirection)1051     public synchronized void earlySipTransportSession(String sessionMethod, String callId,
1052             int sipMessageDirection) {
1053         mSipTransportSession = new SipTransportSessionArray(sessionMethod,
1054                 sipMessageDirection, callId);
1055         mSipTransportSessionArray.add(mSipTransportSession);
1056     }
1057 
1058     /** Response Message */
confirmedSipTransportSession(String callId, int sipResponse)1059     public synchronized void confirmedSipTransportSession(String callId,
1060             int sipResponse) {
1061         SipTransportSessionArray match = mSipTransportSessionArray.stream()
1062                 .filter(d -> d.mCallId.equals(callId)).findFirst().orElse(null);
1063         if (match != null) {
1064             match.mSipResponse = sipResponse;
1065         }
1066     }
1067 
1068     /** Create a new atom when RCS SIP Transport Session changed. */
onSipTransportSessionClosed(int subId, String callId, int sipResponse, boolean isEndedGracefully)1069     public synchronized void onSipTransportSessionClosed(int subId, String callId,
1070             int sipResponse, boolean isEndedGracefully) {
1071         SipTransportSessionArray match = mSipTransportSessionArray.stream()
1072                 .filter(d -> d.mCallId.equals(callId)).findFirst().orElse(null);
1073         if (match != null) {
1074             if (sipResponse != 0) {
1075                 match.mSipResponse = sipResponse;
1076             }
1077             mSipTransportSession.addSipTransportSessionStat(subId, match.mMethod, match.mDirection,
1078                     sipResponse, isEndedGracefully);
1079             mSipTransportSessionArray.removeIf(d -> d.mCallId.equals(callId));
1080         }
1081     }
1082 
1083     /** 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)1084     public synchronized void onImsDedicatedBearerListenerAdded(@NonNull final int listenerId,
1085             @NonNull final int slotId, @NonNull final int ratAtEnd, @NonNull final int qci) {
1086         int subId = getSubId(slotId);
1087         int carrierId = getCarrierId(subId);
1088 
1089         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
1090                 || !isValidCarrierId(carrierId)) {
1091             return;
1092         }
1093 
1094         if (mDedicatedBearerListenerEventMap.containsKey(listenerId)) {
1095             return;
1096         }
1097 
1098         ImsDedicatedBearerListenerEvent preProto = new ImsDedicatedBearerListenerEvent();
1099         preProto.carrierId = carrierId;
1100         preProto.slotId = slotId;
1101         preProto.ratAtEnd = ratAtEnd;
1102         preProto.qci = qci;
1103         preProto.dedicatedBearerEstablished = false;
1104         preProto.eventCount = 1;
1105 
1106         mDedicatedBearerListenerEventMap.put(listenerId, preProto);
1107     }
1108 
1109     /** update previously added atom with dedicatedBearerEstablished = true when
1110      *  DedicatedBearerListener Event changed. */
onImsDedicatedBearerListenerUpdateSession(final int listenerId, final int slotId, final int rat, final int qci, @NonNull final boolean dedicatedBearerEstablished)1111     public synchronized void onImsDedicatedBearerListenerUpdateSession(final int listenerId,
1112             final int slotId, final int rat, final int qci,
1113             @NonNull final boolean dedicatedBearerEstablished) {
1114         int subId = getSubId(slotId);
1115         int carrierId = getCarrierId(subId);
1116 
1117         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
1118                 || !isValidCarrierId(carrierId)) {
1119             return;
1120         }
1121 
1122         if (mDedicatedBearerListenerEventMap.containsKey(listenerId)) {
1123             ImsDedicatedBearerListenerEvent preProto =
1124                     mDedicatedBearerListenerEventMap.get(listenerId);
1125 
1126             preProto.ratAtEnd = rat;
1127             preProto.qci = qci;
1128             preProto.dedicatedBearerEstablished = dedicatedBearerEstablished;
1129 
1130             mDedicatedBearerListenerEventMap.replace(listenerId, preProto);
1131         } else {
1132             ImsDedicatedBearerListenerEvent preProto = new ImsDedicatedBearerListenerEvent();
1133             preProto.carrierId = carrierId;
1134             preProto.slotId = slotId;
1135             preProto.ratAtEnd = rat;
1136             preProto.qci = qci;
1137             preProto.dedicatedBearerEstablished = dedicatedBearerEstablished;
1138             preProto.eventCount = 1;
1139 
1140             mDedicatedBearerListenerEventMap.put(listenerId, preProto);
1141         }
1142     }
1143 
1144     /** add proto to atom when listener is removed, so that I can save the status of dedicatedbearer
1145      *  establishment per listener id */
onImsDedicatedBearerListenerRemoved(@onNull final int listenerId)1146     public synchronized void onImsDedicatedBearerListenerRemoved(@NonNull final int listenerId) {
1147         if (mDedicatedBearerListenerEventMap.containsKey(listenerId)) {
1148 
1149             ImsDedicatedBearerListenerEvent newProto =
1150                     mDedicatedBearerListenerEventMap.get(listenerId);
1151 
1152             mAtomsStorage.addImsDedicatedBearerListenerEvent(newProto);
1153             mDedicatedBearerListenerEventMap.remove(listenerId);
1154         }
1155     }
1156 
1157     /** Create a new atom when DedicatedBearer Event changed. */
onImsDedicatedBearerEvent(int slotId, int ratAtEnd, int qci, int bearerState, boolean localConnectionInfoReceived, boolean remoteConnectionInfoReceived, boolean hasListeners)1158     public synchronized void onImsDedicatedBearerEvent(int slotId, int ratAtEnd, int qci,
1159             int bearerState, boolean localConnectionInfoReceived,
1160             boolean remoteConnectionInfoReceived, boolean hasListeners) {
1161         int subId = getSubId(slotId);
1162         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1163             return;
1164         }
1165 
1166         ImsDedicatedBearerEvent proto = new ImsDedicatedBearerEvent();
1167         proto.carrierId = getCarrierId(subId);
1168         proto.slotId = getSlotId(subId);
1169         proto.ratAtEnd = ratAtEnd;
1170         proto.qci = qci;
1171         proto.bearerState = bearerState;
1172         proto.localConnectionInfoReceived = localConnectionInfoReceived;
1173         proto.remoteConnectionInfoReceived = remoteConnectionInfoReceived;
1174         proto.hasListeners = hasListeners;
1175         proto.count = 1;
1176         mAtomsStorage.addImsDedicatedBearerEvent(proto);
1177     }
1178 
1179     /**
1180      * Update or Create a new atom when Ims Registration Service Desc state changed.
1181      * Use-related parts are already converted from UseStatsWriter based on RcsContactPresenceTuple.
1182      */
onImsRegistrationServiceDescStats(int subId, List<String> serviceIdList, List<String> serviceIdVersionList, int registrationTech)1183     public void onImsRegistrationServiceDescStats(int subId, List<String> serviceIdList,
1184             List<String> serviceIdVersionList, int registrationTech) {
1185         synchronized (mImsRegistrationServiceDescStatsList) {
1186             int carrierId = getCarrierId(subId);
1187             if (!isValidCarrierId(carrierId)) {
1188                 handleImsRegistrationServiceDescStats();
1189                 return;
1190             }
1191             // update cached atom if exists
1192             onStoreCompleteImsRegistrationServiceDescStats(subId);
1193 
1194             if (serviceIdList == null) {
1195                 Rlog.d(TAG, "serviceIds is null or empty");
1196                 return;
1197             }
1198 
1199             int index = 0;
1200             for (String serviceId : serviceIdList) {
1201                 ImsRegistrationServiceDescStats mImsRegistrationServiceDescStats =
1202                         new ImsRegistrationServiceDescStats();
1203 
1204                 mImsRegistrationServiceDescStats.carrierId = carrierId;
1205                 mImsRegistrationServiceDescStats.slotId = getSlotId(subId);
1206                 mImsRegistrationServiceDescStats.serviceIdName = convertServiceIdToValue(serviceId);
1207                 mImsRegistrationServiceDescStats.serviceIdVersion =
1208                         Float.parseFloat(serviceIdVersionList.get(index++));
1209                 mImsRegistrationServiceDescStats.registrationTech = registrationTech;
1210                 mImsRegistrationServiceDescStatsList.add(mImsRegistrationServiceDescStats);
1211             }
1212         }
1213     }
1214 
1215     /** Update duration and cached of ImsRegistrationServiceDescStats when metrics are pulled */
onFlushIncompleteImsRegistrationServiceDescStats()1216     public void onFlushIncompleteImsRegistrationServiceDescStats() {
1217         synchronized (mImsRegistrationServiceDescStatsList) {
1218             for (ImsRegistrationServiceDescStats proto : mImsRegistrationServiceDescStatsList) {
1219                 ImsRegistrationServiceDescStats newProto =
1220                         copyImsRegistrationServiceDescStats(proto);
1221                 long now = getWallTimeMillis();
1222                 // the current time is a placeholder and total registered time will be
1223                 // calculated when generating final atoms
1224                 newProto.publishedMillis = now - proto.publishedMillis;
1225                 mAtomsStorage.addImsRegistrationServiceDescStats(newProto);
1226                 proto.publishedMillis = now;
1227             }
1228         }
1229     }
1230 
1231     /** Create a new atom when Uce Event Stats changed. */
onUceEventStats(int subId, int type, boolean successful, int commandCode, int networkResponse)1232     public synchronized void onUceEventStats(int subId, int type, boolean successful,
1233             int commandCode, int networkResponse) {
1234         UceEventStats proto = new UceEventStats();
1235 
1236         int carrierId = getCarrierId(subId);
1237         if (!isValidCarrierId(carrierId)) {
1238             handleImsRegistrationServiceDescStats();
1239             return;
1240         }
1241         proto.carrierId = carrierId;
1242         proto.slotId = getSlotId(subId);
1243         proto.type = type;
1244         proto.successful = successful;
1245         proto.commandCode = commandCode;
1246         proto.networkResponse = networkResponse;
1247         proto.count = 1;
1248         mAtomsStorage.addUceEventStats(proto);
1249 
1250         /**
1251          * The publishedMillis of ImsRegistrationServiceDescStat is the time gap between
1252          * Publish success and Un publish.
1253          * So, when the publish operation is successful, the corresponding time gap is set,
1254          * and in case of failure, the cached stat is deleted.
1255          */
1256         if (type == UCE_EVENT_STATS__TYPE__PUBLISH) {
1257             if (successful) {
1258                 setImsRegistrationServiceDescStatsTime(proto.carrierId);
1259             } else {
1260                 deleteImsRegistrationServiceDescStats(proto.carrierId);
1261             }
1262         }
1263     }
1264 
1265     /** Create a new atom when Presence Notify Event changed. */
onPresenceNotifyEvent(int subId, String reason, boolean contentBodyReceived, boolean rcsCaps, boolean mmtelCaps, boolean noCaps)1266     public synchronized void onPresenceNotifyEvent(int subId, String reason,
1267             boolean contentBodyReceived, boolean rcsCaps, boolean mmtelCaps, boolean noCaps) {
1268         PresenceNotifyEvent proto = new PresenceNotifyEvent();
1269 
1270         int carrierId = getCarrierId(subId);
1271         if (!isValidCarrierId(carrierId)) {
1272             handleImsRegistrationServiceDescStats();
1273             return;
1274         }
1275 
1276         proto.carrierId = carrierId;
1277         proto.slotId = getSlotId(subId);
1278         proto.reason = convertPresenceNotifyReason(reason);
1279         proto.contentBodyReceived = contentBodyReceived;
1280         proto.rcsCapsCount = rcsCaps ? 1 : 0;
1281         proto.mmtelCapsCount = mmtelCaps ? 1 : 0;
1282         proto.noCapsCount = noCaps ? 1 : 0;
1283         proto.count = 1;
1284         mAtomsStorage.addPresenceNotifyEvent(proto);
1285     }
1286 
1287     /** Update duration a created Ims Registration Desc Stat atom when Un publish event happened. */
onStoreCompleteImsRegistrationServiceDescStats(int subId)1288     public void onStoreCompleteImsRegistrationServiceDescStats(int subId) {
1289         synchronized (mImsRegistrationServiceDescStatsList) {
1290             int carrierId = getCarrierId(subId);
1291             List<ImsRegistrationServiceDescStats> deleteList = new ArrayList<>();
1292             for (ImsRegistrationServiceDescStats proto : mImsRegistrationServiceDescStatsList) {
1293                 if (proto.carrierId == carrierId) {
1294                     proto.publishedMillis = getWallTimeMillis() - proto.publishedMillis;
1295                     mAtomsStorage.addImsRegistrationServiceDescStats(proto);
1296                     deleteList.add(proto);
1297                 }
1298             }
1299             for (ImsRegistrationServiceDescStats proto : deleteList) {
1300                 mImsRegistrationServiceDescStatsList.remove(proto);
1301             }
1302         }
1303     }
1304 
1305     /** Create a new atom when GBA Success Event changed. */
onGbaSuccessEvent(int subId)1306     public synchronized void onGbaSuccessEvent(int subId) {
1307         int carrierId = getCarrierId(subId);
1308         if (!isValidCarrierId(carrierId)) {
1309             return;
1310         }
1311 
1312         GbaEvent proto = new GbaEvent();
1313         proto.carrierId = carrierId;
1314         proto.slotId = getSlotId(subId);
1315         proto.successful = true;
1316         proto.failedReason = -1;
1317         proto.count = 1;
1318         mAtomsStorage.addGbaEvent(proto);
1319     }
1320 
1321     /** Create a new atom when GBA Failure Event changed. */
onGbaFailureEvent(int subId, int reason)1322     public synchronized void onGbaFailureEvent(int subId, int reason) {
1323         int carrierId = getCarrierId(subId);
1324         if (!isValidCarrierId(carrierId)) {
1325             return;
1326         }
1327 
1328         GbaEvent proto = new GbaEvent();
1329         proto.carrierId = carrierId;
1330         proto.slotId = getSlotId(subId);
1331         proto.successful = false;
1332         proto.failedReason = reason;
1333         proto.count = 1;
1334         mAtomsStorage.addGbaEvent(proto);
1335     }
1336 
1337     /** Create or return exist RcsProvisioningCallback based on subId. */
getRcsProvisioningCallback(int subId, boolean enableSingleRegistration)1338     public synchronized RcsProvisioningCallback getRcsProvisioningCallback(int subId,
1339             boolean enableSingleRegistration) {
1340         // find exist obj in Map
1341         RcsProvisioningCallback rcsProvisioningCallback = mRcsProvisioningCallbackMap.get(subId);
1342         if (rcsProvisioningCallback != null) {
1343             return rcsProvisioningCallback;
1344         }
1345 
1346         // create new, add Map and return
1347         rcsProvisioningCallback = new RcsProvisioningCallback(this, subId,
1348                 enableSingleRegistration);
1349         mRcsProvisioningCallbackMap.put(subId, rcsProvisioningCallback);
1350         return rcsProvisioningCallback;
1351     }
1352 
1353     /** Set whether single registration is supported. */
setEnableSingleRegistration(int subId, boolean enableSingleRegistration)1354     public synchronized void setEnableSingleRegistration(int subId,
1355             boolean enableSingleRegistration) {
1356         // find exist obj and set
1357         RcsProvisioningCallback callbackBinder = mRcsProvisioningCallbackMap.get(subId);
1358         if (callbackBinder != null) {
1359             callbackBinder.setEnableSingleRegistration(enableSingleRegistration);
1360         }
1361     }
1362 
removeRcsProvisioningCallback(int subId)1363     private synchronized void removeRcsProvisioningCallback(int subId) {
1364         // remove obj from Map based on subId
1365         mRcsProvisioningCallbackMap.remove(subId);
1366     }
1367 
copyImsRegistrationFeatureTagStats( ImsRegistrationFeatureTagStats proto)1368     private ImsRegistrationFeatureTagStats copyImsRegistrationFeatureTagStats(
1369             ImsRegistrationFeatureTagStats proto) {
1370         ImsRegistrationFeatureTagStats newProto = new ImsRegistrationFeatureTagStats();
1371         newProto.carrierId = proto.carrierId;
1372         newProto.slotId = proto.slotId;
1373         newProto.featureTagName = proto.featureTagName;
1374         newProto.registrationTech = proto.registrationTech;
1375         newProto.registeredMillis = proto.registeredMillis;
1376 
1377         return newProto;
1378     }
1379 
copyRcsAcsProvisioningStats(RcsAcsProvisioningStats proto)1380     private RcsAcsProvisioningStats copyRcsAcsProvisioningStats(RcsAcsProvisioningStats proto) {
1381         RcsAcsProvisioningStats newProto = new RcsAcsProvisioningStats();
1382         newProto.carrierId = proto.carrierId;
1383         newProto.slotId = proto.slotId;
1384         newProto.responseCode = proto.responseCode;
1385         newProto.responseType = proto.responseType;
1386         newProto.isSingleRegistrationEnabled = proto.isSingleRegistrationEnabled;
1387         newProto.count = proto.count;
1388         newProto.stateTimerMillis = proto.stateTimerMillis;
1389 
1390         return newProto;
1391     }
1392 
copyImsRegistrationServiceDescStats( ImsRegistrationServiceDescStats proto)1393     private ImsRegistrationServiceDescStats copyImsRegistrationServiceDescStats(
1394             ImsRegistrationServiceDescStats proto) {
1395         ImsRegistrationServiceDescStats newProto = new ImsRegistrationServiceDescStats();
1396         newProto.carrierId = proto.carrierId;
1397         newProto.slotId = proto.slotId;
1398         newProto.serviceIdName = proto.serviceIdName;
1399         newProto.serviceIdVersion = proto.serviceIdVersion;
1400         newProto.registrationTech = proto.registrationTech;
1401         return newProto;
1402     }
1403 
setImsRegistrationServiceDescStatsTime(int carrierId)1404     private void setImsRegistrationServiceDescStatsTime(int carrierId) {
1405         synchronized (mImsRegistrationServiceDescStatsList) {
1406             for (ImsRegistrationServiceDescStats descStats : mImsRegistrationServiceDescStatsList) {
1407                 if (descStats.carrierId == carrierId) {
1408                     descStats.publishedMillis = getWallTimeMillis();
1409                 }
1410             }
1411         }
1412     }
1413 
deleteImsRegistrationServiceDescStats(int carrierId)1414     private void deleteImsRegistrationServiceDescStats(int carrierId) {
1415         synchronized (mImsRegistrationServiceDescStatsList) {
1416             List<ImsRegistrationServiceDescStats> deleteList = new ArrayList<>();
1417             for (ImsRegistrationServiceDescStats proto : mImsRegistrationServiceDescStatsList) {
1418                 if (proto.carrierId == carrierId) {
1419                     deleteList.add(proto);
1420                 }
1421             }
1422             for (ImsRegistrationServiceDescStats stats : deleteList) {
1423                 mImsRegistrationServiceDescStatsList.remove(stats);
1424             }
1425         }
1426     }
1427 
handleImsRegistrationServiceDescStats()1428     private void handleImsRegistrationServiceDescStats() {
1429         synchronized (mImsRegistrationServiceDescStatsList) {
1430             List<ImsRegistrationServiceDescStats> deleteList = new ArrayList<>();
1431             for (ImsRegistrationServiceDescStats proto : mImsRegistrationServiceDescStatsList) {
1432                 int subId = getSubId(proto.slotId);
1433                 int newCarrierId = getCarrierId(subId);
1434                 if (proto.carrierId != newCarrierId) {
1435                     deleteList.add(proto);
1436                     if (proto.publishedMillis != 0) {
1437                         proto.publishedMillis = getWallTimeMillis() - proto.publishedMillis;
1438                         mAtomsStorage.addImsRegistrationServiceDescStats(proto);
1439                     }
1440                 }
1441             }
1442             for (ImsRegistrationServiceDescStats stats : deleteList) {
1443                 mImsRegistrationServiceDescStatsList.remove(stats);
1444             }
1445         }
1446     }
1447 
getRcsAcsProvisioningStats(int subId)1448     private RcsAcsProvisioningStats getRcsAcsProvisioningStats(int subId) {
1449         int carrierId = getCarrierId(subId);
1450         int slotId = getSlotId(subId);
1451 
1452         for (RcsAcsProvisioningStats stats : mRcsAcsProvisioningStatsList) {
1453             if (stats == null) {
1454                 continue;
1455             }
1456             if (stats.carrierId == carrierId && stats.slotId == slotId) {
1457                 return stats;
1458             }
1459         }
1460         return null;
1461     }
1462 
flushRcsAcsProvisioningStatsInvalid()1463     private void flushRcsAcsProvisioningStatsInvalid() {
1464         List<RcsAcsProvisioningStats> inValidList = new ArrayList<RcsAcsProvisioningStats>();
1465 
1466         int subId;
1467         int newCarrierId;
1468 
1469         for (RcsAcsProvisioningStats stats : mRcsAcsProvisioningStatsList) {
1470             subId = getSubId(stats.slotId);
1471             newCarrierId = getCarrierId(subId);
1472             if (stats.carrierId != newCarrierId) {
1473                 inValidList.add(stats);
1474             }
1475         }
1476 
1477         for (RcsAcsProvisioningStats inValid : inValidList) {
1478             inValid.stateTimerMillis = getWallTimeMillis() - inValid.stateTimerMillis;
1479             mAtomsStorage.addRcsAcsProvisioningStats(inValid);
1480             mRcsAcsProvisioningStatsList.remove(inValid);
1481         }
1482         inValidList.clear();
1483     }
1484 
flushImsRegistrationFeatureTagStatsInvalid()1485     private void flushImsRegistrationFeatureTagStatsInvalid() {
1486         List<ImsRegistrationFeatureTagStats> inValidList =
1487                 new ArrayList<ImsRegistrationFeatureTagStats>();
1488 
1489         int subId;
1490         int newCarrierId;
1491 
1492         for (ImsRegistrationFeatureTagStats stats : mImsRegistrationFeatureTagStatsList) {
1493             subId = getSubId(stats.slotId);
1494             newCarrierId = getCarrierId(subId);
1495             if (stats.carrierId != newCarrierId) {
1496                 inValidList.add(stats);
1497             }
1498         }
1499 
1500         for (ImsRegistrationFeatureTagStats inValid : inValidList) {
1501             inValid.registeredMillis = getWallTimeMillis() - inValid.registeredMillis;
1502             mAtomsStorage.addImsRegistrationFeatureTagStats(inValid);
1503             mImsRegistrationFeatureTagStatsList.remove(inValid);
1504         }
1505         inValidList.clear();
1506     }
1507 
getLastSipDelegateStat(int subId, Set<String> supportedTags)1508     private LastSipDelegateStat getLastSipDelegateStat(int subId, Set<String> supportedTags) {
1509         LastSipDelegateStat stat = null;
1510         for (LastSipDelegateStat lastStat : mLastSipDelegateStatList) {
1511             if (lastStat.compare(subId, supportedTags)) {
1512                 stat = lastStat;
1513                 break;
1514             }
1515         }
1516 
1517         if (stat == null) {
1518             stat = new LastSipDelegateStat(subId, supportedTags);
1519             mLastSipDelegateStatList.add(stat);
1520         }
1521 
1522         return stat;
1523     }
1524 
concludeSipDelegateStat()1525     private void concludeSipDelegateStat() {
1526         if (mLastSipDelegateStatList.isEmpty()) {
1527             return;
1528         }
1529         long now = getWallTimeMillis();
1530         List<LastSipDelegateStat> sipDelegateStatsCopy = new ArrayList<>(mLastSipDelegateStatList);
1531         for (LastSipDelegateStat stat : sipDelegateStatsCopy) {
1532             if (stat.isDestroyed()) {
1533                 stat.conclude(now);
1534                 mLastSipDelegateStatList.remove(stat);
1535             }
1536         }
1537     }
1538 
getLastFeatureTags(int subId)1539     private SipTransportFeatureTags getLastFeatureTags(int subId) {
1540         SipTransportFeatureTags sipTransportFeatureTags;
1541         if (mLastFeatureTagStatMap.containsKey(subId)) {
1542             sipTransportFeatureTags = mLastFeatureTagStatMap.get(subId);
1543         } else {
1544             sipTransportFeatureTags = new SipTransportFeatureTags(subId);
1545             mLastFeatureTagStatMap.put(subId, sipTransportFeatureTags);
1546         }
1547         return sipTransportFeatureTags;
1548     }
1549     @VisibleForTesting
isValidCarrierId(int carrierId)1550     protected boolean isValidCarrierId(int carrierId) {
1551         return carrierId > TelephonyManager.UNKNOWN_CARRIER_ID;
1552     }
1553 
1554     @VisibleForTesting
getSlotId(int subId)1555     protected int getSlotId(int subId) {
1556         return SubscriptionManager.getPhoneId(subId);
1557     }
1558 
1559     @VisibleForTesting
getCarrierId(int subId)1560     protected int getCarrierId(int subId) {
1561         int phoneId = SubscriptionManager.getPhoneId(subId);
1562         Phone phone = PhoneFactory.getPhone(phoneId);
1563         return phone != null ? phone.getCarrierId() : TelephonyManager.UNKNOWN_CARRIER_ID;
1564     }
1565 
1566     @VisibleForTesting
getWallTimeMillis()1567     protected long getWallTimeMillis() {
1568         //time in UTC, preserved across reboots, but can be adjusted e.g. by the user or NTP
1569         return System.currentTimeMillis();
1570     }
1571 
1572     @VisibleForTesting
logd(String msg)1573     protected void logd(String msg) {
1574         Rlog.d(TAG, msg);
1575     }
1576 
1577     @VisibleForTesting
getSubId(int slotId)1578     protected int getSubId(int slotId) {
1579         return SubscriptionManager.getSubscriptionId(slotId);
1580     }
1581 
1582     /** Get a enum value from pre-defined feature tag name list */
1583     @VisibleForTesting
convertTagNameToValue(@onNull String tagName)1584     public int convertTagNameToValue(@NonNull String tagName) {
1585         return FEATURE_TAGS.getOrDefault(tagName.trim().toLowerCase(Locale.ROOT),
1586                 TelephonyProtoEnums.IMS_FEATURE_TAG_CUSTOM);
1587     }
1588 
1589     /** Get a enum value from pre-defined service id list */
1590     @VisibleForTesting
convertServiceIdToValue(@onNull String serviceId)1591     public int convertServiceIdToValue(@NonNull String serviceId) {
1592         return SERVICE_IDS.getOrDefault(serviceId.trim().toLowerCase(Locale.ROOT),
1593                 IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CUSTOM);
1594     }
1595 
1596     /** Get a enum value from pre-defined message type list */
1597     @VisibleForTesting
convertMessageTypeToValue(@onNull String messageType)1598     public int convertMessageTypeToValue(@NonNull String messageType) {
1599         return MESSAGE_TYPE.getOrDefault(messageType.trim().toLowerCase(Locale.ROOT),
1600                 TelephonyProtoEnums.SIP_REQUEST_CUSTOM);
1601     }
1602 
1603     /** Get a enum value from pre-defined reason list */
1604     @VisibleForTesting
convertPresenceNotifyReason(@onNull String reason)1605     public int convertPresenceNotifyReason(@NonNull String reason) {
1606         return NOTIFY_REASONS.getOrDefault(reason.trim().toLowerCase(Locale.ROOT),
1607                 PRESENCE_NOTIFY_EVENT__REASON__REASON_CUSTOM);
1608     }
1609 
1610     /**
1611      * Print all metrics data for debugging purposes
1612      *
1613      * @param rawWriter Print writer
1614      */
printAllMetrics(PrintWriter rawWriter)1615     public synchronized void printAllMetrics(PrintWriter rawWriter) {
1616         if (mAtomsStorage == null || mAtomsStorage.mAtoms == null) {
1617             return;
1618         }
1619 
1620         final IndentingPrintWriter pw = new IndentingPrintWriter(rawWriter, "  ");
1621         PersistAtomsProto.PersistAtoms metricAtoms = mAtomsStorage.mAtoms;
1622 
1623         pw.println("RcsStats Metrics Proto: ");
1624         pw.println("------------------------------------------");
1625         pw.println("ImsRegistrationFeatureTagStats:");
1626         pw.increaseIndent();
1627         for (ImsRegistrationFeatureTagStats stat : metricAtoms.imsRegistrationFeatureTagStats) {
1628             pw.println("[" + stat.carrierId + "]"
1629                     + " [" + stat.slotId + "]"
1630                     + " Feature Tag Name = " + stat.featureTagName
1631                     + ", Registration Tech = " + stat.registrationTech
1632                     + ", Registered Duration (ms) = " + stat.registeredMillis);
1633         }
1634         pw.decreaseIndent();
1635 
1636         pw.println("RcsClientProvisioningStats:");
1637         pw.increaseIndent();
1638         for (RcsClientProvisioningStats stat : metricAtoms.rcsClientProvisioningStats) {
1639             pw.println("[" + stat.carrierId + "]"
1640                     + " [" + stat.slotId + "]"
1641                     + " Event = " + stat.event
1642                     + ", Count = " + stat.count);
1643         }
1644         pw.decreaseIndent();
1645 
1646         pw.println("RcsAcsProvisioningStats:");
1647         pw.increaseIndent();
1648         for (RcsAcsProvisioningStats stat : metricAtoms.rcsAcsProvisioningStats) {
1649             pw.println("[" + stat.carrierId + "]"
1650                     + " [" + stat.slotId + "]"
1651                     + " Response Code = " + stat.responseCode
1652                     + ", Response Type = " + stat.responseType
1653                     + ", Single Registration Enabled = " + stat.isSingleRegistrationEnabled
1654                     + ", Count = " + stat.count
1655                     + ", State Timer (ms) = " + stat.stateTimerMillis);
1656         }
1657         pw.decreaseIndent();
1658 
1659         pw.println("SipDelegateStats:");
1660         pw.increaseIndent();
1661         for (SipDelegateStats stat : metricAtoms.sipDelegateStats) {
1662             pw.println("[" + stat.carrierId + "]"
1663                     + " [" + stat.slotId + "]"
1664                     + " [" + stat.dimension + "]"
1665                     + " Destroy Reason = " + stat.destroyReason
1666                     + ", Uptime (ms) = " + stat.uptimeMillis);
1667         }
1668         pw.decreaseIndent();
1669 
1670         pw.println("SipTransportFeatureTagStats:");
1671         pw.increaseIndent();
1672         for (SipTransportFeatureTagStats stat : metricAtoms.sipTransportFeatureTagStats) {
1673             pw.println("[" + stat.carrierId + "]"
1674                     + " [" + stat.slotId + "]"
1675                     + " Feature Tag Name = " + stat.featureTagName
1676                     + ", Denied Reason = " + stat.sipTransportDeniedReason
1677                     + ", Deregistered Reason = " + stat.sipTransportDeregisteredReason
1678                     + ", Associated Time (ms) = " + stat.associatedMillis);
1679         }
1680         pw.decreaseIndent();
1681 
1682         pw.println("SipMessageResponse:");
1683         pw.increaseIndent();
1684         for (SipMessageResponse stat : metricAtoms.sipMessageResponse) {
1685             pw.println("[" + stat.carrierId + "]"
1686                     + " [" + stat.slotId + "]"
1687                     + " Message Method = " + stat.sipMessageMethod
1688                     + ", Response = " + stat.sipMessageResponse
1689                     + ", Direction = " + stat.sipMessageDirection
1690                     + ", Error = " + stat.messageError
1691                     + ", Count = " + stat.count);
1692         }
1693         pw.decreaseIndent();
1694 
1695         pw.println("SipTransportSession:");
1696         pw.increaseIndent();
1697         for (SipTransportSession stat : metricAtoms.sipTransportSession) {
1698             pw.println("[" + stat.carrierId + "]"
1699                     + " [" + stat.slotId + "]"
1700                     + " Session Method = " + stat.sessionMethod
1701                     + ", Direction = " + stat.sipMessageDirection
1702                     + ", Response = " + stat.sipResponse
1703                     + ", Count = " + stat.sessionCount
1704                     + ", GraceFully Count = " + stat.endedGracefullyCount);
1705         }
1706         pw.decreaseIndent();
1707 
1708         pw.println("ImsDedicatedBearerListenerEvent:");
1709         pw.increaseIndent();
1710         for (ImsDedicatedBearerListenerEvent stat : metricAtoms.imsDedicatedBearerListenerEvent) {
1711             pw.println("[" + stat.carrierId + "]"
1712                     + " [" + stat.slotId + "]"
1713                     + " RAT = " + stat.ratAtEnd
1714                     + ", QCI = " + stat.qci
1715                     + ", Dedicated Bearer Established = " + stat.dedicatedBearerEstablished
1716                     + ", Count = " + stat.eventCount);
1717         }
1718         pw.decreaseIndent();
1719 
1720         pw.println("ImsDedicatedBearerEvent:");
1721         pw.increaseIndent();
1722         for (ImsDedicatedBearerEvent stat : metricAtoms.imsDedicatedBearerEvent) {
1723             pw.println("[" + stat.carrierId + "]"
1724                     + " [" + stat.slotId + "]"
1725                     + " RAT = " + stat.ratAtEnd
1726                     + ", QCI = " + stat.qci
1727                     + ", Bearer State = " + stat.bearerState
1728                     + ", Local Connection Info = " + stat.localConnectionInfoReceived
1729                     + ", Remote Connection Info = " + stat.remoteConnectionInfoReceived
1730                     + ", Listener Existence = " + stat.hasListeners
1731                     + ", Count = " + stat.count);
1732         }
1733         pw.decreaseIndent();
1734 
1735         pw.println("ImsRegistrationServiceDescStats:");
1736         pw.increaseIndent();
1737         for (ImsRegistrationServiceDescStats stat : metricAtoms.imsRegistrationServiceDescStats) {
1738             pw.println("[" + stat.carrierId + "]"
1739                     + " [" + stat.slotId + "]"
1740                     + " Name = " + stat.serviceIdName
1741                     + ", Version = " + stat.serviceIdVersion
1742                     + ", Registration Tech = " + stat.registrationTech
1743                     + ", Published Time (ms) = " + stat.publishedMillis);
1744         }
1745         pw.decreaseIndent();
1746 
1747         pw.println("UceEventStats:");
1748         pw.increaseIndent();
1749         for (UceEventStats stat : metricAtoms.uceEventStats) {
1750             pw.println("[" + stat.carrierId + "]"
1751                     + " [" + stat.slotId + "]"
1752                     + " Type = " + stat.type
1753                     + ", Successful = " + stat.successful
1754                     + ", Code = " + stat.commandCode
1755                     + ", Response = " + stat.networkResponse
1756                     + ", Count = " + stat.count);
1757         }
1758         pw.decreaseIndent();
1759 
1760         pw.println("PresenceNotifyEvent:");
1761         pw.increaseIndent();
1762         for (PresenceNotifyEvent stat : metricAtoms.presenceNotifyEvent) {
1763             pw.println("[" + stat.carrierId + "]"
1764                     + " [" + stat.slotId + "]"
1765                     + " Reason = " + stat.reason
1766                     + ", Body = " + stat.contentBodyReceived
1767                     + ", RCS Count = " + stat.rcsCapsCount
1768                     + ", MMTEL Count = " + stat.mmtelCapsCount
1769                     + ", NoCaps Count = " + stat.noCapsCount
1770                     + ", Count = " + stat.count);
1771         }
1772         pw.decreaseIndent();
1773 
1774         pw.println("GbaEvent:");
1775         pw.increaseIndent();
1776         for (GbaEvent stat : metricAtoms.gbaEvent) {
1777             pw.println("[" + stat.carrierId + "]"
1778                     + " [" + stat.slotId + "]"
1779                     + " Successful = "  + stat.successful
1780                     + ", Fail Reason = " + stat.failedReason
1781                     + ", Count = " + stat.count);
1782         }
1783         pw.decreaseIndent();
1784     }
1785 }
1786