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 package com.android.settings.applications.specialaccess.interactacrossprofiles; 17 18 import static android.app.admin.DevicePolicyResources.Strings.Settings.CONNECTED_WORK_AND_PERSONAL_APPS_TITLE; 19 import static android.content.pm.PackageManager.GET_ACTIVITIES; 20 21 import android.annotation.Nullable; 22 import android.app.admin.DevicePolicyManager; 23 import android.app.settings.SettingsEnums; 24 import android.content.Context; 25 import android.content.pm.ApplicationInfo; 26 import android.content.pm.CrossProfileApps; 27 import android.content.pm.PackageInfo; 28 import android.content.pm.PackageManager; 29 import android.content.pm.UserInfo; 30 import android.os.Bundle; 31 import android.os.UserHandle; 32 import android.os.UserManager; 33 import android.util.IconDrawableFactory; 34 import android.util.Pair; 35 import android.view.View; 36 37 import androidx.preference.Preference; 38 import androidx.preference.Preference.OnPreferenceClickListener; 39 import androidx.preference.PreferenceScreen; 40 41 import com.android.settings.R; 42 import com.android.settings.applications.AppInfoBase; 43 import com.android.settings.search.BaseSearchIndexProvider; 44 import com.android.settings.widget.EmptyTextSettings; 45 import com.android.settingslib.search.SearchIndexable; 46 import com.android.settingslib.widget.AppPreference; 47 48 import java.util.ArrayList; 49 import java.util.List; 50 51 @SearchIndexable 52 public class InteractAcrossProfilesSettings extends EmptyTextSettings { 53 private Context mContext; 54 private PackageManager mPackageManager; 55 private UserManager mUserManager; 56 private CrossProfileApps mCrossProfileApps; 57 private IconDrawableFactory mIconDrawableFactory; 58 59 @Override onCreate(Bundle icicle)60 public void onCreate(Bundle icicle) { 61 super.onCreate(icicle); 62 63 mContext = getContext(); 64 mPackageManager = mContext.getPackageManager(); 65 mUserManager = mContext.getSystemService(UserManager.class); 66 mIconDrawableFactory = IconDrawableFactory.newInstance(mContext); 67 mCrossProfileApps = mContext.getSystemService(CrossProfileApps.class); 68 } 69 70 @Override onResume()71 public void onResume() { 72 super.onResume(); 73 74 final PreferenceScreen screen = getPreferenceScreen(); 75 screen.removeAll(); 76 77 replaceEnterprisePreferenceScreenTitle(CONNECTED_WORK_AND_PERSONAL_APPS_TITLE, 78 R.string.interact_across_profiles_title); 79 80 final ArrayList<Pair<ApplicationInfo, UserHandle>> crossProfileApps = 81 collectConfigurableApps(mPackageManager, mUserManager, mCrossProfileApps); 82 83 final Context prefContext = getPrefContext(); 84 for (final Pair<ApplicationInfo, UserHandle> appData : crossProfileApps) { 85 final ApplicationInfo appInfo = appData.first; 86 final UserHandle user = appData.second; 87 final String packageName = appInfo.packageName; 88 final CharSequence label = appInfo.loadLabel(mPackageManager); 89 90 final Preference pref = new AppPreference(prefContext); 91 pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, user.getIdentifier())); 92 pref.setTitle(mPackageManager.getUserBadgedLabel(label, user)); 93 pref.setSummary(InteractAcrossProfilesDetails.getPreferenceSummary( 94 prefContext, packageName)); 95 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { 96 @Override 97 public boolean onPreferenceClick(Preference preference) { 98 AppInfoBase.startAppInfoFragment(InteractAcrossProfilesDetails.class, 99 mDevicePolicyManager.getResources().getString( 100 CONNECTED_WORK_AND_PERSONAL_APPS_TITLE, 101 () -> getString(R.string.interact_across_profiles_title)), 102 packageName, 103 appInfo.uid, 104 InteractAcrossProfilesSettings.this/* source */, 105 -1/* request */, 106 getMetricsCategory()); 107 return true; 108 } 109 }); 110 screen.addPreference(pref); 111 } 112 } 113 114 @Override onViewCreated(View view, @Nullable Bundle savedInstanceState)115 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 116 super.onViewCreated(view, savedInstanceState); 117 setEmptyText(R.string.interact_across_profiles_empty_text); 118 } 119 120 @Override getPreferenceScreenResId()121 protected int getPreferenceScreenResId() { 122 return R.xml.interact_across_profiles; 123 } 124 125 @Override getMetricsCategory()126 public int getMetricsCategory() { 127 return SettingsEnums.INTERACT_ACROSS_PROFILES; 128 } 129 130 /** 131 * @return the list of applications for the personal profile in the calling user's profile group 132 * that can configure interact across profiles. 133 */ collectConfigurableApps( PackageManager packageManager, UserManager userManager, CrossProfileApps crossProfileApps)134 static ArrayList<Pair<ApplicationInfo, UserHandle>> collectConfigurableApps( 135 PackageManager packageManager, UserManager userManager, 136 CrossProfileApps crossProfileApps) { 137 final UserHandle workProfile = getWorkProfile(userManager); 138 if (workProfile == null) { 139 return new ArrayList<>(); 140 } 141 final UserHandle personalProfile = userManager.getProfileParent(workProfile); 142 if (personalProfile == null) { 143 return new ArrayList<>(); 144 } 145 146 final ArrayList<Pair<ApplicationInfo, UserHandle>> apps = new ArrayList<>(); 147 for (PackageInfo packageInfo : getAllInstalledPackages( 148 packageManager, personalProfile, workProfile)) { 149 if (crossProfileApps.canUserAttemptToConfigureInteractAcrossProfiles( 150 packageInfo.packageName)) { 151 apps.add(new Pair<>(packageInfo.applicationInfo, personalProfile)); 152 } 153 } 154 return apps; 155 } 156 getAllInstalledPackages( PackageManager packageManager, UserHandle personalProfile, UserHandle workProfile)157 private static List<PackageInfo> getAllInstalledPackages( 158 PackageManager packageManager, UserHandle personalProfile, UserHandle workProfile) { 159 List<PackageInfo> personalPackages = packageManager.getInstalledPackagesAsUser( 160 GET_ACTIVITIES, personalProfile.getIdentifier()); 161 List<PackageInfo> workPackages = packageManager.getInstalledPackagesAsUser( 162 GET_ACTIVITIES, workProfile.getIdentifier()); 163 List<PackageInfo> allPackages = new ArrayList<>(personalPackages); 164 for (PackageInfo workPackage : workPackages) { 165 if (allPackages.stream().noneMatch( 166 p -> workPackage.packageName.equals(p.packageName))) { 167 allPackages.add(workPackage); 168 } 169 } 170 return allPackages; 171 } 172 173 /** 174 * @return the number of applications that can interact across profiles. 175 */ getNumberOfEnabledApps( Context context, PackageManager packageManager, UserManager userManager, CrossProfileApps crossProfileApps)176 static int getNumberOfEnabledApps( 177 Context context, PackageManager packageManager, UserManager userManager, 178 CrossProfileApps crossProfileApps) { 179 UserHandle workProfile = getWorkProfile(userManager); 180 if (workProfile == null) { 181 return 0; 182 } 183 UserHandle personalProfile = userManager.getProfileParent(workProfile); 184 if (personalProfile == null) { 185 return 0; 186 } 187 final ArrayList<Pair<ApplicationInfo, UserHandle>> apps = 188 collectConfigurableApps(packageManager, userManager, crossProfileApps); 189 apps.removeIf( 190 app -> !InteractAcrossProfilesDetails.isInteractAcrossProfilesEnabled( 191 context, app.first.packageName) 192 || !crossProfileApps.canConfigureInteractAcrossProfiles( 193 app.first.packageName)); 194 return apps.size(); 195 } 196 197 /** 198 * Returns the work profile in the profile group of the calling user. 199 * Returns null if not found. 200 */ 201 @Nullable getWorkProfile(UserManager userManager)202 static UserHandle getWorkProfile(UserManager userManager) { 203 for (UserInfo user : userManager.getProfiles(UserHandle.myUserId())) { 204 if (userManager.isManagedProfile(user.id)) { 205 return user.getUserHandle(); 206 } 207 } 208 return null; 209 } 210 211 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 212 new BaseSearchIndexProvider(R.xml.interact_across_profiles); 213 } 214