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.server.devicepolicy; 18 19 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; 20 21 import android.accessibilityservice.AccessibilityServiceInfo; 22 import android.annotation.Nullable; 23 import android.annotation.UserIdInt; 24 import android.app.admin.flags.Flags; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.pm.ApplicationInfo; 29 import android.content.pm.PackageInfo; 30 import android.content.pm.PackageManager; 31 import android.content.pm.ResolveInfo; 32 import android.os.IBinder; 33 import android.os.ServiceManager; 34 import android.os.UserHandle; 35 import android.provider.Settings; 36 import android.provider.Telephony; 37 import android.text.TextUtils; 38 import android.util.ArraySet; 39 import android.util.IndentingPrintWriter; 40 import android.view.accessibility.AccessibilityManager; 41 import android.view.accessibility.IAccessibilityManager; 42 import android.view.inputmethod.InputMethodInfo; 43 44 import com.android.internal.R; 45 import com.android.internal.telephony.SmsApplication; 46 import com.android.server.inputmethod.InputMethodManagerInternal; 47 import com.android.server.utils.Slogf; 48 49 import java.util.ArrayList; 50 import java.util.Arrays; 51 import java.util.List; 52 import java.util.Set; 53 54 /** 55 * Utility class to find what personal apps should be suspended to limit personal device use. 56 */ 57 public final class PersonalAppsSuspensionHelper { 58 private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG; 59 60 // Flags to get all packages even if the user is still locked. 61 private static final int PACKAGE_QUERY_FLAGS = 62 PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 63 64 private final Context mContext; 65 private final PackageManager mPackageManager; 66 67 /** 68 * Factory method 69 */ forUser(Context context, @UserIdInt int userId)70 public static PersonalAppsSuspensionHelper forUser(Context context, @UserIdInt int userId) { 71 return new PersonalAppsSuspensionHelper(context.createContextAsUser(UserHandle.of(userId), 72 /* flags= */ 0)); 73 } 74 75 /** 76 * @param context Context for the user whose apps should to be suspended. 77 */ PersonalAppsSuspensionHelper(Context context)78 private PersonalAppsSuspensionHelper(Context context) { 79 mContext = context; 80 mPackageManager = context.getPackageManager(); 81 } 82 83 /** 84 * @return List of packages that should be suspended to limit personal use. 85 */ getPersonalAppsForSuspension()86 String[] getPersonalAppsForSuspension() { 87 final List<PackageInfo> installedPackageInfos = 88 mPackageManager.getInstalledPackages(PACKAGE_QUERY_FLAGS); 89 final Set<String> result = new ArraySet<>(); 90 for (final PackageInfo packageInfo : installedPackageInfos) { 91 final ApplicationInfo info = packageInfo.applicationInfo; 92 if ((!info.isSystemApp() && !info.isUpdatedSystemApp()) 93 || hasLauncherIntent(packageInfo.packageName)) { 94 result.add(packageInfo.packageName); 95 } 96 } 97 result.removeAll(getCriticalPackages()); 98 result.removeAll(getSystemLauncherPackages()); 99 result.removeAll(getAccessibilityServices()); 100 result.removeAll(getInputMethodPackages()); 101 result.remove(getDefaultSmsPackage()); 102 result.remove(getSettingsPackageName()); 103 104 final String[] unsuspendablePackages = 105 mPackageManager.getUnsuspendablePackages(result.toArray(new String[0])); 106 for (final String pkg : unsuspendablePackages) { 107 result.remove(pkg); 108 } 109 return result.toArray(new String[0]); 110 } 111 getSystemLauncherPackages()112 private List<String> getSystemLauncherPackages() { 113 final List<String> result = new ArrayList<>(); 114 final Intent intent = new Intent(Intent.ACTION_MAIN); 115 intent.addCategory(Intent.CATEGORY_HOME); 116 final List<ResolveInfo> matchingActivities = 117 mPackageManager.queryIntentActivities(intent, PACKAGE_QUERY_FLAGS); 118 for (final ResolveInfo resolveInfo : matchingActivities) { 119 if (resolveInfo.activityInfo == null 120 || TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) { 121 Slogf.wtf(LOG_TAG, "Could not find package name for launcher app %s", resolveInfo); 122 continue; 123 } 124 final String packageName = resolveInfo.activityInfo.packageName; 125 try { 126 final ApplicationInfo applicationInfo = 127 mPackageManager.getApplicationInfo(packageName, PACKAGE_QUERY_FLAGS); 128 if (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) { 129 result.add(packageName); 130 } 131 } catch (PackageManager.NameNotFoundException e) { 132 Slogf.e(LOG_TAG, "Could not find application info for launcher app: %s", 133 packageName); 134 } 135 } 136 return result; 137 } 138 getAccessibilityServices()139 private List<String> getAccessibilityServices() { 140 final List<AccessibilityServiceInfo> accessibilityServiceInfos; 141 // Not using AccessibilityManager.getInstance because that guesses 142 // at the user you require based on callingUid and caches for a given 143 // process. 144 final IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE); 145 final IAccessibilityManager service = iBinder == null 146 ? null : IAccessibilityManager.Stub.asInterface(iBinder); 147 final AccessibilityManager am = 148 new AccessibilityManager(mContext, service, mContext.getUserId()); 149 try { 150 accessibilityServiceInfos = am.getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK); 151 } finally { 152 am.removeClient(); 153 } 154 155 final List<String> result = new ArrayList<>(); 156 for (final AccessibilityServiceInfo serviceInfo : accessibilityServiceInfos) { 157 final ComponentName componentName = 158 ComponentName.unflattenFromString(serviceInfo.getId()); 159 if (componentName != null) { 160 result.add(componentName.getPackageName()); 161 } 162 } 163 return result; 164 } 165 getInputMethodPackages()166 private List<String> getInputMethodPackages() { 167 final List<InputMethodInfo> enabledImes = InputMethodManagerInternal.get() 168 .getEnabledInputMethodListAsUser(mContext.getUserId()); 169 final List<String> result = new ArrayList<>(); 170 for (final InputMethodInfo info : enabledImes) { 171 result.add(info.getPackageName()); 172 } 173 return result; 174 } 175 176 @Nullable getSettingsPackageName()177 private String getSettingsPackageName() { 178 final Intent intent = new Intent(Settings.ACTION_SETTINGS); 179 intent.addCategory(Intent.CATEGORY_DEFAULT); 180 final ResolveInfo resolveInfo = 181 mPackageManager.resolveActivity(intent, PACKAGE_QUERY_FLAGS); 182 if (resolveInfo != null) { 183 return resolveInfo.activityInfo.packageName; 184 } 185 return null; 186 } 187 getCriticalPackages()188 private List<String> getCriticalPackages() { 189 return Arrays.asList(mContext.getResources() 190 .getStringArray(R.array.config_packagesExemptFromSuspension)); 191 } 192 hasLauncherIntent(String packageName)193 private boolean hasLauncherIntent(String packageName) { 194 final Intent intentToResolve = new Intent(Intent.ACTION_MAIN); 195 intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); 196 intentToResolve.setPackage(packageName); 197 final List<ResolveInfo> resolveInfos = 198 mPackageManager.queryIntentActivities(intentToResolve, PACKAGE_QUERY_FLAGS); 199 return resolveInfos != null && !resolveInfos.isEmpty(); 200 } 201 getDefaultSmsPackage()202 private String getDefaultSmsPackage() { 203 //TODO(b/319449037): Unflag the following change. 204 if (Flags.defaultSmsPersonalAppSuspensionFixEnabled()) { 205 ComponentName defaultSmsApp = SmsApplication.getDefaultSmsApplicationAsUser( 206 mContext, /*updateIfNeeded=*/ false, mContext.getUser()); 207 return defaultSmsApp != null ? defaultSmsApp.getPackageName() : null; 208 } else { 209 return Telephony.Sms.getDefaultSmsPackage(mContext); 210 } 211 } 212 213 dump(IndentingPrintWriter pw)214 void dump(IndentingPrintWriter pw) { 215 pw.println("PersonalAppsSuspensionHelper"); 216 pw.increaseIndent(); 217 218 DevicePolicyManagerService.dumpApps(pw, "critical packages", getCriticalPackages()); 219 DevicePolicyManagerService.dumpApps(pw, "launcher packages", getSystemLauncherPackages()); 220 DevicePolicyManagerService.dumpApps(pw, "accessibility services", 221 getAccessibilityServices()); 222 DevicePolicyManagerService.dumpApps(pw, "input method packages", getInputMethodPackages()); 223 pw.printf("SMS package: %s\n", getDefaultSmsPackage()); 224 pw.printf("Settings package: %s\n", getSettingsPackageName()); 225 DevicePolicyManagerService.dumpApps(pw, "Packages subject to suspension", 226 getPersonalAppsForSuspension()); 227 228 pw.decreaseIndent(); 229 } 230 } 231