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