1 /*
2  * Copyright (C) 2018 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.dataconnection;
18 
19 import android.content.Context;
20 import android.os.PersistableBundle;
21 import android.telephony.Annotation.ApnType;
22 import android.telephony.CarrierConfigManager;
23 import android.telephony.data.ApnSetting;
24 import android.util.Log;
25 
26 import com.android.internal.telephony.Phone;
27 import com.android.internal.telephony.uicc.IccRecords;
28 import com.android.telephony.Rlog;
29 
30 import java.util.Arrays;
31 import java.util.HashSet;
32 
33 /**
34  * This class represents a apn setting for create PDP link
35  */
36 public class ApnSettingUtils {
37 
38     static final String LOG_TAG = "ApnSetting";
39 
40     private static final boolean DBG = false;
41 
iccidMatches(String mvnoData, String iccId)42     private static boolean iccidMatches(String mvnoData, String iccId) {
43         String[] mvnoIccidList = mvnoData.split(",");
44         for (String mvnoIccid : mvnoIccidList) {
45             if (iccId.startsWith(mvnoIccid)) {
46                 Log.d(LOG_TAG, "mvno icc id match found");
47                 return true;
48             }
49         }
50         return false;
51     }
52 
imsiMatches(String imsiDB, String imsiSIM)53     private static boolean imsiMatches(String imsiDB, String imsiSIM) {
54         // Note: imsiDB value has digit number or 'x' character for seperating USIM information
55         // for MVNO operator. And then digit number is matched at same order and 'x' character
56         // could replace by any digit number.
57         // ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
58         //     that means first 6 digits, 8th and 9th digit
59         //     should be set in USIM for GG Operator.
60         int len = imsiDB.length();
61 
62         if (len <= 0) return false;
63         if (len > imsiSIM.length()) return false;
64 
65         for (int idx = 0; idx < len; idx++) {
66             char c = imsiDB.charAt(idx);
67             if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) {
68                 continue;
69             } else {
70                 return false;
71             }
72         }
73         return true;
74     }
75 
76     /**
77      * Check if MVNO type and data match IccRecords.
78      *
79      * @param r the IccRecords
80      * @param mvnoType the MVNO type
81      * @param mvnoMatchData the MVNO match data
82      * @return {@code true} if MVNO type and data match IccRecords, {@code false} otherwise.
83      */
mvnoMatches(IccRecords r, int mvnoType, String mvnoMatchData)84     public static boolean mvnoMatches(IccRecords r, int mvnoType, String mvnoMatchData) {
85         if (mvnoType == ApnSetting.MVNO_TYPE_SPN) {
86             String spn = r.getServiceProviderNameWithBrandOverride();
87             if ((spn != null) && spn.equalsIgnoreCase(mvnoMatchData)) {
88                 return true;
89             }
90         } else if (mvnoType == ApnSetting.MVNO_TYPE_IMSI) {
91             String imsiSIM = r.getIMSI();
92             if ((imsiSIM != null) && imsiMatches(mvnoMatchData, imsiSIM)) {
93                 return true;
94             }
95         } else if (mvnoType == ApnSetting.MVNO_TYPE_GID) {
96             String gid1 = r.getGid1();
97             int mvno_match_data_length = mvnoMatchData.length();
98             if ((gid1 != null) && (gid1.length() >= mvno_match_data_length)
99                     && gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvnoMatchData)) {
100                 return true;
101             }
102         } else if (mvnoType == ApnSetting.MVNO_TYPE_ICCID) {
103             String iccId = r.getIccId();
104             if ((iccId != null) && iccidMatches(mvnoMatchData, iccId)) {
105                 return true;
106             }
107         }
108 
109         return false;
110     }
111 
112     /**
113      * Check if this APN type is metered.
114      *
115      * @param apnType the APN type
116      * @param phone the phone object
117      * @return {@code true} if the APN type is metered, {@code false} otherwise.
118      */
isMeteredApnType(@pnType int apnType, Phone phone)119     public static boolean isMeteredApnType(@ApnType int apnType, Phone phone) {
120         if (phone == null) {
121             return true;
122         }
123 
124         boolean isRoaming = phone.getServiceState().getDataRoaming();
125         int subId = phone.getSubId();
126 
127         String carrierConfig;
128         // First check if the device is roaming. If yes, use the roaming metered APN list.
129         // Otherwise use the normal metered APN list.
130         if (isRoaming) {
131             carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS;
132         } else {
133             carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS;
134         }
135 
136         if (DBG) {
137             Rlog.d(LOG_TAG, "isMeteredApnType: isRoaming=" + isRoaming);
138         }
139 
140         CarrierConfigManager configManager = (CarrierConfigManager)
141                 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
142         if (configManager == null) {
143             Rlog.e(LOG_TAG, "Carrier config service is not available");
144             return true;
145         }
146 
147         PersistableBundle b = configManager.getConfigForSubId(subId);
148         if (b == null) {
149             Rlog.e(LOG_TAG, "Can't get the config. subId = " + subId);
150             return true;
151         }
152 
153         String[] meteredApnTypes = b.getStringArray(carrierConfig);
154         if (meteredApnTypes == null) {
155             Rlog.e(LOG_TAG, carrierConfig +  " is not available. " + "subId = " + subId);
156             return true;
157         }
158 
159         HashSet<String> meteredApnSet = new HashSet<>(Arrays.asList(meteredApnTypes));
160         if (DBG) {
161             Rlog.d(LOG_TAG, "For subId = " + subId + ", metered APN types are "
162                     + Arrays.toString(meteredApnSet.toArray()));
163         }
164 
165         if (meteredApnSet.contains(ApnSetting.getApnTypeString(apnType))) {
166             if (DBG) Rlog.d(LOG_TAG, ApnSetting.getApnTypeString(apnType) + " is metered.");
167             return true;
168         } else if (apnType == ApnSetting.TYPE_ALL) {
169             // Assuming no configuration error, if at least one APN type is
170             // metered, then this APN setting is metered.
171             if (meteredApnSet.size() > 0) {
172                 if (DBG) Rlog.d(LOG_TAG, "APN_TYPE_ALL APN is metered.");
173                 return true;
174             }
175         }
176 
177         if (DBG) Rlog.d(LOG_TAG, ApnSetting.getApnTypeString(apnType) + " is not metered.");
178         return false;
179     }
180 
181     /**
182      * Check if this APN setting is metered.
183      *
184      * @param apn APN setting
185      * @param phone The phone object
186      * @return True if this APN setting is metered, otherwise false.
187      */
isMetered(ApnSetting apn, Phone phone)188     public static boolean isMetered(ApnSetting apn, Phone phone) {
189         if (phone == null || apn == null) {
190             return true;
191         }
192 
193         for (int apnType : apn.getApnTypes()) {
194             // If one of the APN type is metered, then this APN setting is metered.
195             if (isMeteredApnType(apnType, phone)) {
196                 return true;
197             }
198         }
199         return false;
200     }
201 }
202