I'm working on a research project which involves Bluetooth and the Android OS. I need to make Bluetooth discoverable indefinitely in order for the project to continue.
The Problem:
Android limits discoverability to 300 seconds.
I cannot ask the user every 300 seconds to turn discoverability back on as my application is designed to run in the background without disturbing the user.
As far as I am aware, there is no way to increase the time though Android's GUI. Some sources have called this a safety feature, others have called this a bug. There may be a bit of truth in both...
What I'm Trying / Have Tried:
I'm trying to edit a stable release of cyanogenmod to turn the discoverability timer off (it's possible; there's a configuration file that needs to have a single number changed). This isn't working because I'm having verification problems with the resulting package.
During the past week, I downloaded the cyanogenmod source code, changed a relevant class in the hope that it would make Bluetooth discoverable indefinitely, and tried to recompile. This did not work because (a) the repo is frequently changed, leading to an unstable code base which fails to compile (OR, it could be that I'm using it incorrectly; just because it looked like it was the code's fault in many instances doesn't mean I should blame it for all the problems I encountered!) and (b) the repo decides to periodically "ignore" me (but not always, as I have gotten the code base before!), replying to my synchronization/connection attempts with:
fatal: The remote end hung up unexpectedly
As you might imagine, the above two issues are problematic and very frustrating to deal with.
More Info:
I'm running Android 2.1 via cyanogenmod (v5 I believe). This means the phone is also rooted.
I have a developer phone, which means that the bootloader is unlocked.
My phone is an HTC Magic (32B).
The Big Question:
How can I make Bluetooth indefinitely discoverable on Android?
See the following link:
http://developer.android.com/guide/topics/wireless/bluetooth.html#ConnectingDevices
Specifically, the last sentence in the paragraph below:
Enabling discoverability
If you would like to make the local device discoverable to other devices, call startActivityForResult(Intent, int) with the ACTION_REQUEST_DISCOVERABLE action Intent. This will issue a request to enable discoverable mode through the system settings (without stopping your application). By default, the device will become discoverable for 120 seconds. You can define a different duration by adding the EXTRA_DISCOVERABLE_DURATION Intent extra. The maximum duration an app can set is 3600 seconds, and a value of 0 means the device is always discoverable.
So, this should work:
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0);
startActivity(discoverableIntent);
If you check out the BluetoothAdapter class
you will find the hidden method:
public void setDiscoverableTimeout(int timeout)
Now you only have to find out how to use it. You have to do a method invocation to do so.
Related
I've developed an application that streams music (via internet connection) using service and having trubles streaming content without phone going idle.
While i was developing my application each time i tried case mentioned below the music was reproducing fine.
Use case : search song, select song from results, play song, screen off -> auto play next song from result list
I'm developing using real device - Huawei Mate 20 Lite - OS v8.01 so while debugging it gotta use USB cabel.
Like i said following the use case above while hooked on USB the auto play while screen off works good. The case it doesn't work good is when the cable is not connected (only mobile data turned on).
What I've figured out is that phone when connected on USB is probably keeping the device awake and it doesn't go to idle mode while when not connected after around 5mins the device probably shuts down processes that cost energy or it shuts down connection to mobile data i'm not sure and there's where i need you guys.
Also I've tested app using HTC U Play - OS v6.0 and the streaming goes smooth without interrupts while screen off and phone wasn't touched for 10+mins.
Also I've tried to acquire wakelock inside oncreate and without releasing it just to see if it helps and it doesn't.
pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
wl.acquire();
This problem you are facing can be due to the fact that after Andriod 6.0, all apps are getting optimized for optimizing the battery usage.
If you really think, the reason behind the application to go killed is inactivity. Then, probably, its because of battery optimization software itself.
You can enable another permission while installing the app on the device where you can update the list of unoptimized app by adding an entry for your app.
Originally, you will be able to do the manual settings by following below instruction.
. Head for the ‘Settings‘ app and then ‘Battery‘
. On the ‘three dots‘ menu, top right, you’ll find ‘Battery optimisation‘.
. Here you’ll see a list of all applications which shouldn’t be ‘optimised‘ (for which read ‘can be handled by Doze and App Standby’) – by default the list is usually very small, with almost all apps enabled for ‘optimisation’. Which is fine for general users, but if, like me, you want a few applications to live outside of the new battery optimisations, then tap on the ‘Not optimised‘ pick list and choose ‘All apps‘
. As you’d expect, every application on your phone is listed (this may be quite long) – swipe down until you find the application(s) that you particularly want to always keep running. Tap on the application name
. From the two choices, check the box for ‘Don’t optimise‘.
I have an Asus P00A tablet (Android 7.0, API24) on which the BLE stops after some hours. (This affects any BLE app, not just my app using Android Beacon Library). Apps start working again if I manually switch off BLE then switch it back on.
The BluetoothMedic auto-fix system did not work for my tablet. It runs every 15 minutes but does not find a fault and so does not "power cycle" the Bluetooth. However, I hacked the BluetoothMedic class, adding this:
public void cycleBluetooth(Context context) {...}
and attached this to a button. I find this will restore BLE functionality. So I wondered what would happen if I unconditionally reset the BLE every 15 minutes. I added:
public static final int ALWAYS_RESET = 4;
and then call medic.enablePeriodicTests(context, BluetoothMedic.ALWAYS_RESET);
and add code inside BluetoothTestJob.onStartJob() which then calls BluetoothMedic.cycleBluetooth(). This behaves as expected and so far my app has run perfectly for 18 hours.
I am interested in any advice, such as:
1 Are there any tests other than the two in BluetoothMedic that I can run to detect that my tablet's Bluetooth has stopped? (I am happy to experiment).
2 Any comments on the hack I describe above? Should it be OK to unconditionally reset the Bluetooth every 15 minutes?
3 If the Bluetooth is reset ("power cycled") then is the rest of the Android Bluetooth Library OK with this? That is, will it carry on with monitoring and ranging that has been previously set up, or does the application code need to set take any action to get things going again? Note that this would apply to resets by the existing enablePowerCycleOnFailures() code as well as my ALWAYS_RESET hack above. (Maybe there are some crashes that could happen if the power cycling came at the wrong time?).
4 Could I suggest adding a callback so the application can learn if the Bluetooth has been cycled? Perhaps as a parameter to enablePowerCycleOnFailures()
5 I understand that background activities can be stopped by the OS, especially with Android 8. Would this also affect the regular 15 minute tests set up by enablePeriodicTests()?
The Android Beacon LIbrary's BluetoothMedic, as currently built, relies on the operating system's error code returned by a scan failure (or an advertising failure) to decide if the bluetooth stack is in a bad state warranting a power cycle.
For scans, if the onScanFailed callback is called with an error code of SCAN_FAILED_APPLICATION_REGISTRATION_FAILED which has the value of 2, the module considers it worthy of a power cycle..
For advertisements, if the onStartFailed callback is called with an error code of ADVERTISE_FAILED_INTERNAL_ERROR which has a value of 4, the module considers it worth of a power cycle..
These values were determined via experimentation, witnessing that on some devices, once an error callback is called with these values, bluetooth on the device would not work again without turning it off and back on. You can see the discussion of this in this thread.
You may want to see if there are other error codes on the Asus P00A that indicate a problem worthy of cycling bluetooth. To do this, wait for a failure, and see if attempts to start scanning call the onScanFailed callback with a distinct error code. If such error codes exist, this would be a better solution than cycling power to bluetooth regularly, as cycling power to bluetooth does break BLE GATT connections and the operation of bluetooth classic functions like speakers. The Android Beacon Library itself recovers from these power cycles just fine, although it will obviously not detect beacons until bluetooth is back on.
Because the BluetoothMedic uses the Android Job Scheduler for periodic tests, it is not affected by background limitations on Android 8+.
If you are interested in augmenting these functions in the library, please feel free to open an issue in the Github repo, and issue a Pull Request if you have code to share.
I have some questions on the use of Bluetooth Medic, based on the following observations.
Because Bluetooth stops on one of my devices I have been looking at the BluetoothMedic to see whether it can help. I've looked at the debug messages and the source code. I get somewhat different results depending on whether I use enablePeriodicTests() or individually run runScanTest() and runTransmitterTest().
With enablePeriodicTests(), BluetoothTestJob.onStartJob() runs scan and transmitter tests every 15 minutes, apparently OK. If my beacon is transmitting, I get "Scan test succeeded" then "scan test complete" from the scan test, and if not I get "Timeout running scan test" then "scan test complete". After that the transmitter test runs and I get "Transmitter test succeeded" then "transmitter test complete" in all cases.
However I get different behaviour when I add buttons that execute the runScanTest() and runTransmitterTest() calls. In both cases the code enters the while() loops waiting for a non-null test result, and times out after 5s. Because the test results are null, the calls then return true (for the scan test) and false (for the transmitter test).
In the case of the scan test, the onScanResult() callback is never called if my beacon is not transmitting, but if the beacon is transmitting it is called 10-20 times (I see many "Scan test succeeded" messages) but only AFTER runScanTest() returns.
In the case of the transmitter test, the onStartSuccess() callback is fired once and I see a "Transmitter test succeeded" message but only AFTER the runTransmitterTest() returns.
Behaviour is the same for two devices (Android 7 and 8).
It would be good to have further documentation on these tests and how to use them.
Firstly, what do these tests do and what errors might they find?
Secondly - how should they be used? It looks like runScanTest() and runTransmitterTest() can't be called simply - do they need their own threads or something?
Finally, are they safe to use while the ranging and monitoring code is in action, or do they interfere?
The Android Beacon Library's BluetoothMedic is designed to detect symptoms of the Android bluetooth stack getting into a bad state, and then optionally cycle power to bluetooth to get out of it. The Medic looks for two OS-level error codes that come back from attempting to scan for a Bluetooth LE device or transmit a Bluetooth LE advertisement. These error codes are known to be associated with a bad state of the bluetooth stack from which recovery generally does not happen without intervention.
The simplest way to set it up is passively like this:
BluetoothMedic medic = BluetoothMedic.getInstance();
medic.enablePowerCycleOnFailures(context);
The above two lines will start the medic listening for any bluetooth errors that happen as a result of regular use of the library's functions (scanning and/or transmitting) by the enclosing app. If these are detected, it will cycle power in an attempt to recover.
This is generally safe to do, but cycling power can cause problems to other functions like classic bluetooth -- if the phone is using a bluetooth speaker when this happens, it will obviously disconnect. Bluetooth LE functions are less likely to be adversely affected by a power cycle, as the error condition in the bluetooth stack probably prevents the functions form working, anyway.
As you have seen, you can also set up the BluetoothMedic to run its own test periodically. This may be a a good idea if you app only periodically works with beacons, and you want to proactively recover from any error conditions before your app needs to use them. To set this up, call:
medic.enablePeriodicTests(context, BluetoothMedic.SCAN_TEST | BluetoothMedic.TRANSMIT_TEST);
You can also call the tests directly, but this is an advanced usage which is not the primary design. If you decide to do so, you should definitely do this on a new thread as they will otherwise block the UI thread until the test completes. This will cause problems like you describe.
If there are no BLE devices in the vicinity during the scan test, then the test "times out" and the results are inconclusive -- you don't know for sure that the bluetooth stack is in a good state. The debug line is an indication of that fact.
I've recently updated my android phone to Marshmallow. Unfortunately for me, that broke my python code.
using PyUSB, I can get the device to enter accessory mode. Unfortunately, I can no longer read/write to the device, as now using set_configuration() resets the connection, causing the device to exit accessory mode and re-enter MTP mode.
dev = list(usb.core.find(find_all=True))[0]
dev.ctrl_transfer(0xc0,51,data_or_wLength=2)
dev.ctrl_transfer(0x40,52,wIndex=0,data_or_wLength='SAMSUNG')
dev.ctrl_transfer(0x40,52,wIndex=1,data_or_wLength='SAMSUNG_Android')
dev.ctrl_transfer(0x40,52,wIndex=2,data_or_wLength='16DIGITSERIALNUM')
dev.ctrl_transfer(0x40,52,wIndex=3,data_or_wLength='AOA')
dev.ctrl_transfer(0x40,52,wIndex=4,data_or_wLength='Whatever')
dev.ctrl_transfer(0x40,52,wIndex=5,data_or_wLength='4')
dev.ctrl_transfer(0x40,53)
time.sleep(5)
dev = list(usb.core.find(find_all=True))[0]
dev.set_configuration() ## Aaaaaand we're back to MTP...
I can't find any way around this; no references to this problem. Only solution I can some up with is to learn C and use libusb directly, assuming the PyUSB module is to blame...
Well, this isn't a good solution, but just in case anyone else just happens to come across this problem and find this:
The problem is with the specific backend (libusb0), which sends a message to reset configuration or something like that if an interface is already claimed. In some devices (mine, for example), this causes a programmatic reset of the connection.
My workaround involves using thelibusb1 backend when re-acquiring the device in accessory mode, since it doesn't cause a reset. But since libusb1 won't let me do control transfers, I need to switch between backends.
from usb.backend import libusb0, libusb1, and then be0,be1 = libusb0.get_backend(),libusb1.get_backend().
Then just specify the backend as a parameter to usb.core.find
Is there a way to let my application be discoverable for a time the app is running? I tried to do this with
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0);
startActivityForResult(discoverableIntent, DISC_DURATION);
as shown on Android Developers, but the Duration of 0 doesn't work so the default value 120 is set. Is there a way to set it as visible all the time?
there is no mean for keep Bluetooth discoverable all time.
At the time when you want to find some new devices or other devices can find your device you have to make it discoverable.
After that your device will interchange a shared MAC id and will make a paired.
Performing device discovery is a heavy procedure for the Bluetooth adapter and will consume a lot of its resources. Once you have found a device to connect, be certain that you always stop discovery with cancelDiscovery() before attempting a connection. Also, if you already hold a connection with a device, then performing discovery can significantly reduce the bandwidth available for the connection, so you should not perform discovery while connected.
for more info see this
http://developer.android.com/guide/topics/wireless/bluetooth.html#DiscoveringDevices
There is no way for you to set bluetooth to be continuously discoverable on any 2.* version of Android; see this bug report. It seems like the limitation has been removed in 3.* versions, but I haven't seen any confirmation of this.
I come to the conclusion on three devices I have.
ANDROID v 4.3 and higher : EXTRA_DISCOVERABLE_DURATION 0 works no limit
ANDROIND v 4.1 : EXTRA_DISCOVERABLE_DURATION 0 is max 1 hour. Have to change manually to no limit in parameters.