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