1 /*
2  * Copyright (C) 2022 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.provider.Telephony.Carriers.CONTENT_URI;
20 import static android.telephony.PhoneNumberUtils.areSamePhoneNumber;
21 
22 import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_A;
23 import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_B;
24 import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_C;
25 import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_UNKNOWN;
26 import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__WFC_MODE__CELLULAR_PREFERRED;
27 import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__WFC_MODE__UNKNOWN;
28 import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__WFC_MODE__WIFI_ONLY;
29 import static com.android.internal.telephony.TelephonyStatsLog.PER_SIM_STATUS__WFC_MODE__WIFI_PREFERRED;
30 
31 import android.annotation.Nullable;
32 import android.database.Cursor;
33 import android.net.Uri;
34 import android.provider.Telephony;
35 import android.telephony.AnomalyReporter;
36 import android.telephony.SubscriptionManager;
37 import android.telephony.TelephonyManager;
38 import android.telephony.data.ApnSetting;
39 import android.telephony.ims.ImsManager;
40 import android.telephony.ims.ImsMmTelManager;
41 import android.text.TextUtils;
42 
43 import com.android.internal.telephony.IccCard;
44 import com.android.internal.telephony.Phone;
45 import com.android.internal.telephony.PhoneFactory;
46 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
47 import com.android.internal.telephony.subscription.SubscriptionManagerService;
48 import com.android.internal.telephony.uicc.UiccController;
49 import com.android.internal.telephony.uicc.UiccSlot;
50 import com.android.telephony.Rlog;
51 
52 import java.util.UUID;
53 
54 /** Stores the per SIM status. */
55 public class PerSimStatus {
56     private static final String TAG = "PerSimStatus";
57 
58     private static final long BITMASK_2G =
59             TelephonyManager.NETWORK_TYPE_BITMASK_GSM
60                     | TelephonyManager.NETWORK_TYPE_BITMASK_GPRS
61                     | TelephonyManager.NETWORK_TYPE_BITMASK_EDGE
62                     | TelephonyManager.NETWORK_TYPE_BITMASK_CDMA
63                     | TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;
64 
65     private static final UUID CROSS_SIM_CALLING_STATUS_ANOMALY_UUID =
66             UUID.fromString("377e1a33-d4ac-4039-9cc0-f0d8396757f3");
67 
68     public final int carrierId;
69     public final int phoneNumberSourceUicc;
70     public final int phoneNumberSourceCarrier;
71     public final int phoneNumberSourceIms;
72     public final boolean advancedCallingSettingEnabled;
73     public final boolean voWiFiSettingEnabled;
74     public final int voWiFiModeSetting;
75     public final int voWiFiRoamingModeSetting;
76     public final boolean vtSettingEnabled;
77     public final boolean dataRoamingEnabled;
78     public final long preferredNetworkType;
79     public final boolean disabled2g;
80     public final boolean pin1Enabled;
81     public final int minimumVoltageClass;
82     public final int userModifiedApnTypes;
83     public final long unmeteredNetworks;
84     public final boolean vonrEnabled;
85 
86     public final boolean crossSimCallingEnabled;
87 
88     /** Returns the current sim status of the given {@link Phone}. */
89     @Nullable
getCurrentState(Phone phone)90     public static PerSimStatus getCurrentState(Phone phone) {
91         int[] numberIds = getNumberIds(phone);
92         if (numberIds == null) return null;
93         int carrierId = phone.getCarrierId();
94         ImsMmTelManager imsMmTelManager = getImsMmTelManager(phone);
95         IccCard iccCard = phone.getIccCard();
96         PersistAtomsStorage persistAtomsStorage =
97                 PhoneFactory.getMetricsCollector().getAtomsStorage();
98         return new PerSimStatus(
99                 carrierId,
100                 numberIds[0],
101                 numberIds[1],
102                 numberIds[2],
103                 imsMmTelManager == null ? false : imsMmTelManager.isAdvancedCallingSettingEnabled(),
104                 imsMmTelManager == null ? false : imsMmTelManager.isVoWiFiSettingEnabled(),
105                 imsMmTelManager == null
106                         ? PER_SIM_STATUS__WFC_MODE__UNKNOWN
107                         : wifiCallingModeToProtoEnum(imsMmTelManager.getVoWiFiModeSetting()),
108                 imsMmTelManager == null
109                         ? PER_SIM_STATUS__WFC_MODE__UNKNOWN
110                         : wifiCallingModeToProtoEnum(imsMmTelManager.getVoWiFiRoamingModeSetting()),
111                 imsMmTelManager == null ? false : imsMmTelManager.isVtSettingEnabled(),
112                 phone.getDataRoamingEnabled(),
113                 phone.getAllowedNetworkTypes(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER),
114                 is2gDisabled(phone),
115                 iccCard == null ? false : iccCard.getIccLockEnabled(),
116                 getMinimumVoltageClass(phone),
117                 getUserModifiedApnTypes(phone),
118                 persistAtomsStorage.getUnmeteredNetworks(phone.getPhoneId(), carrierId),
119                 isVonrEnabled(phone),
120                 isCrossSimCallingEnabled(imsMmTelManager));
121     }
122 
PerSimStatus( int carrierId, int phoneNumberSourceUicc, int phoneNumberSourceCarrier, int phoneNumberSourceIms, boolean advancedCallingSettingEnabled, boolean voWiFiSettingEnabled, int voWiFiModeSetting, int voWiFiRoamingModeSetting, boolean vtSettingEnabled, boolean dataRoamingEnabled, long preferredNetworkType, boolean disabled2g, boolean pin1Enabled, int minimumVoltageClass, int userModifiedApnTypes, long unmeteredNetworks, boolean vonrEnabled, boolean crossSimCallingEnabled)123     private PerSimStatus(
124             int carrierId,
125             int phoneNumberSourceUicc,
126             int phoneNumberSourceCarrier,
127             int phoneNumberSourceIms,
128             boolean advancedCallingSettingEnabled,
129             boolean voWiFiSettingEnabled,
130             int voWiFiModeSetting,
131             int voWiFiRoamingModeSetting,
132             boolean vtSettingEnabled,
133             boolean dataRoamingEnabled,
134             long preferredNetworkType,
135             boolean disabled2g,
136             boolean pin1Enabled,
137             int minimumVoltageClass,
138             int userModifiedApnTypes,
139             long unmeteredNetworks,
140             boolean vonrEnabled,
141             boolean crossSimCallingEnabled) {
142         this.carrierId = carrierId;
143         this.phoneNumberSourceUicc = phoneNumberSourceUicc;
144         this.phoneNumberSourceCarrier = phoneNumberSourceCarrier;
145         this.phoneNumberSourceIms = phoneNumberSourceIms;
146         this.advancedCallingSettingEnabled = advancedCallingSettingEnabled;
147         this.voWiFiSettingEnabled = voWiFiSettingEnabled;
148         this.voWiFiModeSetting = voWiFiModeSetting;
149         this.voWiFiRoamingModeSetting = voWiFiRoamingModeSetting;
150         this.vtSettingEnabled = vtSettingEnabled;
151         this.dataRoamingEnabled = dataRoamingEnabled;
152         this.preferredNetworkType = preferredNetworkType;
153         this.disabled2g = disabled2g;
154         this.pin1Enabled = pin1Enabled;
155         this.minimumVoltageClass = minimumVoltageClass;
156         this.userModifiedApnTypes = userModifiedApnTypes;
157         this.unmeteredNetworks = unmeteredNetworks;
158         this.vonrEnabled = vonrEnabled;
159         this.crossSimCallingEnabled = crossSimCallingEnabled;
160     }
161 
isCrossSimCallingEnabled(ImsMmTelManager imsMmTelManager)162     private static boolean isCrossSimCallingEnabled(ImsMmTelManager imsMmTelManager) {
163         try {
164             return imsMmTelManager != null && imsMmTelManager.isCrossSimCallingEnabled();
165         } catch (Exception e) {
166             AnomalyReporter.reportAnomaly(CROSS_SIM_CALLING_STATUS_ANOMALY_UUID,
167                     "Failed to query ImsMmTelManager for cross-SIM calling status!");
168             Rlog.e(TAG, e.getMessage());
169         }
170         return false;
171     }
172 
173     @Nullable
getImsMmTelManager(Phone phone)174     private static ImsMmTelManager getImsMmTelManager(Phone phone) {
175         ImsManager imsManager = phone.getContext().getSystemService(ImsManager.class);
176         if (imsManager == null) {
177             return null;
178         }
179         try {
180             return imsManager.getImsMmTelManager(phone.getSubId());
181         } catch (IllegalArgumentException e) {
182             return null; // Invalid subId
183         }
184     }
185 
186     /**
187      * Returns an array of integer ids representing phone numbers. If number is empty then id will
188      * be 0. Two same numbers will have same id, and different numbers will have different ids. For
189      * example, [1, 0, 1] means that uicc and ims numbers are the same while carrier number is empty
190      * and [1, 2, 3] means all numbers are different.
191      *
192      * <ul>
193      *   <li>Index 0: id associated with {@code PHONE_NUMBER_SOURCE_UICC}.</li>
194      *   <li>Index 1: id associated with {@code PHONE_NUMBER_SOURCE_CARRIER}.</li>
195      *   <li>Index 2: id associated with {@code PHONE_NUMBER_SOURCE_IMS}.</li>
196      * </ul>
197      */
198     @Nullable
getNumberIds(Phone phone)199     private static int[] getNumberIds(Phone phone) {
200         String countryIso = "";
201         String[] numbersFromAllSources;
202 
203         if (SubscriptionManagerService.getInstance() == null) return null;
204         SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
205                 .getSubscriptionInfoInternal(phone.getSubId());
206         if (subInfo != null) {
207             countryIso = subInfo.getCountryIso();
208         }
209         numbersFromAllSources = new String[]{
210                 SubscriptionManagerService.getInstance().getPhoneNumber(phone.getSubId(),
211                         SubscriptionManager.PHONE_NUMBER_SOURCE_UICC, null, null),
212                 SubscriptionManagerService.getInstance().getPhoneNumber(phone.getSubId(),
213                         SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER, null, null),
214                 SubscriptionManagerService.getInstance().getPhoneNumber(phone.getSubId(),
215                         SubscriptionManager.PHONE_NUMBER_SOURCE_IMS, null, null)
216         };
217 
218         int[] numberIds = new int[numbersFromAllSources.length]; // default value 0
219         for (int i = 0, idForNextUniqueNumber = 1; i < numberIds.length; i++) {
220             if (TextUtils.isEmpty(numbersFromAllSources[i])) {
221                 // keep id 0 if number not available
222                 continue;
223             }
224             // the number is available:
225             // try to find the same number from other sources and reuse the id
226             for (int j = 0; j < i; j++) {
227                 if (!TextUtils.isEmpty(numbersFromAllSources[j])
228                         && areSamePhoneNumber(
229                                 numbersFromAllSources[i], numbersFromAllSources[j], countryIso)) {
230                     numberIds[i] = numberIds[j];
231                 }
232             }
233             // didn't find same number (otherwise should not be id 0), assign a new id
234             if (numberIds[i] == 0) {
235                 numberIds[i] = idForNextUniqueNumber++;
236             }
237         }
238         return numberIds;
239     }
240 
241     /**
242      * Returns {@code true} if 2G cellular network is disabled (Allow 2G toggle in the settings).
243      */
is2gDisabled(Phone phone)244     private static boolean is2gDisabled(Phone phone) {
245         return (phone.getAllowedNetworkTypes(
246                                 TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G)
247                         & BITMASK_2G)
248                 == 0;
249     }
250 
251     /** Converts {@link ImsMmTelManager.WifiCallingMode} to the value of PerSimStatus WfcMode. */
wifiCallingModeToProtoEnum(@msMmTelManager.WiFiCallingMode int mode)252     private static int wifiCallingModeToProtoEnum(@ImsMmTelManager.WiFiCallingMode int mode) {
253         switch (mode) {
254             case ImsMmTelManager.WIFI_MODE_WIFI_ONLY:
255                 return PER_SIM_STATUS__WFC_MODE__WIFI_ONLY;
256             case ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED:
257                 return PER_SIM_STATUS__WFC_MODE__CELLULAR_PREFERRED;
258             case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED:
259                 return PER_SIM_STATUS__WFC_MODE__WIFI_PREFERRED;
260             default:
261                 return PER_SIM_STATUS__WFC_MODE__UNKNOWN;
262         }
263     }
264 
265     /** Returns the minimum voltage class supported by the UICC. */
getMinimumVoltageClass(Phone phone)266     private static int getMinimumVoltageClass(Phone phone) {
267         UiccSlot uiccSlot = UiccController.getInstance().getUiccSlotForPhone(phone.getPhoneId());
268         if (uiccSlot == null) {
269             return PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_UNKNOWN;
270         }
271         switch (uiccSlot.getMinimumVoltageClass()) {
272             case UiccSlot.VOLTAGE_CLASS_A:
273                 return PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_A;
274             case UiccSlot.VOLTAGE_CLASS_B:
275                 return PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_B;
276             case UiccSlot.VOLTAGE_CLASS_C:
277                 return PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_C;
278             default:
279                 return PER_SIM_STATUS__SIM_VOLTAGE_CLASS__VOLTAGE_CLASS_UNKNOWN;
280         }
281     }
282 
283     /** Returns the bitmask representing types of APNs modified by user. */
getUserModifiedApnTypes(Phone phone)284     private static int getUserModifiedApnTypes(Phone phone) {
285         String[] projections = {Telephony.Carriers.TYPE};
286         String selection = Telephony.Carriers.EDITED_STATUS + "=?";
287         String[] selectionArgs = {Integer.toString(Telephony.Carriers.USER_EDITED)};
288         try (Cursor cursor =
289                 phone.getContext()
290                         .getContentResolver()
291                         .query(
292                                 Uri.withAppendedPath(CONTENT_URI, "subId/" + phone.getSubId()),
293                                 projections,
294                                 selection,
295                                 selectionArgs,
296                                 null)) {
297             int bitmask = 0;
298             while (cursor != null && cursor.moveToNext()) {
299                 bitmask |= ApnSetting.getApnTypesBitmaskFromString(cursor.getString(0));
300             }
301             return bitmask;
302         }
303     }
304 
305     /** Returns true if VoNR is enabled */
isVonrEnabled(Phone phone)306     static boolean isVonrEnabled(Phone phone) {
307         TelephonyManager telephonyManager =
308                 phone.getContext()
309                         .getSystemService(TelephonyManager.class);
310         if (telephonyManager == null) {
311             return false;
312         }
313         telephonyManager = telephonyManager.createForSubscriptionId(phone.getSubId());
314         return telephonyManager.isVoNrEnabled();
315     }
316 }
317