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