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

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;
}

Related

How to know which SIM is consuming mobile data in dual SIM android phone?

I am building a network monitor app. Here I have successfully implemented all the things like track data usage from Wifi or mobile data, but I want to know which SIM is connected to internet and consuming mobile data.
Using below code I am able to know if my dual sim phone is connected to Wifi or mobile data.
public static String isInternetConnected (Context ctx) {
ConnectivityManager connectivityMgr = (ConnectivityManager) ctx
.getSystemService(CONNECTIVITY_SERVICE);
NetworkInfo wifi = connectivityMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
NetworkInfo mobile = connectivityMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
// Check if wifi or mobile network is available or not. If any of them is
// available or connected then it will return true, otherwise false;
if (wifi != null) {
if (wifi.isConnected()) {
return "wifi";
}
}
if (mobile != null) {
if (mobile.isConnected()) {
return "mobile";
}
}
return "none";
}
How can I get SIM Index or sim operator name that is consuming mobile data in dual sim android phone?
I had searched a lot and I saw many question posted in SO without answer like this.
I am able to get subId of both SIM in dual SIM phone but I am phasing problem to know which SIM is using internet.
Many other application are able to do this like Mubble.
Can any one provide me a solution for it?
After api level 22, you can use the hidden
system api android.telephony.SubscriptionManager#getDefaultDataSubId via reflection to get current active data sim subscription index.
After api level 24, there is a public system api android.telephony.SubscriptionManager#getDefaultDataSubscriptionId to get current active data sim subscription index.
Then, you can create a android.telephony.TelephonyManager or android.telephony.SubscriptionManager#getActiveSubscriptionInfo from subscription index to obtain sim operator information.
Here is a simple solution to get data sim operator for dual sim phone.
public static String getDataSimOperator(Context context) {
if (context == null) {
return null;
}
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (tm != null) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
int dataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
TelephonyManager dataSimManager = tm.createForSubscriptionId(dataSubId);
return dataSimManager.getSimOperator();
} else {
String operator = getDataSimOperatorBeforeN(context);
if (operator != null) {
return operator;
} else {
return tm.getSimOperator();
}
}
} else {
return tm.getSimOperator();
}
}
return null;
}
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
private static String getDataSimOperatorBeforeN(Context context) {
if (context == null) {
return null;
}
int dataSubId = -1;
try {
Method getDefaultDataSubId = SubscriptionManager.class.getDeclaredMethod("getDefaultDataSubId");
if (getDefaultDataSubId != null) {
getDefaultDataSubId.setAccessible(true);
dataSubId = (int) getDefaultDataSubId.invoke(null);
}
} catch (Exception e) {
e.printStackTrace();
}
if (dataSubId != -1) {
SubscriptionManager sm = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
if (sm != null && ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED) {
SubscriptionInfo si = sm.getActiveSubscriptionInfo(dataSubId);
if (si != null) {
// format keep the same with android.telephony.TelephonyManager#getSimOperator
// MCC + MNC format
return String.valueOf(si.getMcc()) + si.getMnc();
}
}
}
return null;
}

Youtube usage calculation using TrafficStats

Using TrafficStats i was checking the youtube app data usage.In some devices it is working fine but not with many other devices.
I found that from developer site, These statistics may not be available on all platforms. If the statistics are not supported by this device, UNSUPPORTED will be returned.
So in these case how can I get the device app usage ?
I was using
TrafficStats.getUidRxBytes(packageInfo.uid) + TrafficStats.getUidTxBytes(packageInfo.uid);
this is returning -1 everytime.
We can use NetworkStats.
https://developer.android.com/reference/android/app/usage/NetworkStats.html
Please see a sample repo which I got the clue.
https://github.com/RobertZagorski/NetworkStats
We can see a similar stackoverflow question as well.
Getting mobile data usage history using NetworkStatsManager
Then I needed to modify this logic for some particular devices. In these devices the normal method won't return proper usage values. So I modified is as
/*
getting youtube usage for both mobile and wifi.
*/
public long getYoutubeTotalusage(Context context) {
String subId = getSubscriberId(context, ConnectivityManager.TYPE_MOBILE);
//both mobile and wifi usage is calculating. For mobile usage we need subscriberid. For wifi we can give it as empty string value.
return getYoutubeUsage(ConnectivityManager.TYPE_MOBILE, subId) + getYoutubeUsage(ConnectivityManager.TYPE_WIFI, "");
}
private long getYoutubeUsage(int networkType, String subScriberId) {
NetworkStats networkStatsByApp;
long currentYoutubeUsage = 0L;
try {
networkStatsByApp = networkStatsManager.querySummary(networkType, subScriberId, 0, System.currentTimeMillis());
do {
NetworkStats.Bucket bucket = new NetworkStats.Bucket();
networkStatsByApp.getNextBucket(bucket);
if (bucket.getUid() == packageUid) {
//rajeesh : in some devices this is immediately looping twice and the second iteration is returning correct value. So result returning is moved to the end.
currentYoutubeUsage = (bucket.getRxBytes() + bucket.getTxBytes());
}
} while (networkStatsByApp.hasNextBucket());
} catch (RemoteException e) {
e.printStackTrace();
}
return currentYoutubeUsage;
}
private String getSubscriberId(Context context, int networkType) {
if (ConnectivityManager.TYPE_MOBILE == networkType) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
return tm.getSubscriberId();
}
return "";
}

Get Bluetooth local mac address in Marshmallow

Pre Marshmallow my app would obtain it's device MAC address via BluetoothAdapter.getDefaultAdapter().getAddress().
Now with Marshmallow Android is returning 02:00:00:00:00:00.
I saw some link(sorry not sure where now) that said you need to add the additional permission
<uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS"/>
to be able to get it. However it isn't working for me.
Is there some additional permission needed to get the mac address?
I am not sure it is pertinent here but the manifest also includes
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
So is there a way to get the local bluetooth mac address?
zmarties is right but you can still get the mac address via reflection or Settings.Secure:
String macAddress = android.provider.Settings.Secure.getString(context.getContentResolver(), "bluetooth_address");
Access to the mac address has been deliberately removed:
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.
(from Android 6.0 Changes)
You can access Mac address from the file
"/sys/class/net/" + networkInterfaceName+ "/address" ,where networkInterfaceName can be wlan0 or eth1.But Its permission may be read-protected,so it may not work in some devices.
I am also attaching the code part which i got from SO.
public static String getWifiMacAddress() {
try {
String interfaceName = "wlan0";
List<NetworkInterface> interfaces = Collections
.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface intf : interfaces) {
if (!intf.getName().equalsIgnoreCase(interfaceName)) {
continue;
}
byte[] mac = intf.getHardwareAddress();
if (mac == null) {
return "";
}
StringBuilder buf = new StringBuilder();
for (byte aMac : mac) {
buf.append(String.format("%02X:", aMac));
}
if (buf.length() > 0) {
buf.deleteCharAt(buf.length() - 1);
}
return buf.toString();
}
} catch (Exception exp) {
exp.printStackTrace();
}
return "";
}
First the following permissions have to be added to Manifest;
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" />
Then,
public static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS = "bluetooth_address";
String macAddress = Settings.Secure.getString(getContentResolver(), SECURE_SETTINGS_BLUETOOTH_ADDRESS);
After that the application has to be signed with OEM / System key. Tested and verified on Android 8.1.0.
Please use the below code to get the bluetooth mac address. let me know if any issues.
private String getBluetoothMacAddress() {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
String bluetoothMacAddress = "";
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){
try {
Field mServiceField = bluetoothAdapter.getClass().getDeclaredField("mService");
mServiceField.setAccessible(true);
Object btManagerService = mServiceField.get(bluetoothAdapter);
if (btManagerService != null) {
bluetoothMacAddress = (String) btManagerService.getClass().getMethod("getAddress").invoke(btManagerService);
}
} catch (NoSuchFieldException e) {
} catch (NoSuchMethodException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
} else {
bluetoothMacAddress = bluetoothAdapter.getAddress();
}
return bluetoothMacAddress;
}
Getting the MAC address via reflection can look like this:
private static String getBtAddressViaReflection() {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Object bluetoothManagerService = new Mirror().on(bluetoothAdapter).get().field("mService");
if (bluetoothManagerService == null) {
Log.w(TAG, "couldn't find bluetoothManagerService");
return null;
}
Object address = new Mirror().on(bluetoothManagerService).invoke().method("getAddress").withoutArgs();
if (address != null && address instanceof String) {
Log.w(TAG, "using reflection to get the BT MAC address: " + address);
return (String) address;
} else {
return null;
}
}
using a reflection library (net.vidageek:mirror) but you'll get the idea.
Since below method return null for android O.
String macAddress = android.provider.Settings.Secure.getString(context.getContentResolver(), "bluetooth_address");
I found new way to get Bluetooth Mac address, you can try by using below command line.
su strings /data/misc/bluedroid/bt_config.conf | grep Address
NOTE: In my case, i was working with root device so my app has super user permission.
As it turns out, I ended up not getting the MAC address from Android. The bluetooth device ended up providing the Android device MAC address, which was stored and then used when needed. Yeah it seems a little funky but on the project I was on, the bluetooth device software was also being developed and this turned out to be the best way to deal with the situation.
Worked great
private String getBluetoothMacAddress() {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
String bluetoothMacAddress = "";
try {
Field mServiceField = bluetoothAdapter.getClass().getDeclaredField("mService");
mServiceField.setAccessible(true);
Object btManagerService = mServiceField.get(bluetoothAdapter);
if (btManagerService != null) {
bluetoothMacAddress = (String) btManagerService.getClass().getMethod("getAddress").invoke(btManagerService);
}
} catch (NoSuchFieldException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignore) {
}
return bluetoothMacAddress;
}

Unable to get data roaming status on Moto Devices Android?

I need to get access to the Data Roaming Status on Moto Device(5.0.1)
if (Settings.Secure.getInt(context.getContentResolver(),Settings.Secure.DATA_ROAMING) == 1) {
//Data Roaming Enabled
flag = true;
} else {
// Data Roaming Disabled
flag = false;
}
I found problem with this when using a Motorola device. Secure Settings in this device are found in android.provider.MotorolaSettings.Secure where as in other devices it's android.provider.Settings.Secure.
Is there a way to resolve this or any other way to get roaming status?
One solution here, use reflection to check if Motorola classes are availables.
If they're not here, you need to use the default api, then call getInt on the available system.
Not able to test it on a Motorola device.
public static boolean isEnabled(Context context){
Class<?> baseSettingsClass = null;
// Retrieve the 'default' settings api
try {
if (android.os.Build.VERSION.SDK_INT >= 17){
baseSettingsClass = Class.forName( "android.provider.Settings$Global");
}
else{
baseSettingsClass = Class.forName( "android.provider.Settings$Secure" );
}
}catch(Exception e){}
Class<?> secureClass = null;
// Try retrieve the motorola class
try{
secureClass = Class.forName("com.motorola.android.provider.MotorolaSettings$Secure" );
}catch(Exception e){}
// If it failed, use the 'default' api class
if (secureClass == null){
if (baseSettingsClass != null){
secureClass = baseSettingsClass;
}
else{
return false;
}
}
try {
// Retrieve the getInt method
Method getIntMethod = secureClass.getDeclaredMethod("getInt", ContentResolver.class, String.class);
// Execute getInt(context.getContentResolver(), Settings.Secure.DATA_ROAMING)
int result = (Integer) (getIntMethod.invoke(null, context.getContentResolver(), (String)baseSettingsClass.getField("DATA_ROAMING").get(null)));
return result == 1;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

Reading data from Arduino UNO R3 kit

I'm trying to read the data already stored by me in the Arduino kit, I'm using the physicaloid library to achieve this. I tested the kit (reading data) by connecting it to my PC using the Type B USB cable provided by Arduino itself and using Tera Term. The data begins to transfer after I press '#' on the keyboard (specific to our implementation).
But when I connect it my Android tablet and use the test project by physicaloid to open a device and start communicating, every time I click 'open' it shows a Toast saying it cannot open. I give permission to access the USB device every time it prompts me. Here is the sample program which I had created to read the data:
if(mPhysicaloid.open()){
Toast.makeText(getBaseContext(), "communicating", Toast.LENGTH_SHORT).show();
String signalToStart = new String("#");
byte[] bufToWrite = signalToStart.getBytes();
mPhysicaloid.write(bufToWrite, bufToWrite.length);
byte[] buf = new byte[255];
mPhysicaloid.read(buf);
String data = new String(buf);
tvResult.setText(data);
mPhysicaloid.close();
}
else
Toast.makeText(getBaseContext(), "no communication with device", Toast.LENGTH_LONG).show();
Now here's what I want to know about the data coming from the Arduino USB cable: is it in the RS232 format where the Android device is not able to understand (I don't know, I may be making a blunder here by asking this data format) or is it in the USB data format that is suitable for the Android device to understand? Please help, I have searched over this the whole day. What can I do to open the device and communicate?
I finally got the idea of reading the data from serial USB device. So I thought I'd share it:
First, get all the USB devices attached (if more than one) and get a suitable interface and search for endpoints to communicate with. While initializing the USB device make sure you consider the USB device which you really want to communicate with. You can do that by considering product id and Vendor id.
The code for doing the above described..
private boolean searchEndPoint() {
usbInterface = null;//class level variables, declare these.
endpointOut = null;
endpointIn = null;
Log.d("USB","Searching device and endpoints...");
if (device == null) {
usbDevices = usbManager.getDeviceList();
Iterator<UsbDevice> deviceIterator = usbDevices.values().iterator();
while (deviceIterator.hasNext()) {
UsbDevice tempDevice = deviceIterator.next();
/**Search device for targetVendorID(class level variables[vendorId = SOME_NUMBER and productId=SOME_NUMBER] which u can find) and targetProductID.*/
if (tempDevice .getVendorId() == vendorId) {
if (tempDevice .getProductId() == productId) {
device = tempDevice ;
}
}
}
}
if (device == null){
Log.d("USB","The device with specified VendorId and ProductId not found");
return false;
}
else
Log.d("USB","device found");
/**Search for UsbInterface with Endpoint of USB_ENDPOINT_XFER_BULK,
*and direction USB_DIR_OUT and USB_DIR_IN
*/
try{
for (int i = 0; i < device.getInterfaceCount(); i++) {
UsbInterface usbif = device.getInterface(i);
UsbEndpoint tOut = null;
UsbEndpoint tIn = null;
int tEndpointCnt = usbif.getEndpointCount();
if (tEndpointCnt >= 2) {
for (int j = 0; j < tEndpointCnt; j++) {
if (usbif.getEndpoint(j).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (usbif.getEndpoint(j).getDirection() == UsbConstants.USB_DIR_OUT) {
tOut = usbif.getEndpoint(j);
} else if (usbif.getEndpoint(j).getDirection() == UsbConstants.USB_DIR_IN) {
tIn = usbif.getEndpoint(j);
}
}
}
if (tOut != null && tIn != null) {
/** This interface have both USB_DIR_OUT
* And USB_DIR_IN of USB_ENDPOINT_XFER_BULK
*/
usbInterface = usbif;
endpointOut = tOut;
endpointIn = tIn;
}
}
}
if (usbInterface == null) {
Log.d("USB","No suitable interface found!");
return false;
} else {
Log.d("USB","Suitable interface found!");
return true;
}
}catch(Exception ex){
ex.printStackTrace();
return false;
}
}
Now you have a device, USB interface, and endpoints ready for communication. Now it's time to establish a connection between your Android device and USB device.
Below is the code for this (and checking whether the connection is up and communicating):
private boolean checkUsbCOMM() {
/**Value for setting request, on the USB connection.*/
final int RQSID_SET_CONTROL_LINE_STATE = 0x22;
boolean success = false;
Log.d("USB","Checking USB Device for communication: ");
try{
Boolean permitToRead = SUSBS_usbManager.hasPermission(SUSBS_device);
if (permitToRead) {
//class level variable(connection, usbManager : declare it)
connection = usbManager.openDevice(device);
if (connection != null) {
connection.claimInterface(usbInterface, true);
int usbResult;
usbResult = connection.controlTransfer(0x21, //requestType
RQSID_SET_CONTROL_LINE_STATE, //SET_CONTROL_LINE_STATE(request)
0, //value
0, //index
null, //buffer
0, //length
500); //timeout = 500ms
Log.i("USB","controlTransfer(SET_CONTROL_LINE_STATE)[must be 0 or greater than 0]: "+usbResult);
if(usbResult >= 0)
success = true;
else
success = false;
}
}
else {
/**If permission is not there then ask for permission*/
usbManager.requestPermission(device, mPermissionIntent);
Log.d("USB","Requesting Permission to access USB Device: ");
}
return success;
}catch(Exception ex){
ex.printStackTrace();
return false;
}
}
Voila, the USB device is now able to communicate. So let's read using a separate thread:
if(device!=null){
Thread readerThread = new Thread(){
public void run(){
int usbResult = -1000;
int totalBytes = 0;
StringBuffer sb = new StringBuffer();
String usbReadResult=null;
byte[] bytesIn ;
try {
while(true){
/**Reading data until there is no more data to receive from USB device.*/
bytesIn = new byte[endpointIn.getMaxPacketSize()];
usbResult = connection.bulkTransfer(endpointIn,
bytesIn, bytesIn.length, 500);
/**The data read during each bulk transfer is logged*/
Log.i("USB","data-length/read: "+usbResult);
/**The USB result is negative when there is failure in reading or
* when there is no more data to be read[That is :
* The USB device stops transmitting data]*/
if(usbResult < 0){
Log.d("USB","Breaking out from while, usb result is -1");
break;
}
/**Total bytes read from the USB device*/
totalBytes = totalBytes+usbResult;
Log.i("USB","TotalBytes read: "+totalBytes);
for(byte b: bytesIn){
if(b == 0 )
break;
else{
sb.append((char) b);
}
}
}
/**Converting byte data into characters*/
usbReadResult = new String(sb);
Log.d("USB","The result: "+usbReadResult);
//usbResult holds the data read.
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
/**Starting thread to read data from USB.*/
SUSBS_readerThread.start();
SUSBS_readerThread.join();
}
For permission, make sure you add a PendingIntent as well add the permission to your manifest.
AndroidManifest : <uses-feature android:name="android.hardware.usb.host" />
PendingIntent:
private PendingIntent mPermissionIntent;
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
mPermissionIntent = PendingIntent.getBroadcast(MainActivity.this,
0, new Intent(ACTION_USB_PERMISSION), 0);
/**Setting up the Broadcast receiver to request a permission to allow the APP to access the USB device*/
IntentFilter filterPermission = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filterPermission);

Categories

Resources