1 package com.android.contacts.location;
2 
3 import android.content.Context;
4 import android.telephony.TelephonyManager;
5 import android.text.TextUtils;
6 
7 import java.util.Locale;
8 
9 /**
10  * This class is used to detect the country where the user is. It is a simplified version of the
11  * country detector service in the framework. The sources of country location are queried in the
12  * following order of reliability:
13  * <ul>
14  * <li>Mobile network</li>
15  * <li>SIM's country</li>
16  * <li>User's default locale</li>
17  * </ul>
18  *
19  * As far as possible this class tries to replicate the behavior of the system's country detector
20  * service:
21  * 1) Order in priority of sources of country location
22  * 2) Mobile network information provided by CDMA phones is ignored
23  */
24 public class CountryDetector {
25     private static final String TAG = "CountryDetector";
26 
27     private static CountryDetector sInstance;
28 
29     private final Context mContext;
30     private final LocaleProvider mLocaleProvider;
31     private final TelephonyManager mTelephonyManager;
32 
33     // Used as a default country code when all the sources of country data have failed in the
34     // exceedingly rare event that the device does not have a default locale set for some reason.
35     private final String DEFAULT_COUNTRY_ISO = "US";
36 
37     /**
38      * Class that can be used to return the user's default locale. This is in its own class so that
39      * it can be mocked out.
40      */
41     public static class LocaleProvider {
getDefaultLocale()42         public Locale getDefaultLocale() {
43             return Locale.getDefault();
44         }
45     }
46 
CountryDetector(Context context)47     private CountryDetector(Context context) {
48         this (context, (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
49                 new LocaleProvider());
50     }
51 
CountryDetector(Context context, TelephonyManager telephonyManager, LocaleProvider localeProvider)52     private CountryDetector(Context context, TelephonyManager telephonyManager,
53             LocaleProvider localeProvider) {
54         mTelephonyManager = telephonyManager;
55         mLocaleProvider = localeProvider;
56         mContext = context;
57     }
58 
59     /**
60      * Factory method for {@link CountryDetector} that allows the caller to provide mock objects.
61      */
getInstanceForTest(Context context, TelephonyManager telephonyManager, LocaleProvider localeProvider)62     public CountryDetector getInstanceForTest(Context context, TelephonyManager telephonyManager,
63             LocaleProvider localeProvider) {
64         return new CountryDetector(context, telephonyManager, localeProvider);
65     }
66 
67     /**
68      * Returns the instance of the country detector. {@link #initialize(Context)} must have been
69      * called previously.
70      *
71      * @return the initialized country detector.
72      */
getInstance(Context context)73     public synchronized static CountryDetector getInstance(Context context) {
74         if (sInstance == null) {
75             sInstance = new CountryDetector(context.getApplicationContext());
76         }
77         return sInstance;
78     }
79 
getCurrentCountryIso()80     public String getCurrentCountryIso() {
81         String result = null;
82         if (isNetworkCountryCodeAvailable()) {
83             result = getNetworkBasedCountryIso();
84         }
85         if (TextUtils.isEmpty(result)) {
86             result = getSimBasedCountryIso();
87         }
88         if (TextUtils.isEmpty(result)) {
89             result = getLocaleBasedCountryIso();
90         }
91         if (TextUtils.isEmpty(result)) {
92             result = DEFAULT_COUNTRY_ISO;
93         }
94         return result.toUpperCase(Locale.US);
95     }
96 
97     /**
98      * @return the country code of the current telephony network the user is connected to.
99      */
getNetworkBasedCountryIso()100     private String getNetworkBasedCountryIso() {
101         return mTelephonyManager.getNetworkCountryIso();
102     }
103 
104     /**
105      * @return the country code of the SIM card currently inserted in the device.
106      */
getSimBasedCountryIso()107     private String getSimBasedCountryIso() {
108         return mTelephonyManager.getSimCountryIso();
109     }
110 
111     /**
112      * @return the country code of the user's currently selected locale.
113      */
getLocaleBasedCountryIso()114     private String getLocaleBasedCountryIso() {
115         Locale defaultLocale = mLocaleProvider.getDefaultLocale();
116         if (defaultLocale != null) {
117             return defaultLocale.getCountry();
118         }
119         return null;
120     }
121 
isNetworkCountryCodeAvailable()122     private boolean isNetworkCountryCodeAvailable() {
123         // On CDMA TelephonyManager.getNetworkCountryIso() just returns the SIM's country code.
124         return mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM;
125     }
126 }
127