I am making an app that gets Wi-Fi and Mobile Data information. The Wi-Fi portion of the app is working fine, but I can't seem to get the data part working. I've heard of issues like this on Samsung phones (I'm testing on one), and I need a workaround without going above my API level of 15.
Here is my current code:
class myPhoneStateListener extends PhoneStateListener {
#Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
super.onSignalStrengthsChanged(signalStrength);
if (signalStrength.isGsm()) {
mobileStrength = signalStrength.getGsmSignalStrength();
mobileStrength = (2 * mobileStrength) - 113;
} else {
mobileStrength = signalStrength.getCdmaDbm();
}
}
}
But it returns null.
/**
* Gets the signal level from a <tt>SignalStrength</tt> as a value in the
* range 0-4. This info is hidden from the public API, so this method
* obtains it via reflection.
*
* #return the signal level, or 0 if Google has broken the hack
*/
public static int getSignalLevel(final SignalStrength signal) {
try {
final Method m = SignalStrength.class.getDeclaredMethod("getLevel", (Class[]) null);
m.setAccessible(true);
return (Integer) m.invoke(signal, (Object[]) null);
} catch (Exception e) {
Log.debug(TAG, "Google hates developers", e);
return 0;
}
}
Do you have set the permission in your AndroidManifest.xml file ?
If not, set this to read the phone state.
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Yours sincerely,
Flemming
Related
I am trying to detect 5G network. I use the telephony manager to get NETWORK_TYPE. Even if I am in 5G network coverage and my phone shows 5G, I do not get NETWORK_TYPE_NR. The NETWORK_TYPE is always 13 i.e. LTE.
The Phones Engineering service mode shows NR related data.
Is there any way to detect NR (Standalone or Non-Standalone) mode?
I also need to get the Cell Information for NR data. I use telephonyManager.getAllCellinfo(), but I never get an instance of cellinfonr.
Any help is appreciated. Thanks
I faced the same problem for a few weeks ago. In my case, I want to detect 5G network on Galaxy S20-5G but the getDataNetworkType() always return 13 NETWORK_TYPE_LTE.
Following by netmonster-core strategy, and here is the code that I extract from them to solve my problem.
public boolean isNRConnected(TelephonyManager telephonyManager) {
try {
Object obj = Class.forName(telephonyManager.getClass().getName())
.getDeclaredMethod("getServiceState", new Class[0]).invoke(telephonyManager, new Object[0]);
// try extracting from string
String serviceState = obj.toString();
boolean is5gActive = serviceState.contains("nrState=CONNECTED") ||
serviceState.contains("nsaState=5") ||
(serviceState.contains("EnDc=true") &&
serviceState.contains("5G Allocated=true"));
if (is5gActive) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
Here is full detector class from netmonster-core:
(DetectorLteAdvancedNrServiceState.kt)
Android Q added a new network type, NETWORK_TYPE_NR for 5G which is
not available for Android Pie. Recently released Samsung S10 fully supports 5G. It can show 5G icon on the status bar when it is on the 5G network.
Is it possible for a third-party app to know if Android Pie device on a 5G network or not?
Any help will be appreciated.
The following link is the definition for the new network type. It is not available on the Android Pie branch.
Source code for Pie release
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/pie-release-2/telephony/java/android/telephony/TelephonyManager.java
The latest source code that has NETWORK_TYPE_NR
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/telephony/java/android/telephony/TelephonyManager.java#2375
I believe they backported the code from Q to Pie as the logic for 5G was implemented at the end of last year in Q (alpha).
So when using
TelephonyManager.getNetworkType()
you will likely get
20 (5G)
EDIT
As per comment below: The network type will be 13 so it doesn't solve the thing.
EDIT
Try using reflection
static boolean isNRConnected(TelephonyManager telephonyManager) {
try {
Object obj = Class.forName(telephonyManager.getClass().getName())
.getDeclaredMethod("getServiceState", new Class[0]).invoke(telephonyManager, new Object[0]);
Method[] methods = Class.forName(obj.getClass().getName()).getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals("getNrStatus") || method.getName().equals("getNrState")) {
method.setAccessible(true);
return ((Integer) method.invoke(obj, new Object[0])).intValue() == 3;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
There is an official documentation to detect 5G on Android 11.
https://developer.android.com/about/versions/11/features/5g#detection
Call TelephonyManager.listen(), passing in LISTEN_DISPLAY_INFO_CHANGED, to determine if the user has a 5G network connection.
as #Hunter suggested you need to use "listen" from telephony manager but if it's api 30+ you need to have READ_PHONE permission granted and listen for LISTEN_DISPLAY_INFO_CHANGED and override onDisplayInfoChanged from PhoneStateListener
(context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager).listen(customPhoneStateListener,PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
The listener should be something like this:
private class CustomPhoneStateListener : PhoneStateListener() {
override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
super.onDisplayInfoChanged(telephonyDisplayInfo)
when (telephonyDisplayInfo.overrideNetworkType) {
//5G
OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO,
OVERRIDE_NETWORK_TYPE_NR_NSA,
OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> setNetworkChange(NETWORK_TYPE_NR)
OVERRIDE_NETWORK_TYPE_LTE_CA -> {
setNetworkChange(19) //LTE+
}
else -> setNetworkChange(telephonyDisplayInfo.networkType)
}
} else {
setNetworkChange(telephonyDisplayInfo.networkType)
}
}
}
Link: https://developer.android.com/about/versions/11/features/5g#detection
Couldn't add this as a comment, but as #Pavel Machala said, looking at the ServiceState class in the AOSP yields the following:
/**
* Get the NR 5G status of the mobile data network.
* #return the NR 5G status.
* #hide
*/
public #NRStatus int getNrStatus() {
final NetworkRegistrationState regState = getNetworkRegistrationState(
NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
if (regState == null) return NetworkRegistrationState.NR_STATUS_NONE;
return regState.getNrStatus();
}
I have extracted the ServiceState.java from SM-G977N firmware and it confirms that they have added
ServiceState.getNrStatus()
5G(NR) is active is if NetworkRegistrationState.NR_STATUS_CONNECTED = 3;
I am wondering if there is any way to get Wi-Fi hotspot status (on or off) in Android Oreo (API 26 and higher) without using reflection, because it will not work on Android P according to the new restrictions on non-sdk interfaces
I've seen that there are some methods that provide the ability to disable a WiFi hotspot using reflection, but I would like to avoid those and simply being able to know if the hotspot is enabled.
Thanks in advance!
Please look at the following code. This will help you
public class WifiApManager {
private final WifiManager mWifiManager;
public WifiApManager(Context context) {
mWifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
}
/*the following method is for getting the wifi hotspot state*/
public WIFI_AP_STATE getWifiApState() {
try {
Method method = mWifiManager.getClass().getMethod("getWifiApState");
int tmp = ((Integer) method.invoke(mWifiManager));
// Fix for Android 4
if (tmp > 10) {
tmp = tmp - 10;
}
return WIFI_AP_STATE.class.getEnumConstants()[tmp];
} catch (Exception e) {
Log.e(this.getClass().toString(), "", e);
return WIFI_AP_STATE.WIFI_AP_STATE_FAILED;
}
}
/**
* Return whether Wi-Fi Hotspot is enabled or disabled.
*
* #return {#code true} if Wi-Fi AP is enabled
* #see #getWifiApState()
*/
public boolean isWifiApEnabled() {
return getWifiApState() == WIFI_AP_STATE.WIFI_AP_STATE_ENABLED;
}
}
Where WIFI_AP_STATE is an enum which is as follows
public enum WIFI_AP_STATE {
WIFI_AP_STATE_DISABLING,
WIFI_AP_STATE_DISABLED,
WIFI_AP_STATE_ENABLING,
WIFI_AP_STATE_ENABLED,
WIFI_AP_STATE_FAILED
}
I can easily get the sigalStrength in Android via callback
onSignalStrengthsChanged(SignalStrength signalStrength)
and retrieve the signalStrength trough the passed object
int signal_strength = signalStrength.getGsmSignalStrength();
and according to the documentation the value varies between 0 and 39.99
Now I want to display this in my app in an indicator that updates as the signalStrenght varies - exactly what you can see in the statusbar on your phone.
So my question - how can I use the variable for this? If its linear its easy to just use intervalls from 1 - 10, 11 - 20 etc. But I guess its not that easy?
I know I can standardize this value just through a call to ..
int level = signalStrength.getLevel()
That is by calling getLevel then I gets a value between 0 - 4, just as RSSI does for WIFI. But the problem is that it requires api 23 and that means I can only reach approx 40% of the android market.
So - If I do not want to use getLevel, how could I use getGsmSignalStrength() accordingly?
#Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
super.onSignalStrengthsChanged(signalStrength);
int signal_strength = signalStrength.getGsmSignalStrength();
//int level = signalStrength.getLevel(); //api 23
Toast.makeText(context, "signalStrength: " + signal_strength, Toast.LENGTH_SHORT).show();
}
Try following code snippet in your onSignalStrengthsChanged. I achieved it using reflection. It works for all type of network classes. Hope it helps.
#Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
super.onSignalStrengthsChanged(signalStrength);
int level = 0;
try {
final Method m = SignalStrength.class.getDeclaredMethod("getLevel", (Class[]) null);
m.setAccessible(true);
level = (Integer) m.invoke(signalStrength, (Object[]) null);
} catch (Exception e) {
e.printStackTrace();
}
}
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 "";
}