• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.carrierconfig;
2 
3 import android.Manifest;
4 import android.annotation.NonNull;
5 import android.content.Context;
6 import android.content.res.AssetManager;
7 import android.content.res.Resources;
8 import android.database.Cursor;
9 import android.os.PersistableBundle;
10 import android.provider.Telephony;
11 import android.service.carrier.CarrierIdentifier;
12 import android.telephony.CarrierConfigManager;
13 import android.telephony.TelephonyManager;
14 import android.test.InstrumentationTestCase;
15 import android.util.Log;
16 
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.lang.reflect.Field;
20 import java.lang.reflect.Modifier;
21 import java.util.ArrayList;
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Set;
25 
26 import junit.framework.AssertionFailedError;
27 
28 import org.xmlpull.v1.XmlPullParser;
29 import org.xmlpull.v1.XmlPullParserException;
30 import org.xmlpull.v1.XmlPullParserFactory;
31 
32 public class CarrierConfigTest extends InstrumentationTestCase {
33     private static final String TAG = "CarrierConfigTest";
34 
35     /**
36      * Iterate over all XML files in assets/ and ensure they parse without error.
37      */
testAllFilesParse()38     public void testAllFilesParse() {
39         forEachConfigXml(new ParserChecker() {
40             public void check(XmlPullParser parser, String mccmnc) throws XmlPullParserException,
41                     IOException {
42                 PersistableBundle b = DefaultCarrierConfigService.readConfigFromXml(parser,
43                         new CarrierIdentifier("001", "001", "Test", "001001123456789", "", ""), "");
44                 assertNotNull("got null bundle", b);
45             }
46         });
47     }
48 
49     /**
50      * Check that the config bundles in XML files have valid filter attributes.
51      * This checks the attribute names only.
52      */
testFilterValidAttributes()53     public void testFilterValidAttributes() {
54         forEachConfigXml(new ParserChecker() {
55             public void check(XmlPullParser parser, String mccmnc) throws XmlPullParserException,
56                     IOException {
57                 int event;
58                 while (((event = parser.next()) != XmlPullParser.END_DOCUMENT)) {
59                     if (event == XmlPullParser.START_TAG
60                             && "carrier_config".equals(parser.getName())) {
61                         for (int i = 0; i < parser.getAttributeCount(); ++i) {
62                             String attribute = parser.getAttributeName(i);
63                             switch (attribute) {
64                                 case "mcc":
65                                 case "mnc":
66                                 case "gid1":
67                                 case "gid2":
68                                 case "spn":
69                                 case "imsi":
70                                 case "device":
71                                 case "cid":
72                                 case "name":
73                                 case "sku":
74                                     break;
75                                 default:
76                                     fail("Unknown attribute '" + attribute
77                                             + "' at " + parser.getPositionDescription());
78                                     break;
79                             }
80                         }
81                     }
82                 }
83             }
84         });
85     }
86 
87     /**
88      * Check that XML files named after mccmnc are those without matching carrier id.
89      * If there is a matching carrier id, all configurations should move to carrierid.xml which
90      * has a higher matching priority than mccmnc.xml
91      */
testCarrierConfigFileNaming()92     public void testCarrierConfigFileNaming() {
93         forEachConfigXml(new ParserChecker() {
94             public void check(XmlPullParser parser, String mccmnc) throws XmlPullParserException,
95                     IOException {
96                 if (mccmnc == null) {
97                     // only check file named after mccmnc
98                     return;
99                 }
100                 int event;
101                 while (((event = parser.next()) != XmlPullParser.END_DOCUMENT)) {
102                     if (event == XmlPullParser.START_TAG
103                             && "carrier_config".equals(parser.getName())) {
104                         String mcc = null;
105                         String mnc = null;
106                         String spn = null;
107                         String gid1 = null;
108                         String gid2 = null;
109                         String imsi = null;
110                         for (int i = 0; i < parser.getAttributeCount(); ++i) {
111                             String attribute = parser.getAttributeName(i);
112                             switch (attribute) {
113                                 case "mcc":
114                                     mcc = parser.getAttributeValue(i);
115                                     break;
116                                 case "mnc":
117                                     mnc = parser.getAttributeValue(i);
118                                     break;
119                                 case "gid1":
120                                     gid1 = parser.getAttributeValue(i);
121                                     break;
122                                 case "gid2":
123                                     gid2 = parser.getAttributeValue(i);
124                                     break;
125                                 case "spn":
126                                     spn = parser.getAttributeValue(i);
127                                     break;
128                                 case "imsi":
129                                     imsi = parser.getAttributeValue(i);
130                                     break;
131                                 default:
132                                     fail("Unknown attribute '" + attribute
133                                             + "' at " + parser.getPositionDescription());
134                                     break;
135                             }
136                         }
137                         mcc = (mcc != null) ? mcc : mccmnc.substring(0, 3);
138                         mnc = (mnc != null) ? mnc : mccmnc.substring(3);
139                         // check if there is a valid carrier id
140                         int carrierId = getCarrierId(getInstrumentation().getTargetContext(),
141                                 new CarrierIdentifier(mcc, mnc, spn, imsi, gid1, gid2));
142                         if (carrierId != TelephonyManager.UNKNOWN_CARRIER_ID) {
143                             fail("unexpected carrier_config_mccmnc.xml with matching carrier id: "
144                                     + carrierId + ", please move to carrier_config_carrierid.xml");
145                         }
146                     }
147                 }
148             }
149         });
150     }
151 
152     /**
153      * Tests that the variable names in each XML file match actual keys in CarrierConfigManager.
154      */
testVariableNames()155     public void testVariableNames() {
156         final Set<String> varXmlNames = getCarrierConfigXmlNames();
157         // organize them into sets by type or unknown
158         forEachConfigXml(new ParserChecker() {
159             public void check(XmlPullParser parser, String mccmnc) throws XmlPullParserException,
160                     IOException {
161                 int event;
162                 while (((event = parser.next()) != XmlPullParser.END_DOCUMENT)) {
163                     if (event == XmlPullParser.START_TAG) {
164                         switch (parser.getName()) {
165                             case "int-array":
166                             case "string-array":
167                                 // string-array and int-array require the 'num' attribute
168                                 final String varNum = parser.getAttributeValue(null, "num");
169                                 assertNotNull("No 'num' attribute in array: "
170                                         + parser.getPositionDescription(), varNum);
171                             case "int":
172                             case "long":
173                             case "boolean":
174                             case "string":
175                                 // NOTE: This doesn't check for other valid Bundle values, but it
176                                 // is limited to the key types in CarrierConfigManager.
177                                 final String varName = parser.getAttributeValue(null, "name");
178                                 assertNotNull("No 'name' attribute: "
179                                         + parser.getPositionDescription(), varName);
180                                 assertTrue("Unknown variable: '" + varName
181                                         + "' at " + parser.getPositionDescription(),
182                                         varXmlNames.contains(varName));
183                                 // TODO: Check that the type is correct.
184                                 break;
185                             case "carrier_config_list":
186                             case "item":
187                             case "carrier_config":
188                                 // do nothing
189                                 break;
190                             default:
191                                 fail("unexpected tag: '" + parser.getName()
192                                         + "' at " + parser.getPositionDescription());
193                                 break;
194                         }
195                     }
196                 }
197             }
198         });
199     }
200 
201     /**
202      * Utility for iterating over each XML document in the assets folder.
203      *
204      * This can be used with {@link #forEachConfigXml} to run checks on each XML document.
205      * {@link #check} should {@link #fail} if the test does not pass.
206      */
207     private interface ParserChecker {
check(XmlPullParser parser, String mccmnc)208         void check(XmlPullParser parser, String mccmnc) throws XmlPullParserException, IOException;
209     }
210 
211     /**
212      * Utility for iterating over each XML document in the assets folder.
213      */
forEachConfigXml(ParserChecker checker)214     private void forEachConfigXml(ParserChecker checker) {
215         AssetManager assetMgr = getInstrumentation().getTargetContext().getAssets();
216         String mccmnc = null;
217         try {
218             String[] files = assetMgr.list("");
219             assertNotNull("failed to list files", files);
220             assertTrue("no files", files.length > 0);
221             for (String fileName : files) {
222                 try {
223                     if (!fileName.startsWith("carrier_config_")) continue;
224                     if (fileName.startsWith("carrier_config_mccmnc_")) {
225                         mccmnc = fileName.substring("carrier_config_mccmnc_".length(),
226                                 fileName.indexOf(".xml"));
227 
228                     }
229                     XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
230                     XmlPullParser parser = factory.newPullParser();
231                     parser.setInput(assetMgr.open(fileName), "utf-8");
232 
233                     checker.check(parser, mccmnc);
234 
235                 } catch (Throwable e) {
236                     throw new AssertionError("Problem in " + fileName + ": " + e.getMessage(), e);
237                 }
238             }
239             // Check vendor.xml too
240             try {
241                 Resources res = getInstrumentation().getTargetContext().getResources();
242                 checker.check(res.getXml(R.xml.vendor), mccmnc);
243             } catch (Throwable e) {
244                 throw new AssertionError("Problem in vendor.xml: " + e.getMessage(), e);
245             }
246         } catch (IOException e) {
247             fail(e.toString());
248         }
249     }
250 
251     /**
252      * Get the set of config variable names, as used in XML files.
253      */
getCarrierConfigXmlNames()254     private Set<String> getCarrierConfigXmlNames() {
255         Set<String> names = new HashSet<>();
256         // get values of all KEY_ members of CarrierConfigManager as well as its nested classes.
257         names.addAll(getCarrierConfigXmlNames(CarrierConfigManager.class));
258         for (Class nested : CarrierConfigManager.class.getDeclaredClasses()) {
259             Log.i("CarrierConfigTest", nested.toString());
260             if (Modifier.isStatic(nested.getModifiers())) {
261                 names.addAll(getCarrierConfigXmlNames(nested));
262             }
263         }
264         return names;
265     }
266 
getCarrierConfigXmlNames(Class clazz)267     private Set<String> getCarrierConfigXmlNames(Class clazz) {
268         // get values of all KEY_ members of clazz
269         Field[] fields = clazz.getDeclaredFields();
270         HashSet<String> varXmlNames = new HashSet<>();
271         for (Field f : fields) {
272             if (!f.getName().startsWith("KEY_")) continue;
273             if (!Modifier.isStatic(f.getModifiers())) {
274                 fail("non-static key in " + clazz.getName() + ":" + f.toString());
275             }
276             try {
277                 String value = (String) f.get(null);
278                 varXmlNames.add(value);
279             }
280             catch (IllegalAccessException e) {
281                 throw new AssertionError("Failed to get config key: " + e.getMessage(), e);
282             }
283         }
284         assertTrue("Found zero keys", varXmlNames.size() > 0);
285         return varXmlNames;
286     }
287 
288     // helper function to get carrier id from carrierIdentifier
getCarrierId(@onNull Context context, @NonNull CarrierIdentifier carrierIdentifier)289     private int getCarrierId(@NonNull Context context,
290                              @NonNull CarrierIdentifier carrierIdentifier) {
291         try {
292             getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
293                     Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
294             List<String> args = new ArrayList<>();
295             args.add(carrierIdentifier.getMcc() + carrierIdentifier.getMnc());
296             if (carrierIdentifier.getGid1() != null) {
297                 args.add(carrierIdentifier.getGid1());
298             }
299             if (carrierIdentifier.getGid2() != null) {
300                 args.add(carrierIdentifier.getGid2());
301             }
302             if (carrierIdentifier.getImsi() != null) {
303                 args.add(carrierIdentifier.getImsi());
304             }
305             if (carrierIdentifier.getSpn() != null) {
306                 args.add(carrierIdentifier.getSpn());
307             }
308             try (Cursor cursor = context.getContentResolver().query(
309                     Telephony.CarrierId.All.CONTENT_URI,
310                     /* projection */ null,
311                     /* selection */ Telephony.CarrierId.All.MCCMNC + "=? AND "
312                             + Telephony.CarrierId.All.GID1
313                             + ((carrierIdentifier.getGid1() == null) ? " is NULL" : "=?") + " AND "
314                             + Telephony.CarrierId.All.GID2
315                             + ((carrierIdentifier.getGid2() == null) ? " is NULL" : "=?") + " AND "
316                             + Telephony.CarrierId.All.IMSI_PREFIX_XPATTERN
317                             + ((carrierIdentifier.getImsi() == null) ? " is NULL" : "=?") + " AND "
318                             + Telephony.CarrierId.All.SPN
319                             + ((carrierIdentifier.getSpn() == null) ? " is NULL" : "=?"),
320                 /* selectionArgs */ args.toArray(new String[args.size()]), null)) {
321                 if (cursor != null) {
322                     while (cursor.moveToNext()) {
323                         return cursor.getInt(cursor.getColumnIndex(Telephony.CarrierId.CARRIER_ID));
324                     }
325                 }
326             }
327         } catch (SecurityException e) {
328             fail("Should be able to access APIs protected by a permission apps cannot get");
329         } finally {
330             getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
331         }
332         return TelephonyManager.UNKNOWN_CARRIER_ID;
333     }
334 }
335