I am currently writing an application for a client who wants to gather data regarding the signal strength at set intervals.
Currently I am using this code:
private static class MyPhoneStateListener extends PhoneStateListener
{
#Override
public void onSignalStrengthsChanged(SignalStrength signalStrength)
{
super.onSignalStrengthsChanged(signalStrength);
telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
InfoStore.setSignal(String.valueOf(signalStrength.getGsmSignalStrength()));
}
};
This works fine, however the client wants the signal strength in both level (I guess how many bars?), DBM, and ASU.
Anyone have any clue how to read the signal strengths using those different forms?
As mentioned by Charles Ma and Kevin Krumwiede the relevant Android methods are hidden (probably for good reason), however it is still possible to get the values by reflection. Thus one solution to original question:
private class MyPhoneStateListener extends PhoneStateListener
{
public static final int INVALID = Integer.MAX_VALUE;
public int signalStrengthDbm = INVALID;
public int signalStrengthAsuLevel = INVALID;
#Override
public void onSignalStrengthsChanged(SignalStrength signalStrength)
{
signalStrengthDbm = getSignalStrengthByName(signalStrength, "getDbm");
signalStrengthAsuLevel = getSignalStrengthByName(signalStrength, "getAsuLevel");
}
private int getSignalStrengthByName(SignalStrength signalStrength, String methodName)
{
try
{
Class classFromName = Class.forName(SignalStrength.class.getName());
java.lang.reflect.Method method = classFromName.getDeclaredMethod(methodName);
Object object = method.invoke(signalStrength);
return (int)object;
}
catch (Exception ex)
{
return INVALID;
}
}
}
In android 4.x the SignalStrength class has getAsuLevel, getDbm, as well as getLevel (bars) methods.
If you need this to work for older android versions, have a look at the source code and you can copy the implementations of those methods over.
http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.1.2_r1/android/telephony/SignalStrength.java/
The only thing that you can't get is the Lte measurements in older android versions, but you can probably use java reflection to see if the getLte* methods exist and call it.
Calculate dBm by
int SignalStrength_ASU = signalStrength.getGsmSignalStrength();
int SignalStrength_dBm = (2 * SignalStrength_ASU) - 113; // -> dBm
Related
I am trying to test unit my ServiceHelper class that is providing a list of Wifi's SSID to show in my view.
So I am providing an observable using RxJava + RxAndroid. As you can see I have chosen the Observable.create() to create from native my observable (Arraylist of Items) witch is depending on a BroadcastReciever inner class.
I need to test unit this method. I don't have a good idea about how to test or mock this kind of service that does depend on Sensors.
public class WifiPhoneServiceHelper {
private Context context;
private ArrayList<String> ssidList = new ArrayList<>();
private List<ScanResult> scanResultList;
private WifiManager wifiManager;
public WifiPhoneServiceHelper(Context context) {
this.context = context;
}
public Observable<ArrayList<String>> getObservableSsidWifiList() {
return Observable.create(new Observable.OnSubscribe<ArrayList<String>>() {
#Override
public void call(final Subscriber<? super ArrayList<String>> subscriber) {
wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
scanResultList = wifiManager.getScanResults();
for (int i = 0; i < scanResultList.size(); i++) {
String ssidDetected = scanResultList.get(i).SSID;
ssidList.add(ssidDetected);
}
subscriber.onNext(ssidList);
}
}
};
context.registerReceiver(receiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
if (!wifiManager.isWifiEnabled()) {
wifiManager.setWifiEnabled(true);
}
wifiManager.startScan();
subscriber.add(new MainThreadSubscription() {
#Override
protected void onUnsubscribe() {
context.unregisterReceiver(receiver);
Timber.d("unregisterReceiver BroadcastReceiver SCAN_RESULTS_AVAILABLE_ACTION");
if (!subscriber.isUnsubscribed()) {
subscriber.unsubscribe();
}
}
});
}
});
}
In order to unit test your code you need to description of all functionality into separate interface and then implement it for Android and create mock implementation for unit tests (Mockito).
Alex effectively suggests that you create an interface between your code and the Wi-Fi system, and in production code have it implemented by Android's WifiManager, but in testing, implemented by your own test harness.
You could certainly do that but another option is Robolectric. This is an Android unit test framework that can be used to mock large parts of the Android system without doing as much groundwork as you would with Mockito. Specifically it can mock the Wi-Fi system and therefore you can have it pretend that a given network exists or doesn't. The advantages to this are you can largely leave your Wi-Fi using code as it is, without heavy refactoring for test, but you still don't need a real device and it's not dependent on the real world's networking environment.
In particular look for examples based around ShadowWifiManager. The other half of the puzzle is producing event broadcasts to stimulate your code, but this should also be feasible.
It's beyond the scope of this answer to fully describe the establishment of a Robolectric test environment, but hopefully that gives you something to begin researching around.
I am defining a class (RemoteBluetoothDevice), within which there is a nested inner class for bluetooth low energy associated parameters. Basically, if the discovered remote bluetooth device is of type DEVICE_TYPE_CLASSIC, the first four private member variables are set accordingly, using available APIs, such as getUuids(), getType(), etc. When the device type is determined as DEVICE_TYPE_LE or DEVICE_TYPE_DUAL, I would like to instantiate the inner class (i.e., leCharacteristics) to hold all related BLE related parameters (those listed herein and the one I might add in future). There is a private member variable (i.e., leCharacteristicsReference) which will hold the reference to the inner class instance in case we instantiate it when the discovered device supports BLE.
public class RemoteBluetoothDevice {
private BluetoothDevice device;
private Parcelable[] bluetoothRemoteDeviceUuids;
private RelationWithRemoteDeviceState bluetoothRemoteDeviceBondingRequestAndConnectionStatus;
private RemoteBluetoothDeviceType remoteBluetoothDeviceType;
private leCharacteristics leCharacteristicsReference = null; // Ref to inner class instance
private class leCharacteristics {
private int bluetoothRemoteLeDeviceRssi;
private byte[] bluetoothRemoteLeDeviceScanRecord;
private leCharacteristics(int rssi, byte[] scanRecord) {
this.bluetoothRemoteLeDeviceRssi = rssi;
this.bluetoothRemoteLeDeviceScanRecord = scanRecord;
}
}
My main question here relates to design patterns and the best possible practice in defining such nested classes. Since a device may or may not support BLE, the outer class may or may not have to have its inner class instantiated when being constructed (using any of its constructors). In fact, depending on what the device type is, we may choose to instantiate the inner class or not. I am aware of few (but not all TBH) design patterns in Java but none would be applicable to this. So, any help is highly appreciated.
Thanks!
[EDIT] After some research, I came across with "Local Inner Classes". Since I had never used them before, I had no idea if they could possibly be applicable to the aforementioned case (I admit that I am still not sure if this is good practice at all!). What I understood, I could add an if clause where I check it the discovered device supports BLE. IF yes, then I place the inner class inside the if clause and declare it therein. I could define getter and setter methods for it so that the outer class (in my case, RemoteBluetoothDevice) is able to set or get the inner class variables.
Here is what I came up with so far:
public RemoteBluetoothDevice(BluetoothDevice newDevice) throws IllegalArgumentException {
if (newDevice.getType() == BluetoothDevice.DEVICE_TYPE_CLASSIC) {
this.device = newDevice;
if (device.getUuids() != null)
this.bluetoothRemoteDeviceUuids = newDevice.getUuids();
else
this.bluetoothRemoteDeviceUuids = null;
this.remoteBluetoothDeviceType = RemoteBluetoothDeviceType.DEVICE_TYPE_CLASSIC;
} else {
throw new IllegalArgumentException();
}
}
// General constructor which also sets BLE-related parameters
// when this class is instantiated with relevant arguments
public RemoteBluetoothDevice(BluetoothDevice newDevice, int rssi, byte[] scanRecord)
throws IllegalArgumentException {
if (newDevice.getType() == BluetoothDevice.DEVICE_TYPE_LE ||
newDevice.getType() == BluetoothDevice.DEVICE_TYPE_DUAL) {
// Nested local inner class to represent bluetooth low energy
// related capabilties of the remote bluetooth device
class leCharacteristics {
private int bluetoothRemoteLeDeviceRssi;
private byte[] bluetoothRemoteLeDeviceScanRecord;
public leCharacteristics(int rssi, byte[] scanRecord) {
this.bluetoothRemoteLeDeviceRssi = rssi;
this.bluetoothRemoteLeDeviceScanRecord = scanRecord;
}
public void bluetoothRemoteLeDeviceRssiSetter(int newBluetoothRemoteLeDeviceRssi) {
this.bluetoothRemoteLeDeviceRssi = newBluetoothRemoteLeDeviceRssi;
}
public int bluetoothRemoteLeDeviceRssiGetter() {
return this.bluetoothRemoteLeDeviceRssi;
}
public void bluetoothRemoteLeDeviceScanRecordSetter(byte[] newBluetoothRemoteLeDeviceScanRecord) {
this.bluetoothRemoteLeDeviceScanRecord = newBluetoothRemoteLeDeviceScanRecord;
}
public byte[] bluetoothRemoteLeDeviceScanRecordGetter() {
return this.bluetoothRemoteLeDeviceScanRecord;
}
}
this.device = newDevice;
// Instantiate the nested inner class by invoking its constructor
// and relevant arguments corresponding to the BLE remote device
leCharacteristics leCharacteristicsReference = new leCharacteristics(rssi, scanRecord);
if (device.getUuids() != null)
this.bluetoothRemoteDeviceUuids = newDevice.getUuids();
else
this.bluetoothRemoteDeviceUuids = null;
if (this.device.getType() == BluetoothDevice.DEVICE_TYPE_LE) {
this.remoteBluetoothDeviceType = RemoteBluetoothDeviceType.DEVICE_TYPE_LE;
} else if (this.device.getType() == BluetoothDevice.DEVICE_TYPE_DUAL) {
this.remoteBluetoothDeviceType = RemoteBluetoothDeviceType.DEVICE_TYPE_DUAL;
} else {
this.remoteBluetoothDeviceType = RemoteBluetoothDeviceType.DEVICE_TYPE_LE;
}
} else {
throw new IllegalArgumentException();
}
}
Here, I decided to define two different constructors for the outer class. When a remote device, which does support BLE, is discovered, the second constructor is called, which also comes with two more arguments, namely, RSSI and scanRecord. In this case, the inner local class is defined, for which a constructor is further defined (I think I could simply place a block {..} where the inner class' internal members could be initialized). On the other hand, when a classic device is discovered, the first and simpler constructor of the outer class is called, where we do not have any declaration and instantiation for any inner class.
I eventually implemented this using both "Local Inner Classes" as well as stand-alone class which can be instantiated by the main RemoteBluetoothDevice class. It turned out that the latter was a cleaner solution as it did not require sequential instantiations, which could be overlooked throughout the code.
in java i know this code is a good programming practice,
but i read some article there are good programming practice that
is bad for Android, i just want to know if this type of code
can affect the Aplication Performance issue when it comes to android programing?
for example
public class Main {
static int age = getAge(10); /***************** THIS LINE */
public static void main(String[] args) {
System.out.println(age);
}
private static int getAge(int i) { /***************** THIS METHOD */
i = i + 1;
return i;
}
}
This situation seems perfectly fine and wouldn't effect performance.
I personally would be careful with this practice though, you could potentially call a method dependant on variables that are yet to be initialized.
public class Main {
static int age = getDogYears(10);
int dogRatio; // dogRatio is not yet initialized
public static void main(String[] args) {
System.out.println(age);
dogRatio = 7;
}
private static int getDogyears(int i) {
i = i * dogRatio; // null pointer exception because dogRatio is not initialized
return i;
}
}
If the problem is really this simple though I would also ask why you wouldn't just make age = 11;
You're not going to see any noticeable performance hits, and I can't see anything wrong with the code, but it could be improved.
This method is a lot easier to read and cleans up the code a little:
private static int getAge(int i){
return i++;
}
You also then have to wonder why you even need it. As you're passing it an integer and not the value of a variable, why not just initialise it without the method call?
static int age = 11;
I'm trying to creat an app that would save the current signal strength. So far I've seen many examples that are all extending PhoneStateListener, but all of them use a Toast to display this information, like in this example:
http://www.firstdroid.com/2010/05/12/get-provider-gsm-signal-strength/
I was wondering a couple of things:
a) Do I always have to use the PhoneStateListener AND override the onSignalStrengthsChanged(SignalStrength signalStrength) ?
b) How can I access the value signalStrength.getGsmSignalStrength() from outside the PhoneStateListener class?
Thanks in advance
U can try with it. I cant say its the final solution.If u want to access certain data from other activity, then try it with shared preference.Shared preference value is visible from other activities
I might be late to answer your question, but if you are still looking for the answer, here it is:
a) yes you have to use the PhoneStateListener and override the onSignalStrengthsChanged as, in my knowledge that is the only way to get current cells' signal strength for GSM. The listener is only called in big signal strength changes, so you yourself cannot control the listener. the listener will automatically update or make a toast when it is called. So, it is better to declare the listener and ask it to listen at onCreate().
b) For accessing the the RSSI value from outside the Listener is not really difficult, just store the value in a variable and make a method like getRSSI(), which will return you the value when its called. The example is given below:
public class GsmRSSI extends Activity{
MyPhoneStateListener MyListener;
TelephonyManager Tel;
ArrayList<String> signalStrength = new ArrayList<String>();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyListener = new MyPhoneStateListener();
Tel = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
Tel.listen(MyListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
timer.schedule(new TimerTask() {
public void run() {
String rssi = MyListener.getStrength();
if(!rssi.equals(""))
signalStrength.add(rssi);
}
}, 0, 5000);//it will add the rssi value after every 5000ms
}
private class MyPhoneStateListener extends PhoneStateListener {
String gsmStrength = "";
#Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
super.onSignalStrengthsChanged(signalStrength);
gsmStrength = String.valueOf(signalStrength.getGsmSignalStrength()* 2 - 113);
}
public String getStrength() {
return gsmStrength;
}
}
}
This should do the work for you. But at the begining you might not get any rssi value for a little while as the listener is only called when there is a significant change in the rssi.
I am trying to get bit Error rate by using getGthe following coding but i am always get -1 only. Do you anybody have idea to get valid Bit Error Rate. please help to get correct value.
The code are following.
public class GetGsmSignalStrengthActivity extends Activity {
TelephonyManager Tel;
MyPhoneStateListener MyListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Tel = ( TelephonyManager )getSystemService(Context.TELEPHONY_SERVICE);
Tel.listen(MyListener ,PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
}
private class MyPhoneStateListener extends PhoneStateListener
{
#Override
public void onSignalStrengthsChanged(SignalStrength signalStrength)
{
super.onSignalStrengthsChanged(signalStrength);
TextView dummy1 = (TextView)findViewById(R.id.textView5);
String x,m ="";
x=String.valueOf(signalStrength.getGsmBitErrorRate());
dummy4.setText("Bit Error : " +x);
}
}
}
Read this bug report comment #4. You see that it is optional and therefore possible. It will return -1 if it isn't provided by the modem manufacturer.
Quote:
The problem is that AT+CSQ in TS 27.007 section 8.5 is listed as optional. Therefore the modem manufacturer has the choice whether or not to provide both the signal and bit error rate using standard interfaces. So when you see -1 that means the modem people haven't implemented it.