2

I'm working with Heart Rate sensor on Samsung S6. In order access this sensor, starting from SDK Version > 23, BODY_SENSORS permission must be granted by the user https://stackoverflow.com/a/32636039/8204927 So far I've done the following

  1. Added BODY_SENSORS permission in Manifest

  2. Since targeted SDK Version is 23, first I check if BODY_SENSORS permission is granted to the Application, and if not, display Prompt asking user to grant the permission

  3. Once user grants the permission I access the HeartRateSensor in the following way

    SensorManager sensorManager = ((SensorManager)getSystemService(SENSOR_SERVICE));

    Sensor heartRateSensor = sensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE); sensorManager.registerListener(this, heartRateSensor, SensorManager.SENSOR_DELAY_NORMAL);

And it all works fine, I'm getting bpm readings as expected.

However, when I move this logic to another Activity I'm getting heartRateSensor == null, even though BODY_SENSORS has been granted in the same way as described above. What I discovered is that Android's SystemSensorManager has cached all the available Sensor Type in mSensorListByType list before the BODY_SENSORS permission has been granted, and that list isn't refreshed after granting permission.

By debugging I've discovered that second Activity overrides onResume

@Override
protected void onResume() {
    super.onResume();
    ...
}

and that super.onResume(); calls

public List<Sensor> getSensorList(int type) {
        // cache the returned lists the first time
...
}

of the SensorManager class, which populates mSensorListByType, and that list is not updated until the app restarts.

Is there any way to force SensorManager to update this list after the BODY_SENSOR permission is granted in run time, or I will have to asked the user to grant the BODY_SENSOR permission before any Activity that overrides onResume is started?

pavleku
  • 21
  • 3
  • I found the cause of this problem. It turns out that class ViewCrawler of the Mixpanel API is subscribed to the ActivityLifecycleCallbacks and when Activity is resumed activity.getSystemService(Context.SENSOR_SERVICE) is called, which calls getSensorList, and then the available sensor list is caches and it appears that it cannot be altered anymore while application runs. Although this identifies the cause of the problem, I still need a way to update the sensor list after obtaining BODY_SENSOR permission in the runtime. – pavleku Jul 27 '17 at 15:27

2 Answers2

0

I found a workaround for this issue.

MixPanel API subscribes to the Application.ActivityLifecycleCallbacks, and and when first Activity that overrides onResume is created, MixPanel will access ACCELEROMETER Sensor in a ViewCrawler class. This will cause SensorManager to cache all available Sensors at the moment, and this list of Sensors will not be updated even after BODY_SENSOR permission is granted.

By adding following meta-data tag under application tag in Manifest

         android:name="com.mixpanel.android.MPConfig.DisableGestureBindingUI"
         android:value="true" />

MixPanel API is prevented from accessing TYPE_ACCELEROMETER Sensor and thus caching of all available Sensors on first call of onResume.

Overall, this was not the solution I was hoping for. By looking at the documentation and the SDK code, it appears that this is an Android bug. This Sensor caching behavior was added to the API 1, and that changes in permission policy that were added to API 23 were not anticipated.

pavleku
  • 21
  • 3
0

As @pavleku already found out the reason of the problem in the comments, I would like to have some additions to that.

  • Sadly, the suggested workaround did not work for me. In my use case, I was trying to implement reading heart rate sensor from a Samsung phone, in which I was experiencing the same behaviour.
  • Strangely this behaviour only appeared in Marshmallow and Nougat versions. Above API level 26 the sensor list has been updated automagically and the behaviour was gone.
  • There is also an alternative way to check if a device has BODY_SENSOR capabilities, without checking it from SensorManager. You can also do that via PackageManager.hasSystemFeature(FEATURE_SENSOR_HEART_RATE)

So all in all, after hours of trying to update the sensor list manually after granting permission, I have also failed miserably :)

iskae
  • 111
  • 1
  • 4