Android:应用因后台位置被谷歌拒绝

mar*_*337 6 android android-manifest android-location android-permissions android-fusedlocation

最近我的应用程序出现问题,Google Play因为他们发现我正在使用后台位置而突然被拒绝。但实际上我没有使用这个功能。我只有ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION权限,我正在使用FusedLocationProviderClient我的应用程序获取位置。此位置仅由应用程序内的用户操作请求,因此如果它在后台,则永远不会调用。我检查了合并清单功能,并尝试查找我导入的某些库是否使用了后台位置权限,但我什么也没找到。我还预防性地添加<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" tools:node="remove"/>到我的清单中以阻止任何后台位置权限请求。我根本没有任何与位置有关的后台服务。唯一的后台服务FirebaseMessagingService用于推送通知。

最近有人有这个问题吗?

更新:

我在我的应用程序中检查了合并清单,但在那里找不到ACCESS_BACKGROUND_LOCATION权限。但是我发现了一些可以触发后台位置的服务,但我不确定。它们是 Firebase Crashlytics 的一部分,可能用于将数据发送到 Firebase,并且可以在后台工作。但我不认为他们正在发送任何位置。它们也是来自 Google 的 firebase 插件的一部分。

 <service
        android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.JobInfoSchedulerService"
        android:exported="false"
        android:permission="android.permission.BIND_JOB_SERVICE" >
    </service>

    <receiver
        android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.AlarmManagerSchedulerBroadcastReceiver"
        android:exported="false" />
Run Code Online (Sandbox Code Playgroud)

更新#2:

这是我用来获取位置的代码。

主要活动:

     /**
     * Updating location every second/1 meter
     */
    var currLocation: GpsLocation? = null
    private var locationManager : LocationManager? = null
    private fun initLocationManager() {
        if (app.hasLocationPermission){
            locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
        }
        changeLocationUpdaters(true)
    }

    private fun changeLocationUpdaters(isEnabled: Boolean){
        if (ActivityCompat.checkSelfPermission(
                        this@MainActivity,
                        Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(
                        this@MainActivity,
                        Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            locationManager?.apply{
                if (isEnabled && app.hasLocationPermission){
                    requestLocationUpdates(LocationManager.GPS_PROVIDER, LOCATION_UPDATE_TIME_INTERVAL, LOCATION_UPDATE_DIST_INTERVAL, this@MainActivity)
                    requestLocationUpdates(LocationManager.NETWORK_PROVIDER, LOCATION_UPDATE_TIME_INTERVAL, LOCATION_UPDATE_DIST_INTERVAL, this@MainActivity)
                } else {
                    removeUpdates(this@MainActivity)
                }
            }
        } else {
            return
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后在应用程序处于后台时删除位置更新程序:

override fun onPause() {
  super.onPause()
  changeLocationUpdaters(false)
}

override fun onResume() {
  super.onResume()
  changeLocationUpdaters(true)
}
Run Code Online (Sandbox Code Playgroud)

然后我使用FusedLocationProviderinsideFragment来获得更准确的位置。它仅通过调用函数使用,因此不像以前那样自动化。它用于 GoogleMap 类以及应用程序内的一些 onClick 事件以返回当前位置。没有服务或更新程序调用它。

private inner class LocationCb(val lp: FusedLocationProviderClient,
                                   val onFailure: (()->Unit)? = null,
                                   val onSuccess: (GpsLocation)->Unit)
        : LocationCallback() {

        init {
            val lr = LocationRequest.create().apply {
                priority = LocationRequest.PRIORITY_HIGH_ACCURACY
                interval = 200
            }
            val lsr = LocationSettingsRequest.Builder().run {
                addLocationRequest(lr)
                build()
            }
            val check = LocationServices.getSettingsClient(activity!!).checkLocationSettings(lsr)
            check.addOnCompleteListener {
                try {
                    check.getResult(ApiException::class.java)
                    val task = lp.requestLocationUpdates(lr, this, Looper.getMainLooper())
                    task.addOnFailureListener {
                        onFailure?.invoke()
                    }
                } catch (e: ApiException) {
                    when (e.statusCode) {
                        LocationSettingsStatusCodes.RESOLUTION_REQUIRED-> if(!locationResolutionAsked){
                            // Location settings are not satisfied. But could be fixed by showing the user a dialog.
                            try {
                                // Cast to a resolvable exception.
                                val re = e as ResolvableApiException
                                // Show the dialog by calling startResolutionForResult(), and check the result in onActivityResult().
                                re.startResolutionForResult(mainActivity, MainActivity.REQUEST_LOCATION_SETTINGS)
                                locationResolutionAsked = true
                            } catch (e: Exception) {
                                e.printStackTrace()
                            }
                        }
                        LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE->{
                            App.warn("Location is not available")
                            onFailure?.invoke()
                        }
                    }
                }
            }
        }

        fun cancel(){
            lp.removeLocationUpdates(this)
            currLocCb = null
        }

        override fun onLocationResult(lr: LocationResult) {
            cancel()
            val ll = lr.lastLocation
            onSuccess(GpsLocation(ll.longitude, ll.latitude))
        }
    }
Run Code Online (Sandbox Code Playgroud)

此位置提供程序在返回结果后被取消,因此只能一次性使用。但香港专业教育学院加入了类似取消方法内onPauseonStop用于Fragment比在MainActivity确保其无效时,应用程序是在后台。

override fun onStop() {
   super.onStop()
   currLocCb?.cancel()
}

override fun onPause() {
   super.onPause()
   currLocCb?.cancel()
}
Run Code Online (Sandbox Code Playgroud)

Ana*_*lii 5

合并的清单可能不包含所有权限

不幸的是,并非所有库都会发布包含所有必要元素的清单<uses-permission>。这意味着,简单地检查合并的内容AndroidManifest.xml并没有多大帮助 - 您必须检查每个库的文档以找出它真正需要哪些权限,或者只是AndroidManifest.xml预先向您自己的库添加必要的权限。

API 29 后台权限限制

您还提到您的目标 SDK 是29. 因此,根据此处的AndroidManifest.xml官方文档,如果需要,您必须明确设置权限。以前,如果应用程序具有前台位置访问权限(基本上是ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION),则会自动授予权限。

在 Android 10(API 级别 29)及更高版本上,您必须在应用清单中声明 ACCESS_BACKGROUND_LOCATION 权限,才能在运行时请求后台位置访问。在早期版本的 Android 上,当您的应用接收前台位置访问时,它也会自动接收后台位置访问。

因此,对于旧版本,您的应用程序会被ACCESS_BACKGROUND_LOCATION自动授予,因为它是事先授予ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION

请求后台位置需要 ACCESS_BACKGROUND_LOCATION

此外,即使您或您的任何库没有ACCESS_BACKGROUND_LOCATION在任何地方设置,系统仍然会认为您的应用程序在任何情况下都使用后台位置,除了

属于您的应用程序的活动可见。

您的应用程序正在运行前台服务。当前台服务运行时,系统通过显示持久通知来提高用户意识。当您的应用程序置于后台时,例如当用户按下设备上的“主页”按钮或关闭设备的显示屏时,您的应用程序将保留访问权限。

结论

后者的意思是可能有一个或多个需要的库ACCESS_BACKGROUND_LOCATION,但无论出于何种原因它都不存在于它们中AndroidManifest.xml。它曾经适用于API < 29,因为您的应用程序被自动授予权限(由于前台位置权限)。

另外,现在,如果当前位置的任何使用是在可见范围之外Activity或不在Foreground Service. 因此,请确保您没有在应用程序的任何部分这样做。

更新

根据您更新的问题,您OnCompleteListener通过致电以下方式请求当前位置lp.requestLocationUpdates

...
        check.addOnCompleteListener {
            try {
                check.getResult(ApiException::class.java)
                val task = lp.requestLocationUpdates(lr, this, Looper.getMainLooper())
                task.addOnFailureListener {
                    onFailure?.invoke()
                }
...
Run Code Online (Sandbox Code Playgroud)

这可能是一个问题(我不能确定,因为您没有显示该类在应用程序中的使用方式),因为应用程序可能会在完成之前转到后台OnCompleteListener,因此将在后台请求该位置。

如上一节所述,这样做时系统会认为您需要后台位置权限才能执行此操作。OnCompleteListener因此,如果您的应用程序进入后台,您必须取消订阅回调。

您可以使用另一个版本,addOnCompleteListener也接受您的Activity实例,如下所示

public Task addOnCompleteListener(Activity活动,OnCompleteListener监听器)

在这种情况下,侦听器将在 期间自动删除Activity.onStop()


Pav*_*sha 0

首先,ACCESS_BACKGROUND_LOCATION从清单中完全删除单词。即使tools:node="remove"

第二:如果您没有ACCESS_BACKGROUND_LOCATION手动添加,并不意味着它不存在 - 有些库可能已经为您添加了它。不是检查您的项目清单文件 - 检查合并的清单 - 它的通常路径是:(如果您有口味,则在您的情况下可能会有所不同)

/project/module/build/intermediates/manifests/full/debug/AndroidManifest.xml

检查那里是否有ACCESS_BACKGROUND_LOCATION权限 - 如果有 - 这意味着某个库将其添加到那里。手动检查所有库的所有清单,找出其中添加了它的库。当你找到它时 - 删除它。

如果您的项目严重依赖于目标库 - 您还有另一个解决方案 - 在应用程序和 Play 商店控制台中编写一个披露信息,说明为什么需要使用后台位置并在位置权限对话框之前显示它,并显示如下消息:

We need access to your location in the background to ensure our app can function correctly.
Run Code Online (Sandbox Code Playgroud)

请记住,此消息可能不够描述性 - 但如果是,谷歌的测试人员会通知您。

不管怎样,披露是最后的解决方案......

如果您没有ACCESS_BACKGROUND_LOCATION,并且您不在前台服务中使用位置,而仅在应用程序运行时使用位置 - 写一封信给谷歌支持人员,其中包含您的所有论点,并询问他们到底是什么导致了拒绝问题。要有礼貌,脾气温和——事情就会得到解决。我过去也遇到过类似的问题,与他们的发布支持联系总是有帮助的。