expo: [Android] [ExpoLocation] Unhandled promise rejection: Error: Not authorized to use background location services

Summary

On Android 11+ if someone calls Location.startLocationUpdatesAsync() without having granted “Always” permissions, you a user will receive Not Authorized. For apps, such as a run tracker or turn by turn navigation, we need to access background location while the app is “minimized”. This is different than strictly asking for ACCESS_BACKGROUND_LOCATION, which requires more strict approval and permissions. Instead, we can have a foreground service that stays alive and handles the location events without requiring ACCESS_BACKGROUND_LOCATION. This will also allow the user to select “When in use” and still be able to minimize the app and receive location events.

I am not an android developer so this is a little outside my scope on being able to implement effectively.

Another package accomplishes by also using a Headless Task to handle the location events (react-native-background-geolocation).

Managed or bare workflow? If you have ios/ or android/ directories in your project, the answer is bare!

bare

What platform(s) does this occur on?

Android

SDK Version (managed workflow only)

No response

Environment

Expo CLI 3.21.10 environment info: System: OS: macOS 11.0.1 Shell: 5.8 - /bin/zsh Binaries: Node: 15.5.1 - /usr/local/bin/node Yarn: 1.22.10 - /usr/local/bin/yarn npm: 7.3.0 - /usr/local/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman IDEs: Android Studio: 3.6 AI-192.7142.36.36.6308749 Xcode: 12.4/12D4e - /usr/bin/xcodebuild npmPackages: react: 16.13.1 => 16.13.1 react-dom: ^16.13.1 => 16.13.1 react-native: 0.63.4 => 0.63.4 npmGlobalPackages: expo-cli: 3.21.10

Reproducible demo or steps to reproduce from a blank project

  1. Install Expo Location
  2. Ask/Grant “When in Use” Location Permissions
  3. Set Foreground Service Setting Config
  4. Define on location task per expo documentation for background tracking
  5. Start location with startLocationUpdatesAsync()
  6. Error: Permissions not available.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 10
  • Comments: 29 (9 by maintainers)

Most upvoted comments

Hi @MaRaSu (and everyone else)!

I can definitely comment more on this. Essentially Google made some pretty massive changes in 4 consecutive Android API versions. Android 8, 9, 10, and 11 have all different behavior related to location permissions. This is not only confusing for a lot of people, also outside React Native and/or Expo, it’s also frustrating for developers. Especially in the Google Play Console, where Google doesn’t give actionable feedback when something isn’t configured or built property (see this issue for example).

That being said, we are sorry for the confusion and frustration some of you might have encountered. We are currently making 2 major changes in the SDK to fix this issue.

  1. Deprecating (but not yet dropping) expo-permission This sounds like a big change, but in the past few SDKs we moved to adding “permission requesters” in the packages themselves, like Camera.requestPermissionsAsync. It should remove some ambiguity we currently have with both methods. We will still keep the expo-permission package around to give everyone enough time to eventually move over. We will give more context and info to this in the SDK release announcements.

  2. Splitting the LOCATION permission into LOCATION_FOREGROUND and aLOCATION_BACKGROUND This is the most important change related to this issue. Previously, we tried to guess or estimate whenever we need to request the background location permission. But this turned out to be even more confusing. In order to fix that, and to fully comply with Google’s policy, we are giving APIs to make this decision yourself. This allows everyone to only request fore- or background location permissions. (You can see the change here) We are trying to make these changes with some backward compatibility. Because these changes are fairly big, we will only make them for SDK 41. If you are running SDK 39 or 40 it should be fairly easy to update, we use the same RN version in these SDKs.

Hope this gives more context and some “hope” for the path forward.

BTW, My desperation to get my app out has also sent me searching for answers elsewhere as well. I’m checking a few implementations of the foreground services as I write this to see if I can use those. Specifically, this package

I don’t want to hijack this thread but help people who are in the same boat as me and @MaRaSu . While we wait for the next expo release, I did a quick and dirty poc on android 11 with this package, and I see that it seems that I could use this along with react-native-geolocation-service (but I guess we could just as well use expo-location’s watchPositionAsync).

I didn’t have to request access_background_location permission but still getting location updates every five seconds at least 15 minutes after I pressed the home button and turned the display off. I got a total of 172 updates in 860 seconds so seems very reliable on the emulator.

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.myapp">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <application
      android:name=".MainApplication"
      android:label="@string/app_name"
      android:icon="@mipmap/ic_launcher"
      android:roundIcon="@mipmap/ic_launcher_round"
      android:allowBackup="false"
      android:theme="@style/AppTheme">
      <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
        android:launchMode="singleTask"
        android:windowSoftInputMode="adjustResize">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
      </activity>
      <meta-data
      android:name="com.supersami.foregroundservice.notification_channel_name"
      android:value="Sticky Title"
    />
    <meta-data
      android:name="com.supersami.foregroundservice.notification_channel_description"
      android:value="Sticky Description."
    />
    <meta-data
      android:name="com.supersami.foregroundservice.notification_color"
      android:resource="@color/blue"
    />
    <!-- android:foregroundServiceType="location" -->
    <service android:name="com.supersami.foregroundservice.ForegroundService" android:foregroundServiceType="location"></service>
    <service android:name="com.supersami.foregroundservice.ForegroundServiceTask" android:foregroundServiceType="location"></service>
    </application>
</manifest>

Screenshot_1616482745 Screenshot 2021-03-23 at 12 28 59 PM Screenshot_1616482724

Update 1: Checked on real devices running android 6, 9 and 10… works as expected! I also wrote a short guide on medium.

Hi @MaRaSu and @devashishsethia! Thanks for the detailed write-up of your use cases. Locations turned out to be a dramatic complex thing, you can use it with a combination of all sorts. This definitely helps us to understand what the API should be capable of, so many thanks for that.

We are still in QA phase for the next SDK. Does either one of you have a simple plain Expo project that contains example code for this use case? If so, I’d be happy to test this during our QA or beta period. If not, I need to take some time to modify my test app “office marathon” (which is live on the Play Store, got past the background review 😄 )

If I look at the Android docs, the use case you describe should be possible. But I think we are missing really small specifics in our current code. Like starting the service with a FOREGROUND_SERVICE_TYPE_LOCATION flag, to let Android know that “this foreground service should have access to location while the pinned notification is active”. (It’s currently not set in this method)

@devashishsethia my interpretation of @byCedric reply is that in SDK41 it will be possible to do what you refer to (and what my app desperately needs too) without needing to request access_background_location permission and try to go through Google’s special review.

However in the current SDK40 (and earlier versions) use of Location.startLocationUpdatesAsync() is linked to background location permission as @byCedric explained above. It was apparently an abstraction that worked well enough prior to Google tightening up the policy on background location permission. Now Expo’s location & permission APIs need refactoring and therefore the fix will come only in SDK41.

@byCedric thanks once more, this indeed settles the matter. With this I can now spread the word to other places where people are asking about this.

Short answer is yes, the changes we are making should allow exactly that 😄

The long answer is best described in this medium post I think. We still need the “foreground service” to actively pull in the location data, but that’s not part of the foreground/background issue here.

As for the terminology, we try to match Google’s own terminology matched in this guide. In that guide they explain the use cases and how they relate to foreground/background location. They also recommend how to request it, making it compliant with their policies.

Hope this helps!

We current use the transistor soft plug in above for background tracking but have random crashes with it we can’t seem to figure out. It works generally well but with random issues. The issues could be unrelated to Transitor soft so this isn’t really a knock at them them.

Can i work on this issue?

Yep, you are definitely right about the inferior option. I’ll get back to you guys when I know more!

@byCedric I also took a quick look at “office marathon” app and the Track.ts is practically the same as in my app. Now, the trick is to make it work with foreground location permission only. I’m not competent with native Android, however I feel you are on the right track with your last paragraph on changes needed in Expo. That was also why I got worried and kept on pushing this issue when you were referring only to “permissions refactoring” but not detailing out the other changes needed ;=)

My app is on managed workflow and I would prefer to keep it that way. I can well wait until SDK41 assuming this will be truly fixed. It seems some apps, like your “office marathon” have gotten through the Google special background location review - still it’s very much an inferior solution because of the complications of getting the “allow all the time” permission from the user by requiring to guide her / him to Android settings.

BTW, My desperation to get my app out has also sent me searching for answers elsewhere as well. I’m checking a few implementations of the foreground services as I write this to see if I can use those. Specifically, this package

@MaRaSu That’s right!

We still have specific methods which are built for background location access, like the Location.startLocationUpdatesAsync. This method communicates with the expo-task-manager, and runs only in the background. Because of that, it requires background location permission. With the refactored permissions API in SDK 41, you can handle the permission request yourself and tailor it perfectly according to Google’s policies for your app.

But, if you want to listen to location changes in the app itself (not in a background task), you can use other methods built for that. I think the Location.watchPositionAsync is the counterpart of Location.startLocationUpdatesAsync that works without background location permission 😄

Hope this clarifies a few things.

@mariomurrent-softwaresolutions A little bit more on the transistor soft solution and why we want to get off of it, its basically pretty sensitive to when you turn on and off the tracking. If you call start too quickly after calling it before, it’ll crash. This has gotten better over time and is a pretty fringe case, but our application does a lot of conditional starting & stoping based on app state and whether tracking is on or not.