| /* |
| * Copyright (C) 2019 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.settings.bugreporthandler; |
| |
| import android.app.ActivityManager; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.ResolveInfo; |
| import android.content.pm.UserInfo; |
| import android.os.RemoteException; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.provider.Settings; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.util.Pair; |
| import android.widget.Toast; |
| |
| import com.android.settings.R; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.stream.Collectors; |
| |
| /** |
| * Utility methods related to BugReportHandler. |
| */ |
| public class BugReportHandlerUtil { |
| private static final String TAG = "BugReportHandlerUtil"; |
| private static final String INTENT_BUGREPORT_REQUESTED = |
| "com.android.internal.intent.action.BUGREPORT_REQUESTED"; |
| |
| public static final String SHELL_APP_PACKAGE = "com.android.shell"; |
| |
| public BugReportHandlerUtil() { |
| } |
| |
| /** |
| * Check is BugReportHandler enabled on the device. |
| * |
| * @param context Context |
| * @return true if BugReportHandler is enabled, or false otherwise |
| */ |
| public boolean isBugReportHandlerEnabled(Context context) { |
| return context.getResources().getBoolean( |
| com.android.internal.R.bool.config_bugReportHandlerEnabled); |
| } |
| |
| /** |
| * Fetch the package name currently used as bug report handler app and the user. |
| * |
| * @param context Context |
| * @return a pair of two values, first one is the current bug report handler app, second is |
| * the user. |
| */ |
| public Pair<String, Integer> getCurrentBugReportHandlerAppAndUser(Context context) { |
| |
| String handlerApp = getCustomBugReportHandlerApp(context); |
| int handlerUser = getCustomBugReportHandlerUser(context); |
| |
| boolean needToResetOutdatedSettings = false; |
| if (!isBugreportAllowlistedApp(handlerApp)) { |
| handlerApp = getDefaultBugReportHandlerApp(context); |
| handlerUser = UserHandle.USER_SYSTEM; |
| } else if (getBugReportHandlerAppReceivers(context, handlerApp, handlerUser).isEmpty()) { |
| // It looks like the settings are outdated, need to reset outdated settings. |
| // |
| // i.e. |
| // If user chooses which profile and which bugreport-allowlisted app in that |
| // profile to handle a bugreport, then user remove the profile. |
| // === RESULT === |
| // The chosen bugreport handler app is outdated because the profile is removed, |
| // so need to reset outdated settings |
| handlerApp = getDefaultBugReportHandlerApp(context); |
| handlerUser = UserHandle.USER_SYSTEM; |
| needToResetOutdatedSettings = true; |
| } |
| |
| if (!isBugreportAllowlistedApp(handlerApp) |
| || getBugReportHandlerAppReceivers(context, handlerApp, handlerUser).isEmpty()) { |
| // It looks like current handler app may be too old and doesn't support to handle a |
| // bugreport, so change to let shell to handle a bugreport and need to reset |
| // settings. |
| handlerApp = SHELL_APP_PACKAGE; |
| handlerUser = UserHandle.USER_SYSTEM; |
| needToResetOutdatedSettings = true; |
| } |
| |
| if (needToResetOutdatedSettings) { |
| setBugreportHandlerAppAndUser(context, handlerApp, handlerUser); |
| } |
| |
| return Pair.create(handlerApp, handlerUser); |
| } |
| |
| private String getCustomBugReportHandlerApp(Context context) { |
| // Get the package of custom bugreport handler app |
| return Settings.Global.getString(context.getContentResolver(), |
| Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP); |
| } |
| |
| private int getCustomBugReportHandlerUser(Context context) { |
| return Settings.Global.getInt(context.getContentResolver(), |
| Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER, UserHandle.USER_NULL); |
| } |
| |
| private String getDefaultBugReportHandlerApp(Context context) { |
| return context.getResources().getString( |
| com.android.internal.R.string.config_defaultBugReportHandlerApp); |
| } |
| |
| /** |
| * Change current bug report handler app and user. |
| * |
| * @param context Context |
| * @param handlerApp the package name of the handler app |
| * @param handlerUser the id of the handler user |
| * @return whether the change succeeded |
| */ |
| public boolean setCurrentBugReportHandlerAppAndUser(Context context, String handlerApp, |
| int handlerUser) { |
| if (!isBugreportAllowlistedApp(handlerApp)) { |
| return false; |
| } else if (getBugReportHandlerAppReceivers(context, handlerApp, handlerUser).isEmpty()) { |
| return false; |
| } |
| setBugreportHandlerAppAndUser(context, handlerApp, handlerUser); |
| return true; |
| } |
| |
| /** |
| * Fetches ApplicationInfo objects and user ids for all currently valid BugReportHandler. |
| * A BugReportHandler is considered valid if it can receive BUGREPORT_REQUESTED intent. |
| * |
| * @param context Context |
| * @return pair objects for all currently valid BugReportHandler packages and user id |
| */ |
| public List<Pair<ApplicationInfo, Integer>> getValidBugReportHandlerInfos(Context context) { |
| final List<Pair<ApplicationInfo, Integer>> validBugReportHandlerApplicationInfos = |
| new ArrayList<>(); |
| List<String> bugreportAllowlistedPackages; |
| try { |
| bugreportAllowlistedPackages = |
| ActivityManager.getService().getBugreportWhitelistedPackages(); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Failed to get bugreportAllowlistedPackages:", e); |
| return validBugReportHandlerApplicationInfos; |
| } |
| |
| // Add "Shell with system user" as System default preference on top of screen |
| if (bugreportAllowlistedPackages.contains(SHELL_APP_PACKAGE) |
| && !getBugReportHandlerAppReceivers(context, SHELL_APP_PACKAGE, |
| UserHandle.USER_SYSTEM).isEmpty()) { |
| try { |
| validBugReportHandlerApplicationInfos.add( |
| Pair.create( |
| context.getPackageManager().getApplicationInfo(SHELL_APP_PACKAGE, |
| PackageManager.MATCH_ANY_USER), UserHandle.USER_SYSTEM) |
| ); |
| } catch (PackageManager.NameNotFoundException e) { |
| } |
| } |
| |
| final UserManager userManager = context.getSystemService(UserManager.class); |
| final List<UserInfo> profileList = userManager.getProfiles(UserHandle.getCallingUserId()); |
| // Only add non-Shell app as normal preference |
| final List<String> nonShellPackageList = bugreportAllowlistedPackages.stream() |
| .filter(pkg -> !SHELL_APP_PACKAGE.equals(pkg)).collect(Collectors.toList()); |
| Collections.sort(nonShellPackageList); |
| for (String pkg : nonShellPackageList) { |
| for (UserInfo profile : profileList) { |
| final int userId = profile.getUserHandle().getIdentifier(); |
| if (getBugReportHandlerAppReceivers(context, pkg, userId).isEmpty()) { |
| continue; |
| } |
| try { |
| validBugReportHandlerApplicationInfos.add( |
| Pair.create(context.getPackageManager() |
| .getApplicationInfo(pkg, PackageManager.MATCH_ANY_USER), |
| userId)); |
| } catch (PackageManager.NameNotFoundException e) { |
| } |
| } |
| } |
| return validBugReportHandlerApplicationInfos; |
| } |
| |
| private boolean isBugreportAllowlistedApp(String app) { |
| // Verify the app is bugreport-allowlisted |
| if (TextUtils.isEmpty(app)) { |
| return false; |
| } |
| try { |
| return ActivityManager.getService().getBugreportWhitelistedPackages().contains(app); |
| } catch (RemoteException e) { |
| Log.e(TAG, "Failed to get bugreportAllowlistedPackages:", e); |
| return false; |
| } |
| } |
| |
| private List<ResolveInfo> getBugReportHandlerAppReceivers(Context context, String handlerApp, |
| int handlerUser) { |
| // Use the app package and the user id to retrieve the receiver that can handle a |
| // broadcast of the intent. |
| final Intent intent = new Intent(INTENT_BUGREPORT_REQUESTED); |
| intent.setPackage(handlerApp); |
| return context.getPackageManager() |
| .queryBroadcastReceiversAsUser(intent, PackageManager.MATCH_SYSTEM_ONLY, |
| handlerUser); |
| } |
| |
| private void setBugreportHandlerAppAndUser(Context context, String handlerApp, |
| int handlerUser) { |
| Settings.Global.putString(context.getContentResolver(), |
| Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP, |
| handlerApp); |
| Settings.Global.putInt(context.getContentResolver(), |
| Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER, handlerUser); |
| } |
| |
| /** |
| * Show a toast to explain the chosen bug report handler can no longer be chosen. |
| */ |
| public void showInvalidChoiceToast(Context context) { |
| final Toast toast = Toast.makeText(context, |
| R.string.select_invalid_bug_report_handler_toast_text, Toast.LENGTH_SHORT); |
| toast.show(); |
| } |
| } |