blob: c687184265c13f097e743725d2db2f19a2d3fd42 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.devicepolicy;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.IBinder;
import android.os.ServiceManager;
import android.provider.Settings;
import android.provider.Telephony;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManager;
import android.view.inputmethod.InputMethodInfo;
import com.android.internal.R;
import com.android.server.inputmethod.InputMethodManagerInternal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
/**
* Utility class to find what personal apps should be suspended to limit personal device use.
*/
public class PersonalAppsSuspensionHelper {
private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
// Flags to get all packages even if the user is still locked.
private static final int PACKAGE_QUERY_FLAGS =
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
private final Context mContext;
private final PackageManager mPackageManager;
/**
* @param context Context for the user whose apps should to be suspended.
*/
public PersonalAppsSuspensionHelper(Context context) {
mContext = context;
mPackageManager = context.getPackageManager();
}
/**
* @return List of packages that should be suspended to limit personal use.
*/
String[] getPersonalAppsForSuspension() {
final List<PackageInfo> installedPackageInfos =
mPackageManager.getInstalledPackages(PACKAGE_QUERY_FLAGS);
final Set<String> result = new ArraySet<>();
for (final PackageInfo packageInfo : installedPackageInfos) {
final ApplicationInfo info = packageInfo.applicationInfo;
if ((!info.isSystemApp() && !info.isUpdatedSystemApp())
|| hasLauncherIntent(packageInfo.packageName)) {
result.add(packageInfo.packageName);
}
}
result.removeAll(getCriticalPackages());
result.removeAll(getSystemLauncherPackages());
result.removeAll(getAccessibilityServices());
result.removeAll(getInputMethodPackages());
result.remove(Telephony.Sms.getDefaultSmsPackage(mContext));
result.remove(getSettingsPackageName());
final String[] unsuspendablePackages =
mPackageManager.getUnsuspendablePackages(result.toArray(new String[0]));
for (final String pkg : unsuspendablePackages) {
result.remove(pkg);
}
Slog.i(LOG_TAG, "Packages subject to suspension: " + String.join(",", result));
return result.toArray(new String[0]);
}
private List<String> getSystemLauncherPackages() {
final List<String> result = new ArrayList<>();
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
final List<ResolveInfo> matchingActivities =
mPackageManager.queryIntentActivities(intent, PACKAGE_QUERY_FLAGS);
for (final ResolveInfo resolveInfo : matchingActivities) {
if (resolveInfo.activityInfo == null
|| TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) {
Slog.wtf(LOG_TAG, "Could not find package name for launcher app" + resolveInfo);
continue;
}
final String packageName = resolveInfo.activityInfo.packageName;
try {
final ApplicationInfo applicationInfo =
mPackageManager.getApplicationInfo(packageName, PACKAGE_QUERY_FLAGS);
if (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) {
result.add(packageName);
}
} catch (PackageManager.NameNotFoundException e) {
Slog.e(LOG_TAG, "Could not find application info for launcher app: " + packageName);
}
}
return result;
}
private List<String> getAccessibilityServices() {
final List<AccessibilityServiceInfo> accessibilityServiceInfos =
getAccessibilityManagerForUser(mContext.getUserId())
.getEnabledAccessibilityServiceList(FEEDBACK_ALL_MASK);
final List<String> result = new ArrayList<>();
for (final AccessibilityServiceInfo serviceInfo : accessibilityServiceInfos) {
final ComponentName componentName =
ComponentName.unflattenFromString(serviceInfo.getId());
if (componentName != null) {
result.add(componentName.getPackageName());
}
}
return result;
}
private List<String> getInputMethodPackages() {
final List<InputMethodInfo> enabledImes = InputMethodManagerInternal.get()
.getEnabledInputMethodListAsUser(mContext.getUserId());
final List<String> result = new ArrayList<>();
for (final InputMethodInfo info : enabledImes) {
result.add(info.getPackageName());
}
return result;
}
@Nullable
private String getSettingsPackageName() {
final Intent intent = new Intent(Settings.ACTION_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
final ResolveInfo resolveInfo =
mPackageManager.resolveActivity(intent, PACKAGE_QUERY_FLAGS);
if (resolveInfo != null) {
return resolveInfo.activityInfo.packageName;
}
return null;
}
private List<String> getCriticalPackages() {
return Arrays.asList(mContext.getResources()
.getStringArray(R.array.config_packagesExemptFromSuspension));
}
private boolean hasLauncherIntent(String packageName) {
final Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
intentToResolve.setPackage(packageName);
final List<ResolveInfo> resolveInfos =
mPackageManager.queryIntentActivities(intentToResolve, PACKAGE_QUERY_FLAGS);
return resolveInfos != null && !resolveInfos.isEmpty();
}
private AccessibilityManager getAccessibilityManagerForUser(int userId) {
final IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
final IAccessibilityManager service =
iBinder == null ? null : IAccessibilityManager.Stub.asInterface(iBinder);
return new AccessibilityManager(mContext, service, userId);
}
}