I am trying to get a list of networks on Android devices that have multiple SIM cards "dual sim."
I use the TelephonyManager class but the method getNetworkType only returns the network for the first sim "sim 1."
There's no API for this before Android Android 5.1 (API22). But then you have SubscriptionManager and its getActiveSubscriptionInfoList()
I have found a posible solution. I have used the android reflection to call TelephonyManager methods for example if i want the data Network I can use getDataNetworkType as follows:
getNetworkTypeReflection(telephonyManager, "getDataNetworkType", slot, false);
private static String getNetworkTypeReflection(final TelephonyManager telephony, final String predictedMethodName, final int slotID, final boolean isPrivate) {
String result = null;
try {
final Class<?> telephonyClass = Class.forName(telephony.getClass().getName());
final Class<?>[] parameter = new Class[1];
parameter[0] = int.class;
final Method getSubtecnology;
if (slotID != -1) {
if (isPrivate) {
getSubtecnology = telephonyClass.getDeclaredMethod(predictedMethodName, parameter);
} else {
getSubtecnology = telephonyClass.getMethod(predictedMethodName, parameter);
}
} else {
if (isPrivate) {
getSubtecnology = telephonyClass.getDeclaredMethod(predictedMethodName);
} else {
getSubtecnology = telephonyClass.getMethod(predictedMethodName);
}
}
final Object obPhone;
final Object[] obParameter = new Object[1];
obParameter[0] = slotID;
if (getSubtecnology != null) {
if (slotID != -1) {
obPhone = getSubtecnology.invoke(telephony, obParameter);
} else {
obPhone = getSubtecnology.invoke(telephony);
}
if (obPhone != null) {
result = obPhone.toString();
}
}
} catch (Exception e) {
//e.printStackTrace();
return null;
}
return result;
}
The problem is that this option only works on Android 5.1 (API22) but only in some device in others you need Android 7.0 (API24).
If anyone has other options are welcome.
Related
I want to get the user's current region. Typically country. I tried the below code... but It gives me always "in" although I changed the region from "India" to "United States" in settings.
public static String getUserCountry2(Context context) {
try {
final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
final String simCountry = tm.getSimCountryIso();
if (simCountry != null && simCountry.length() == 2) { // SIM country code is available
return simCountry.toLowerCase(Locale.US);
} else if (tm.getPhoneType() != TelephonyManager.PHONE_TYPE_CDMA) { // device is not 3G (would be unreliable)
String networkCountry = tm.getNetworkCountryIso();
if (networkCountry != null && networkCountry.length() == 2) { // network country code is available
return networkCountry.toLowerCase(Locale.US);
}
}
} catch (Exception e) {
}
return null;
}
Settings from where I'm changing Region
I try below code but it return null for both.
TelephonyManager telephonyInfo = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
telephonyInfo.serialSIM1 = telephonyManager.getSimSerialNumber();
telephonyInfo.serialSIM2 = null;
try {
telephonyInfo.serialSIM1 = getSerialBySlot(context, "getSimSerialNumberGemini", 0);
telephonyInfo.serialSIM2 = getSerialBySlot(context, "getSimSerialNumberGemini", 1);
} catch (GeminiMethodNotFoundException e) {
e.printStackTrace();
try {
telephonyInfo.serialSIM1 = getSerialBySlot(context, "getSimSerialNumber", 0);
telephonyInfo.serialSIM2 = getSerialBySlot(context, "getSimSerialNumber", 1);
} catch (GeminiMethodNotFoundException e1) {
//Call here for next manufacturer's predicted method name if you wish
e1.printStackTrace();
}
}
private static String getSerialBySlot(Context context, String predictedMethodName, int slotID) throws GeminiMethodNotFoundException {
String lsSerialNumber = null;
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
try{
Class<?> telephonyClass = Class.forName(telephony.getClass().getName());
Class<?>[] parameter = new Class[1];
parameter[0] = int.class;
Method getSimID = telephonyClass.getMethod(predictedMethodName, parameter);
Object[] obParameter = new Object[1];
obParameter[0] = slotID;
Object ob_phone = getSimID.invoke(telephony, obParameter);
if(ob_phone != null){
lsSerialNumber = ob_phone.toString();
}
} catch (Exception e) {
e.printStackTrace();
throw new GeminiMethodNotFoundException(predictedMethodName);
}
return lsSerialNumber;
}
private static class GeminiMethodNotFoundException extends Exception {
private static final long serialVersionUID = -996812356902545308L;
public GeminiMethodNotFoundException(String info) {
super(info);
}
}
Then use it.
String getSerialNumber1 = telephonyInfo.getSerialSIM1();
String getSerialNumber2 = telephonyInfo.getSerialSIM2();
Log.i(TAG, "state getSerialNumber1 : " + getSerialNumber1);
Log.i(TAG, "state getSerialNumber2 : " + getSerialNumber2);
Below is the log which is got in logcat
09-16 07:20:07.042 6973-6973/app.myApp I/SimChangeReceiver: state getSerialNumber1 : null
09-16 07:20:07.042 6973-6973/app.myApp I/SimChangeReceiver: state getSerialNumber2 : null
I can get IMEI and the dual sim or not.
I search around for the related question,But I did not found any other method.
try this i have not tested but it might help you.
Instead of using getSimSerialNumber, you can use SubscriptionManager.
// Requires, android.permission.READ_PHONE_STATE
SubscriptionManager sm = SubscriptionManager.from(mContext);
// it returns a list with a SubscriptionInfo instance for each simcard
// there is other methods to retrieve SubscriptionInfos (see [2])
List<SubscriptionInfo> sis = sm.getActiveSubscriptionInfoList();
// getting first SubscriptionInfo
SubscriptionInfo si = sis.get(0);
// getting iccId
String iccId = si.getIccId();
Note that I'm talking about Android Lollipop. For android 6.0 we can use method canDrawOverlays() to check that SYSTEM_ALERT_WINDOW is granted or not.
With Android Lollipop, almost devices grant this permission by default. But on some devices of Xiaomi, Meizu.. it is not granted. Users need to go to the App info to allow it.
How can we check it programmatically to warn users?
in MIUI use
public static boolean isMiuiFloatWindowOpAllowed(#NonNull Context context) {
final int version = Build.VERSION.SDK_INT;
if (version >= 19) {
return checkOp(context, OP_SYSTEM_ALERT_WINDOW); //See AppOpsManager.OP_SYSTEM_ALERT_WINDOW=24 /*#hide/
} else {
return (context.getApplicationInfo().flags & 1<<27) == 1;
}
}
public static boolean checkOp(Context context, int op, String packageName, int uid) {
final int version = Build.VERSION.SDK_INT;
if (version >= 19) {
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
try {
return (AppOpsManager.MODE_ALLOWED == (Integer) ReflectUtils.invokeMethod(manager, "checkOp", op, uid, packageName));
} catch (Exception e) {
e.printStackTrace();
}
} else {
Flog.e("Below API 19 cannot invoke!");
}
return false;
}
ReflectUtils.java
public static Object invokeMethod(#NonNull Object receiver, String methodName, Object... methodArgs) throws Exception {
Class<?>[] argsClass = null;
if (methodArgs != null && methodArgs.length != 0) {
int length = methodArgs.length;
argsClass = new Class[length];
for (int i=0; i<length; i++) {
argsClass[i] = getBaseTypeClass(methodArgs[i].getClass());
}
}
Method method = receiver.getClass().getMethod(methodName, argsClass);
return method.invoke(receiver, methodArgs);
}
Reflection is risky because you take things for granted...and things can change in future versions of Android. The following method only uses reflection if the proper way fails.
#SuppressLint("NewApi")
public static boolean canDrawOverlayViews(Context con){
if(Build.VERSION.SDK_INT< Build.VERSION_CODES.LOLLIPOP)
return true;
try {
return Settings.canDrawOverlays(con);
}
catch(NoSuchMethodError e){
return canDrawOverlaysUsingReflection(con);
}
}
public static boolean canDrawOverlaysUsingReflection(Context context) {
try {
AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
Class clazz = AppOpsManager.class;
Method dispatchMethod = clazz.getMethod("checkOp", new Class[] { int.class, int.class, String.class });
//AppOpsManager.OP_SYSTEM_ALERT_WINDOW = 24
int mode = (Integer) dispatchMethod.invoke(manager, new Object[] { 24, Binder.getCallingUid(), context.getApplicationContext().getPackageName() });
return AppOpsManager.MODE_ALLOWED == mode;
} catch (Exception e) { return false; }
}
I'm developing an app that gets the MAC address of the device, but since Android 6.0 my code doesn't work, giving me an incorrect value.
Here's my code...
public String ObtenMAC()
{
WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo info = manager.getConnectionInfo();
return(info.getMacAddress().toUpperCase());
}
Instead of the real MAC address, it returns an strange code: 02:00:00:00:00:00.
Please refer to Android 6.0 Changes.
To provide users with greater data protection, starting in this release, Android removes programmatic access to the device’s local hardware identifier for apps using the Wi-Fi and Bluetooth APIs. The WifiInfo.getMacAddress() and the BluetoothAdapter.getAddress() methods now return a constant value of 02:00:00:00:00:00.
To access the hardware identifiers of nearby external devices via Bluetooth and Wi-Fi scans, your app must now have the ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permissions.
Use below code to get Mac address in Android 6.0
public static String getMacAddr() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(Integer.toHexString(b & 0xFF) + ":");
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
} catch (Exception ex) {
//handle exception
}
return "";
}
I didn't get the above answer to work, but stumbled upon another answer.
Here is a complete and simple method on getting the IPv6 address and then getting the mac address from it.
How to get Wi-Fi Mac address in Android Marshmallow
public static String getMacAddr() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(String.format("%02X:",b));
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
} catch (Exception ex) {
}
return "02:00:00:00:00:00";
}
Tested it already and it works. Many thanks to Rob Anderson!
this is the complete 2 ways code of getting it successfully on Marshmallow , just copy past this and it will work !
//Android 6.0 : Access to mac address from WifiManager forbidden
private static final String marshmallowMacAddress = "02:00:00:00:00:00";
private static final String fileAddressMac = "/sys/class/net/wlan0/address";
public static String recupAdresseMAC(WifiManager wifiMan) {
WifiInfo wifiInf = wifiMan.getConnectionInfo();
if(wifiInf.getMacAddress().equals(marshmallowMacAddress)){
String ret = null;
try {
ret= getAdressMacByInterface();
if (ret != null){
return ret;
} else {
ret = getAddressMacByFile(wifiMan);
return ret;
}
} catch (IOException e) {
Log.e("MobileAccess", "Erreur lecture propriete Adresse MAC");
} catch (Exception e) {
Log.e("MobileAcces", "Erreur lecture propriete Adresse MAC ");
}
} else{
return wifiInf.getMacAddress();
}
return marshmallowMacAddress;
}
private static String getAdressMacByInterface(){
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (nif.getName().equalsIgnoreCase("wlan0")) {
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(String.format("%02X:",b));
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
}
} catch (Exception e) {
Log.e("MobileAcces", "Erreur lecture propriete Adresse MAC ");
}
return null;
}
private static String getAddressMacByFile(WifiManager wifiMan) throws Exception {
String ret;
int wifiState = wifiMan.getWifiState();
wifiMan.setWifiEnabled(true);
File fl = new File(fileAddressMac);
FileInputStream fin = new FileInputStream(fl);
StringBuilder builder = new StringBuilder();
int ch;
while((ch = fin.read()) != -1){
builder.append((char)ch);
}
ret = builder.toString();
fin.close();
boolean enabled = WifiManager.WIFI_STATE_ENABLED == wifiState;
wifiMan.setWifiEnabled(enabled);
return ret;
}
manifest :
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
Summary : this code will try to get the MAC address first by Interface and if its failed it get it by file system.
Note:for file System way, you need to enable WIFI to access the file.
thnx to Sam's answer here https://stackoverflow.com/a/39288868/3818437
You can get the MAC address from the IPv6 local address. E.g., the IPv6 address "fe80::1034:56ff:fe78:9abc" corresponds to the MAC address "12-34-56-78-9a-bc". See the code below. Getting the WiFi IPv6 address only requires android.permission.INTERNET.
See the Wikipedia page IPv6 address, particularly the note about "local addresses" fe80::/64 and the section about "Modified EUI-64".
/**
* Gets an EUI-48 MAC address from an IPv6 link-local address.
* E.g., the IPv6 address "fe80::1034:56ff:fe78:9abc"
* corresponds to the MAC address "12-34-56-78-9a-bc".
* <p/>
* See the note about "local addresses" fe80::/64 and the section about "Modified EUI-64" in
* the Wikipedia article "IPv6 address" at https://en.wikipedia.org/wiki/IPv6_address
*
* #param ipv6 An Inet6Address object.
* #return The EUI-48 MAC address as a byte array, null on error.
*/
private static byte[] getMacAddressFromIpv6(final Inet6Address ipv6)
{
byte[] eui48mac = null;
if (ipv6 != null) {
/*
* Make sure that this is an fe80::/64 link-local address.
*/
final byte[] ipv6Bytes = ipv6.getAddress();
if ((ipv6Bytes != null) &&
(ipv6Bytes.length == 16) &&
(ipv6Bytes[0] == (byte) 0xfe) &&
(ipv6Bytes[1] == (byte) 0x80) &&
(ipv6Bytes[11] == (byte) 0xff) &&
(ipv6Bytes[12] == (byte) 0xfe)) {
/*
* Allocate a byte array for storing the EUI-48 MAC address, then fill it
* from the appropriate bytes of the IPv6 address. Invert the 7th bit
* of the first byte and discard the "ff:fe" portion of the modified
* EUI-64 MAC address.
*/
eui48mac = new byte[6];
eui48mac[0] = (byte) (ipv6Bytes[8] ^ 0x2);
eui48mac[1] = ipv6Bytes[9];
eui48mac[2] = ipv6Bytes[10];
eui48mac[3] = ipv6Bytes[13];
eui48mac[4] = ipv6Bytes[14];
eui48mac[5] = ipv6Bytes[15];
}
}
return eui48mac;
}
I try to get mac address with 2 methods, first by Interface and if its failed, i get it by file system, but you need to enable wifi to access the file.
//Android 6.0 : Access to mac address from WifiManager forbidden
private static final String marshmallowMacAddress = "02:00:00:00:00:00";
private static final String fileAddressMac = "/sys/class/net/wlan0/address";
public static String recupAdresseMAC(WifiManager wifiMan) {
WifiInfo wifiInf = wifiMan.getConnectionInfo();
if(wifiInf.getMacAddress().equals(marshmallowMacAddress)){
String ret = null;
try {
ret= getAdressMacByInterface();
if (ret != null){
return ret;
} else {
ret = getAddressMacByFile(wifiMan);
return ret;
}
} catch (IOException e) {
Log.e("MobileAccess", "Erreur lecture propriete Adresse MAC");
} catch (Exception e) {
Log.e("MobileAcces", "Erreur lecture propriete Adresse MAC ");
}
} else{
return wifiInf.getMacAddress();
}
return marshmallowMacAddress;
}
private static String getAdressMacByInterface(){
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (nif.getName().equalsIgnoreCase("wlan0")) {
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(String.format("%02X:",b));
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
}
} catch (Exception e) {
Log.e("MobileAcces", "Erreur lecture propriete Adresse MAC ");
}
return null;
}
private static String getAddressMacByFile(WifiManager wifiMan) throws Exception {
String ret;
int wifiState = wifiMan.getWifiState();
wifiMan.setWifiEnabled(true);
File fl = new File(fileAddressMac);
FileInputStream fin = new FileInputStream(fl);
ret = convertStreamToString(fin);
fin.close();
boolean enabled = WifiManager.WIFI_STATE_ENABLED == wifiState;
wifiMan.setWifiEnabled(enabled);
return ret;
}
Add this line to your manifest.
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
I recommend you to persist your mac address in your preferences like here
mac = activity.getSharedPreferences("MAC_ADDRESS", Context.MODE_PRIVATE).getString("MAC_ADDRESS", "");
if(mac == null || mac.equals("")){
WifiManager wifiMan = (WifiManager) activity.getSystemService(Context.WIFI_SERVICE);
mac = MobileAccess.recupAdresseMAC(wifiMan);
if(mac != null && !mac.equals("")){
SharedPreferences.Editor editor = activity.getSharedPreferences("MAC_ADDRESS", Context.MODE_PRIVATE).edit();
editor.putString("MAC_ADDRESS", mac).commit();
}
}
Its Perfectly Fine
package com.keshav.fetchmacaddress;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.List;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e("keshav","getMacAddr -> " +getMacAddr());
}
public static String getMacAddr() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(Integer.toHexString(b & 0xFF) + ":");
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
} catch (Exception ex) {
//handle exception
}
return "";
}
}
First you need to add Internet user permission.
<uses-permission android:name="android.permission.INTERNET" />
Then you can find the mac over the NetworkInterfaces API.
public static String getMacAddr() {
try {
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equalsIgnoreCase("wlan0")) continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
res1.append(String.format("%02X:",b));
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
} catch (Exception ex) {
}
return "02:00:00:00:00:00";
}
The answers are mostly correct, but keep care, that there is a change in android 7. You will need to use the
DevicePolicyManager and the Method getWifiMacAddress. The official docs has a typo, which means that you shouldnt copy/paste it from there.
DevicePolicyManager.getWifiMacAddress()
Refs:
https://developer.android.com/about/versions/nougat/android-7.0-changes.html
Get Device mac adress in Android Nougat and O programmatically
Use wifiInfo.getBSSID() to get Mac Address of AccessPoint instead of getMacAddress method.
This is a more kotlin way to get Mac Address
fun getMacAddress(): String =
NetworkInterface.getNetworkInterfaces().toList()
.firstOrNull { it.name.equals("wlan0", ignoreCase = true) }?.let {
it.hardwareAddress?.let { macBytes ->
StringBuilder().apply {
for (b in macBytes) {
append(String.format("%02X:", b))
}
if (isNotEmpty()) {
deleteCharAt(lastIndex)
}
}
}.toString()
} ?: "02:00:00:00:00:00"
After a lot of research on forums, now I know that there is no way to find IMSI or SIM serial number for both the SIM cards in a dual SIM phone (except for contacting the manufacturer). Now my changed question is, can we at all detect that the phone has two SIMs? I believe it can be detected with some intelligence. Few ways I can think of are:
Dialing an USSD code and tracing the logs for IMEI number (I tried this with *139# in India. It worked.) This will give me IMEI number for the SIM from which I dialed the USSD code. (It is presumed that the phone follows android guidelines and has two IMEI numbers.)
Storing the SIM serial number and/or IMSI for the SIM. And after detection of any other IMSI/Serial number even if the phone was not rebooted (i.e. the SIM was switched) by tracing some logs or by some broadcast event handling.
By dialing *06# you will get to see both IMEI numbers. By some way, get those two numbers. (Something like screen capturing and image parsing for text.)
If anyone can think of some other ways, they are most welcome. I would really appreciate any kind of help regarding this. Also, if anyone has any information about any manufacturers APIs or links to contact them, please do share with the community people.
Update 23 March'15 :
Official multiple SIM API is available now from Android 5.1 onwards
Other possible option :
You can use Java reflection to get both IMEI numbers.
Using these IMEI numbers you can check whether the phone is a DUAL SIM or not.
Try following activity :
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TelephonyInfo telephonyInfo = TelephonyInfo.getInstance(this);
String imeiSIM1 = telephonyInfo.getImsiSIM1();
String imeiSIM2 = telephonyInfo.getImsiSIM2();
boolean isSIM1Ready = telephonyInfo.isSIM1Ready();
boolean isSIM2Ready = telephonyInfo.isSIM2Ready();
boolean isDualSIM = telephonyInfo.isDualSIM();
TextView tv = (TextView) findViewById(R.id.tv);
tv.setText(" IME1 : " + imeiSIM1 + "\n" +
" IME2 : " + imeiSIM2 + "\n" +
" IS DUAL SIM : " + isDualSIM + "\n" +
" IS SIM1 READY : " + isSIM1Ready + "\n" +
" IS SIM2 READY : " + isSIM2Ready + "\n");
}
}
And here is TelephonyInfo.java :
import java.lang.reflect.Method;
import android.content.Context;
import android.telephony.TelephonyManager;
public final class TelephonyInfo {
private static TelephonyInfo telephonyInfo;
private String imeiSIM1;
private String imeiSIM2;
private boolean isSIM1Ready;
private boolean isSIM2Ready;
public String getImsiSIM1() {
return imeiSIM1;
}
/*public static void setImsiSIM1(String imeiSIM1) {
TelephonyInfo.imeiSIM1 = imeiSIM1;
}*/
public String getImsiSIM2() {
return imeiSIM2;
}
/*public static void setImsiSIM2(String imeiSIM2) {
TelephonyInfo.imeiSIM2 = imeiSIM2;
}*/
public boolean isSIM1Ready() {
return isSIM1Ready;
}
/*public static void setSIM1Ready(boolean isSIM1Ready) {
TelephonyInfo.isSIM1Ready = isSIM1Ready;
}*/
public boolean isSIM2Ready() {
return isSIM2Ready;
}
/*public static void setSIM2Ready(boolean isSIM2Ready) {
TelephonyInfo.isSIM2Ready = isSIM2Ready;
}*/
public boolean isDualSIM() {
return imeiSIM2 != null;
}
private TelephonyInfo() {
}
public static TelephonyInfo getInstance(Context context){
if(telephonyInfo == null) {
telephonyInfo = new TelephonyInfo();
TelephonyManager telephonyManager = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
telephonyInfo.imeiSIM1 = telephonyManager.getDeviceId();;
telephonyInfo.imeiSIM2 = null;
try {
telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context, "getDeviceIdGemini", 0);
telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context, "getDeviceIdGemini", 1);
} catch (GeminiMethodNotFoundException e) {
e.printStackTrace();
try {
telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context, "getDeviceId", 0);
telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context, "getDeviceId", 1);
} catch (GeminiMethodNotFoundException e1) {
//Call here for next manufacturer's predicted method name if you wish
e1.printStackTrace();
}
}
telephonyInfo.isSIM1Ready = telephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY;
telephonyInfo.isSIM2Ready = false;
try {
telephonyInfo.isSIM1Ready = getSIMStateBySlot(context, "getSimStateGemini", 0);
telephonyInfo.isSIM2Ready = getSIMStateBySlot(context, "getSimStateGemini", 1);
} catch (GeminiMethodNotFoundException e) {
e.printStackTrace();
try {
telephonyInfo.isSIM1Ready = getSIMStateBySlot(context, "getSimState", 0);
telephonyInfo.isSIM2Ready = getSIMStateBySlot(context, "getSimState", 1);
} catch (GeminiMethodNotFoundException e1) {
//Call here for next manufacturer's predicted method name if you wish
e1.printStackTrace();
}
}
}
return telephonyInfo;
}
private static String getDeviceIdBySlot(Context context, String predictedMethodName, int slotID) throws GeminiMethodNotFoundException {
String imei = null;
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
try{
Class<?> telephonyClass = Class.forName(telephony.getClass().getName());
Class<?>[] parameter = new Class[1];
parameter[0] = int.class;
Method getSimID = telephonyClass.getMethod(predictedMethodName, parameter);
Object[] obParameter = new Object[1];
obParameter[0] = slotID;
Object ob_phone = getSimID.invoke(telephony, obParameter);
if(ob_phone != null){
imei = ob_phone.toString();
}
} catch (Exception e) {
e.printStackTrace();
throw new GeminiMethodNotFoundException(predictedMethodName);
}
return imei;
}
private static boolean getSIMStateBySlot(Context context, String predictedMethodName, int slotID) throws GeminiMethodNotFoundException {
boolean isReady = false;
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
try{
Class<?> telephonyClass = Class.forName(telephony.getClass().getName());
Class<?>[] parameter = new Class[1];
parameter[0] = int.class;
Method getSimStateGemini = telephonyClass.getMethod(predictedMethodName, parameter);
Object[] obParameter = new Object[1];
obParameter[0] = slotID;
Object ob_phone = getSimStateGemini.invoke(telephony, obParameter);
if(ob_phone != null){
int simState = Integer.parseInt(ob_phone.toString());
if(simState == TelephonyManager.SIM_STATE_READY){
isReady = true;
}
}
} catch (Exception e) {
e.printStackTrace();
throw new GeminiMethodNotFoundException(predictedMethodName);
}
return isReady;
}
private static class GeminiMethodNotFoundException extends Exception {
private static final long serialVersionUID = -996812356902545308L;
public GeminiMethodNotFoundException(String info) {
super(info);
}
}
}
Edit :
Getting access of methods like "getDeviceIdGemini" for other SIM slot's detail has prediction that method exist.
If that method's name doesn't match with one given by device manufacturer than it will not work. You have to find corresponding method name for those devices.
Finding method names for other manufacturers can be done using Java reflection as follows :
public static void printTelephonyManagerMethodNamesForThisDevice(Context context) {
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
Class<?> telephonyClass;
try {
telephonyClass = Class.forName(telephony.getClass().getName());
Method[] methods = telephonyClass.getMethods();
for (int idx = 0; idx < methods.length; idx++) {
System.out.println("\n" + methods[idx] + " declared by " + methods[idx].getDeclaringClass());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
EDIT :
As Seetha pointed out in her comment :
telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context, "getDeviceIdDs", 0);
telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context, "getDeviceIdDs", 1);
It is working for her. She was successful in getting two IMEI numbers for both the SIM in Samsung Duos device.
Add <uses-permission android:name="android.permission.READ_PHONE_STATE" />
EDIT 2 :
The method used for retrieving data is for Lenovo A319 and other phones by that manufacture (Credit Maher Abuthraa):
telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context, "getSimSerialNumberGemini", 0);
telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context, "getSimSerialNumberGemini", 1);
I have a Samsung Duos device with Android 4.4.4 and the method suggested by Seetha in the accepted answer (i.e. call getDeviceIdDs) does not work for me, as the method does not exist. I was able to recover all the information I needed by calling method "getDefault(int slotID)", as shown below:
public static void samsungTwoSims(Context context) {
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
try{
Class<?> telephonyClass = Class.forName(telephony.getClass().getName());
Class<?>[] parameter = new Class[1];
parameter[0] = int.class;
Method getFirstMethod = telephonyClass.getMethod("getDefault", parameter);
Log.d(TAG, getFirstMethod.toString());
Object[] obParameter = new Object[1];
obParameter[0] = 0;
TelephonyManager first = (TelephonyManager) getFirstMethod.invoke(null, obParameter);
Log.d(TAG, "Device Id: " + first.getDeviceId() + ", device status: " + first.getSimState() + ", operator: " + first.getNetworkOperator() + "/" + first.getNetworkOperatorName());
obParameter[0] = 1;
TelephonyManager second = (TelephonyManager) getFirstMethod.invoke(null, obParameter);
Log.d(TAG, "Device Id: " + second.getDeviceId() + ", device status: " + second.getSimState()+ ", operator: " + second.getNetworkOperator() + "/" + second.getNetworkOperatorName());
} catch (Exception e) {
e.printStackTrace();
}
}
Also, I rewrote the code that iteratively tests for methods to recover this information so that it uses an array of method names instead of a sequence of try/catch. For instance, to determine if we have two active SIMs we could do:
private static String[] simStatusMethodNames = {"getSimStateGemini", "getSimState"};
public static boolean hasTwoActiveSims(Context context) {
boolean first = false, second = false;
for (String methodName: simStatusMethodNames) {
// try with sim 0 first
try {
first = getSIMStateBySlot(context, methodName, 0);
// no exception thrown, means method exists
second = getSIMStateBySlot(context, methodName, 1);
return first && second;
} catch (GeminiMethodNotFoundException e) {
// method does not exist, nothing to do but test the next
}
}
return false;
}
This way, if a new method name is suggested for some device, you can simply add it to the array and it should work.
There are several native solutions I've found while searching the way to check network operator.
For API >=17:
TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
// Get information about all radio modules on device board
// and check what you need by calling #getCellIdentity.
final List<CellInfo> allCellInfo = manager.getAllCellInfo();
for (CellInfo cellInfo : allCellInfo) {
if (cellInfo instanceof CellInfoGsm) {
CellIdentityGsm cellIdentity = ((CellInfoGsm) cellInfo).getCellIdentity();
//TODO Use cellIdentity to check MCC/MNC code, for instance.
} else if (cellInfo instanceof CellInfoWcdma) {
CellIdentityWcdma cellIdentity = ((CellInfoWcdma) cellInfo).getCellIdentity();
} else if (cellInfo instanceof CellInfoLte) {
CellIdentityLte cellIdentity = ((CellInfoLte) cellInfo).getCellIdentity();
} else if (cellInfo instanceof CellInfoCdma) {
CellIdentityCdma cellIdentity = ((CellInfoCdma) cellInfo).getCellIdentity();
}
}
In AndroidManifest add permission:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>
To get network operator you can check mcc and mnc codes:
https://en.wikipedia.org/wiki/Mobile_country_code (general information).
https://clients.txtnation.com/hc/en-us/articles/218719768-MCCMNC-mobile-country-code-and-mobile-network-code-list- (quite full and quite latest list of operators).
For API >=22:
final SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
final List<SubscriptionInfo> activeSubscriptionInfoList = subscriptionManager.getActiveSubscriptionInfoList();
for (SubscriptionInfo subscriptionInfo : activeSubscriptionInfoList) {
final CharSequence carrierName = subscriptionInfo.getCarrierName();
final CharSequence displayName = subscriptionInfo.getDisplayName();
final int mcc = subscriptionInfo.getMcc();
final int mnc = subscriptionInfo.getMnc();
final String subscriptionInfoNumber = subscriptionInfo.getNumber();
}
For API >=23. To just check if phone is dual/triple/many sim:
TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
if (manager.getPhoneCount() == 2) {
// Dual sim
}
I am able to read both the IMEI's from OnePlus 2 Phone
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
TelephonyManager manager = (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
Log.i(TAG, "Single or Dual Sim " + manager.getPhoneCount());
Log.i(TAG, "Default device ID " + manager.getDeviceId());
Log.i(TAG, "Single 1 " + manager.getDeviceId(0));
Log.i(TAG, "Single 2 " + manager.getDeviceId(1));
}
I was taking a look at the call logs and I noticed that apart from the usual fields in the contents of managedCursor, we have a column "simid" in Dual SIM phones (I checked on Xolo A500s Lite), so as to tag each call in the call log with a SIM. This value is either 1 or 2, most probably denoting SIM1/SIM2.
managedCursor = context.getContentResolver().query(contacts, null, null, null, null);
managedCursor.moveToNext();
for(int i=0;i<managedCursor.getColumnCount();i++)
{//for dual sim phones
if(managedCursor.getColumnName(i).toLowerCase().equals("simid"))
indexSIMID=i;
}
I did not find this column in a single SIM phone (I checked on Xperia L).
So although I don't think this is a foolproof way to check for dual SIM nature, I am posting it here because it could be useful to someone.
Tips:
You can try to use
ctx.getSystemService("phone_msim")
instead of
ctx.getSystemService(Context.TELEPHONY_SERVICE)
If you have already tried Vaibhav's answer and telephony.getClass().getMethod() fails, above is what works for my Qualcomm mobile.
I have found these system properties on Samsung S8
SystemProperties.getInt("ro.multisim.simslotcount", 1) > 1
Also, according to the source: https://android.googlesource.com/platform/frameworks/base/+/master/telephony/java/com/android/internal/telephony/TelephonyProperties.java
getprop persist.radio.multisim.config returns "dsds" or "dsda" on multi sim.
I have tested this on Samsung S8 and it works
for sdk api 26+ (Build.VERSION_CODES.O):
val telephony = context.getSystemService(Service.TELEPHONY_SERVICE) as? TelephonyManager
// check telephony on null
val simCount = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
telephony.activeModemCount
} else {
telephony.phoneCount
}
Commonsware says this is not possible. Please see the following:
Detecting Dual SIM using Android SDK is not possible.
Here is further dialog on the subject:
Google dev team guy says detecting Dual SIM using Android SDK is not possible.