1 /*
2  * Copyright (C) 2020 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 
17 package com.android.managedprovisioning.ota;
18 
19 import static android.provider.Settings.Global.CONNECTED_APPS_ALLOWED_PACKAGES;
20 import static android.provider.Settings.Global.CONNECTED_APPS_DISALLOWED_PACKAGES;
21 import static android.provider.Settings.Global.getString;
22 
23 import android.Manifest;
24 import android.app.AppOpsManager;
25 import android.app.admin.DevicePolicyManager;
26 import android.app.admin.flags.Flags;
27 import android.content.Context;
28 import android.content.pm.CrossProfileApps;
29 import android.content.pm.PackageManager;
30 import android.content.pm.UserInfo;
31 import android.os.UserHandle;
32 import android.os.UserManager;
33 
34 import com.android.internal.annotations.VisibleForTesting;
35 import com.android.managedprovisioning.task.interactacrossprofiles.CrossProfileAppsSnapshot;
36 
37 import java.util.Arrays;
38 import java.util.Collections;
39 import java.util.Optional;
40 import java.util.Set;
41 import java.util.stream.Collectors;
42 
43 
44 /**
45  * Controller for granting the INTERACT_ACROSS_PROFILES appop at boot time.
46  */
47 public class CrossProfileAppsPregrantController {
48 
49     private final Context mContext;
50     private final UserManager mUserManager;
51     private final PackageManager mPackageManager;
52     private final DevicePolicyManager mDevicePolicyManager;
53     private final CrossProfileApps mCrossProfileApps;
54     private final CrossProfileAppsSnapshot mCrossProfileAppsSnapshot;
55     private final AppOpsManager mAppOpsManager;
56     private final Set<String> mUserDisallowedCrossProfilePackages;
57     private final Set<String> mUserAllowedCrossProfilePackages;
58 
CrossProfileAppsPregrantController(Context context)59     public CrossProfileAppsPregrantController(Context context) {
60         this(context,
61                 context.getSystemService(UserManager.class),
62                 context.getPackageManager(),
63                 context.getSystemService(DevicePolicyManager.class),
64                 context.getSystemService(CrossProfileApps.class),
65                 context.getSystemService(AppOpsManager.class));
66     }
67 
68     @VisibleForTesting
CrossProfileAppsPregrantController(Context context, UserManager userManager, PackageManager packageManager, DevicePolicyManager devicePolicyManager, CrossProfileApps crossProfileApps, AppOpsManager appOpsManager)69     CrossProfileAppsPregrantController(Context context,
70             UserManager userManager,
71             PackageManager packageManager,
72             DevicePolicyManager devicePolicyManager,
73             CrossProfileApps crossProfileApps,
74             AppOpsManager appOpsManager) {
75         mContext = context;
76         mUserManager = userManager;
77         mPackageManager = packageManager;
78         mDevicePolicyManager = devicePolicyManager;
79         mCrossProfileApps = crossProfileApps;
80         mAppOpsManager = appOpsManager;
81         mCrossProfileAppsSnapshot = new CrossProfileAppsSnapshot(context);
82 
83         mUserAllowedCrossProfilePackages = getConfigurablePackageSetFromSetting(
84                 CONNECTED_APPS_ALLOWED_PACKAGES);
85         mUserDisallowedCrossProfilePackages = getConfigurablePackageSetFromSetting(
86                 CONNECTED_APPS_DISALLOWED_PACKAGES);
87     }
88 
checkCrossProfileAppsPermissions()89     public void checkCrossProfileAppsPermissions() {
90         if (!isParentProfileOfManagedProfile()) {
91             return;
92         }
93 
94         Set<String> crossProfilePackages =
95                 mCrossProfileAppsSnapshot.getSnapshot(mContext.getUserId());
96 
97         String op = AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES);
98         for (String crossProfilePackageName : getConfigurableDefaultCrossProfilePackages()) {
99             if (crossProfilePackages.contains(crossProfilePackageName)
100                     && !appOpIsChangedFromDefault(op, crossProfilePackageName)
101                     && !mUserDisallowedCrossProfilePackages.contains(crossProfilePackageName)) {
102                 mCrossProfileApps.setInteractAcrossProfilesAppOp(crossProfilePackageName,
103                         AppOpsManager.MODE_ALLOWED);
104             }
105         }
106         if (Flags.backupConnectedAppsSettings()) {
107             mUserAllowedCrossProfilePackages.stream()
108                     .filter(packageName -> !appOpIsChangedFromDefault(op, packageName))
109                     .filter(mCrossProfileApps::canConfigureInteractAcrossProfiles)
110                     .forEach(packageName -> mCrossProfileApps.setInteractAcrossProfilesAppOp(
111                             packageName, AppOpsManager.MODE_ALLOWED));
112         }
113     }
114 
getConfigurablePackageSetFromSetting(String settingsKey)115     private Set<String> getConfigurablePackageSetFromSetting(String settingsKey) {
116         if (!Flags.backupConnectedAppsSettings()) {
117             return Collections.emptySet();
118         }
119         return Optional.ofNullable(getString(mContext.getContentResolver(), settingsKey))
120                 .map(settingString -> Arrays.stream(settingString.split(","))
121                         .collect(Collectors.toSet()))
122                 .orElse(Collections.emptySet());
123     }
124 
appOpIsChangedFromDefault(String op, String packageName)125     private boolean appOpIsChangedFromDefault(String op, String packageName) {
126         try {
127             int uid = mPackageManager.getPackageUid(packageName, /* flags= */ 0);
128             return mAppOpsManager.unsafeCheckOpNoThrow(op, uid, packageName)
129                     != AppOpsManager.MODE_DEFAULT;
130         } catch (PackageManager.NameNotFoundException e) {
131             return false;
132         }
133     }
134 
getConfigurableDefaultCrossProfilePackages()135     private Set<String> getConfigurableDefaultCrossProfilePackages() {
136         Set<String> defaultPackages = mDevicePolicyManager.getDefaultCrossProfilePackages();
137         return defaultPackages.stream().filter(
138                 mCrossProfileApps::canConfigureInteractAcrossProfiles).collect(Collectors.toSet());
139     }
140 
isParentProfileOfManagedProfile()141     private boolean isParentProfileOfManagedProfile() {
142         int currentUserId = android.os.Process.myUserHandle().getIdentifier();
143         for (UserInfo userInfo : mUserManager.getProfiles(currentUserId)) {
144             UserHandle userHandle = userInfo.getUserHandle();
145             if (userInfo.isManagedProfile() &&
146                     mUserManager.getProfileParent(userHandle).getIdentifier() == currentUserId) {
147                 return true;
148             }
149         }
150         return false;
151     }
152 }
153