1 /*
2  * Copyright (C) 2009 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 android.telephony.cts;
18 
19 import android.bluetooth.BluetoothAdapter;
20 import android.content.Context;
21 import android.content.pm.PackageManager;
22 import android.cts.util.ReadElf;
23 import android.cts.util.TestThread;
24 import android.net.ConnectivityManager;
25 import android.net.wifi.WifiInfo;
26 import android.net.wifi.WifiManager;
27 import android.os.Build;
28 import android.os.Looper;
29 import android.telephony.CellLocation;
30 import android.telephony.PhoneStateListener;
31 import android.telephony.TelephonyManager;
32 import android.test.AndroidTestCase;
33 import android.util.Log;
34 
35 import com.android.internal.telephony.PhoneConstants;
36 
37 import java.util.regex.Pattern;
38 
39 public class TelephonyManagerTest extends AndroidTestCase {
40     private TelephonyManager mTelephonyManager;
41     private boolean mOnCellLocationChangedCalled = false;
42     private final Object mLock = new Object();
43     private static final int TOLERANCE = 1000;
44     private PhoneStateListener mListener;
45     private static ConnectivityManager mCm;
46     private static final String TAG = "android.telephony.cts.TelephonyManagerTest";
47 
48     @Override
setUp()49     protected void setUp() throws Exception {
50         super.setUp();
51         mTelephonyManager =
52             (TelephonyManager)getContext().getSystemService(Context.TELEPHONY_SERVICE);
53         mCm = (ConnectivityManager)getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
54     }
55 
56     @Override
tearDown()57     protected void tearDown() throws Exception {
58         if (mListener != null) {
59             // unregister the listener
60             mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
61         }
62         super.tearDown();
63     }
64 
testListen()65     public void testListen() throws Throwable {
66         if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
67             Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
68             return;
69         }
70 
71         if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
72             // TODO: temp workaround, need to adjust test to for CDMA
73             return;
74         }
75 
76         // Test register
77         TestThread t = new TestThread(new Runnable() {
78             public void run() {
79                 Looper.prepare();
80 
81                 mListener = new PhoneStateListener() {
82                     @Override
83                     public void onCellLocationChanged(CellLocation location) {
84                         if(!mOnCellLocationChangedCalled) {
85                             synchronized (mLock) {
86                                 mOnCellLocationChangedCalled = true;
87                                 mLock.notify();
88                             }
89                         }
90                     }
91                 };
92                 mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_CELL_LOCATION);
93                 CellLocation.requestLocationUpdate();
94                 Looper.loop();
95             }
96         });
97         t.start();
98         synchronized (mLock) {
99             while (!mOnCellLocationChangedCalled) {
100                 mLock.wait();
101             }
102         }
103         assertTrue(mOnCellLocationChangedCalled);
104 
105         // Test unregister
106         t = new TestThread(new Runnable() {
107             public void run() {
108                 Looper.prepare();
109                 // unregister the listener
110                 mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
111                 mOnCellLocationChangedCalled = false;
112                 // unregister again, to make sure doing so does not call the listener
113                 mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
114                 CellLocation.requestLocationUpdate();
115                 Looper.loop();
116             }
117         });
118 
119         t.start();
120         synchronized (mLock) {
121             mLock.wait(TOLERANCE);
122         }
123         assertFalse(mOnCellLocationChangedCalled);
124     }
125 
126     /**
127      * The getter methods here are all related to the information about the telephony.
128      * These getters are related to concrete location, phone, service provider company, so
129      * it's no need to get details of these information, just make sure they are in right
130      * condition(>0 or not null).
131      */
testTelephonyManager()132     public void testTelephonyManager() {
133         assertTrue(mTelephonyManager.getNetworkType() >= TelephonyManager.NETWORK_TYPE_UNKNOWN);
134         assertTrue(mTelephonyManager.getPhoneType() >= TelephonyManager.PHONE_TYPE_NONE);
135         assertTrue(mTelephonyManager.getSimState() >= TelephonyManager.SIM_STATE_UNKNOWN);
136         assertTrue(mTelephonyManager.getDataActivity() >= TelephonyManager.DATA_ACTIVITY_NONE);
137         assertTrue(mTelephonyManager.getDataState() >= TelephonyManager.DATA_DISCONNECTED);
138         assertTrue(mTelephonyManager.getCallState() >= TelephonyManager.CALL_STATE_IDLE);
139 
140         // Make sure devices without MMS service won't fail on this
141         if (mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE) {
142             assertFalse(mTelephonyManager.getMmsUserAgent().isEmpty());
143             assertFalse(mTelephonyManager.getMmsUAProfUrl().isEmpty());
144         }
145 
146         // The following methods may return null. Simply call them to make sure they do not
147         // throw any exceptions.
148         mTelephonyManager.getVoiceMailNumber();
149         mTelephonyManager.getSimOperatorName();
150         mTelephonyManager.getNetworkCountryIso();
151         mTelephonyManager.getCellLocation();
152         mTelephonyManager.getSimSerialNumber();
153         mTelephonyManager.getSimOperator();
154         mTelephonyManager.getNetworkOperatorName();
155         mTelephonyManager.getSubscriberId();
156         mTelephonyManager.getLine1Number();
157         mTelephonyManager.getNetworkOperator();
158         mTelephonyManager.getSimCountryIso();
159         mTelephonyManager.getVoiceMailAlphaTag();
160         mTelephonyManager.getNeighboringCellInfo();
161         mTelephonyManager.isNetworkRoaming();
162         mTelephonyManager.getDeviceId();
163         mTelephonyManager.getDeviceId(mTelephonyManager.getDefaultSim());
164         mTelephonyManager.getDeviceSoftwareVersion();
165         mTelephonyManager.getPhoneCount();
166     }
167 
168     /**
169      * Tests that the phone count returned is valid.
170      */
testGetPhoneCount()171     public void testGetPhoneCount() {
172         int phoneCount = mTelephonyManager.getPhoneCount();
173         int phoneType = mTelephonyManager.getPhoneType();
174         switch (phoneType) {
175             case TelephonyManager.PHONE_TYPE_GSM:
176             case TelephonyManager.PHONE_TYPE_CDMA:
177                 assertTrue("Phone count should be > 0", phoneCount > 0);
178                 break;
179             case TelephonyManager.PHONE_TYPE_NONE:
180                 assertTrue("Phone count should be 0", phoneCount == 0 || phoneCount == 1);
181                 break;
182             default:
183                 throw new IllegalArgumentException("Did you add a new phone type? " + phoneType);
184         }
185     }
186 
187     /**
188      * Tests that the device properly reports either a valid IMEI if
189      * GSM, a valid MEID or ESN if CDMA, or a valid MAC address if
190      * only a WiFi device.
191      */
testGetDeviceId()192     public void testGetDeviceId() {
193         String deviceId = mTelephonyManager.getDeviceId();
194         verifyDeviceId(deviceId);
195     }
196 
197     /**
198      * Tests that the device properly reports either a valid IMEI if
199      * GSM, a valid MEID or ESN if CDMA, or a valid MAC address if
200      * only a WiFi device.
201      */
testGetDeviceIdForSlotId()202     public void testGetDeviceIdForSlotId() {
203         String deviceId = mTelephonyManager.getDeviceId(mTelephonyManager.getDefaultSim());
204         verifyDeviceId(deviceId);
205         // Also verify that no exception is thrown for any slot id (including invalid ones)
206         for (int i = -1; i <= mTelephonyManager.getPhoneCount(); i++) {
207             mTelephonyManager.getDeviceId(i);
208         }
209     }
210 
verifyDeviceId(String deviceId)211     private void verifyDeviceId(String deviceId) {
212         int phoneType = mTelephonyManager.getPhoneType();
213         switch (phoneType) {
214             case TelephonyManager.PHONE_TYPE_GSM:
215                 assertGsmDeviceId(deviceId);
216                 break;
217 
218             case TelephonyManager.PHONE_TYPE_CDMA:
219                 // LTE device is using IMEI as device id
220                 if (mTelephonyManager.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE) {
221                     assertGsmDeviceId(deviceId);
222                 } else {
223                     assertCdmaDeviceId(deviceId);
224                 }
225                 break;
226 
227             case TelephonyManager.PHONE_TYPE_NONE:
228                 if (mCm.getNetworkInfo(ConnectivityManager.TYPE_WIFI) != null) {
229                     assertSerialNumber();
230                     assertMacAddress(getWifiMacAddress());
231                 } else if (mCm.getNetworkInfo(ConnectivityManager.TYPE_BLUETOOTH) != null) {
232                     assertSerialNumber();
233                     assertMacAddress(getBluetoothMacAddress());
234                 } else {
235                     assertTrue(mCm.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET) != null);
236                 }
237                 break;
238 
239             default:
240                 throw new IllegalArgumentException("Did you add a new phone type? " + phoneType);
241         }
242     }
243 
assertGsmDeviceId(String deviceId)244     private static void assertGsmDeviceId(String deviceId) {
245         // IMEI may include the check digit
246         String imeiPattern = "[0-9]{14,15}";
247         assertTrue("IMEI device id " + deviceId + " does not match pattern " + imeiPattern,
248                 Pattern.matches(imeiPattern, deviceId));
249         if (deviceId.length() == 15) {
250             // if the ID is 15 digits, the 15th must be a check digit.
251             assertImeiCheckDigit(deviceId);
252         }
253     }
254 
assertImeiCheckDigit(String deviceId)255     private static void assertImeiCheckDigit(String deviceId) {
256         int expectedCheckDigit = getLuhnCheckDigit(deviceId.substring(0, 14));
257         int actualCheckDigit = Character.digit(deviceId.charAt(14), 10);
258         assertEquals("Incorrect check digit for " + deviceId, expectedCheckDigit, actualCheckDigit);
259     }
260 
261     /**
262      * Use decimal value (0-9) to index into array to get sum of its digits
263      * needed by Lunh check.
264      *
265      * Example: DOUBLE_DIGIT_SUM[6] = 3 because 6 * 2 = 12 => 1 + 2 = 3
266      */
267     private static final int[] DOUBLE_DIGIT_SUM = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9};
268 
269     /**
270      * Calculate the check digit by starting from the right, doubling every
271      * each digit, summing all the digits including the doubled ones, and
272      * finding a number to make the sum divisible by 10.
273      *
274      * @param deviceId not including the check digit
275      * @return the check digit
276      */
getLuhnCheckDigit(String deviceId)277     private static int getLuhnCheckDigit(String deviceId) {
278         int sum = 0;
279         int dontDoubleModulus = deviceId.length() % 2;
280         for (int i = deviceId.length() - 1; i >= 0; --i) {
281             int digit = Character.digit(deviceId.charAt(i), 10);
282             if (i % 2 == dontDoubleModulus) {
283                 sum += digit;
284             } else {
285                 sum += DOUBLE_DIGIT_SUM[digit];
286             }
287         }
288         sum %= 10;
289         return sum == 0 ? 0 : 10 - sum;
290     }
291 
assertCdmaDeviceId(String deviceId)292     private static void assertCdmaDeviceId(String deviceId) {
293         // CDMA device IDs may either be a 14-hex-digit MEID or an
294         // 8-hex-digit ESN.  If it's an ESN, it may not be a
295         // pseudo-ESN.
296         if (deviceId.length() == 14) {
297             assertMeidFormat(deviceId);
298         } else if (deviceId.length() == 8) {
299             assertHexadecimalEsnFormat(deviceId);
300         } else {
301             fail("device id on CDMA must be 14-digit hex MEID or 8-digit hex ESN.");
302         }
303     }
304 
assertHexadecimalEsnFormat(String deviceId)305     private static void assertHexadecimalEsnFormat(String deviceId) {
306         String esnPattern = "[0-9a-fA-F]{8}";
307         assertTrue("ESN hex device id " + deviceId + " does not match pattern " + esnPattern,
308                    Pattern.matches(esnPattern, deviceId));
309         assertFalse("ESN hex device id " + deviceId + " must not be a pseudo-ESN",
310                     "80".equals(deviceId.substring(0, 2)));
311     }
312 
assertMeidFormat(String deviceId)313     private static void assertMeidFormat(String deviceId) {
314         // MEID must NOT include the check digit.
315         String meidPattern = "[0-9a-fA-F]{14}";
316         assertTrue("MEID device id " + deviceId + " does not match pattern " + meidPattern,
317                    Pattern.matches(meidPattern, deviceId));
318     }
319 
assertSerialNumber()320     private void assertSerialNumber() {
321         assertNotNull("Non-telephony devices must have a Build.SERIAL number.",
322                 Build.SERIAL);
323         assertTrue("Hardware id must be no longer than 20 characters.",
324                 Build.SERIAL.length() <= 20);
325         assertTrue("Hardware id must be alphanumeric.",
326                 Pattern.matches("[0-9A-Za-z]+", Build.SERIAL));
327     }
328 
assertMacAddress(String macAddress)329     private void assertMacAddress(String macAddress) {
330         String macPattern = "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}";
331         assertTrue("MAC Address " + macAddress + " does not match pattern " + macPattern,
332                 Pattern.matches(macPattern, macAddress));
333     }
334 
335     /** @return mac address which requires the WiFi system to be enabled */
getWifiMacAddress()336     private String getWifiMacAddress() {
337         WifiManager wifiManager = (WifiManager) getContext()
338                 .getSystemService(Context.WIFI_SERVICE);
339 
340         boolean enabled = wifiManager.isWifiEnabled();
341 
342         try {
343             if (!enabled) {
344                 wifiManager.setWifiEnabled(true);
345             }
346 
347             WifiInfo wifiInfo = wifiManager.getConnectionInfo();
348             return wifiInfo.getMacAddress();
349 
350         } finally {
351             if (!enabled) {
352                 wifiManager.setWifiEnabled(false);
353             }
354         }
355     }
356 
getBluetoothMacAddress()357     private String getBluetoothMacAddress() {
358         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
359         if (adapter == null) {
360             return "";
361         }
362 
363         return adapter.getAddress();
364     }
365 
366     private static final String ISO_COUNTRY_CODE_PATTERN = "[a-z]{2}";
367 
testGetNetworkCountryIso()368     public void testGetNetworkCountryIso() {
369         PackageManager packageManager = getContext().getPackageManager();
370         String countryCode = mTelephonyManager.getNetworkCountryIso();
371         if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
372             assertTrue("Country code '" + countryCode + "' did not match "
373                     + ISO_COUNTRY_CODE_PATTERN,
374                     Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
375         } else {
376             // Non-telephony may still have the property defined if it has a SIM.
377         }
378     }
379 
testGetSimCountryIso()380     public void testGetSimCountryIso() {
381         PackageManager packageManager = getContext().getPackageManager();
382         String countryCode = mTelephonyManager.getSimCountryIso();
383         if (packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
384             assertTrue("Country code '" + countryCode + "' did not match "
385                     + ISO_COUNTRY_CODE_PATTERN,
386                     Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
387         } else {
388             // Non-telephony may still have the property defined if it has a SIM.
389         }
390     }
391 }
392