Merge "Have a setting in Developer Options to choose bug report handler"

This commit is contained in:
Paul Chang
2019-12-17 03:20:19 +00:00
committed by Android (Google) Code Review
12 changed files with 900 additions and 5 deletions

View File

@@ -1974,6 +1974,18 @@
<activity android:name="Settings$WebViewAppPickerActivity"
android:label="@string/select_webview_provider_dialog_title" />
<activity android:name="Settings$BugReportHandlerPickerActivity"
android:label="@string/bug_report_handler_title"
android:exported="true"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.settings.BUGREPORT_HANDLER_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.bugreporthandler.BugReportHandlerPicker" />
</activity>
<activity android:name=".bluetooth.BluetoothPairingDialog"
android:excludeFromRecents="true"
android:windowSoftInputMode="stateVisible|adjustResize"

View File

@@ -11372,4 +11372,22 @@
<!-- DSU Loader Loading. Do not translate. -->
<string name="dsu_loader_loading" translatable="false">Loading...</string>
<!-- Name of dev option called "Bug report handler" [CHAR LIMIT=NONE] -->
<string name="bug_report_handler_title">Bug report handler</string>
<!-- Developer Settings: Footer text for bug report handler picker [CHAR LIMIT=NONE] -->
<string name="bug_report_handler_picker_footer_text">Determines which app handles the Bug Report shortcut on your device.</string>
<!-- Label of personal profile app for current setting [CHAR LIMIT=NONE] -->
<string name="personal_profile_app">(Personal)</string>
<!-- Label of work profile app for current setting [CHAR LIMIT=NONE] -->
<string name="work_profile_app">(Work)</string>
<!-- Title of Shell app for current setting [CHAR LIMIT=NONE] -->
<string name="shell_app">Android System (Shell)</string>
<!-- Developer settings: text for the bug report handler selection toast shown if an invalid bug report handler was chosen. [CHAR LIMIT=NONE] -->
<string name="select_invalid_bug_report_handler_toast_text">This choice is no longer valid. Try again.</string>
</resources>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/bug_report_handler_title"
settings:staticPreferenceLocation="append" >
</PreferenceScreen>

View File

@@ -35,6 +35,11 @@
android:title="@*android:string/bugreport_title"
android:dialogTitle="@*android:string/bugreport_title" />
<Preference
android:key="bug_report_handler"
android:title="@string/bug_report_handler_title"
android:fragment="com.android.settings.bugreporthandler.BugReportHandlerPicker" />
<Preference
android:key="system_server_heap_dump"
android:title="@string/capture_system_heap_dump_title" />

View File

@@ -169,6 +169,10 @@ public class Settings extends SettingsActivity {
public static class WifiCallingDisclaimerActivity extends SettingsActivity { /* empty */ }
public static class MobileNetworkListActivity extends SettingsActivity {}
public static class GlobalActionsPanelSettingsActivity extends SettingsActivity {}
/**
* Activity for BugReportHandlerPicker.
*/
public static class BugReportHandlerPickerActivity extends SettingsActivity { /* empty */ }
// Top level categories for new IA
public static class NetworkDashboardActivity extends SettingsActivity {}

View File

@@ -0,0 +1,205 @@
/*
* 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 static android.provider.Settings.ACTION_BUGREPORT_HANDLER_SETTINGS;
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.defaultapps.DefaultAppPickerFragment;
import com.android.settingslib.applications.DefaultAppInfo;
import com.android.settingslib.widget.FooterPreference;
import java.util.ArrayList;
import java.util.List;
/**
* Picker for BugReportHandler.
*/
public class BugReportHandlerPicker extends DefaultAppPickerFragment {
private static final String TAG = "BugReportHandlerPicker";
private BugReportHandlerUtil mBugReportHandlerUtil;
private FooterPreference mFooter;
private static String getHandlerApp(String key) {
int index = key.lastIndexOf('#');
String handlerApp = key.substring(0, index);
return handlerApp;
}
private static int getHandlerUser(String key) {
int index = key.lastIndexOf('#');
int handlerUser = 0;
try {
handlerUser = Integer.parseInt(key.substring(index + 1));
} catch (NumberFormatException nfe) {
Log.e(TAG, "Failed to get handlerUser");
}
return handlerUser;
}
@VisibleForTesting
static String getKey(String handlerApp, int handlerUser) {
return handlerApp + "#" + handlerUser;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.bug_report_handler_settings;
}
@Override
protected void addStaticPreferences(PreferenceScreen screen) {
if (mFooter == null) {
mFooter = new FooterPreference(screen.getContext());
mFooter.setIcon(R.drawable.ic_info_outline_24dp);
mFooter.setSingleLineTitle(false);
mFooter.setTitle(R.string.bug_report_handler_picker_footer_text);
mFooter.setSelectable(false);
}
screen.addPreference(mFooter);
}
@Override
protected List<DefaultAppInfo> getCandidates() {
final Context context = getContext();
final List<Pair<ApplicationInfo, Integer>> validBugReportHandlerInfos =
getBugReportHandlerUtil().getValidBugReportHandlerInfos(context);
final List<DefaultAppInfo> candidates = new ArrayList<>();
for (Pair<ApplicationInfo, Integer> info : validBugReportHandlerInfos) {
candidates.add(createDefaultAppInfo(context, mPm, info.second, info.first));
}
return candidates;
}
private BugReportHandlerUtil getBugReportHandlerUtil() {
if (mBugReportHandlerUtil == null) {
setBugReportHandlerUtil(createDefaultBugReportHandlerUtil());
}
return mBugReportHandlerUtil;
}
@VisibleForTesting
void setBugReportHandlerUtil(BugReportHandlerUtil bugReportHandlerUtil) {
mBugReportHandlerUtil = bugReportHandlerUtil;
}
@VisibleForTesting
BugReportHandlerUtil createDefaultBugReportHandlerUtil() {
return new BugReportHandlerUtil();
}
@Override
protected String getDefaultKey() {
final Pair<String, Integer> pair =
getBugReportHandlerUtil().getCurrentBugReportHandlerAppAndUser(getContext());
return getKey(pair.first, pair.second);
}
@Override
protected boolean setDefaultKey(String key) {
return getBugReportHandlerUtil().setCurrentBugReportHandlerAppAndUser(getContext(),
getHandlerApp(key),
getHandlerUser(key));
}
@Override
protected void onSelectionPerformed(boolean success) {
super.onSelectionPerformed(success);
if (success) {
final Activity activity = getActivity();
final Intent intent = activity == null ? null : activity.getIntent();
if (intent != null && ACTION_BUGREPORT_HANDLER_SETTINGS.equals(intent.getAction())) {
// If this was started through ACTION_BUGREPORT_HANDLER_SETTINGS then return once
// we have chosen a new handler.
getActivity().finish();
}
} else {
getBugReportHandlerUtil().showInvalidChoiceToast(getContext());
updateCandidates();
}
}
@Override
public int getMetricsCategory() {
return SettingsEnums.SETTINGS_BUGREPORT_HANDLER;
}
@VisibleForTesting
DefaultAppInfo createDefaultAppInfo(Context context, PackageManager pm, int userId,
PackageItemInfo packageItemInfo) {
return new BugreportHandlerAppInfo(context, pm, userId, packageItemInfo,
getDescription(packageItemInfo.packageName, userId));
}
private String getDescription(String handlerApp, int handlerUser) {
final Context context = getContext();
if (BugReportHandlerUtil.SHELL_APP_PACKAGE.equals(handlerApp)) {
return context.getString(R.string.system_default_app);
}
final UserHandle managedProfile = Utils.getManagedProfile(mUserManager);
if (managedProfile != null && managedProfile.getIdentifier() == handlerUser) {
return context.getString(R.string.work_profile_app);
}
return context.getString(R.string.personal_profile_app);
}
private static class BugreportHandlerAppInfo extends DefaultAppInfo {
private final Context mContext;
BugreportHandlerAppInfo(Context context, PackageManager pm, int userId,
PackageItemInfo packageItemInfo, String summary) {
super(context, pm, userId, packageItemInfo, summary, true /* enabled */);
mContext = context;
}
@Override
public String getKey() {
if (packageItemInfo != null) {
return BugReportHandlerPicker.getKey(packageItemInfo.packageName, userId);
} else {
return null;
}
}
@Override
public CharSequence loadLabel() {
if (mContext == null || packageItemInfo == null) {
return null;
}
if (BugReportHandlerUtil.SHELL_APP_PACKAGE.equals(packageItemInfo.packageName)) {
return mContext.getString(R.string.shell_app);
}
return super.loadLabel();
}
}
}

View File

@@ -0,0 +1,246 @@
/*
* 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 (!isBugreportWhitelistedApp(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-whitelisted 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 (!isBugreportWhitelistedApp(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 (!isBugreportWhitelistedApp(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> bugreportWhitelistedPackages;
try {
bugreportWhitelistedPackages =
ActivityManager.getService().getBugreportWhitelistedPackages();
} catch (RemoteException e) {
Log.e(TAG, "Failed to get bugreportWhitelistedPackages:", e);
return validBugReportHandlerApplicationInfos;
}
// Add "Shell with system user" as System default preference on top of screen
if (bugreportWhitelistedPackages.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 = bugreportWhitelistedPackages.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 isBugreportWhitelistedApp(String app) {
// Verify the app is bugreport-whitelisted
if (TextUtils.isEmpty(app)) {
return false;
}
try {
return ActivityManager.getService().getBugreportWhitelistedPackages().contains(app);
} catch (RemoteException e) {
Log.e(TAG, "Failed to get bugreportWhitelistedPackages:", 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();
}
}

View File

@@ -56,6 +56,7 @@ import com.android.settings.backup.UserBackupSettingsActivity;
import com.android.settings.biometrics.face.FaceSettings;
import com.android.settings.biometrics.fingerprint.FingerprintSettings;
import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
import com.android.settings.bugreporthandler.BugReportHandlerPicker;
import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.PreviouslyConnectedDeviceDashboardFragment;
@@ -106,14 +107,14 @@ import com.android.settings.network.MobileNetworkListFragment;
import com.android.settings.network.NetworkDashboardFragment;
import com.android.settings.nfc.AndroidBeam;
import com.android.settings.nfc.PaymentSettings;
import com.android.settings.notification.app.AppBubbleNotificationSettings;
import com.android.settings.notification.app.AppNotificationSettings;
import com.android.settings.notification.app.ChannelNotificationSettings;
import com.android.settings.notification.ConfigureNotificationSettings;
import com.android.settings.notification.NotificationAccessSettings;
import com.android.settings.notification.NotificationAssistantPicker;
import com.android.settings.notification.history.NotificationStation;
import com.android.settings.notification.SoundSettings;
import com.android.settings.notification.app.AppBubbleNotificationSettings;
import com.android.settings.notification.app.AppNotificationSettings;
import com.android.settings.notification.app.ChannelNotificationSettings;
import com.android.settings.notification.history.NotificationStation;
import com.android.settings.notification.zen.ZenAccessSettings;
import com.android.settings.notification.zen.ZenModeAutomationSettings;
import com.android.settings.notification.zen.ZenModeBlockedEffectsSettings;
@@ -287,7 +288,8 @@ public class SettingsGateway {
BatterySaverScheduleSettings.class.getName(),
MobileNetworkListFragment.class.getName(),
GlobalActionsPanelSettings.class.getName(),
DarkModeSettingsFragment.class.getName()
DarkModeSettingsFragment.class.getName(),
BugReportHandlerPicker.class.getName()
};
public static final String[] SETTINGS_FOR_RESTRICTED = {

View File

@@ -0,0 +1,87 @@
/*
* 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.development;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.UserManager;
import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.bugreporthandler.BugReportHandlerUtil;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
/**
* PreferenceController for BugReportHandler
*/
public class BugReportHandlerPreferenceController extends DeveloperOptionsPreferenceController
implements PreferenceControllerMixin {
private static final String KEY_BUG_REPORT_HANDLER = "bug_report_handler";
private final UserManager mUserManager;
private final BugReportHandlerUtil mBugReportHandlerUtil;
public BugReportHandlerPreferenceController(Context context) {
super(context);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mBugReportHandlerUtil = new BugReportHandlerUtil();
}
@Override
public boolean isAvailable() {
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES)
&& mBugReportHandlerUtil.isBugReportHandlerEnabled(mContext);
}
@Override
public String getPreferenceKey() {
return KEY_BUG_REPORT_HANDLER;
}
@Override
public void updateState(Preference preference) {
final CharSequence currentBugReportHandlerAppLabel = getCurrentBugReportHandlerAppLabel();
if (!TextUtils.isEmpty(currentBugReportHandlerAppLabel)) {
mPreference.setSummary(currentBugReportHandlerAppLabel);
} else {
mPreference.setSummary(R.string.app_list_preference_none);
}
}
@VisibleForTesting
CharSequence getCurrentBugReportHandlerAppLabel() {
final String handlerApp = mBugReportHandlerUtil.getCurrentBugReportHandlerAppAndUser(
mContext).first;
if (BugReportHandlerUtil.SHELL_APP_PACKAGE.equals(handlerApp)) {
return mContext.getString(R.string.shell_app);
}
ApplicationInfo applicationInfo;
try {
applicationInfo = mContext.getPackageManager().getApplicationInfo(handlerApp,
PackageManager.MATCH_ANY_USER);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
return applicationInfo.loadLabel(mContext.getPackageManager());
}
}

View File

@@ -428,6 +428,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new MemoryUsagePreferenceController(context));
controllers.add(new BugReportPreferenceController(context));
controllers.add(new BugReportHandlerPreferenceController(context));
controllers.add(new SystemServerHeapDumpPreferenceController(context));
controllers.add(new LocalBackupPasswordPreferenceController(context));
controllers.add(new StayAwakePreferenceController(context, lifecycle));

View File

@@ -0,0 +1,177 @@
/*
* 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 static android.provider.Settings.ACTION_BUGREPORT_HANDLER_SETTINGS;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.graphics.drawable.ColorDrawable;
import android.util.Pair;
import androidx.fragment.app.FragmentActivity;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.widget.RadioButtonPreference;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowPackageManager;
import org.robolectric.util.ReflectionHelpers;
import java.util.Collections;
@RunWith(RobolectricTestRunner.class)
public class BugReportHandlerPickerTest {
private static final String PACKAGE_NAME = "com.example.test";
private static final int USER_ID = 0;
@Mock
private FragmentActivity mActivity;
private Context mContext;
private ShadowPackageManager mPackageManager;
private BugReportHandlerPicker mPicker;
private BugReportHandlerUtil mBugReportHandlerUtil;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mPackageManager = Shadows.shadowOf(mContext.getPackageManager());
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.name = PACKAGE_NAME;
applicationInfo.uid = 0;
applicationInfo.flags = 0;
applicationInfo.packageName = PACKAGE_NAME;
final PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = PACKAGE_NAME;
packageInfo.applicationInfo = applicationInfo;
mPackageManager.addPackage(packageInfo);
mPackageManager.setUnbadgedApplicationIcon(PACKAGE_NAME, new ColorDrawable());
mPicker = spy(new BugReportHandlerPicker());
doNothing().when(mPicker).updateCandidates();
doNothing().when(mPicker).updateCheckedState(any());
doReturn(mActivity).when(mPicker).getActivity();
ReflectionHelpers.setField(mPicker, "mMetricsFeatureProvider",
mock(MetricsFeatureProvider.class));
mBugReportHandlerUtil = mock(BugReportHandlerUtil.class);
mPicker.setBugReportHandlerUtil(mBugReportHandlerUtil);
}
@After
public void tearDown() {
mPackageManager.removePackage(PACKAGE_NAME);
}
@Test
public void clickItem_success() {
testClickingItemSuccess();
}
@Test
public void clickItem_fail() {
testClickingItemFail();
}
@Test
public void clickItem_usingBugReportHandlerSettingIntent_success() {
useBugReportHandlerSettingIntent();
testClickingItemSuccess();
verify(mActivity, times(1)).finish();
}
@Test
public void clickItem_fromBugReportHandlerSettingIntent_fail() {
useBugReportHandlerSettingIntent();
testClickingItemFail();
}
private static ApplicationInfo createApplicationInfo(String packageName) {
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.packageName = packageName;
return applicationInfo;
}
private void testClickingItemSuccess() {
when(mBugReportHandlerUtil.getValidBugReportHandlerInfos(any()))
.thenReturn(Collections.singletonList(Pair.create(
createApplicationInfo(PACKAGE_NAME), USER_ID)));
when(mBugReportHandlerUtil.setCurrentBugReportHandlerAppAndUser(any(), eq(PACKAGE_NAME),
eq(USER_ID))).thenReturn(true);
RadioButtonPreference defaultPackagePref = mock(RadioButtonPreference.class);
when(defaultPackagePref.getKey()).thenReturn(
BugReportHandlerPicker.getKey(PACKAGE_NAME, USER_ID));
mPicker.onRadioButtonClicked(defaultPackagePref);
verify(mBugReportHandlerUtil, times(1)).setCurrentBugReportHandlerAppAndUser(any(),
eq(PACKAGE_NAME), eq(USER_ID));
verify(mPicker, times(1)).updateCheckedState(
BugReportHandlerPicker.getKey(PACKAGE_NAME, USER_ID));
verify(mBugReportHandlerUtil, never()).showInvalidChoiceToast(any());
}
private void testClickingItemFail() {
when(mBugReportHandlerUtil.getValidBugReportHandlerInfos(any()))
.thenReturn(Collections.singletonList(Pair.create(
createApplicationInfo(PACKAGE_NAME), USER_ID)));
when(mBugReportHandlerUtil.setCurrentBugReportHandlerAppAndUser(any(), eq(PACKAGE_NAME),
eq(USER_ID))).thenReturn(false);
RadioButtonPreference defaultPackagePref = mock(RadioButtonPreference.class);
when(defaultPackagePref.getKey()).thenReturn(
BugReportHandlerPicker.getKey(PACKAGE_NAME, USER_ID));
mPicker.onRadioButtonClicked(defaultPackagePref);
verify(mBugReportHandlerUtil, times(1)).setCurrentBugReportHandlerAppAndUser(any(),
eq(PACKAGE_NAME), eq(USER_ID));
// Ensure we update the list of packages when we click a non-valid package - the list must
// have changed, otherwise this click wouldn't fail.
verify(mPicker, times(1)).updateCandidates();
verify(mBugReportHandlerUtil, times(1)).showInvalidChoiceToast(any());
}
private void useBugReportHandlerSettingIntent() {
Intent intent = new Intent(ACTION_BUGREPORT_HANDLER_SETTINGS);
when(mActivity.getIntent()).thenReturn(intent);
}
}

View File

@@ -0,0 +1,115 @@
/*
* 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.development;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.UserManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.bugreporthandler.BugReportHandlerUtil;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
public class BugReportHandlerPreferenceControllerTest {
@Mock
private PreferenceScreen mPreferenceScreen;
@Mock
private UserManager mUserManager;
@Mock
private BugReportHandlerUtil mBugReportHandlerUtil;
@Mock
private Preference mPreference;
private BugReportHandlerPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mController = spy(new BugReportHandlerPreferenceController(RuntimeEnvironment.application));
ReflectionHelpers.setField(mController, "mUserManager", mUserManager);
ReflectionHelpers.setField(mController, "mBugReportHandlerUtil", mBugReportHandlerUtil);
}
@Test
public void isAvailable_hasDebugRestriction_notAvailable() {
doReturn(true).when(mUserManager).hasUserRestriction(anyString());
doReturn(true).when(mBugReportHandlerUtil).isBugReportHandlerEnabled(any(Context.class));
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_bugReportHandlerDisabled_notAvailable() {
doReturn(false).when(mBugReportHandlerUtil).isBugReportHandlerEnabled(any(Context.class));
doReturn(false).when(mUserManager).hasUserRestriction(anyString());
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_noDebugRestrictionAndBugReportHandlerEnabled_available() {
doReturn(false).when(mUserManager).hasUserRestriction(anyString());
doReturn(true).when(mBugReportHandlerUtil).isBugReportHandlerEnabled(any(Context.class));
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void updateState_hasCurrentBugReportHandlerAppLabel_setAppLabel() {
when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
.thenReturn(mPreference);
mController.displayPreference(mPreferenceScreen);
doReturn("SomeRandomAppLabel!!!").when(mController).getCurrentBugReportHandlerAppLabel();
mController.updateState(mPreference);
verify(mPreference).setSummary("SomeRandomAppLabel!!!");
}
@Test
public void updateState_noCurrentBugReportHandlerAppLabel_setAppDefaultLabel() {
when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
.thenReturn(mPreference);
mController.displayPreference(mPreferenceScreen);
doReturn(null).when(mController).getCurrentBugReportHandlerAppLabel();
mController.updateState(mPreference);
verify(mPreference).setSummary(R.string.app_list_preference_none);
}
}