1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.intentresolver 17 18 import android.Manifest 19 import android.Manifest.permission.INTERACT_ACROSS_USERS 20 import android.Manifest.permission.INTERACT_ACROSS_USERS_FULL 21 import android.app.ActivityManager 22 import android.content.Context 23 import android.content.Intent 24 import android.content.PermissionChecker 25 import android.content.pm.ApplicationInfo 26 import android.content.pm.PackageManager 27 import android.content.pm.PackageManager.PERMISSION_GRANTED 28 import android.os.UserHandle 29 import android.os.UserManager 30 import android.util.Log 31 import com.android.intentresolver.data.repository.DevicePolicyResources 32 import javax.inject.Inject 33 import javax.inject.Singleton 34 35 private const val TAG: String = "IntentForwarding" 36 37 @Singleton 38 class IntentForwarding 39 @Inject 40 constructor( 41 private val resources: DevicePolicyResources, 42 private val userManager: UserManager, 43 private val packageManager: PackageManager 44 ) { 45 forwardMessageFornull46 fun forwardMessageFor(intent: Intent): String? { 47 val contentUserHint = intent.contentUserHint 48 if ( 49 contentUserHint != UserHandle.USER_CURRENT && contentUserHint != UserHandle.myUserId() 50 ) { 51 val originUserInfo = userManager.getUserInfo(contentUserHint) 52 val originIsManaged = originUserInfo?.isManagedProfile ?: false 53 val targetIsManaged = userManager.isManagedProfile 54 return when { 55 originIsManaged && !targetIsManaged -> resources.forwardToPersonalMessage 56 !originIsManaged && targetIsManaged -> resources.forwardToWorkMessage 57 else -> null 58 } 59 } 60 return null 61 } 62 isPermissionGrantednull63 private fun isPermissionGranted(permission: String, uid: Int) = 64 ActivityManager.checkComponentPermission( 65 /* permission = */ permission, 66 /* uid = */ uid, 67 /* owningUid= */ -1, 68 /* exported= */ true 69 ) 70 71 /** 72 * Returns whether the package has the necessary permissions to interact across profiles on 73 * behalf of a given user. 74 * 75 * This means meeting the following condition: 76 * * The app's [ApplicationInfo.crossProfile] flag must be true, and at least one of the 77 * following conditions must be fulfilled 78 * * `Manifest.permission.INTERACT_ACROSS_USERS_FULL` granted. 79 * * `Manifest.permission.INTERACT_ACROSS_USERS` granted. 80 * * `Manifest.permission.INTERACT_ACROSS_PROFILES` granted, or the corresponding AppOps 81 * `android:interact_across_profiles` is set to "allow". 82 */ 83 fun canAppInteractAcrossProfiles(context: Context, packageName: String): Boolean { 84 val applicationInfo: ApplicationInfo 85 try { 86 applicationInfo = packageManager.getApplicationInfo(packageName, 0) 87 } catch (e: PackageManager.NameNotFoundException) { 88 Log.e(TAG, "Package $packageName does not exist on current user.") 89 return false 90 } 91 if (!applicationInfo.crossProfile) { 92 return false 93 } 94 95 val packageUid = applicationInfo.uid 96 97 if (isPermissionGranted(INTERACT_ACROSS_USERS_FULL, packageUid) == PERMISSION_GRANTED) { 98 return true 99 } 100 if (isPermissionGranted(INTERACT_ACROSS_USERS, packageUid) == PERMISSION_GRANTED) { 101 return true 102 } 103 return PermissionChecker.checkPermissionForPreflight( 104 context, 105 Manifest.permission.INTERACT_ACROSS_PROFILES, 106 PermissionChecker.PID_UNKNOWN, 107 packageUid, 108 packageName 109 ) == PERMISSION_GRANTED 110 } 111 } 112