0

I'm trying to let my Android Wear watch face determine the current location of the device. This has worked before but after uninstalling and reinstalling the package, it just fails. I think I did everything right and it has already worked like that. I have no idea what the issue is or even how to catch it. Here's the relevant code:

class MyWatchFaceService : CanvasWatchFaceService() {
    override fun onCreateEngine(): Engine {
        return Engine()
    }

    inner class Engine : CanvasWatchFaceService.Engine() {
        private lateinit var fusedLocationClient: FusedLocationProviderClient
        private var lastKnownLocation: Location? = null

        override fun onCreate(holder: SurfaceHolder) {
            super.onCreate(holder)
            fusedLocationClient = LocationServices.getFusedLocationProviderClient(this@MyWatchFaceService)
            updateLocation()
        }

        fun updateLocation() {
            Logger.getAnonymousLogger().info("updateLocation")
            try {
                fusedLocationClient.lastLocation
                        .addOnSuccessListener { location: Location? ->
                            if (location != null) {
                                // Location available, use it and update later
                                Logger.getAnonymousLogger().info("- location available")
                                lastKnownLocation = location
                            }
                        }
                        .addOnFailureListener {ex: Exception ->
                            Logger.getAnonymousLogger().warning("- location NOT accessible (inner): " + ex.toString())
                        }
            } catch (ex: SecurityException) {
                // Nothing we can do, no location available
                Logger.getAnonymousLogger().warning("- location NOT accessible: " + ex.toString())
            }
            Logger.getAnonymousLogger().info("updateLocation (end)")
        }
    }
}

I then find this in the log:

03-22 09:41:59.521 7390-7390/de.unclassified.watchface1 I/null: updateLocation
03-22 09:41:59.536 7390-7390/de.unclassified.watchface1 I/null: updateLocation (end)
03-22 09:41:59.830 7390-7405/de.unclassified.watchface1 E/AndroidRuntime: FATAL EXCEPTION: GoogleApiHandler
                                                                          Process: de.unclassified.watchface1, PID: 7390
                                                                          java.lang.SecurityException: Client must have ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to perform any location operations.
                                                                              at android.os.Parcel.readException(Parcel.java:1684)
                                                                              at android.os.Parcel.readException(Parcel.java:1637)
                                                                              at com.google.android.gms.internal.zzeu.zza(Unknown Source)
                                                                              at com.google.android.gms.internal.zzcfa.zzif(Unknown Source)
                                                                              at com.google.android.gms.internal.zzcfd.getLastLocation(Unknown Source)
                                                                              at com.google.android.gms.internal.zzcfk.getLastLocation(Unknown Source)
                                                                              at com.google.android.gms.location.zzg.zza(Unknown Source)
                                                                              at com.google.android.gms.common.api.internal.zze.zza(Unknown Source)
                                                                              at com.google.android.gms.common.api.internal.zzbo.zzb(Unknown Source)
                                                                              at com.google.android.gms.common.api.internal.zzbo.zzaiw(Unknown Source)
                                                                              at com.google.android.gms.common.api.internal.zzbo.onConnected(Unknown Source)
                                                                              at com.google.android.gms.common.internal.zzac.onConnected(Unknown Source)
                                                                              at com.google.android.gms.common.internal.zzn.zzakr(Unknown Source)
                                                                              at com.google.android.gms.common.internal.zze.zzw(Unknown Source)
                                                                              at com.google.android.gms.common.internal.zzi.zzaks(Unknown Source)
                                                                              at com.google.android.gms.common.internal.zzh.handleMessage(Unknown Source)
                                                                              at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                              at android.os.Looper.loop(Looper.java:154)
                                                                              at android.os.HandlerThread.run(HandlerThread.java:61)
03-22 09:41:59.843 7390-7405/de.unclassified.watchface1 I/Process: Sending signal. PID: 7390 SIG: 9

So it does call my updateLocation function, then leaves again, and then there is an exception. But it's not from calling my code as far as I can see. Where does it come from? It doesn't have a stack trace with source files.

The package already has all relevant permissions, as before, along with some more for other tasks.

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

So what's going on here and why does neither the try/catch nor the failure listener do something?

Tung Tran
  • 2,885
  • 2
  • 17
  • 24
ygoe
  • 18,655
  • 23
  • 113
  • 210
  • 3
    You need to ask for `Runtime permission`. – ADM Mar 22 '18 at 08:56
  • Possible duplicate of [Error: Client must have ACCESS\_COARSE\_LOCATION or ACCESS\_FINE\_LOCATION](https://stackoverflow.com/questions/38431587/error-client-must-have-access-coarse-location-or-access-fine-location) – Stefan Golubović Mar 22 '18 at 10:02

2 Answers2

1

COARSE Location is dangerous, hence, Google forces you to request it in runtime, just declare in Android Manifest is not enough. Please refer this link for a sample: https://developer.android.com/training/permissions/requesting.html

A quick code is here:

if (ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.ACCESS_FINE_LOCATION)
    != PackageManager.PERMISSION_GRANTED) {
   ActivityCompat.requestPermissions(thisActivity,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                0x0)
}

You should request both Fine and Coarse permissions.

Cao Minh Vu
  • 1,900
  • 1
  • 16
  • 21
  • I don't need fine location, just doing sunrise/sunset calculations. But this code doesn't work. I don't have an activity, just the service. How can I request the permission from the service? – ygoe Mar 22 '18 at 09:15
  • You have to use an Activity to request permission. If your application does not contain any Activities, just create a blank one (no UI), request the permission and then finish it. Please be noted that you can request the permission from any Activities, just make sure it is granted before you go into your service. – Cao Minh Vu Mar 22 '18 at 09:21
0

Before you start the service, or better, provide the user with that service, you need to make sure that they permit giving you their coarse/fine location, thus you need to give them the option in an Activity.

Think of it this way, you service runs in background and you need the user's permission to do your work, and you cannot assume that the user is present when the service is running. so you have to require the permission when the user is present, which is when the Activity is running.

Adib Faramarzi
  • 3,798
  • 3
  • 29
  • 44
  • Actually a watch face always seems to start in the foreground. But it's not an activity, it's just a visible, interactive service. I'm new to Android programming, so this is a bit confusing. – ygoe Mar 22 '18 at 10:04
  • If you are trying this on Wear OS, you may want to look [here](https://developer.android.com/training/articles/wear-permissions.html) as it is a bit different. – Adib Faramarzi Mar 22 '18 at 10:22
  • Interesting pictures but no code, so I can't use that very well. – ygoe Mar 22 '18 at 10:41
  • Did you try creating an activity and requesting permissions in it? (start the activity in your service as the guide suggests). – Adib Faramarzi Mar 22 '18 at 10:57