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.
Related
List<CellInfo> cellInfoList = telephonyManager.getAllCellInfo();
First, I got total cellInfoList, and separated to CellInfo
cellRSRP = ((CellSignalStrengthNr) ((CellInfoNr) cellInfo).getCellSignalStrength()).getCsiRsrp();
I tried to get 5G RSRP rate for a few days, and this is the best I can approach and this doesn't work.
When It comes to LTE Cell Data,
cellRSRP = ((CellInfoLte) cellInfo).getCellSignalStrength().getRsrp();
It works like this, and It outputs the value well.
The Difference of these two Codes is:
Change CellInfoLte -> CellInfoNr
Additional CellSignalStrengthNr casting
(Because (CellInfoNr) cellInfo).getCellSignalStrength() returns CellSignalStrength,
Not CellSignalStrengthNr.)
(Deliberately casted.)
Is 5G Cell Signal Strength measurement isn't ready by now?
I've spent a lot of time in this problem, but didn't found a single solution.
I have a similar issue and the following casting seems to be working. Unfortunately, there is not possible to test it on real 5g radio in our country.
int mCsiRsrp = ((CellSignalStrengthNr) ((CellInfoNr) cellInfo).getCellSignalStrength()).getCsiRsrp();
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.
I am capturing the RSSI values of LTE signals using the below code:
cInfoList = telephony_manager.getAllCellInfo()
for (CellInfo info : cInfoList){
if (info instanceof CellInfoLte) {
CellSignalStrengthLte signalstrength_lte = ((CellInfoLte) info).getCellSignalStrength();
displayAsu.setText(signalstrength_lte.getAsuLevel() + "");
displayDbm.setText(signalstrength_lte.getDbm() + "");
}
}
(*note: I just simplified my code: for-loop doesn't override text fields.)
In one phone (LG G4) I am getting meaningful vales: Asu_level=32, dbm=-108
But in another phone (Samsung Galaxy S6) I am getting invalid values: Asu_level=97, dbm=1022
In Samsung phone's Settings->AboutPhone->Status->SignalStrength I see -107dBm 33 asu (which make sense)
LG G4: Android 5.1, API 22 and
Samsung Galaxy S6: Android 5.0.2, API 21
Why does the same code show different behaviors (Asu levels) on different phones?
The S6 appears to put corrupted signal level values in its CellInfoLte objects (and unset levels in its CellInfoCdma objects). https://github.com/Tombarr/Signal-Strength-Detector is an app which uses reflection to dump out a plethora of signal level related data. On my S6, I can see that SignalStrength (which is the parameter to the PhoneStateListener.onSignalStrengthsChanged callback) includes sane looking mCdmaDbm/mLteRsrp values. It's obviously less convenient and presumably more overhead to create a TelephonyManager listener but it looks like that's what it takes on this device :-/
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...
I currently have a HC-06 Bluetooth device connected to my Arduino Mega 2560 in order to receive strings sent from an Android device. With the HC-06 on Serial 0, I am receiving the data without error with the following code:
String inString = "";
int index = 0;
boolean stringComplete = false;
void setup() {
Serial.begin(9600);
pinMode(pwmPin, OUTPUT);
}
void loop() {
if(stringComplete) {
ParseSerialData(); // Parse the received data
inString = ""; // Reset inString to empty
stringComplete = false; // Reset the system for further input of data
}
}
void serialEvent() {
while(Serial.available() && stringComplete == false) {
char inChar = Serial.read();
inData[index] = inChar; // Store it in char array
index++;
if (inChar == '\n') { // Check for termination character
index = 0; // Reset the index
stringComplete = true; // Set completion of read to true
} else {
inString += inChar; // Also store as string
}
}
}
When I try to replace "Serial" with "Serial1" and "serialEvent()" with "serialEvent1()" and move the Bluetooth device to the TX1 and RX1, this program no longer works.
I have read that some people had similar problems when using AVR-GCC 4.4.x and solved the issue by downgrading to 4.3.x, but I have 4.3.2 (on Windows 8.1, same problem has arisen with Arduino IDE 1.0.3, 1.0.5-r2, and 1.5.6-r2).
I added the following print statements (with Serial 0 to print to the monitor on my PC) to the code with the Bluetooth device still on Serial 1:
String inString = "";
int index = 0;
boolean stringComplete = false;
void setup() {
Serial1.begin(9600);
Serial.begin(9600);
pinMode(pwmPin, OUTPUT);
Serial.println("Setting up...");
}
void loop() {
if(stringComplete) {
ParseSerialData();
inString = "";
stringComplete = false;
}
}
void serialEvent1() {
Serial.println("In serialEvent1...");
while(Serial1.available() && stringComplete == false) {
Serial.println("Char in...");
char inChar = Serial1.read();
Serial.println("WTF");
Serial.println(inChar);
Serial.println("WTF2");
inData[index] = inChar;
index++;
if (inChar == '\n'){
Serial.println("Termination char read...");
index = 0;
stringComplete = true;
}else{
inString += inChar;
}
}
}
Doing this, on the monitor I get:
Setting up...
In serialEvent1...
Char in...
WTF
WTF2
inChar typically prints as nothing, but during one test it was printing as an '#' character. The string sent is "s,1\n" from the Android device.
Based on the print out, the serial event is triggered by availability of serial data, but Serial1.available() remains true for only the first iteration, 's' is not read in (nor any of the other characters that do when Serial is used), and a termination character (newline char) is never read in so that I can begin parsing.
I also tried various baud rates with no difference in behavior. Based on reading Arduino documentation, serial port 1 should work just like serial port 0, and I did not miss substituting Serial for Serial1 in any part of the code.
What could be causing errors in communicating over Serial1 in the same way that has worked flawlessly on Serial0?
I also found a related Stack Overflow question, which was solved with something similar to my code (which works perfectly with Serial0 and is based on the Arduino documentation) using an expected termination character (the difference being that his code implements serial reading in the main loop, whereas mine is in a serialEvent). For some reason, it seems that both of us were having issues with Serial1 data not showing as available at the start of the next iteration. For some reason, serialEvent1 is not being called again for me. And I still don't understand why the first/only character read is not 's.' At first I was thinking that the stream was getting flushed before getting to the serial event again, but that still doesn't account for reading in an incorrect first character.
Also, I added the following Serial1 print statement to run multiple times in the Arduino setup and the Android device receives it each time with no errors, so sending data is working just fine:
Serial1.print("b,1\n");
Or even
Serial1.print("A long test message. A long test message.\n");
I'm fairly close to answering my own question now with further testing/debugging. I actually think the answer may end up being hardware-related rather than software. I wanted to find out if the problem was with the data sent from the HC-06 to port 1, or with the reading function of port 1. I basically had serial port 0 read in data, then send it serially to port 1, which would read that data, and send feedback over Bluetooth to the Android device. Serial port 1 read the data fine coming from port 0, so the issue is reading in data specifically from the HC-06. It may simply be a voltage level issue, so the answer may not belong with Stack Overflow. I will leave the question unanswered though until I definitively have found the root cause (allowing for the possibility that I might need some define statement for the HC-06 or serial port 1 for data to be read correctly, though I'm guessing a voltage level conversion may do the trick. I'm not sure why there would be such a difference between Serial0 and Serial1 though).
I solved the problem enabling the pull-up resistor of the RX1 pin:
Serial1.begin(9600);
pinMode(19, INPUT);
digitalWrite(19, HIGH);
Therefore the 3 V is "overridden" by Arduino's 5 V for logical HIGH and zero is pulled down by Bluetooth's TX for logical LOW.
I did it slightly differently by using the INPUT_PULLUP feature to pull the hardware Serial3 pin HIGH:
Serial3.begin(19200);
pinMode(15, INPUT_PULLUP); // Serial3 receive pin
It works a treat. Previously my serial communications between two Arduino Mega 2560s had been mainly corruption with the occasional correct transmission. Now it is mainly the correct transmission. Previously, the time taken to receive a valid serial value was up to 2.5 seconds (due to having to repeatedly retry to get a valid transmit). Now the time taken to get a valid transmit is 20 ms. (I use a twisted pair of single core wires about 35 cm length.)
After checking the serial data lines on my oscilloscope, I was able to come up with a solution to my issue. With the setup described about the related Stack Overflow question (Bluetooth TX → RX0, TX0 → RX1, TX1 → Bluetooth RX), I checked the signals sent by the Bluetooth device and by the Arduino's serial port 0.
The Bluetooth device's signal was low at 400 mV and high at 3.68 V, while the Arduino's port 0 sent low at 0V and high at 5 V. I knew the Bluetooth device was a 3.3V level device, but the Arduino should read anything above about 3V as high, so this should have not been an issue (and it obviously wasn't on Serial 0).
Anyway, I replaced the HC-06 Bluetooth device with a backup one I had purchased and everything works fine with the code using Serial1 that I posted in my question. This Bluetooth device was transmitting low at about 160 mV and high at 3.3 V.
It seems that my problem was rooted in the hardware (voltage levels) as expected, and that for some reason Serial1 is more sensitive to changes in digital levels than is Serial0.