Files
app_Settings/src/com/android/settings/bugreporthandler/BugReportHandlerUtil.java
Nikhil Kumar 109084137a Refactor power menu bug reporting flow to work for multiple admins
Existing implementation of power menu bugreporting flow works only
for the primary user. In HSUM(Headless system user mode) there is
going to be more than one admin users who are allowed to trigger bug
report from the power menu, below refactorings has been done do handle
this case.

Configurations for custom_bugreport_handler_app and custom_bugreport_handler_user are moved from Global to
Secure settings so that each user can save them.

The current context user will be the fallback default user in the
absence of a correctly configured handlerApp or handlerUser.

Retrieval of the current bug report handler apps are refactored to add
shell as user's default preferences for any user(current user).

Test: manually tested the bugreport is working for multiple admin with
different combination of handlerApp and handlerUser

Bug: 261184202
Change-Id: I3110c1f9619ede2edeb744daf37521318f0ff6e7
2022-12-13 12:31:20 +00:00

249 lines
10 KiB
Java

/*
* 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 = context.getUserId();
} 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 = context.getUserId();
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 = context.getUserId();
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.Secure.getString(context.getContentResolver(),
Settings.Secure.CUSTOM_BUGREPORT_HANDLER_APP);
}
private int getCustomBugReportHandlerUser(Context context) {
return Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.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;
}
int currentUser = UserHandle.getCallingUserId();
// Add "Shell" as user's default preference on top of screen
if (bugreportAllowlistedPackages.contains(SHELL_APP_PACKAGE)
&& !getBugReportHandlerAppReceivers(context, SHELL_APP_PACKAGE,
currentUser).isEmpty()) {
try {
validBugReportHandlerApplicationInfos.add(
Pair.create(
context.getPackageManager().getApplicationInfo(SHELL_APP_PACKAGE,
PackageManager.MATCH_ANY_USER), currentUser)
);
} catch (PackageManager.NameNotFoundException e) {
}
}
final UserManager userManager = context.getSystemService(UserManager.class);
final List<UserInfo> profileList = userManager.getProfiles(currentUser);
// 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.Secure.putString(context.getContentResolver(),
Settings.Secure.CUSTOM_BUGREPORT_HANDLER_APP,
handlerApp);
Settings.Secure.putInt(context.getContentResolver(),
Settings.Secure.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();
}
}