1 /*
2  * Copyright (C) 2023 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 com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_EMERGENCY;
20 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_NORMAL;
21 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_UNKNOWN;
22 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_AIEC;
23 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_AMBULANCE;
24 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE;
25 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD;
26 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MIEC;
27 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE;
28 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_POLICE;
29 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
30 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_DATABASE;
31 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_DEFAULT;
32 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG;
33 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING;
34 import static com.android.internal.telephony.TelephonyStatsLog.EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_SIM;
35 
36 import android.telephony.emergency.EmergencyNumber;
37 import android.util.SparseIntArray;
38 
39 import com.android.internal.telephony.nano.PersistAtomsProto;
40 
41 import java.util.ArrayList;
42 import java.util.List;
43 
44 /**
45  * EmergencyStats logs the atoms for consolidated emergency number list in framework. It also logs
46  * the details of a dialed emergency number. To avoid repeated information this class stores the
47  * emergency numbers list in map and verifies the information for duplicacy before logging it. Note:
48  * This locally stored information will erase on process restart scenarios (like reboot, crash,
49  * etc.).
50  */
51 public class EmergencyNumberStats {
52 
53     private static final String TAG = EmergencyNumberStats.class.getSimpleName();
54     private static final SparseIntArray sRoutesMap;
55     private static final SparseIntArray sServiceCategoriesMap;
56     private static final SparseIntArray sSourcesMap;
57     private static EmergencyNumberStats sInstance;
58 
59     static {
60         sRoutesMap = new SparseIntArray() {
61             {
62                 put(EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY,
63                         EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_EMERGENCY);
64                 put(EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL,
65                         EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_NORMAL);
66                 put(EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN,
67                         EMERGENCY_NUMBERS_INFO__ROUTE__EMERGENCY_CALL_ROUTE_UNKNOWN);
68             }
69         };
70 
71         sServiceCategoriesMap = new SparseIntArray() {
72             {
73                 put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
74                         EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
75                 put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
76                         EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_POLICE);
77                 put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
78                         EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
79                 put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
80                         EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE);
81                 put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD,
82                         EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD);
83                 put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE,
84                         EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE);
85                 put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC,
86                         EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_MIEC);
87                 put(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC,
88                         EMERGENCY_NUMBERS_INFO__SERVICE_CATEGORIES__EMERGENCY_SERVICE_CATEGORY_AIEC);
89             }
90         };
91 
92         sSourcesMap = new SparseIntArray() {
93             {
94                 put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
95                         EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
96                 put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM,
97                         EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_SIM);
98                 put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE,
99                         EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_DATABASE);
100                 put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
101                         EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
102                 put(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT,
103                         EMERGENCY_NUMBERS_INFO__SOURCES__EMERGENCY_NUMBER_SOURCE_DEFAULT);
104             }
105         };
106     }
107 
EmergencyNumberStats()108     private EmergencyNumberStats() {
109     }
110 
111     /** Static method to provide singleton instance for EmergencyNumberStats. */
getInstance()112     public static EmergencyNumberStats getInstance() {
113         if (sInstance == null) {
114             sInstance = new EmergencyNumberStats();
115         }
116         return sInstance;
117     }
118 
119     /**
120      * It converts the {@link android.telephony.emergency.EmergencyNumber} to
121      * {@link PersistAtomsProto.EmergencyNumber} for
122      * logging the EmergencyNumber atoms with pulled event.
123      *
124      * @param emergencyNumberList android.telephony.EmergencyNumber list
125      * @param assetVersion        assert version
126      * @param otaVersion          ota version
127      * @param isDbRoutingIgnored  flag that defines if routing is ignored through database.
128      */
convertEmergencyNumbersListToProto( List<EmergencyNumber> emergencyNumberList, int assetVersion, int otaVersion, boolean isDbRoutingIgnored)129     public PersistAtomsProto.EmergencyNumbersInfo[] convertEmergencyNumbersListToProto(
130             List<EmergencyNumber> emergencyNumberList, int assetVersion, int otaVersion,
131             boolean isDbRoutingIgnored) {
132         List<PersistAtomsProto.EmergencyNumbersInfo> numberProtoList = new ArrayList<>();
133         for (EmergencyNumber number : emergencyNumberList) {
134             numberProtoList.add(convertEmergencyNumberToProto(number, assetVersion, otaVersion,
135                     isDbRoutingIgnored));
136         }
137         return numberProtoList.toArray(new PersistAtomsProto.EmergencyNumbersInfo[0]);
138     }
139 
convertEmergencyNumberToProto( EmergencyNumber number, int assetVer, int otaVer, boolean isDbRoutingIgnored)140     private PersistAtomsProto.EmergencyNumbersInfo convertEmergencyNumberToProto(
141             EmergencyNumber number, int assetVer, int otaVer, boolean isDbRoutingIgnored) {
142         String dialNumber = number.getNumber();
143         PersistAtomsProto.EmergencyNumbersInfo emergencyNumber =
144                 new PersistAtomsProto.EmergencyNumbersInfo();
145         emergencyNumber.isDbVersionIgnored = isDbRoutingIgnored;
146         emergencyNumber.assetVersion = assetVer;
147         emergencyNumber.otaVersion = otaVer;
148         emergencyNumber.number = dialNumber;
149         emergencyNumber.countryIso = number.getCountryIso();
150         emergencyNumber.mnc = number.getMnc();
151         emergencyNumber.route = sRoutesMap.get(number.getEmergencyCallRouting());
152         emergencyNumber.urns = number.getEmergencyUrns().toArray(new String[0]);
153         emergencyNumber.serviceCategories = getMappedServiceCategories(
154                 number.getEmergencyServiceCategories());
155         emergencyNumber.sources = getMappedSources(number.getEmergencyNumberSources());
156         return emergencyNumber;
157     }
158 
getMappedServiceCategories(List<Integer> serviceCategories)159     private int[] getMappedServiceCategories(List<Integer> serviceCategories) {
160         if (serviceCategories == null || serviceCategories.isEmpty()) {
161             return null;
162         }
163         return serviceCategories.stream().map(sServiceCategoriesMap::get).mapToInt(
164                 Integer::intValue).toArray();
165     }
166 
getMappedSources(List<Integer> sources)167     private int[] getMappedSources(List<Integer> sources) {
168         if (sources == null || sources.isEmpty()) {
169             return null;
170         }
171         return sources.stream().map(sSourcesMap::get).mapToInt(Integer::intValue).toArray();
172     }
173 }
174