Unexpected telephonyManager.getSimCountryIso() behaviour - android

I am creating an app that depending on which country your mobile network provider is from, displays a list of all alternative mobile network providers from that same country. To achieve this, I was retrieving the country code using telephonyManager.getSimCountryIso().
Official android developer docs say : "Returns the ISO country code equivalent for the SIM provider's country code", so from this I was expecting country code will always be always the same independently from device location. But thats not how it actually works!
For example I recently experienced this case:
I have an android device with SIM card from Spain belonging to a spanish network provider. So if I am in Spain telephonyManager.getSimCountryIso() returns "es". Everything working fine to that point. The problem is when I travel to France for example I debug the app and find out the telephonyManager.getSimCountryIso() is returning country code: "nl" (from Netherlands!? and I am in France in roaming but with the same spanish SIM card!). I am using the same device and the same SIM card than in Spain so country code ISO should still be "es".
My question is How does this method actually work? why am I getting country code "nl" ( Netherlands) if I am using a spanish SIM card?
Thanks in advance for any help

You can use MCC MNC to get SIM country, it is SIM configured and has nothing to do with the network you are on.
Configuration config = getResources().getConfiguration();
int countryCode = config.mcc;
You can find MCC list here MccTable.java
For example Spain is 214 and France is 208
MCC should work on all GSM devices with SIM card but it is unreliable on CDMA networks
For CDMA devices I found the following solution
if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
// Gives MCC + MNC
String homeOperator = ((String) get.invoke(c, "ro.cdma.home.operator.numeric"));
String country = homeOperator.substring(0, 3); // the last three digits is MNC
} else {
Configuration config = getResources().getConfiguration();
int countryCode = config.mcc;
}

You can try to get country code from TelephonyManager (from SIM or CDMA devices), and if not available try to get it from local configuration. Here is a complete example.
private static String getDeviceCountryCode(Context context) {
String countryCode;
// try to get country code from TelephonyManager service
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if(tm != null) {
// query first getSimCountryIso()
countryCode = tm.getSimCountryIso();
if (countryCode != null && countryCode.length() == 2)
return countryCode.toLowerCase();
if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
// special case for CDMA Devices
countryCode = getCDMACountryIso();
} else {
// for 3G devices (with SIM) query getNetworkCountryIso()
countryCode = tm.getNetworkCountryIso();
}
if (countryCode != null && countryCode.length() == 2)
return countryCode.toLowerCase();
}
// if network country not available (tablets maybe), get country code from Locale class
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
countryCode = context.getResources().getConfiguration().getLocales().get(0).getCountry();
} else {
countryCode = context.getResources().getConfiguration().locale.getCountry();
}
if (countryCode != null && countryCode.length() == 2)
return countryCode.toLowerCase();
// general fallback to "us"
return "us";
}
#SuppressLint("PrivateApi")
private static String getCDMACountryIso() {
try {
// try to get country code from SystemProperties private class
Class<?> systemProperties = Class.forName("android.os.SystemProperties");
Method get = systemProperties.getMethod("get", String.class);
// get homeOperator that contain MCC + MNC
String homeOperator = ((String) get.invoke(systemProperties,
"ro.cdma.home.operator.numeric"));
// first 3 chars (MCC) from homeOperator represents the country code
int mcc = Integer.parseInt(homeOperator.substring(0, 3));
// mapping just countries that actually use CDMA networks
switch (mcc) {
case 330: return "PR";
case 310: return "US";
case 311: return "US";
case 312: return "US";
case 316: return "US";
case 283: return "AM";
case 460: return "CN";
case 455: return "MO";
case 414: return "MM";
case 619: return "SL";
case 450: return "KR";
case 634: return "SD";
case 434: return "UZ";
case 232: return "AT";
case 204: return "NL";
case 262: return "DE";
case 247: return "LV";
case 255: return "UA";
}
} catch (ClassNotFoundException ignored) {
} catch (NoSuchMethodException ignored) {
} catch (IllegalAccessException ignored) {
} catch (InvocationTargetException ignored) {
} catch (NullPointerException ignored) {
}
return null;
}
Also another idea is to try an API request like in this answer, or to use fine location.
References here and here

Related

getAllCellInfo() returns empty list on Nexus 5x

I read all posts about getAllCellInfo() and have a similar situation like this.
The code posted below runs well on Android 6 (OnePlus One) but returns an empty list of Cellinfo on Google Nexus 5x Android 8.1 API 27.
I have set the Permission ACCESS CORSE LOCATION in the manifest and asking for permission when running the app first time.
Here is a code snippet:
try {
TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
String networkOperator = "";
if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
networkOperator = tm.getNetworkOperator();
}
if (!TextUtils.isEmpty(networkOperator)) { //is not empty with Nexus 5x
mcc = networkOperator.substring(0, 3);
mnc = networkOperator.substring(3);
} else {
mcc = "Unbekannt";
mnc = "Unbekannt";
}
List<CellInfo> cell = tm.getAllCellInfo();
System.out.println("List: "+cell);
if (cell != null) { //Always null with Nexus 5x
// TODO
} else {
cellID = "ERROR";
lac = "ERROR";
}
}
catch(Exception ex) {
mcc = "No Permission";
mnc = "No Permission";
cellID = "ERROR";
lac = "ERROR";
}
}
When trying other apps like "Network Cell Info lite" these apps get all the cellinformation :(
Any Help is very appreciated.
In Android 8.0 and above,to access getAllCellInfo() your app should be in the foreground.
If you want to access getAllCellInfo() you need to enable Location Service too.

Getting the Wrong Mobile Prefix in Android

I am making an app where I will get user's complete phone number from the sim - including the country and mobile prefixes. The phone number I have is 061555555, so when I save it to server it should look like this: +38761555555.
And here is my problem - when I use the code from below I get the following: +387218900032555555, ie. instead of 061 becoming 61, it becomes 218900032. This line number gives the wrong number:
MyPhoneNumber = tMgr.getLine1Number();
I have also tried this and this.
This is my code:
// Get users phone number
private String getMyPhoneNumber() {
TelephonyManager tMgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
// Get the SIM country ISO code
String simCountry = tMgr.getSimCountryIso().toUpperCase();
// Get the operator code of the active SIM (MCC + MNC)
String simOperatorCode = tMgr.getSimOperator();
// Get the name of the SIM operator
String simOperatorName = tMgr.getSimOperatorName();
// Get the SIM’s serial number
String simSerial = tMgr.getSimSerialNumber();
String MyPhoneNumber = "0000000000";
try {
MyPhoneNumber = tMgr.getLine1Number();
} catch (NullPointerException ex) {
}
if (MyPhoneNumber.equals("")) {
MyPhoneNumber = tMgr.getSubscriberId();
}
PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
Phonenumber.PhoneNumber countryNumberProto = null;
try {
countryNumberProto = phoneUtil.parse(MyPhoneNumber, simCountry);
} catch (NumberParseException e) {
System.err.println("NumberParseException was thrown: " + e.toString());
}
String myPhone = phoneUtil.format(countryNumberProto, PhoneNumberUtil.PhoneNumberFormat.E164);
// I tried INTERNATIONAL and NATIONAL as well
return myPhone;
}
I think you are parsing "national_number" instead of "country_code". Check your JSON parsing code fields logic
{
"country_code": 41,
"national_number": 446681800
}
https://github.com/googlei18n/libphonenumber
I checked my settings->status and found out my phone number is unknown, and instead of my phone number it displays IMEI, so I guess I will have the user confirm his phone number by entering it manually.

How to find out whether android device has cellular radio module?

How can I find out for sure that device really has gsm, cdma or other cellular network equipment (not just WiFi)?
I don't want to check current connected network state, because device can be offline in the moment.
And I don't want to check device id via ((TelephonyManager) act.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId() because some devices would just give you polymorphic or dummy device ID.
Actualy, I need to check cell equipment exactly for skipping TelephonyManager.getDeviceId and performing Settings.Secure.ANDROID_ID check on those devices that don't have cellular radio. I have at least one tablet (Storage Options Scroll Excel 7") which returns different IMEIs every time you ask it, although it should return null as it has no cell radio (the same situation here: Android: getDeviceId() returns an IMEI, adb shell dumpsys iphonesubinfo returns Device ID=NULL). But I need to have reliable device id that is the same every time I ask.
I'd be glad to hear your thoughts!
If you're publishing in the store, and you want to limit your application only being visible to actual phones, you could add a <uses-feature> into your manifest that asks for android.hardware.telephony. Check out if that works for you from the documentation.
Just in case somebody needs complete solution for this:
Reflection is used because some things may not exist on some firmware versions.
MainContext - main activity context.
static public int getSDKVersion()
{
Class<?> build_versionClass = null;
try
{
build_versionClass = android.os.Build.VERSION.class;
}
catch (Exception e)
{
}
int retval = -1;
try
{
retval = (Integer) build_versionClass.getField("SDK_INT").get(build_versionClass);
}
catch (Exception e)
{
}
if (retval == -1)
retval = 3; //default 1.5
return retval;
}
static public boolean hasTelephony()
{
TelephonyManager tm = (TelephonyManager) Hub.MainContext.getSystemService(Context.TELEPHONY_SERVICE);
if (tm == null)
return false;
//devices below are phones only
if (Utils.getSDKVersion() < 5)
return true;
PackageManager pm = MainContext.getPackageManager();
if (pm == null)
return false;
boolean retval = false;
try
{
Class<?> [] parameters = new Class[1];
parameters[0] = String.class;
Method method = pm.getClass().getMethod("hasSystemFeature", parameters);
Object [] parm = new Object[1];
parm[0] = "android.hardware.telephony";
Object retValue = method.invoke(pm, parm);
if (retValue instanceof Boolean)
retval = ((Boolean) retValue).booleanValue();
else
retval = false;
}
catch (Exception e)
{
retval = false;
}
return retval;
}

How to retrieve SIM card IMSI in Android?

public String getSubscriberId(){
operator = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
String IMSI = operator.getSubscriberId();
return IMSI;
}
simID = (TextView) findViewById(R.id.text2);
simIMSI = getSubscriberId().toString();
if (simIMSI.equals("")){
simID.setText("No SIM card detected!");
}
else{
simID.setText(simIMSI.toString());
SaveUniqueId(simIMSI.toString());
}
I wish to retrieve the phone SIM card IMSI and display in a layout, I run the program using an emulator even though I know emulator does not have SIM card attached but it should have result like "No SIM card detected" right? But why I get error for this coding or is it something wrong in my "getSubscriberId()"?
String serviceName = Context.TELEPHONY_SERVICE;
TelephonyManager m_telephonyManager = (TelephonyManager) getSystemService(serviceName);
Now let's start with actual source code to retrieve the information:-
public String findDeviceID() {
String deviceID = null;
String serviceName = Context.TELEPHONY_SERVICE;
TelephonyManager m_telephonyManager = (TelephonyManager) getSystemService(serviceName);
int deviceType = m_telephonyManager.getPhoneType();
switch (deviceType) {
case (TelephonyManager.PHONE_TYPE_GSM):
break;
case (TelephonyManager.PHONE_TYPE_CDMA):
break;
case (TelephonyManager.PHONE_TYPE_NONE):
break;
default:
break;
}
deviceID = m_telephonyManager.getDeviceId();
return deviceID;
}
For more details please refer this site http://ashnatechnologies.blogspot.in/2010/10/how-to-get-imei-on-android-devices.html
You can visit this link http://www.anddev.org/tinytut_-_getting_the_imsi_-_imei_sim-device_unique_ids-t446.html
use following code for IMSI
String imsi = android.os.SystemProperties.get(android.telephony.TelephonyProperties.PROPERTY_IMSI);
This code should work.
String myIMSI =
android.os.SystemProperties.get(android.telephony.TelephonyProperties.PROPERTY_IMSI);

How can I check whether the Sim Card is available in an android device?

I need help checking whether a device has a sim card programatically. Please provide sample code.
Use TelephonyManager.
http://developer.android.com/reference/android/telephony/TelephonyManager.html
As Falmarri notes, you will want to use getPhoneType FIRST of all, to see if you are even dealing with a GSM phone. If you are, then you can also get the SIM state.
TelephonyManager telMgr = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
int simState = telMgr.getSimState();
switch (simState) {
case TelephonyManager.SIM_STATE_ABSENT:
// do something
break;
case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
// do something
break;
case TelephonyManager.SIM_STATE_PIN_REQUIRED:
// do something
break;
case TelephonyManager.SIM_STATE_PUK_REQUIRED:
// do something
break;
case TelephonyManager.SIM_STATE_READY:
// do something
break;
case TelephonyManager.SIM_STATE_UNKNOWN:
// do something
break;
}
EDIT:
Starting at API 26 (Android O Preview) you can query the SimState for individual sim slots by using getSimState(int slotIndex) ie:
int simStateMain = telMgr.getSimState(0);
int simStateSecond = telMgr.getSimState(1);
official documentation
If you're developing with and older api, you can use TelephonyManager's
String getDeviceId (int slotIndex)
//returns null if device ID is not available. ie. query slotIndex 1 in a single sim device
int devIdSecond = telMgr.getDeviceId(1);
//if(devIdSecond == null)
// no second sim slot available
which was added in API 23 - docs here
You can check with the below code :
public static boolean isSimSupport(Context context)
{
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); //gets the current TelephonyManager
return !(tm.getSimState() == TelephonyManager.SIM_STATE_ABSENT);
}
Found another way to do this.
public static boolean isSimStateReadyorNotReady() {
int simSlotCount = sSlotCount;
String simStates = SystemProperties.get("gsm.sim.state", "");
if (simStates != null) {
String[] slotState = simStates.split(",");
int simSlot = 0;
while (simSlot < simSlotCount && slotState.length > simSlot) {
String simSlotState = slotState[simSlot];
Log.d("MultiSimUtils", "isSimStateReadyorNotReady() : simSlot = " + simSlot + ", simState = " + simSlotState);
if (simSlotState.equalsIgnoreCase("READY") || simSlotState.equalsIgnoreCase("NOT_READY")) {
return true;
}
simSlot++;
}
}
return false;
}
Thanks #Arun kumar answer, kotlin version as below
fun isSIMInserted(context: Context): Boolean {
return TelephonyManager.SIM_STATE_ABSENT != (context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).simState
}

Categories

Resources