Android MCC and MNC - android

I'm trying to get the mcc and mnc after a SIM LOADED state, in order to check if the SIM card did change without READ PHONE STATE permissions, in order to disable the app requests for some networks and in some countries that the user do not want.
Since getSimOperator() may change according to the current carrier (e.g. when the user is on roaming or not) I decided to use the getNetworkOperator().
Although this method can return null even if the SIM is LOADED and may return different results e.g. a lycamobile card with GSM only connection is giving me mnc = 01 and when I take the SIM card out and put it again it gives me mnc = 04.
Does some one know why the mnc gives different results for the getNetworkOperator()? Which method is better, getNetworkOperator() or getSimOperator() for this case?
Also, I cannot use getResources().getConfiguration().mcc because it gives a int number which might remove the 0 before e.g. gives 4 instead of 04.
This is my code to check a SIM state change:
#Override
public void onReceive(final Context context, Intent intent) {
if (intent != null) {
Bundle extras = intent.getExtras();
if (extras != null) {
String ss = extras.getString(EXTRAS_SIM_STATUS);
if (ss != null && (ss.equals("LOADED"))) {
TelephonyManager telephonyManager = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && hasPermissions(READ_PHONE_STATE)) {
//here I get the imsi
}else{
L.d("NetworkOperator result %s", telephonyManager.getNetworkOperator());
//saving in shared preferences in order to check if the sim is allowed or not
//this is also called on application onCreate() so I can check the current SIM
}
}
}
}
}
PS: the SIM card that I'm using only has GSM connection. Also I tried with an other card (with 4g capabilities) and everything works as expected the mnc is the same 01 for a vodafone card.

Let me share some information with you.. It may help.
MCC, MNC, PLMN
First, you must understand MCC and MNC:
MCC - Mobile Country Code
This is the country code. It always has 3 digits. Some countries can use more than one MCC.
MNC - Mobile Network Code
This is the network code. It can have 2 or 3 digits.
MCCMNC - PLMN Number.
Those numbers are used to differentiate one carrier from another one.
For example, you can check a list at www.mcc-mnc.com:
AT&T in US has following PLMN Number:
MCC = 310
MNC = 410
PLMN = 310410
Telefonica in Brazil, for example, has following PLMN Number:
MCC = 724
MNC = 10
PLMN = 72410
Based on the information above, we can imagine that we have PLMNs with 5 or 6 digits.
Since MNC can have 2 or 3 digits, I think you can retrieve the MNC number as follows (knowing that MCC always have 3 digits):
String plmn = "33402"; // OR 334020
String mcc = plmn.substring(0,3);
String mnc = plmn.substring(3,plmn.length());
System.out.println("plmn[" + plmn + "] mcc[" + mcc + "] mnc[" + mnc + "]");
All carriers in same country share the same MCC numbers. Some countries has more than one MCC... But even then, all carriers in that country uses one of that MCC.
So, if you want to check the location of some carrier, you must read its MCC (and never its MNC).
SIM and Network
One information stored in the simcard is that PLMN number. The PLMN number is retrieved from its IMSI. Its PLMN number are the 5 or 6 first digits of its IMSI.
This way, you can determine which carrier owns that SIMCard.
On the other rand, the network tower also broadcast its PLMN.
This way, an device can search for its HOME network every time you reboot you device (or move to another location).
HOME vs ROAMING
Home situation is when PLMN Number from the simcard is the same of the current registered network.
Roaming is when the SIMCard has a different PLMN than the registered cell.
Of course, some carriers has more than one PLMN Number and phone should be considered HOME in those situations. But this is another story.
Finally getSimOperator() and getNetworkOperator()
getSimOperator() This method returns MCCMNC (PLMN) from SIMCard
getNetworkOperator() This method returns MCCMNC (PLMN) from current camped Carrier
It is up to you
Now, you know the difference between simcard and network operator. So, you have to decide how to block/allow your app.
If you block by getSimOperator() you are blocking the simcards from a specific country. This way, regarless the registered cell, feature will remain blocked (even if the user is travelling to a country with the feature enabled).
If you block by getNetworkOperator() you are blocking the feature in specific networks/Contries. This way, if the user which can use the app, may have issues when he travells to a country where the feature is blocked.

Related

TrafficStats API's getMobileRxBytes() and getMobileTxBytes() not working properly

The issue is known: when wifi is up, TrafficStats.getMobileRxBytes() and getMobileTxBytes() return 0 since Lollipop (https://issuetracker.google.com/issues/37009612).
I found a workaround ignoring zero values, except that on some devices (e.g Samsung 5G), when on wifi,  we get non-0 values. It brings only rmnet1 interface value traffic (rmnet1 is for VoLTE, rmnet0 for normal data).
1/ why only on Samsung devices? while it seems to be handled by Android OS
2/ another observation still on Samsung 5G devices (at least on Samsung S20): when wifi is down, cell counter (all cell traffic since boot: rmnet0 + rmnet1) is inconsistent, sometime we get a value V1, sometime a value V2 (different from V1)
A similar experience?
I haven't used 5G enabled devices, but as of i know, using NetworkStats.Bucket's object we can query for RxBytes and TxBytes with the help of querySummaryForDevice() which takes around 4 parameters
Connectivity(Mobile data, WIFI)
SubscriberID(here for OS 10+ we need to pass null and for previous versions we need to
pass subscriber id using TELEPHONY_SERVICE).
Starttime
Endtime.
you can get subscriberID using below code
private String getSubscriberId(Context context, int networkType) {
String deviceUniqueIdentifier = null;
if (ConnectivityManager.TYPE_MOBILE == networkType) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
return tm.getSubscriberId();
}
return "";
}
This worked for me to get the RxBytes and TxBytes.

Android get MCC + MNC String for neighbouring cells

The mobile country code consists of three decimal digits and the mobile network code consists of two or three decimal digits (for example: MNC of 001 is not the same as MNC of 01) https://en.wikipedia.org/wiki/Mobile_country_code
so to get MCC + MNC as a string how do you get the phone's MCC and MNC in Android? can be used for the main serving cell for an SIM-card slot.
However, instead, I need these values for neighboring cells.
telephonyManager::getAllCellInfo
returns the list of cells where each nicely holds this information (in theory), but I cannot access it.
My current minSdkVersion=26.
getAllCellInfo()!!.mapNotNull { cell ->
when (cell) {
is CellInfoGsm -> {
println(cell.cellIdentity.mcc)
println(cell.cellIdentity.mccString) // only available from 28 onwards
}
}
}
...
Where:
cell.cellIdentity.mcc is deprecated
cell.cellIdentity.mcc returns an int. I.e. I miss any of the leading 0 digits.
How can these be preserved to accurately find the right MCC/MNC?
You can use new mccString method only for Android 28 and above. On older API there is no way to get distinct representations for "01" and "001". However these values should be used only for testing purposes and should not be encountered in real life. So, you can just suppress deprecation for mcc.
telephonyManager.allCellInfo.forEach { cell ->
if (cell is CellInfoGsm) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
println(cell.cellIdentity.mccString)
} else {
#Suppress("DEPRECATION")
println(cell.cellIdentity.mcc)
}
}
}

GSM RSSI and LTE RSSI and RSRP

I am looking to create an app that gets information about the phones connection to the cellular network.
My understanding is that RSSI is a measure of cellular signal with GSM and RSRP is a good measure for LTE.
To keep it consistent, is it possible to get a RSSI measure for LTE?
I am confused about what classes to use to get some of this information. At the moment, I am using the phone state listener which gives me a SignalStrength object. Using this object, I can call the two string method that provides me the following information when i split it. I am a little confused on what some of this means.
String ssignal = signalStrength.toString();
String[] parts = ssignal.split(" ");
The parts[] array will then contain these elements:
part[0] = "Signalstrength:" _ignore this, it's just the title_
parts[1] = GsmSignalStrength
parts[2] = GsmBitErrorRate
parts[3] = CdmaDbm
parts[4] = CdmaEcio
parts[5] = EvdoDbm
parts[6] = EvdoEcio
parts[7] = EvdoSnr
parts[8] = LteSignalStrength
parts[9] = LteRsrp
parts[10] = LteRsrq
parts[11] = LteRssnr
parts[12] = LteCqi
parts[13] = gsm|lte|cdma
parts[14] = _not really sure what this number is_
What is part 8 providing? RSSI?
Also, when you look at the signal strength in the android settings, it gives you the RSSI for GSM. When connected to LTE, is it giving us the RSRP or RSSI? It seems its providing RSRP.
My understanding is that part[1] provides the RSSI when connected on GSM. However, i am unsure, and interested about, part[2] (what is the rate measured against? what unit of time), part[8] (what does it measure exactly?), part[10] and part [11](what unit is it measured in and what is the unit range)
I understand this thread is all over the place. Hopefully it makes a little bit of sense and someone can clear something up.
Cheers guys!
To put it simply, RSSI and RSRP are signal level measurements for GSM and LTE, respectively. They are not exactly the same, because GSM and LTE are very different technologies. However, they both indicate the same type of information. RSRP holds no meaning in GSM and RSSI means something different in LTE.
This question may be worth reading:
How to get LTE signal strength in Android?
Most of what you are looking for, I was able to find here: https://developer.android.com/reference/packages.html
GsmSignalStrength - GSM Signal Strength, valid values are (0-31, 99) as defined in TS 27.007 8.5
GsmBitErrorRate - GSM bit error rate (0-7, 99) as defined in TS 27.007 8.5
CdmaDbm - CDMA RSSI value in dBm
CdmaEcio - CDMA Ec/Io value in dB*10
EvdoDbm - EVDO RSSI value in dBm
EvdoEcio - EVDO Ec/Io value in dB*10
EvdoSnr - Signal to noise ratio. Valid values are 0-8. 8 is the highest.
I could not locate the following, but here is what I suspect:
LteSignalStrength - LTE Signal Strength in ASU (0-31, 99)
LteRsrp - LTE RSRP value in dBm
LteRssnr - LTE SINR value in dB
LteCqi - LTE CQI (no units)
gsm|lte|cdma - Network type

Private vs public addresses in Bluetooth low energy on Android

A Bluetooth low energy device is uniquely identified by it's address (in the Android API they call this the MAC address and denote it as colon separated hex values e.g. 11:aa:22:bb:33:cc).
But to uniquely identify a BLE address you need to know if it's a public or a private address. In essence, 49 bits are necessary to identify an address, not 48.
Random addresses can be either static random, non-resolvable private or resolvable private and these types are separated by a bit pattern in the two most significant bytes (11, 00 and 10 respectively).
But I don't see anywhere that you can separate public and random addresses just by looking at the 48 bits in the address.
So how does this work in the Android API? How do they know what device to connect to when they don't know if the address you've specified are public or random?
The API in question is for instance the getRemoteDevice function. It says:
Valid Bluetooth hardware addresses must be upper case, in a format such as
"00:11:22:33:AA:BB". The helper checkBluetoothAddress(String) is available
to validate a Bluetooth address.
A BluetoothDevice will always be returned for a valid hardware address,
even if this adapter has never seen that device.
So you give the function 48 bits of data and there is no way to tell it if the address is public or private. This means the device is not uniquely identified.
Since nobody else seems to have an answer to offer I started testing on my own.
I tried making an app that creates a device from a string representation of an address and tried setting up my device with the 48 bit address alternating the public or private bit to see what the Android stack does.
private final BluetoothGattCallback leGattCallback = new BluetoothGattCallback() {
#Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i("Fisken", "Gatt connected " + gatt.getDevice().getAddress() + " status " + status);
if (status != BluetoothGatt.GATT_SUCCESS) {
Log.w("Fisken", "Disconnect and close");
gatt.disconnect();
gatt.close();
}
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i("Fisken", "Gatt disconnected " + gatt.getDevice().getAddress() + " status " + status);
if (status != BluetoothGatt.GATT_SUCCESS) {
Log.w("Fisken", "Disconnect and close");
gatt.disconnect();
}
gatt.close();
}
}
};
BluetoothAdapter mBluetoothAdapter = ((BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
BluetoothDevice d = mBluetoothAdapter.getRemoteDevice("FF:55:44:33:22:11");
d.connectGatt(this, false, leGattCallback);
With this code, if I start my BLE peripheral with a random address everything works as expected. However, if I try running it with the same address with the public bit set, logcat says "Gatt connected", but that's just not true. And I'm never able to disconnect.
Update: I did some more testing to figure this out. The onConnectionStateChange event I get is just the connection attempt timing out. The status is set to either 133 (if I get STATE_CONNECTED) or 257 (if I get a STATE_DISCONNECTED) and I've seen both. In either case I should (and now do in the sample code) cancel the connection attempt and close the client.
I've also found out that if I do a scan first, so that the device I'm trying to connect to have been seen recently and then do a connect based solely on the device mac address then I am able to connect to both random and public addresses without any trouble.
So this seems to be a bug/and or missing feature in the Android API. It does not allow you to connect to a public address without first having scanned for it. It does however work for random addresses.
It is possible to guess if the address is public or random, though it will not work in every case.
As you say above, in case of a random address, both MSB are either 00xx, 01xx or 11xx... so if it is 10xx, then it is a public address (from a company whose OUI starts with 8,9, A or B)
Also, the number of registered OUI is very limited compared to what is existing, so by searching the potential OUI in the IEEE database, a matching result will probably mean a public adress.
Registered OUI count: ~20500, so 0.12% out of 2^24 bits and 0.48% out of 2^22 bits.
Without the IEEE database, it is possible to rely on the fact that the first LSB of a OUI is always 0, and the second LSB is almost always 0 (actually, it should be always 0 as these addresses are universally administered).
Also, other statiscal analysis can be used: for instance, 60% of the OUI start with 00. On the other hand, a non resolvable private address, has only a probability of 1.66% to start with 00 (with uniform random generator).
I think your original 'need 49 bits to distinguish between public and random addresses' is correct. I cannot find anything in the encoding of an IEEE public address which restricts the MSB to be '10' which, if true, would solve the problem.
So the only thing one can use is the 'random address' bit setting in the advertisements of the peripheral or the equivalent bit setting in the connection initiation packet of the central. If these bits are not set, then the address the said endpoint exposes is public.
I will add:
From core spec Vol 6 Part B section 1.3 Device addresses:
Calling MS = most significant
Static random address: two MB bits of MS byte are 1 1 such that the MS byte is 11xxxxxx & with 0xC0
Non-resolvable private address: two MB bits of MS byte are 0 0 such that the MS byte is 00xxxxxx & with 0x00
Resolvable private address: two MB bits of MS byte are 0 1 such that the MS byte is 01xxxxxx & with 0x40
There is no way to distinguish a public address from one of the above types of addresses without also having the address type flag. Thus the need for the '49th' bit (the extra flag). The address alone does not do it!
Whether the advertising address is public or private is set in the header of the advertising message. Setting the address type in the application layer to public means that the BLE link layer will transmit the actual "MAC"address. Setting the address type to static/private resolvable, indicates that the BLE link layer will scramble the address with an identity resolving key (IRK).
You can differentiate between public and private addresses by looking at the 2 most significant bits. An address is 48 bits long, not 49. See Core Bluetooth Specification v4.2, Vol 6, Part B, Section 1.3.
This seems too obvious for everyone to have missed but from what I have seen the PDU Hdr bytes contain a TxAdd bit that indicates whether or not the MAC is public or private...

Visualize the wifi signal strength

I was able to scan all wifi networks with the wifimanager.getScanResults().
In the next step I extracted the SSID and signal level of the wifi network.
private List<String> showOnlySSIDAndLevel(List<ScanResult> networks) {
List<String> networkList = new ArrayList<String>();
for(ScanResult result : networks){
int level = WifiManager.calculateSignalLevel(result.level, 5) + 1;
String content = result.SSID + " \tSignal strength: " + level;
networkList.add(content);
}
return networkList;
}
I have the signal level in a format, where it's between 1 - 5 (5 is the best signal level)
How can I use this to have a graphical representation of the different wifi signal levels.
I coudn't find a UI for this. Do I need a graphic for each of the 5 possible level or is there another way? How is the internal Android Settings (Wifi scan results) doing it?
I think found a better option ...
imageView.setIcon(android.R.drawable.stat_sys_wifi_signal_<strength>)
where strength is a variable in the range of 0 to 4...
For more details refer to
http://androiddrawableexplorer.appspot.com/
You can use the built-in RatingBar component. You can make it read-only by giving it a ratingBarStyleSmall or ratingBarStyleIndicator style.

Categories

Resources