I'm developing an application for my final thesis on computer science, and I need to collect and log accelerometer data. I need to acquire it for a whole day long, so there are serious battery constraints (for instance, I cannot leave the screen on). Also, this isn't a market targeted application, so it is pretty acceptable to do some serious hacking, even low level C/C++ coding, if required.
It is well known that on many devices the listeners for accelerometer events stop generating events when screen goes off (some links regarding this problem: http://code.google.com/p/android/issues/detail?id=3708 , Accelerometer stops delivering samples when the screen is off on Droid/Nexus One even with a WakeLock). I have thoroughly searched for some alternatives, some of them include workarounds that do not work for my device (LG P990, stock ROM).
So what happens is this:
When you register an event listener for android accelerometer sensor in a Service, it works fine until the screen is turned off. I have already tried to register the eventListener on a Service, on an IntentService, tried to acquire WakeLocks. Regarding wakelocks, I can verify that the service is still running watching the LOGcat output, but it seems the accelerometer is put into sleep mode. One of the workarounds presented in some of the links is to unregister and re-register the event listener periodically using the thread of an IntentService like in this code snippet bellow
synchronized private static PowerManager.WakeLock getLock(Context context) {
if (lockStatic==null) {
PowerManager mgr=(PowerManager)context.getSystemService(Context.POWER_SERVICE);
lockStatic = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,NAME);
lockStatic.setReferenceCounted(true);
}
return(lockStatic);
}
#Override
protected void onHandleIntent(Intent intent) {
sensorManager=(SensorManager) getSystemService(SENSOR_SERVICE);
sensorManager.unregisterListener(this);
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
synchronized (this) {
boolean run = true;
while (run){
try {
wait(1000);
getLock(AccelerometerService.this).acquire();
sensorManager=(SensorManager) getSystemService(SENSOR_SERVICE);
sensorManager.unregisterListener(this);
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
Log.d("Accelerometer service", "tick!");
} catch (Exception e) {
run = false;
Log.d("Accelerometer service", "interrupted; cause: " + e.getMessage());
}
}
}
}
#Override
public void onSensorChanged(SensorEvent event) {
Log.d("accelerometer event received", "xyz: "+ event.values[0] + "," + event.values[1] + "," + event.values[2]);
}
which indeed makes the onSensorChange be called every time we unregister/register the listener. The problem is that the event received contains always the same values, regardless of me shaking the device.
So, basically my questions are: ( bear with me, I'm almost finishing :P )
is it possible to have low level access (C/C++ approach) to the accelerometer hardware WITHOUT registering to an event listener?
is there any other workaround or hack?
could anyone with a more up-to-date phone kindly test if the problem persists in firmware 3.0 and above?
[UPDATE]
Unfortunately, it seems to be a bug with some cellphones. More details in my answer.
Basically, it is a problem with my phone. Other users have reported this also happens with their phones, from different brands but same Android version. Other persons have no problem at all - strongly indicating that this is not a problem with the stock version of android but from the implementations of each company for their hardware drivers.
I need constant accelerometer data delivered and cannot have a dongle measure this data for me - I have an Arduino with Bluetooth and accelerometer, so I could have implemented this solution. So I decided that the temporary solution for my cellphone was to let the screen on (dimmed) and ignore battery consumption. Later on I will perform the tests for battery usage using another android phone which works with the screen turned off.
More information about the bug
I've researched some more and found reports from other Android users and I think maybe I understand what is happening. The library libsensors.so which has the drivers for the phone sensors is not developed by Google but by each cellphone vendor - of course, because each cellphone has its own specific hardware. Google only provides a C header file so that the developers know what they have to implement. On some implementations for these drivers, the developers simply turn the accelerometer off when the screen goes off, thus preventing the sensor event listener to receive new events.
I also tested this with CyanogenMod RC7.2 but it did not work either, because accelerometer drivers are original from LG.
E-mails exchanged with HR department of LG
I sent an e-mail to the developers of the LG P990 and finally got some concrete answers! This may be of great help to some people like me that are experiencing these issues with Android. I wrote the following question
Hello! I am developing my thesis in computer science and currently I
am fetching data from accelerometer hardware. As of now, I found out
that the accelerometers do not send events when the screen is off, so
even when I grab a wakelock from within one of my programs, I can
verify that my program is still running (through LOGcat output) but no
accelerometer event comes out. I have to dim my screen on (which I
cannot afford, the battery drains too fast) to start receiving
accelerometer events again. I also tried accessing it through native C
code, registering on the accelerometer events but the result was the
same, the accelerometer did not throw any values, even though I was
rotating my device. So I was wondering if I could have direct access
to the hardware, with native code, without having to register to a
listener. Is this possible? If so, could you kindly give some further
advice? I would appreciate very much any help! Martin
For what I received this response:
Dear Martin, We received the answer from Dev. Team. They said that you
can’t get accelerometer event while your phone screen is off. Because
HAL layer didn’t implement sysFS path to get H/W event such as
accelerometer and there is no public API to get event. Thank you. Best
Regards. (Sean Kim)
I then sent an e-mail back, saying among other things, that I considered this a bug, since one should have access to all the hardware when acquiring a wake lock:
[...] I asked this question because I have some friends that also have
Android phones with the same gingerbread version but from other
cellphone brands, and some of them reported they receive events from
the accelerometers when the screen is turned off. I read on some
forums that this bug - I consider it a bug, since when I acquire a
Wakelock I would expect to have some processing going on - depends on
the sensor drivers that the vendors implement for their cellphones. Is
there any possibility that these drivers can be updated or will this
bug be corrected at some point? This would help me enormously with my
ongoing work [...]
And then I received this answer:
In my knowledge from Dev. Team, That isn’t bug. That is a limitless of
this phone because of H/W architecture. We need to redesign the HAL
architecture and device driver to support your request. But, as you
know that is too difficult due to lack of resource. We are trying to
help you with our all efforts but we cannot support your request as I
mentioned. (Sean Kim)
So they apparently know about this but are not trying to correct this because either they don't think it is a bug - which I still strongly believe is a logical flaw - or they don't have the time/resources to correct it.
Bottom line
If you have a cellphone that does not send accelerometer events with the screen off, try updating your firmware. If this does not solve and you really want to do some serious hacking, re implement your hardware layer - hint: it's probably something to do with libsensors.so.
I am not sure if this will actually assist you, but I have found a work around (Not sure how well if will help the battery saving though).
Using bash I have a while loop cat-ing /sys/devices/virtual/accelerometer/accelerometer/acc_file, and when I turn off the screen via the power button the output continues, but is frozen. (I had sshd running in a chroot hence the being able to see it.)
However, in echoing 0 > /sys/devices/platform/msm_fb.196609/leds/lcd-backlight/brightness. The screen turns off, and the output is continuous.
This is on a SGH-T589W, running android version 2.3.6.
I applied the PARTIAL_WAKE_LOCK and it worked like charm to me. Tested on OS 5.0, 6.0 & 7.0. Here's my code to acquire and release wake lock. Be careful it increased the battery drainage, so acquire and release it intelligently.
public void acquireWakeLock() {
final PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
releaseWakeLock();
//Acquire new wake lock
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "PARTIAL_WAKE_LOCK");
mWakeLock.acquire();
}
public void releaseWakeLock() {
if (mWakeLock != null && mWakeLock.isHeld()) {
mWakeLock.release();
mWakeLock = null;
}
}
Ref: https://developer.android.com/training/scheduling/wakelock.html#cpu
I am working on a prediction sdk, who use device sensors (accelerometer/gyroscope) data and predicts the user events. I experienced the same issue.
I've ran into similar problems with the Samsung Nexus running Android 4.0.2 using other system services that stop/pause while the screen is off even though a PARTIAL_WAKE_LOCK is acquired. My solution was to use a SCREEN_DIM_WAKE_LOCK as in:
lockStatic = mgr.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK,NAME);
It would be far better to have the screen fully off, but at least this solution works although it would be even better if I could limit using a SCREEN_DIM_WAKE_LOCK to only those devices/OSes that require it.
I hate to disappoint you, but some devices just don't keep acceleromet on while they are in a sleep mode. Some do, others don't. You can check any pedometer weight loss app on store most of them explicitly state that this won't work on some devices.
If the partial wake lock option is not available for your phone it means the driver for the sensor has early_suspend enabled.
There are two options.
1: Disable EARLY_SUSPEND in the driver
2: Add a run time flag that can enable/disable early_suspend functionality at the driver level.
ex. cat /sys/module/earlysuspend/sensor 1/0
IMO the second option should have been there from the beginning.
Related
I'm developing a tracking application and I need to prevent users from turning off the basic sensors used to determine the location. I can not modify the devices ROM or have root access (or at least it would be very desirable to had not), but I thought of using the Device Administration API to perform these functions through the Profile Owner or Device Owner modes. I'm basically looking for a method to block these functions in Android settings.
I'm unsure about whether this is possible and how to do it, I have not found examples in GitHub for applications that have implemented this. Could anyone give me a light, some example or specific documentation?
I tried to follow these three documentations, without success in finding a solution to this specific feature:
https://source.android.com/devices/tech/admin
https://developer.android.com/guide/topics/admin/device-admin
https://developers.google.com/android/management/introduction
This is an excerpt from what I've been trying:
setUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, true);
setUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, active);
setUserRestriction(UserManager.DISALLOW_CONFIG_BLUETOOTH, active);
private void setUserRestriction(String restriction, boolean disallow){
if (disallow) {
mDevicePolicyManager.addUserRestriction(mAdminComponentName, restriction);
} else {
mDevicePolicyManager.clearUserRestriction(mAdminComponentName,
restriction);
}
}
DISALLOW_CONFIG_BLUETOOTH
Added in API level 18
public static final String DISALLOW_CONFIG_BLUETOOTH
Specifies if a user is disallowed from configuring bluetooth. This does not restrict the user from turning bluetooth on or off. The default value is false.
This restriction doesn't prevent the user from using bluetooth. For disallowing usage of bluetooth completely on the device, use DISALLOW_BLUETOOTH.
This restriction has no effect in a managed profile.
Key for user restrictions.
Type: Boolean
You cannot prevent them from turning GPS, WIFI and Bluetooth off. What you can do is have an implementation as below or use this library.
https://github.com/KI-labs/gps-permission-checks-livedata
You can't, obviously for security reasons. If you want to achive something like that you'll probably need to modify the devices ROM. You should create a BroadcastReceiver and keep tracking Internet and Bluetooth connection changes, than you can properly handle it when user disconnect them pausing the service, showing a dialog, finishing the application or whatever you need to do.
It would be pretty weird if an app could have some control of user settings, imagine if you install an app, then suddently you can't disable wi-fi anymore until you unistall it. You can't do that for a good reason
Preventing bluetooth/wifi disconnection will also prevent usage of aircraft mode, that is a security issue bounded in the ROM and not overridable.
As suggested above your option is to monitor for wifi/bluetooth/gps deactivations and prompt the user with an alert.
By the way, GPS is not affected by aircraft mode, as it's a pure receiver and doesn't make active transmissions. In that case GPS will be always active and collecting informations (if active and the phone is not in power save mode, aka relying on wifi location). I suggest you to check if the user activated aircraft mode, in order to be less annoying with your alerts (air mode is mandatory in same situations, and should be considered "legal" by your application, and maybe less critical than an user voluntary disconnection
In simple words, You cannot, but you can listen to when wifi is enabled/connected, and you can prompt a dialog stating the reason.
This way it gives the user a more concise grip on what needs to be done.
Just a suggestion
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'm going to use Activity Recognition API to do some optimizations on an App.
In short I need to know when a device is STILL. But I do not want the App became battery draining by leaving sensors always on.
On is reported:
To conserve battery, activity reporting may stop when the device is 'STILL' for an extended period of time. It will resume once the device moves again. This only happens on devices that support the Sensor.TYPE_SIGNIFICANT_MOTION hardware.
I would register to ActivityRecognitionAPI only for devices that support significant motion detection by HW.
But I cannot find a way to check when this support exists.
Using:
SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
boolean supportSignificantMotion = (sensor != null);
does not discriminate if support is by HW or by SW (on most devices this may be implemented just by software on accelerometer sensor).
Does anyone know if is it possibile to perform that check?
Thanks
I was just checking around the Activity Recognition API and I had the same question.
As far as I know, there is no way to check if it is a hardware or a software sensor, but as I read at https://source.android.com/devices/sensors/sensor-types.html#significant_motion, I guess that all the significant motion sensors are software. Maybe that documentation you reported is a typo.
I think that it should work as long as it is a wake up sensor.
The thing that confused me is the statement
getDefaultSensor(SENSOR_TYPE_SIGNIFICANT_MOTION) returns a non-wake-up
sensor
Non wake-up sensors won't wake up the device, as described in http://developer.android.com/intl/pt-br/reference/android/hardware/Sensor.html#isWakeUpSensor()
But in the documentation of significant motion sensor (http://developer.android.com/intl/pt-br/reference/android/hardware/Sensor.html#TYPE_SIGNIFICANT_MOTION) says that this is a wake up sensor.
Maybe it depends on the software implementation?
If so, it would be possible to check with http://developer.android.com/intl/pt-br/reference/android/hardware/Sensor.html#isWakeUpSensor()
I'm still searching and testing. If I get any news, I'll update the answer.
Let me know if you got any news aswell, please.
PS: I know this isn't really an answer, but it was too long to be a comment
I have an application in which I am sending network data over WiFI. Everything is fine until I turn the display off or the device goes to 'sleep'. I'm already locking the WiFi however, it seems to be the case that the CPU speed ramps down when in sleep which causes my streaming to not behave properly (i.e. packets don't flow as fast as I would like as they do when the device is not sleeping).
I know that I possibly can/possibly should address this at the protocol level however, that might possibly not be possible as well...
Is there any way to "prevent the CPU from going to 'sleep' when the screen is off"? If so, how? If not, any advice on how to keep the speed of my WiFi stream consistent whether the device is in sleep mode or not?
Grab a PARTIAL_WAKE_LOCK from the PowerManager. You'll also need to add the WAKE_LOCK permission to your manifest.
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Tag");
wl.acquire();
//do what you need to do
wl.release();
Okay, so, after much more research and experimenting, it seems that the real issue is the fact that, at least on some phones, their WiFi goes into a 'partial sleep' mode EVEN IF you've taken the WiFi lock. It seems that this is what the 'WIFI_MODE_FULL_HIGH_PERF' flag was invented for when taking the WiFi lock... unfortunately, this flag is only available on some devices/Android versions (I have no clue as to which but, it wasn't available to me). So, therefore, it isn't a fix for all devices.
The only "solution" (which is actually a kludge) seems to be to 'detect when the screen is turned off and then, set an alarm that turns the screen back on immediately thereafter'. The links that helped a little bit with this are:
How to keep a task alive after phone sleeps?
and
http://android.modaco.com/topic/330272-screen-off-wifi-off/
I hope that this helps people who are experiencing WiFi disruption when the phone goes to sleep/screen is turned off (and the phone is unplugged/disconnected [e.g. you won't see this effect when connected to adb; only when the phone is running with nothing connected to it]).
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.