Improved READ_PHONE_STATE / READ_PHONE_NUMBERS permissions usage

This commit is contained in:
Sylvain Berfini 2021-10-11 10:12:08 +02:00
parent c1dafcb9b9
commit 4337dd5da8
14 changed files with 125 additions and 27 deletions

View file

@ -20,13 +20,13 @@
package org.linphone.activities.assistant.fragments
import android.Manifest
import android.content.pm.PackageManager
import androidx.databinding.ViewDataBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.linphone.R
import org.linphone.activities.GenericFragment
import org.linphone.activities.assistant.viewmodels.AbstractPhoneViewModel
import org.linphone.compatibility.Compatibility
import org.linphone.core.tools.Log
import org.linphone.utils.PermissionHelper
import org.linphone.utils.PhoneNumberUtils
@ -41,19 +41,19 @@ abstract class AbstractPhoneFragment<T : ViewDataBinding> : GenericFragment<T>()
) {
if (requestCode == 0) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i("[Assistant] READ_PHONE_STATE permission granted")
Log.i("[Assistant] READ_PHONE_STATE/READ_PHONE_NUMBERS permission granted")
updateFromDeviceInfo()
} else {
Log.w("[Assistant] READ_PHONE_STATE permission denied")
Log.w("[Assistant] READ_PHONE_STATE/READ_PHONE_NUMBERS permission denied")
}
}
}
protected fun checkPermission() {
if (!resources.getBoolean(R.bool.isTablet)) {
if (!PermissionHelper.get().hasReadPhoneState()) {
Log.i("[Assistant] Asking for READ_PHONE_STATE permission")
requestPermissions(arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
if (!PermissionHelper.get().hasReadPhoneStateOrPhoneNumbersPermission()) {
Log.i("[Assistant] Asking for READ_PHONE_STATE/READ_PHONE_NUMBERS permission")
Compatibility.requestReadPhoneStateOrNumbersPermission(requireActivity(), 0)
} else {
updateFromDeviceInfo()
}

View file

@ -111,7 +111,7 @@ class ControlsFragment : GenericFragment<CallControlsFragmentBinding>() {
viewLifecycleOwner,
{
it.consume {
if (!PermissionHelper.get().hasWriteExternalStorage()) {
if (!PermissionHelper.get().hasWriteExternalStoragePermission()) {
Log.i("[Controls Fragment] Asking for WRITE_EXTERNAL_STORAGE permission")
requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 1)
}

View file

@ -124,7 +124,7 @@ class CallsViewModel : ViewModel() {
}
fun takeScreenshot() {
if (!PermissionHelper.get().hasWriteExternalStorage()) {
if (!PermissionHelper.get().hasWriteExternalStoragePermission()) {
askWriteExternalStoragePermissionEvent.value = Event(true)
} else {
currentCallViewModel.value?.takeScreenshot()

View file

@ -402,7 +402,7 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
}
binding.setAttachFileClickListener {
if (PermissionHelper.get().hasReadExternalStorage() && PermissionHelper.get().hasCameraPermission()) {
if (PermissionHelper.get().hasReadExternalStoragePermission() && PermissionHelper.get().hasCameraPermission()) {
pickFile()
} else {
Log.i("[Chat Room] Asking for READ_EXTERNAL_STORAGE and CAMERA permissions")

View file

@ -70,7 +70,7 @@ class ChatMessagesListViewModel(private val chatRoom: ChatRoom) : ViewModel() {
return
}
if (Version.sdkStrictlyBelow(Version.API29_ANDROID_10) && !PermissionHelper.get().hasWriteExternalStorage()) {
if (Version.sdkStrictlyBelow(Version.API29_ANDROID_10) && !PermissionHelper.get().hasWriteExternalStoragePermission()) {
for (content in chatMessage.contents) {
if (content.isFileTransfer) {
Log.i("[Chat Messages] Android < 10 detected and WRITE_EXTERNAL_STORAGE permission isn't granted yet")

View file

@ -19,11 +19,14 @@
*/
package org.linphone.activities.main.dialer.fragments
import android.Manifest
import android.annotation.TargetApi
import android.app.Dialog
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle
@ -44,9 +47,11 @@ import org.linphone.activities.navigateToConfigFileViewer
import org.linphone.activities.navigateToContacts
import org.linphone.core.tools.Log
import org.linphone.databinding.DialerFragmentBinding
import org.linphone.mediastream.Version
import org.linphone.utils.AppUtils
import org.linphone.utils.DialogUtils
import org.linphone.utils.Event
import org.linphone.utils.PermissionHelper
class DialerFragment : SecureFragment<DialerFragmentBinding>() {
private lateinit var viewModel: DialerViewModel
@ -164,6 +169,10 @@ class DialerFragment : SecureFragment<DialerFragmentBinding>() {
viewModel.transferVisibility.value = sharedViewModel.pendingCallTransfer
checkForUpdate()
if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) {
checkPermissions()
}
}
override fun onPause() {
@ -185,6 +194,28 @@ class DialerFragment : SecureFragment<DialerFragmentBinding>() {
viewModel.enteredUri.value = sharedViewModel.dialerUri
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i("[Dialer] READ_PHONE_STATE permission has been granted")
coreContext.initPhoneStateListener()
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
@TargetApi(Version.API23_MARSHMALLOW_60)
private fun checkPermissions() {
if (!PermissionHelper.get().hasReadPhoneStatePermission()) {
Log.i("[Dialer] Asking for READ_PHONE_STATE permission")
requestPermissions(arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
}
}
private fun displayDebugPopup() {
val alertDialog = MaterialAlertDialogBuilder(requireContext())
alertDialog.setTitle(getString(R.string.debug_popup_title))

View file

@ -69,7 +69,7 @@ class Api21Compatibility {
}
suspend fun addImageToMediaStore(context: Context, content: Content): Boolean {
if (!PermissionHelper.get().hasWriteExternalStorage()) {
if (!PermissionHelper.get().hasWriteExternalStoragePermission()) {
Log.e("[Media Store] Write external storage permission denied")
return false
}
@ -100,7 +100,7 @@ class Api21Compatibility {
}
suspend fun addVideoToMediaStore(context: Context, content: Content): Boolean {
if (!PermissionHelper.get().hasWriteExternalStorage()) {
if (!PermissionHelper.get().hasWriteExternalStoragePermission()) {
Log.e("[Media Store] Write external storage permission denied")
return false
}
@ -132,7 +132,7 @@ class Api21Compatibility {
}
suspend fun addAudioToMediaStore(context: Context, content: Content): Boolean {
if (!PermissionHelper.get().hasWriteExternalStorage()) {
if (!PermissionHelper.get().hasWriteExternalStoragePermission()) {
Log.e("[Media Store] Write external storage permission denied")
return false
}

View file

@ -19,7 +19,9 @@
*/
package org.linphone.compatibility
import android.Manifest
import android.annotation.TargetApi
import android.app.Activity
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.ContentValues
@ -45,6 +47,20 @@ import org.linphone.utils.LinphoneUtils
@TargetApi(29)
class Api29Compatibility {
companion object {
fun hasReadPhoneStatePermission(context: Context): Boolean {
val granted = Compatibility.hasPermission(context, Manifest.permission.READ_PHONE_STATE)
if (granted) {
Log.d("[Permission Helper] Permission READ_PHONE_STATE is granted")
} else {
Log.w("[Permission Helper] Permission READ_PHONE_STATE is denied")
}
return granted
}
fun requestReadPhoneStatePermission(activity: Activity, code: Int) {
activity.requestPermissions(arrayOf(Manifest.permission.READ_PHONE_STATE), code)
}
fun createMessageChannel(
context: Context,
notificationManager: NotificationManagerCompat

View file

@ -19,15 +19,32 @@
*/
package org.linphone.compatibility
import android.Manifest
import android.annotation.TargetApi
import android.app.Activity
import android.content.Context
import android.content.pm.ShortcutManager
import org.linphone.core.ChatRoom
import org.linphone.core.tools.Log
import org.linphone.utils.LinphoneUtils
@TargetApi(30)
class Api30Compatibility {
companion object {
fun hasReadPhoneNumbersPermission(context: Context): Boolean {
val granted = Compatibility.hasPermission(context, Manifest.permission.READ_PHONE_NUMBERS)
if (granted) {
Log.d("[Permission Helper] Permission READ_PHONE_NUMBERS is granted")
} else {
Log.w("[Permission Helper] Permission READ_PHONE_NUMBERS is denied")
}
return granted
}
fun requestReadPhoneNumbersPermission(activity: Activity, code: Int) {
activity.requestPermissions(arrayOf(Manifest.permission.READ_PHONE_NUMBERS), code)
}
fun removeChatRoomShortcut(context: Context, chatRoom: ChatRoom) {
val peerAddress = chatRoom.peerAddress.asStringUriOnly()
val localAddress = chatRoom.localAddress.asStringUriOnly()

View file

@ -44,6 +44,24 @@ class Compatibility {
}
}
// See https://developer.android.com/about/versions/11/privacy/permissions#phone-numbers
fun hasReadPhoneStateOrNumbersPermission(context: Context): Boolean {
return if (Version.sdkAboveOrEqual(Version.API30_ANDROID_11)) {
Api30Compatibility.hasReadPhoneNumbersPermission(context)
} else {
Api29Compatibility.hasReadPhoneStatePermission(context)
}
}
// See https://developer.android.com/about/versions/11/privacy/permissions#phone-numbers
fun requestReadPhoneStateOrNumbersPermission(activity: Activity, code: Int) {
if (Version.sdkAboveOrEqual(Version.API30_ANDROID_11)) {
Api30Compatibility.requestReadPhoneNumbersPermission(activity, code)
} else {
Api29Compatibility.requestReadPhoneStatePermission(activity, code)
}
}
fun getDeviceName(context: Context): String {
return when (Version.sdkAboveOrEqual(Version.API25_NOUGAT_71)) {
true -> Api25Compatibility.getDeviceName(context)

View file

@ -313,13 +313,7 @@ class CoreContext(val context: Context, coreConfig: Config) {
configureCore()
try {
phoneStateListener =
Compatibility.createPhoneListener(context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager)
} catch (exception: SecurityException) {
val hasReadPhoneStatePermission = PermissionHelper.get().hasReadPhoneState()
Log.e("[Context] Failed to create phone state listener: $exception, READ_PHONE_STATE permission status is $hasReadPhoneStatePermission")
}
initPhoneStateListener()
EmojiCompat.init(BundledEmojiCompatConfig(context))
collator.strength = Collator.NO_DECOMPOSITION
@ -410,6 +404,21 @@ class CoreContext(val context: Context, coreConfig: Config) {
/* Call related functions */
fun initPhoneStateListener() {
if (PermissionHelper.get().hasReadPhoneStatePermission()) {
try {
phoneStateListener =
Compatibility.createPhoneListener(context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager)
} catch (exception: SecurityException) {
val hasReadPhoneStatePermission =
PermissionHelper.get().hasReadPhoneStateOrPhoneNumbersPermission()
Log.e("[Context] Failed to create phone state listener: $exception, READ_PHONE_STATE permission status is $hasReadPhoneStatePermission")
}
} else {
Log.w("[Context] Can't create phone state listener, READ_PHONE_STATE permission isn't granted")
}
}
fun answerCallVideoUpdateRequest(call: Call, accept: Boolean) {
val params = core.createCallParams(call)
@ -655,7 +664,7 @@ class CoreContext(val context: Context, coreConfig: Config) {
return
}
if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10) || PermissionHelper.get().hasWriteExternalStorage()) {
if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10) || PermissionHelper.get().hasWriteExternalStoragePermission()) {
for (content in message.contents) {
if (content.isFile && content.filePath != null && content.userData == null) {
addContentToMediaStore(content)
@ -676,7 +685,7 @@ class CoreContext(val context: Context, coreConfig: Config) {
return
}
if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10) || PermissionHelper.get().hasWriteExternalStorage()) {
if (Version.sdkAboveOrEqual(Version.API29_ANDROID_10) || PermissionHelper.get().hasWriteExternalStoragePermission()) {
coroutineScope.launch {
when (content.type) {
"image" -> {

View file

@ -81,7 +81,7 @@ class TelecomHelper private constructor(context: Context) {
@SuppressLint("MissingPermission")
fun findExistingAccount(context: Context): PhoneAccount? {
if (!PermissionHelper.exists()) PermissionHelper.create(context)
if (PermissionHelper.get().hasReadPhoneState()) {
if (PermissionHelper.get().hasReadPhoneStateOrPhoneNumbersPermission()) {
var account: PhoneAccount? = null
val phoneAccountHandleList: List<PhoneAccountHandle> =
telecomManager.selfManagedPhoneAccounts

View file

@ -21,6 +21,8 @@ package org.linphone.utils
import android.Manifest
import android.content.Context
import android.os.Build
import androidx.annotation.RequiresApi
import org.linphone.compatibility.Compatibility
import org.linphone.core.tools.Log
@ -50,15 +52,19 @@ class PermissionHelper private constructor(private val context: Context) {
return hasPermission(Manifest.permission.WRITE_CONTACTS)
}
fun hasReadPhoneState(): Boolean {
fun hasReadPhoneStatePermission(): Boolean {
return hasPermission(Manifest.permission.READ_PHONE_STATE)
}
fun hasReadExternalStorage(): Boolean {
fun hasReadPhoneStateOrPhoneNumbersPermission(): Boolean {
return Compatibility.hasReadPhoneStateOrNumbersPermission(context)
}
fun hasReadExternalStoragePermission(): Boolean {
return hasPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
}
fun hasWriteExternalStorage(): Boolean {
fun hasWriteExternalStoragePermission(): Boolean {
return hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
@ -70,6 +76,7 @@ class PermissionHelper private constructor(private val context: Context) {
return hasPermission(Manifest.permission.RECORD_AUDIO)
}
@RequiresApi(Build.VERSION_CODES.O)
fun hasTelecomManagerPermissions(): Boolean {
return hasPermission(Manifest.permission.READ_PHONE_NUMBERS) &&
hasPermission(Manifest.permission.MANAGE_OWN_CALLS)

View file

@ -41,7 +41,7 @@ class PhoneNumberUtils {
@SuppressLint("MissingPermission", "HardwareIds")
fun getDevicePhoneNumber(context: Context): String? {
if (PermissionHelper.get().hasReadPhoneState()) {
if (PermissionHelper.get().hasReadPhoneStateOrPhoneNumbersPermission()) {
try {
val tm = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
return tm.line1Number