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.task; 18 19 import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES; 20 21 import android.Manifest; 22 import android.app.AppOpsManager; 23 import android.app.admin.DevicePolicyManager; 24 import android.content.Context; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.CrossProfileApps; 27 import android.content.pm.PackageManager; 28 import android.os.UserHandle; 29 import android.os.UserManager; 30 import android.util.ArraySet; 31 32 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker; 33 import com.android.managedprovisioning.common.ProvisionLogger; 34 import com.android.managedprovisioning.model.ProvisioningParams; 35 import com.android.managedprovisioning.task.interactacrossprofiles.CrossProfileAppsSnapshot; 36 37 import java.util.HashSet; 38 import java.util.List; 39 import java.util.Set; 40 41 /** 42 * Task which resets the {@code INTERACT_ACROSS_USERS} app op when the OEM whitelist is changed. 43 */ 44 public class UpdateInteractAcrossProfilesAppOpTask extends AbstractProvisioningTask { 45 46 private final CrossProfileAppsSnapshot mCrossProfileAppsSnapshot; 47 private final CrossProfileApps mCrossProfileApps; 48 private final DevicePolicyManager mDevicePolicyManager; 49 private final AppOpsManager mAppOpsManager; 50 private final PackageManager mPackageManager; 51 private final UserManager mUserManager; 52 UpdateInteractAcrossProfilesAppOpTask(Context context, ProvisioningParams provisioningParams, Callback callback, ProvisioningAnalyticsTracker provisioningAnalyticsTracker)53 public UpdateInteractAcrossProfilesAppOpTask(Context context, 54 ProvisioningParams provisioningParams, 55 Callback callback, 56 ProvisioningAnalyticsTracker provisioningAnalyticsTracker) { 57 super(context, provisioningParams, callback, provisioningAnalyticsTracker); 58 mCrossProfileAppsSnapshot = new CrossProfileAppsSnapshot(context); 59 mCrossProfileApps = context.getSystemService(CrossProfileApps.class); 60 mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class); 61 mAppOpsManager = context.getSystemService(AppOpsManager.class); 62 mPackageManager = context.getPackageManager(); 63 mUserManager = context.getSystemService(UserManager.class); 64 } 65 66 @Override run(int userId)67 public void run(int userId) { 68 Set<String> previousCrossProfileApps = 69 mCrossProfileAppsSnapshot.hasSnapshot(userId) ? 70 mCrossProfileAppsSnapshot.getSnapshot(userId) : 71 new ArraySet<>(); 72 mCrossProfileAppsSnapshot.takeNewSnapshot(userId); 73 Set<String> currentCrossProfileApps = mCrossProfileAppsSnapshot.getSnapshot(userId); 74 75 updateAfterOtaChanges(previousCrossProfileApps, currentCrossProfileApps); 76 } 77 updateAfterOtaChanges( Set<String> previousCrossProfilePackages, Set<String> currentCrossProfilePackages)78 private void updateAfterOtaChanges( 79 Set<String> previousCrossProfilePackages, Set<String> currentCrossProfilePackages) { 80 mCrossProfileApps.resetInteractAcrossProfilesAppOps( 81 previousCrossProfilePackages, currentCrossProfilePackages); 82 Set<String> newCrossProfilePackages = new HashSet<>(currentCrossProfilePackages); 83 newCrossProfilePackages.removeAll(previousCrossProfilePackages); 84 85 grantNewConfigurableDefaultCrossProfilePackages(newCrossProfilePackages); 86 reapplyCrossProfileAppsPermission(); 87 } 88 grantNewConfigurableDefaultCrossProfilePackages( Set<String> newCrossProfilePackages)89 private void grantNewConfigurableDefaultCrossProfilePackages( 90 Set<String> newCrossProfilePackages) { 91 final String op = 92 AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES); 93 for (String crossProfilePackageName : newCrossProfilePackages) { 94 if (!mCrossProfileApps.canConfigureInteractAcrossProfiles(crossProfilePackageName)) { 95 continue; 96 } 97 try { 98 final int uid = mPackageManager.getPackageUid( 99 crossProfilePackageName, /* flags= */ 0); 100 if (appOpIsChangedFromDefault(op, uid, crossProfilePackageName)) { 101 continue; 102 } 103 } catch (PackageManager.NameNotFoundException e) { 104 ProvisionLogger.loge("Missing package, this should not happen.", e); 105 continue; 106 } 107 mCrossProfileApps.setInteractAcrossProfilesAppOp(crossProfilePackageName, 108 AppOpsManager.MODE_ALLOWED); 109 } 110 } 111 112 /** 113 * Iterate over all apps and reapply the app-op permission. 114 * 115 * <p>This is to fix an issue that existed in Android 11 where the appop was set per-package 116 * instead of per-UID causing issues for applications with shared UIDs. 117 */ reapplyCrossProfileAppsPermission()118 private void reapplyCrossProfileAppsPermission() { 119 final Set<Integer> uids = getUidsWithNonDefaultMode(); 120 reapplyCrossProfileAppsPermissionForUids(uids); 121 } 122 getUidsWithNonDefaultMode()123 private Set<Integer> getUidsWithNonDefaultMode() { 124 final String op = AppOpsManager.permissionToOp( 125 Manifest.permission.INTERACT_ACROSS_PROFILES); 126 final Set<Integer> uids = new HashSet<>(); 127 for (ApplicationInfo appInfo : getAllInstalledApps()) { 128 if (appOpIsChangedFromDefault( 129 op, appInfo.uid, appInfo.packageName)) { 130 uids.add(appInfo.uid); 131 } 132 } 133 return uids; 134 } 135 getAllInstalledApps()136 private Set<ApplicationInfo> getAllInstalledApps() { 137 final Set<ApplicationInfo> apps = new HashSet<>(); 138 List<UserHandle> profiles = mUserManager.getAllProfiles(); 139 for (UserHandle profile : profiles) { 140 if (profile.getIdentifier() != mContext.getUserId() 141 && !mUserManager.isManagedProfile(profile.getIdentifier())) { 142 continue; 143 } 144 try { 145 apps.addAll( 146 mContext.createPackageContextAsUser( 147 /* packageName= */ "android", /* flags= */ 0, profile) 148 .getPackageManager().getInstalledApplications(/* flags= */ 0)); 149 } catch (PackageManager.NameNotFoundException ignored) { 150 // Should never happen. 151 } 152 } 153 return apps; 154 } 155 reapplyCrossProfileAppsPermissionForUids(Set<Integer> uids)156 private void reapplyCrossProfileAppsPermissionForUids(Set<Integer> uids) { 157 for (int uid : uids) { 158 reapplyCrossProfileAppsPermissionForUid(uid); 159 } 160 } 161 reapplyCrossProfileAppsPermissionForUid(int uid)162 private void reapplyCrossProfileAppsPermissionForUid(int uid) { 163 final String op = AppOpsManager.permissionToOp( 164 Manifest.permission.INTERACT_ACROSS_PROFILES); 165 final String[] packages = mPackageManager.getPackagesForUid(uid); 166 final int consolidatedUidMode = getConsolidatedModeForPackagesInUid(uid, packages, op); 167 setAppOpForPackagesInUid(uid, packages, consolidatedUidMode); 168 } 169 getConsolidatedModeForPackagesInUid(int uid, String[] packages, String op)170 private int getConsolidatedModeForPackagesInUid(int uid, String[] packages, String op) { 171 int uidMode = AppOpsManager.MODE_DEFAULT; 172 for (String packageName : packages) { 173 if (mCrossProfileApps.canConfigureInteractAcrossProfiles(packageName)) { 174 final int packageMode = mAppOpsManager.unsafeCheckOpNoThrow(op, uid, packageName); 175 if (shouldUpdateUidMode(packageMode, uidMode)) { 176 uidMode = packageMode; 177 } 178 } 179 } 180 return uidMode; 181 } 182 shouldUpdateUidMode(int packageMode, @AppOpsManager.Mode int uidMode)183 private boolean shouldUpdateUidMode(int packageMode, @AppOpsManager.Mode int uidMode) { 184 if (!appOpIsChangedFromDefault(packageMode)) { 185 return false; 186 } 187 if (!appOpIsChangedFromDefault(uidMode)) { 188 return true; 189 } 190 if (packageMode == AppOpsManager.MODE_ALLOWED) { 191 return true; 192 } 193 return false; 194 } 195 setAppOpForPackagesInUid( int uid, String[] packages, @AppOpsManager.Mode int mode)196 private void setAppOpForPackagesInUid( 197 int uid, String[] packages, @AppOpsManager.Mode int mode) { 198 for (String packageName : packages) { 199 setInteractAcrossProfilesAppOpForPackage(uid, packageName, mode); 200 } 201 } 202 203 /** 204 * Sets the package appop to default mode and the uid appop to {@code mode}. 205 */ setInteractAcrossProfilesAppOpForPackage( int uid, String packageName, @AppOpsManager.Mode int mode)206 private void setInteractAcrossProfilesAppOpForPackage( 207 int uid, String packageName, @AppOpsManager.Mode int mode) { 208 mAppOpsManager.setMode( 209 OP_INTERACT_ACROSS_PROFILES, uid, packageName, 210 AppOpsManager.opToDefaultMode(OP_INTERACT_ACROSS_PROFILES)); 211 mAppOpsManager.setUidMode(OP_INTERACT_ACROSS_PROFILES, uid, mode); 212 } 213 appOpIsChangedFromDefault(String op, int uid, String packageName)214 private boolean appOpIsChangedFromDefault(String op, int uid, String packageName) { 215 return mAppOpsManager.unsafeCheckOpNoThrow(op, uid, packageName) 216 != AppOpsManager.MODE_DEFAULT; 217 } 218 appOpIsChangedFromDefault(int mode)219 private boolean appOpIsChangedFromDefault(int mode) { 220 return mode != AppOpsManager.MODE_DEFAULT; 221 } 222 } 223