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