Snap for 4560043 from 442a53c5fb to pi-release

Change-Id: I73749a08a6fbd6d4674716abefa4b045905c01d6
This commit is contained in:
android-build-team Robot
2018-01-23 08:23:42 +00:00
37 changed files with 944 additions and 208 deletions

View File

@@ -3311,6 +3311,25 @@
</intent-filter>
</activity>
<activity android:name="Settings$AdvancedConnectedDeviceActivity"
android:label="@string/connected_device_connections_title"
android:taskAffinity="com.android.settings"
android:parentActivityName="Settings$ConnectedDeviceDashboardActivity"
android:enabled="false">
<intent-filter android:priority="1">
<action android:name="com.android.settings.ADVANCED_CONNECTED_DEVICE_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment" />
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true" />
</activity>
<provider android:name=".slices.SettingsSliceProvider"
android:authorities="com.android.settings.slices"
android:exported="true">

View File

@@ -115,6 +115,10 @@
android:text="@string/erase_external_storage_description" />
</LinearLayout>
</LinearLayout>
<include layout="@layout/reset_esim_checkbox"
android:id="@+id/erase_esim_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
<Button

View File

@@ -13,7 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@@ -21,7 +22,8 @@
android:clickable="true"
android:visibility="gone">
<CheckBox android:id="@+id/erase_esim"
<CheckBox
android:id="@+id/erase_esim"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
@@ -30,12 +32,14 @@
android:clickable="false"
android:duplicateParentState="true" />
<LinearLayout android:layout_width="match_parent"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="vertical">
<TextView android:id="@+id/erase_esim_title"
<TextView
android:id="@+id/erase_esim_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/reset_checkbox_title_padding_top"

View File

@@ -3248,7 +3248,7 @@
<!-- SD card & phone storage settings screen, list of items in user data storage (USB storage or SD card) that will be erased during this operation [CHAR LIMIT=NONE] -->
<string name="master_clear_desc_also_erases_external">"<li>Music</li>\n<li>Photos</li>\n<li>Other user data</li>"</string>
<!-- SD card & phone storage settings screen, list of items on an eSIM (embedded SIM) that will be erased during this operation [CHAR LIMIT=NONE] -->
<string name="master_clear_desc_also_erases_esim">"<li>Carriers on eSIM</li>"</string>
<string name="master_clear_desc_also_erases_esim">"<li>eSIMs</li>"</string>
<!-- SD card & phone storage settings screen, notification if there are eSIM (embedded SIM) profiles present that the user's mobile service plan will not be canceled [CHAR LIMIT=NONE] -->
<string name="master_clear_desc_no_cancel_mobile_plan">"\n\nThis will not cancel your mobile service plan.</string>
<!-- SD card & phone storage settings screen, instructions about whether to also erase the external storage (SD card) when erasing the internal storage [CHAR LIMIT=NONE] -->
@@ -4719,7 +4719,7 @@
<string name="power_charge_remaining"><xliff:g id="until_charged">%1$s</xliff:g> to charge</string>
<!-- Title for the background activity setting, which allows a user to control whether an app can run in the background [CHAR_LIMIT=40] -->
<string name="background_activity_title">Background activity</string>
<string name="background_activity_title">Restricted</string>
<!-- Summary for the background activity [CHAR_LIMIT=120] -->
<string name="background_activity_summary">Allow the app to run in the background</string>
<!-- Summary for the background activity when it is on [CHAR_LIMIT=120] -->
@@ -7602,6 +7602,11 @@
<!-- Button label to say no to the question of whether to require PIN/password/pattern to start your device. [CHAR LIMIT=20] -->
<string name="encryption_interstitial_no">No</string>
<!-- Label to say yes to the question of whether app is restricted. [CHAR LIMIT=20] -->
<string name="restricted_true_label">Yes</string>
<!-- Label to say no to the question of whether app is restricted. [CHAR LIMIT=20] -->
<string name="restricted_false_label">No</string>
<!-- Title for encryption dialog that disables TalkBack. [CHAR_LIMIT=25] -->
<string name="encrypt_talkback_dialog_require_pin">Require PIN?</string>
@@ -8335,6 +8340,8 @@
<string name="disabled_by_policy_title_camera">Camera not allowed</string>
<!-- Title for dialog displayed to tell user that screenshots are disabled by an admin [CHAR LIMIT=50] -->
<string name="disabled_by_policy_title_screen_capture">Screenshot not allowed</string>
<!-- Title for dialog displayed to tell user that turning off backups is disallowed by an admin [CHAR LIMIT=50] -->
<string name="disabled_by_policy_title_turn_off_backups">Can\'t turn off backups</string>
<!-- Shown when the user tries to change a settings locked by an admin [CHAR LIMIT=200] -->
<string name="default_admin_support_msg">This action is disabled. To learn more, contact your
organization\'s admin.</string>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 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:key="directory_access_details"
android:title="@string/directory_access"/>

View File

@@ -27,6 +27,22 @@
android:key="action_buttons"
android:order="-9999"/>
<PreferenceCategory
android:title="@string/battery_detail_manage_title">
<Preference
android:key="background_activity"
android:title="@string/background_activity_title"
android:selectable="true"/>
<Preference
android:key="battery_optimization"
android:title="@string/high_power_apps"
android:summary="@string/high_power_off"
android:selectable="true"/>
</PreferenceCategory>
<Preference
android:key="high_usage"
android:icon="@drawable/ic_battery_alert_24dp"
@@ -52,20 +68,4 @@
</PreferenceCategory>
<PreferenceCategory
android:title="@string/battery_detail_manage_title">
<SwitchPreference
android:key="background_activity"
android:title="@string/background_activity_title"
android:selectable="true"/>
<Preference
android:key="battery_optimization"
android:title="@string/high_power_apps"
android:summary="@string/high_power_off"
android:selectable="true"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -111,7 +111,6 @@
android:value="com.android.settings.Settings$VrListenersSettingsActivity" />
</Preference>
<!-- TODO(b/63720392): add when ready
<Preference
android:key="special_app_directory_access"
android:title="@string/directory_access"
@@ -119,8 +118,7 @@
settings:keywords="@string/keywords_directory_access">
<extra
android:name="classname"
android:value="com.android.settings.Settings$DirectoryeAccessSettingsActivity" />
android:value="com.android.settings.Settings$DirectoryAccessSettingsActivity" />
</Preference>
-->
</PreferenceScreen>

View File

@@ -76,7 +76,10 @@ public class MasterClear extends InstrumentedPreferenceFragment {
private static final String TAG = "MasterClear";
private static final int KEYGUARD_REQUEST = 55;
private static final int CREDENTIAL_CONFIRM_REQUEST = 56;
@VisibleForTesting static final int CREDENTIAL_CONFIRM_REQUEST = 56;
private static final String KEY_SHOW_ESIM_RESET_CHECKBOX
= "masterclear.allow_retain_esim_profiles_after_fdr";
static final String ERASE_EXTERNAL_EXTRA = "erase_sd";
static final String ERASE_ESIMS_EXTRA = "erase_esim";
@@ -85,6 +88,8 @@ public class MasterClear extends InstrumentedPreferenceFragment {
private Button mInitiateButton;
private View mExternalStorageContainer;
@VisibleForTesting CheckBox mExternalStorage;
private View mEsimStorageContainer;
@VisibleForTesting CheckBox mEsimStorage;
private ScrollView mScrollView;
private final OnGlobalLayoutListener mOnGlobalLayoutListener = new OnGlobalLayoutListener() {
@@ -134,8 +139,7 @@ public class MasterClear extends InstrumentedPreferenceFragment {
void showFinalConfirmation() {
Bundle args = new Bundle();
args.putBoolean(ERASE_EXTERNAL_EXTRA, mExternalStorage.isChecked());
// TODO: Offer the user a choice to wipe eSIMs when it is technically feasible to do so.
args.putBoolean(ERASE_ESIMS_EXTRA, true);
args.putBoolean(ERASE_ESIMS_EXTRA, mEsimStorage.isChecked());
((SettingsActivity) getActivity()).startPreferencePanel(
this, MasterClearConfirm.class.getName(),
args, R.string.master_clear_confirm_title, null, null, 0);
@@ -157,9 +161,12 @@ public class MasterClear extends InstrumentedPreferenceFragment {
.setAction("android.accounts.action.PRE_FACTORY_RESET");
// Check to make sure that the intent is supported.
final PackageManager pm = context.getPackageManager();
final List<ResolveInfo> resolutions =
pm.queryIntentActivities(requestAccountConfirmation, 0);
if (resolutions != null && resolutions.size() > 0) {
final ResolveInfo resolution = pm.resolveActivity(requestAccountConfirmation, 0);
if (resolution != null
&& resolution.activityInfo != null
&& packageName.equals(resolution.activityInfo.packageName)) {
// Note that we need to check the packagename to make sure that an Activity resolver
// wasn't returned.
getActivity().startActivityForResult(
requestAccountConfirmation, CREDENTIAL_CONFIRM_REQUEST);
return true;
@@ -214,6 +221,8 @@ public class MasterClear extends InstrumentedPreferenceFragment {
mInitiateButton.setOnClickListener(mInitiateListener);
mExternalStorageContainer = mContentView.findViewById(R.id.erase_external_container);
mExternalStorage = (CheckBox) mContentView.findViewById(R.id.erase_external);
mEsimStorageContainer = mContentView.findViewById(R.id.erase_esim_container);
mEsimStorage = (CheckBox) mContentView.findViewById(R.id.erase_esim);
mScrollView = (ScrollView) mContentView.findViewById(R.id.master_clear_scrollview);
/*
@@ -248,11 +257,25 @@ public class MasterClear extends InstrumentedPreferenceFragment {
}
if (showWipeEuicc()) {
final View esimAlsoErased = mContentView.findViewById(R.id.also_erases_esim);
esimAlsoErased.setVisibility(View.VISIBLE);
if (showWipeEuiccCheckbox()) {
TextView title = mContentView.findViewById(R.id.erase_esim_title);
title.setText(R.string.erase_esim_storage);
mEsimStorageContainer.setVisibility(View.VISIBLE);
mEsimStorageContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mEsimStorage.toggle();
}
});
} else {
final View esimAlsoErased = mContentView.findViewById(R.id.also_erases_esim);
esimAlsoErased.setVisibility(View.VISIBLE);
final View noCancelMobilePlan = mContentView.findViewById(R.id.no_cancel_mobile_plan);
noCancelMobilePlan.setVisibility(View.VISIBLE);
final View noCancelMobilePlan = mContentView.findViewById(
R.id.no_cancel_mobile_plan);
noCancelMobilePlan.setVisibility(View.VISIBLE);
mEsimStorage.setChecked(true /* checked */);
}
}
final UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
@@ -293,6 +316,12 @@ public class MasterClear extends InstrumentedPreferenceFragment {
return Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) != 0;
}
@VisibleForTesting
boolean showWipeEuiccCheckbox() {
return SystemProperties
.getBoolean(KEY_SHOW_ESIM_RESET_CHECKBOX, false /* def */);
}
@VisibleForTesting
protected boolean isEuiccEnabled(Context context) {
EuiccManager euiccManager = (EuiccManager) context.getSystemService(Context.EUICC_SERVICE);

View File

@@ -81,8 +81,7 @@ public class ResetNetworkConfirm extends InstrumentedPreferenceFragment {
@Override
protected Boolean doInBackground(Void... params) {
return mRecoverySystem.wipeEuiccData(
mContext, true /* isWipeEuicc */, mPackageName);
return mRecoverySystem.wipeEuiccData(mContext, mPackageName);
}
@Override

View File

@@ -178,5 +178,10 @@ public class Settings extends SettingsActivity {
public static class StorageDashboardActivity extends SettingsActivity {}
public static class UserAndAccountDashboardActivity extends SettingsActivity {}
public static class SystemDashboardActivity extends SettingsActivity {}
public static class AdvancedConnectedDeviceActivity extends SettingsActivity {
public static final boolean isEnabled() {
return FeatureFlagUtils.isEnabled(null /* context */, CONNECTED_DEVICE_V2);
}
}
}

View File

@@ -150,6 +150,9 @@ public class ShowAdminSupportDetailsDialog extends Activity
case DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE:
titleView.setText(R.string.disabled_by_policy_title_screen_capture);
break;
case DevicePolicyManager.POLICY_MANDATORY_BACKUPS:
titleView.setText(R.string.disabled_by_policy_title_turn_off_backups);
break;
default:
// Use general text if no specialized title applies
titleView.setText(R.string.disabled_by_policy_title);

View File

@@ -514,7 +514,8 @@ public final class Utils extends com.android.settingslib.Utils {
Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
CharSequence title, boolean isShortcut, int metricsCategory) {
startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
titleResPackageName, titleResId, title, isShortcut, metricsCategory, 0);
titleResPackageName, titleResId, title, isShortcut, metricsCategory,
Intent.FLAG_ACTIVITY_NEW_TASK);
}

View File

@@ -33,11 +33,15 @@ import com.android.settingslib.applications.ApplicationsState.AppFilter;
import java.util.Set;
// TODO(b/63720392): add unit tests
// TODO(b/72055774): add unit tests
public class AppStateDirectoryAccessBridge extends AppStateBaseBridge {
private static final String TAG = "DirectoryAccessBridge";
// TODO(b/72055774): set to false once feature is ready (or use Log.isLoggable)
static final boolean DEBUG = true;
static final boolean VERBOSE = true;
public AppStateDirectoryAccessBridge(ApplicationsState appState, Callback callback) {
super(appState, callback);
}
@@ -59,27 +63,34 @@ public class AppStateDirectoryAccessBridge extends AppStateBaseBridge {
@Override
public void init(Context context) {
try (Cursor cursor = context.getContentResolver().query(
new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(AUTHORITY).appendPath(TABLE_PACKAGES).appendPath("*")
.build(), TABLE_PACKAGES_COLUMNS, null, null)) {
mPackages = null;
final Uri providerUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(AUTHORITY).appendPath(TABLE_PACKAGES).appendPath("*")
.build();
try (Cursor cursor = context.getContentResolver().query(providerUri,
TABLE_PACKAGES_COLUMNS, null, null)) {
if (cursor == null) {
Log.w(TAG, "didn't get cursor");
Log.w(TAG, "Didn't get cursor for " + providerUri);
return;
}
final int count = cursor.getCount();
if (count == 0) {
Log.d(TAG, "no packages");
if (DEBUG) {
Log.d(TAG, "No packages anymore (was " + mPackages + ")");
}
return;
}
mPackages = new ArraySet<>(count);
while (cursor.moveToNext()) {
mPackages.add(cursor.getString(TABLE_PACKAGES_COL_PACKAGE));
}
Log.v(TAG, "init(): " + mPackages);
if (DEBUG) {
Log.d(TAG, "init(): " + mPackages);
}
}
}
@Override
public boolean filterApp(AppEntry info) {
return mPackages != null && mPackages.contains(info.info.packageName);

View File

@@ -17,84 +17,236 @@
package com.android.settings.applications;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.AUTHORITY;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_DIRECTORY;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_GRANTED;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_PACKAGE;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_VOLUME_UUID;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COLUMNS;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_DIRECTORY;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_GRANTED;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_PACKAGE;
import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS_COL_VOLUME_UUID;
import static com.android.settings.applications.AppStateDirectoryAccessBridge.DEBUG;
import static com.android.settings.applications.AppStateDirectoryAccessBridge.VERBOSE;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
import android.support.v7.preference.Preference.OnPreferenceClickListener;
import android.text.TextUtils;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceScreen;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.Pair;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.widget.EntityHeaderController;
import com.android.settings.widget.EntityHeaderController.ActionType;
import com.android.settingslib.applications.AppUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Detailed settings for an app's directory access permissions (A.K.A Scoped Directory Access).
*
* <p>Currently, it shows the entry for which the user denied access with the "Do not ask again"
* flag checked on: the user than can use the settings toggle to reset that deniel.
*
* <p>This fragments dynamically lists all such permissions, starting with one preference per
* directory in the primary storage, then adding additional entries for the external volumes (one
* entry for the whole volume).
*/
// TODO(b/63720392): explain its layout
// TODO(b/63720392): add unit tests
public class DirectoryAccessDetails extends AppInfoWithHeader implements OnPreferenceChangeListener,
OnPreferenceClickListener {
private static final String MY_TAG = "DirectoryAccessDetails";
// TODO(b/72055774): add unit tests
public class DirectoryAccessDetails extends AppInfoBase {
@SuppressWarnings("hiding")
private static final String TAG = "DirectoryAccessDetails";
private boolean mCreated;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (mCreated) {
Log.w(TAG, "onActivityCreated(): ignoring duplicate call");
return;
}
mCreated = true;
if (mPackageInfo == null) {
Log.w(TAG, "onActivityCreated(): no package info");
return;
}
final Activity activity = getActivity();
final Preference pref = EntityHeaderController
.newInstance(activity, this, /* header= */ null )
.setRecyclerView(getListView(), getLifecycle())
.setIcon(IconDrawableFactory.newInstance(getPrefContext())
.getBadgedIcon(mPackageInfo.applicationInfo))
.setLabel(mPackageInfo.applicationInfo.loadLabel(mPm))
.setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
.setPackageName(mPackageName)
.setUid(mPackageInfo.applicationInfo.uid)
.setHasAppInfoLink(false)
.setButtonActions(ActionType.ACTION_NONE, ActionType.ACTION_NONE)
.done(activity, getPrefContext());
getPreferenceScreen().addPreference(pref);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (true) {
// TODO(b/63720392): temporarily hack so the screen doesn't crash..
addPreferencesFromResource(R.xml.app_ops_permissions_details);
// ... we need to dynamically create the preferences by calling the provider instead:
try (Cursor cursor = getContentResolver().query(
new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(AUTHORITY).appendPath(TABLE_PERMISSIONS).appendPath("*")
.build(),
TABLE_PERMISSIONS_COLUMNS, null, null)) {
if (cursor == null) {
Log.w(TAG, "didn't get cursor");
return;
final Context context = getPrefContext();
addPreferencesFromResource(R.xml.directory_access_details);
final PreferenceScreen prefsGroup = getPreferenceScreen();
// Set external directory UUIDs.
ArraySet<String> externalDirectoryUuids = null;
final Uri providerUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(AUTHORITY).appendPath(TABLE_PERMISSIONS).appendPath("*")
.build();
// Query provider for entries.
try (Cursor cursor = context.getContentResolver().query(providerUri,
TABLE_PERMISSIONS_COLUMNS, null, new String[] { mPackageName }, null)) {
if (cursor == null) {
Log.w(TAG, "Didn't get cursor for " + mPackageName);
return;
}
final int count = cursor.getCount();
if (count == 0) {
if (DEBUG) {
Log.d(TAG, "No permissions for " + mPackageName);
}
final int count = cursor.getCount();
if (count == 0) {
Log.d(TAG, "no permissions");
return;
// TODO(b/72055774): display empty message
return;
}
while (cursor.moveToNext()) {
final String pkg = cursor.getString(TABLE_PERMISSIONS_COL_PACKAGE);
final String uuid = cursor.getString(TABLE_PERMISSIONS_COL_VOLUME_UUID);
final String dir = cursor.getString(TABLE_PERMISSIONS_COL_DIRECTORY);
if (VERBOSE) {
Log.v(TAG, "Pkg:" + pkg + " uuid: " + uuid + " dir: " + dir);
}
while (cursor.moveToNext()) {
final String pkg = cursor.getString(TABLE_PERMISSIONS_COL_PACKAGE);
final String uuid = cursor.getString(TABLE_PERMISSIONS_COL_VOLUME_UUID);
final String dir = cursor.getString(TABLE_PERMISSIONS_COL_DIRECTORY);
final boolean granted = cursor.getInt(TABLE_PERMISSIONS_COL_GRANTED) == 1;
Log.v(MY_TAG, "pkg:" + pkg + " uuid: " + uuid + " dir: " + dir + "> "
+ granted);
if (!mPackageName.equals(pkg)) {
// Sanity check, shouldn't happen
Log.w(TAG, "Ignoring " + uuid + "/" + dir + " due to package mismatch: "
+ "expected " + mPackageName + ", got " + pkg);
continue;
}
if (uuid == null) {
// Primary storage entry: add right away
prefsGroup.addPreference(
newPreference(context, dir, providerUri, /* uuid= */ null, dir));
} else {
// External volume entry: save it for later.
if (externalDirectoryUuids == null) {
externalDirectoryUuids = new ArraySet<>(1);
}
externalDirectoryUuids.add(uuid);
}
}
}
// Add entries from external volumes
if (externalDirectoryUuids != null) {
if (VERBOSE) {
Log.v(TAG, "adding external directories: " + externalDirectoryUuids);
}
// Query StorageManager to get the user-friendly volume names.
final StorageManager sm = context.getSystemService(StorageManager.class);
final List<VolumeInfo> volumes = sm.getVolumes();
if (volumes.isEmpty()) {
Log.w(TAG, "StorageManager returned no secondary volumes");
return;
}
final Map<String, String> volumeNames = new HashMap<>(volumes.size());
for (VolumeInfo volume : volumes) {
final String uuid = volume.getFsUuid();
if (uuid == null) continue; // Primary storage; not used.
String name = sm.getBestVolumeDescription(volume);
if (name == null) {
Log.w(TAG, "No description for " + volume + "; using uuid instead: " + uuid);
name = uuid;
}
volumeNames.put(uuid, name);
}
if (VERBOSE) {
Log.v(TAG, "UUID -> name mapping: " + volumeNames);
}
externalDirectoryUuids.forEach((uuid) ->{
final String name = volumeNames.get(uuid);
// TODO(b/72055774): add separator
prefsGroup.addPreference(
newPreference(context, name, providerUri, uuid, /* dir= */ null));
});
}
return;
}
private SwitchPreference newPreference(Context context, String title, Uri providerUri,
String uuid, String dir) {
final SwitchPreference pref = new SwitchPreference(context);
pref.setKey(String.format("%s:%s", uuid, dir));
pref.setTitle(title);
pref.setChecked(false);
pref.setOnPreferenceChangeListener((unused, value) -> {
resetDoNotAskAgain(context, value, providerUri, uuid, dir);
return true;
});
return pref;
}
private void resetDoNotAskAgain(Context context, Object value, Uri providerUri,
@Nullable String uuid, @Nullable String directory) {
if (!Boolean.class.isInstance(value)) {
// Sanity check
Log.wtf(TAG, "Invalid value from switch: " + value);
return;
}
final boolean newValue = ((Boolean) value).booleanValue();
if (DEBUG) {
Log.d(TAG, "Asking " + providerUri + " to update " + uuid + "/" + directory + " to "
+ newValue);
}
final ContentValues values = new ContentValues(1);
values.put(COL_GRANTED, newValue);
final int updated = context.getContentResolver().update(providerUri, values,
null, new String[] { mPackageName, uuid, directory });
if (DEBUG) {
Log.d(TAG, "Updated " + updated + " entries for " + uuid + "/" + directory);
}
}
@Override
public boolean onPreferenceClick(Preference preference) {
// TODO(b/63720392): implement or remove listener
return false;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
// TODO(b/63720392): implement or remove listener
return false;
}
@Override
protected boolean refreshUi() {
// TODO(b/63720392): implement
return true;
}

View File

@@ -27,14 +27,19 @@ import com.android.settings.SettingsActivity;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.widget.GearPreference;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HeadsetProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import android.util.Log;
/**
* Update the bluetooth devices. It gets bluetooth event from {@link LocalBluetoothManager} using
@@ -45,6 +50,7 @@ import java.util.Map;
* whether the {@link CachedBluetoothDevice} is relevant.
*/
public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
private static final String TAG = "BluetoothDeviceUpdater";
private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
"persist.bluetooth.showdeviceswithoutnames";
@@ -55,6 +61,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
private final boolean mShowDeviceWithoutNames;
private DashboardFragment mFragment;
private Preference.OnPreferenceClickListener mDevicePreferenceClickListener = null;
@VisibleForTesting
final GearPreference.OnGearClickListener mDeviceProfilesListener = pref -> {
@@ -73,6 +80,38 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
};
private class PreferenceClickListener implements
Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
final CachedBluetoothDevice device =
((BluetoothDevicePreference) preference).getBluetoothDevice();
if (device == null) {
return false;
}
// Set the device as active per profile only if the device supports that profile
// TODO: The active device selector location might change in the future
Log.i(TAG, "OnPreferenceClickListener: device=" + device);
boolean result = false;
A2dpProfile a2dpProfile = mLocalManager.getProfileManager().getA2dpProfile();
if ((a2dpProfile != null) && device.isConnectedProfile(a2dpProfile)) {
if (a2dpProfile.setActiveDevice(device.getDevice())) {
Log.i(TAG, "OnPreferenceClickListener: A2DP active device=" + device);
result = true;
}
}
HeadsetProfile headsetProfile = mLocalManager.getProfileManager().getHeadsetProfile();
if ((headsetProfile != null) && device.isConnectedProfile(headsetProfile)) {
if (headsetProfile.setActiveDevice(device.getDevice())) {
Log.i(TAG, "OnPreferenceClickListener: Headset active device=" + device);
result = true;
}
}
return result;
}
}
public BluetoothDeviceUpdater(DashboardFragment fragment,
DevicePreferenceCallback devicePreferenceCallback) {
this(fragment, devicePreferenceCallback, Utils.getLocalBtManager(fragment.getContext()));
@@ -87,6 +126,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
mPreferenceMap = new HashMap<>();
mLocalManager = localManager;
mDevicePreferenceClickListener = new PreferenceClickListener();
}
/**
@@ -141,6 +181,18 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
@Override
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {}
@Override
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
Collection<CachedBluetoothDevice> cachedDevices =
mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
// TODO: The state update of the Cached Bluetooth Devices should be
// moved to the device manager: b/72316092
for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) {
boolean isActive = Objects.equals(cachedBluetoothDevice, activeDevice);
cachedBluetoothDevice.setActiveDevice(isActive, bluetoothProfile);
}
}
/**
* Set the context to generate the {@link Preference}, so it could get the correct theme.
*/
@@ -176,6 +228,7 @@ public abstract class BluetoothDeviceUpdater implements BluetoothCallback {
new BluetoothDevicePreference(mPrefContext, cachedDevice,
mShowDeviceWithoutNames);
btPreference.setOnGearClickListener(mDeviceProfilesListener);
btPreference.setOnPreferenceClickListener(mDevicePreferenceClickListener);
mPreferenceMap.put(device, btPreference);
mDevicePreferenceCallback.onDeviceAdded(btPreference);
}

View File

@@ -76,6 +76,10 @@ public final class BluetoothSummaryUpdater extends SummaryUpdater implements Blu
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
}
@Override
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
}
@Override
public void register(boolean listening) {
if (mBluetoothAdapter == null) {

View File

@@ -277,6 +277,9 @@ public abstract class DeviceListPreferenceFragment extends
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
@Override
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { }
/**
* Return the key of the {@link PreferenceGroup} that contains the bluetooth devices
*/

View File

@@ -57,6 +57,7 @@ import com.android.settings.applications.assist.ManageAssist;
import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragmentOld;
import com.android.settings.datausage.DataPlanUsageSummary;
@@ -254,7 +255,8 @@ public class SettingsGateway {
LockscreenDashboardFragment.class.getName(),
BluetoothDeviceDetailsFragment.class.getName(),
DataUsageList.class.getName(),
DirectoryAccessDetails.class.getName()
DirectoryAccessDetails.class.getName(),
AdvancedConnectedDeviceDashboardFragment.class.getName()
};
public static final String[] SETTINGS_FOR_RESTRICTED = {

View File

@@ -31,6 +31,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
@@ -56,11 +57,14 @@ public class ZonePicker extends InstrumentedFragment
private List<RegionInfo> mRegions;
private Map<String, List<TimeZoneInfo>> mZoneInfos;
private List<TimeZoneInfo> mFixedOffsetTimeZones;
private TimeZoneAdapter mTimeZoneAdapter;
private String mSelectedTimeZone;
private boolean mSelectByRegion;
private DataLoader mDataLoader;
private TimeZoneAdapter mTimeZoneAdapter;
private RecyclerView mRecyclerView;
private LinearLayout mRegionSpinnerLayout;
private Spinner mRegionSpinner;
@Override
public int getMetricsCategory() {
@@ -88,15 +92,17 @@ public class ZonePicker extends InstrumentedFragment
final ArrayAdapter<RegionInfo> regionAdapter = new ArrayAdapter<>(getContext(),
R.layout.filter_spinner_item, mRegions);
regionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
final Spinner spinner = view.findViewById(R.id.tz_region_spinner);
spinner.setAdapter(regionAdapter);
spinner.setOnItemSelectedListener(this);
setupForCurrentTimeZone(spinner);
mRegionSpinnerLayout = view.findViewById(R.id.tz_region_spinner_layout);
mRegionSpinner = view.findViewById(R.id.tz_region_spinner);
mRegionSpinner.setAdapter(regionAdapter);
mRegionSpinner.setOnItemSelectedListener(this);
setupForCurrentTimeZone();
setHasOptionsMenu(true);
return view;
}
private void setupForCurrentTimeZone(Spinner spinner) {
private void setupForCurrentTimeZone() {
final String localeRegionId = mLocale.getCountry().toUpperCase(Locale.ROOT);
final String currentTimeZone = TimeZone.getDefault().getID();
boolean fixedOffset = currentTimeZone.startsWith("Etc/GMT") ||
@@ -105,12 +111,12 @@ public class ZonePicker extends InstrumentedFragment
for (int regionIndex = 0; regionIndex < mRegions.size(); regionIndex++) {
final RegionInfo region = mRegions.get(regionIndex);
if (localeRegionId.equals(region.getId())) {
spinner.setSelection(regionIndex);
mRegionSpinner.setSelection(regionIndex);
}
if (!fixedOffset) {
for (String timeZoneId: region.getTimeZoneIds()) {
if (TextUtils.equals(timeZoneId, mSelectedTimeZone)) {
spinner.setSelection(regionIndex);
mRegionSpinner.setSelection(regionIndex);
return;
}
}
@@ -179,16 +185,15 @@ public class ZonePicker extends InstrumentedFragment
private void setSelectByRegion(boolean selectByRegion) {
mSelectByRegion = selectByRegion;
getView().findViewById(R.id.tz_region_spinner_layout).setVisibility(
mRegionSpinnerLayout.setVisibility(
mSelectByRegion ? View.VISIBLE : View.GONE);
List<TimeZoneInfo> tzInfos;
if (selectByRegion) {
Spinner regionSpinner = getView().findViewById(R.id.tz_region_spinner);
int selectedRegion = regionSpinner.getSelectedItemPosition();
int selectedRegion = mRegionSpinner.getSelectedItemPosition();
if (selectedRegion == -1) {
// Arbitrarily pick the first item if no region was selected above.
selectedRegion = 0;
regionSpinner.setSelection(selectedRegion);
mRegionSpinner.setSelection(selectedRegion);
}
tzInfos = getTimeZoneInfos(mRegions.get(selectedRegion));
} else {

View File

@@ -374,7 +374,7 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
@Override
public void onLimitBackgroundActivity() {
mBackgroundActivityPreferenceController.setUnchecked(
mBackgroundActivityPreferenceController.setRestricted(
findPreference(mBackgroundActivityPreferenceController.getPreferenceKey()));
}
}

View File

@@ -43,12 +43,12 @@ import com.android.settingslib.fuelgauge.PowerWhitelistBackend;
* Controller to control whether an app can run in the background
*/
public class BackgroundActivityPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
implements PreferenceControllerMixin {
private static final String TAG = "BgActivityPrefContr";
private static final String KEY_BACKGROUND_ACTIVITY = "background_activity";
@VisibleForTesting
static final String KEY_BACKGROUND_ACTIVITY = "background_activity";
private final PackageManager mPackageManager;
private final AppOpsManager mAppOpsManager;
private final UserManager mUserManager;
private final int mUid;
@@ -70,7 +70,6 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
int uid, String packageName, PowerWhitelistBackend backend) {
super(context);
mPowerWhitelistBackend = backend;
mPackageManager = context.getPackageManager();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mDpm = new DevicePolicyManagerWrapper(
(DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE));
@@ -86,11 +85,6 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
final int mode = mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage);
final boolean whitelisted = mPowerWhitelistBackend.isWhitelisted(mTargetPackage);
// Set checked or not before we may set it disabled
if (mode != AppOpsManager.MODE_ERRORED) {
final boolean checked = whitelisted || mode != AppOpsManager.MODE_IGNORED;
((SwitchPreference) preference).setChecked(checked);
}
if (whitelisted || mode == AppOpsManager.MODE_ERRORED
|| Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mTargetPackage)) {
preference.setEnabled(false);
@@ -109,9 +103,8 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
* Called from the warning dialog, if the user decides to go ahead and disable background
* activity for this package
*/
public void setUnchecked(Preference preference) {
public void setRestricted(Preference preference) {
mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_IGNORED);
((SwitchPreference) preference).setChecked(false);
updateSummary(preference);
}
@@ -121,17 +114,21 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean switchOn = (Boolean) newValue;
if (!switchOn) {
final WarningDialogFragment dialogFragment = new WarningDialogFragment();
dialogFragment.setTargetFragment(mFragment, 0);
dialogFragment.show(mFragment.getFragmentManager(), TAG);
return false;
public boolean handlePreferenceTreeClick(Preference preference) {
if (KEY_BACKGROUND_ACTIVITY.equals(preference.getKey())) {
final int mode = mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mUid, mTargetPackage);
final boolean restricted = mode == AppOpsManager.MODE_IGNORED;
if (!restricted) {
showDialog();
return false;
}
mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_ALLOWED);
updateSummary(preference);
return true;
}
mBatteryUtils.setForceAppStandby(mUid, mTargetPackage, AppOpsManager.MODE_ALLOWED);
updateSummary(preference);
return true;
return false;
}
@VisibleForTesting
@@ -146,12 +143,19 @@ public class BackgroundActivityPreferenceController extends AbstractPreferenceCo
if (mode == AppOpsManager.MODE_ERRORED) {
preference.setSummary(R.string.background_activity_summary_disabled);
} else {
final boolean checked = mode != AppOpsManager.MODE_IGNORED;
preference.setSummary(checked ? R.string.background_activity_summary_on
: R.string.background_activity_summary_off);
final boolean restricted = mode == AppOpsManager.MODE_IGNORED;
preference.setSummary(restricted ? R.string.restricted_true_label
: R.string.restricted_false_label);
}
}
@VisibleForTesting
void showDialog() {
final WarningDialogFragment dialogFragment = new WarningDialogFragment();
dialogFragment.setTargetFragment(mFragment, 0 /* requestCode */);
dialogFragment.show(mFragment.getFragmentManager(), TAG);
}
interface WarningConfirmationListener {
void onLimitBackgroundActivity();
}

View File

@@ -272,7 +272,7 @@ public class BatteryInfo {
void onParsingDone();
}
private static void parse(BatteryStats stats, BatteryDataParser... parsers) {
public static void parse(BatteryStats stats, BatteryDataParser... parsers) {
long startWalltime = 0;
long endWalltime = 0;
long historyStart = 0;

View File

@@ -19,9 +19,12 @@ package com.android.settings.fuelgauge.batterytip;
import android.content.Context;
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.text.format.DateUtils;
import android.util.KeyValueListParser;
import android.util.Log;
import java.time.Duration;
/**
* Class to store the policy for battery tips, which comes from
* {@link Settings.Global}
@@ -34,6 +37,8 @@ public class BatteryTipPolicy {
private static final String KEY_BATTERY_SAVER_TIP_ENABLED = "battery_saver_tip_enabled";
private static final String KEY_HIGH_USAGE_ENABLED = "high_usage_enabled";
private static final String KEY_HIGH_USAGE_APP_COUNT = "high_usage_app_count";
private static final String KEY_HIGH_USAGE_PERIOD_MS = "high_usage_period_ms";
private static final String KEY_HIGH_USAGE_BATTERY_DRAINING = "high_usage_battery_draining";
private static final String KEY_APP_RESTRICTION_ENABLED = "app_restriction_enabled";
private static final String KEY_REDUCED_BATTERY_ENABLED = "reduced_battery_enabled";
private static final String KEY_REDUCED_BATTERY_PERCENT = "reduced_battery_percent";
@@ -80,6 +85,24 @@ public class BatteryTipPolicy {
*/
public final int highUsageAppCount;
/**
* The size of the window(milliseconds) for checking if the device is being heavily used
*
* @see Settings.Global#BATTERY_TIP_CONSTANTS
* @see #KEY_HIGH_USAGE_PERIOD_MS
*/
public final long highUsagePeriodMs;
/**
* The battery draining threshold to detect whether device is heavily used.
* If battery drains more than {@link #highUsageBatteryDraining} in last {@link
* #highUsagePeriodMs}, treat device as heavily used.
*
* @see Settings.Global#BATTERY_TIP_CONSTANTS
* @see #KEY_HIGH_USAGE_BATTERY_DRAINING
*/
public final int highUsageBatteryDraining;
/**
* {@code true} if app restriction tip is enabled
*
@@ -143,6 +166,9 @@ public class BatteryTipPolicy {
batterySaverTipEnabled = mParser.getBoolean(KEY_BATTERY_SAVER_TIP_ENABLED, true);
highUsageEnabled = mParser.getBoolean(KEY_HIGH_USAGE_ENABLED, true);
highUsageAppCount = mParser.getInt(KEY_HIGH_USAGE_APP_COUNT, 3);
highUsagePeriodMs = mParser.getLong(KEY_HIGH_USAGE_PERIOD_MS,
Duration.ofHours(2).toMillis());
highUsageBatteryDraining = mParser.getInt(KEY_HIGH_USAGE_BATTERY_DRAINING, 25);
appRestrictionEnabled = mParser.getBoolean(KEY_APP_RESTRICTION_ENABLED, true);
reducedBatteryEnabled = mParser.getBoolean(KEY_REDUCED_BATTERY_ENABLED, false);
reducedBatteryPercent = mParser.getInt(KEY_REDUCED_BATTERY_PERCENT, 50);

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2018 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.fuelgauge.batterytip;
import android.os.BatteryStats;
import com.android.settings.fuelgauge.BatteryInfo;
/**
* DataParser used to go through battery data and detect whether battery is
* heavily used.
*/
public class HighUsageDataParser implements BatteryInfo.BatteryDataParser {
/**
* time period to check the battery usage
*/
private final long mTimePeriodMs;
/**
* treat device as heavily used if battery usage is more than {@code threshold}. 1 means 1%
* battery usage.
*/
private int mThreshold;
private long mEndTimeMs;
private byte mEndBatteryLevel;
private byte mLastPeriodBatteryLevel;
private int mBatteryDrain;
public HighUsageDataParser(long timePeriodMs, int threshold) {
mTimePeriodMs = timePeriodMs;
mThreshold = threshold;
}
@Override
public void onParsingStarted(long startTime, long endTime) {
mEndTimeMs = endTime;
}
@Override
public void onDataPoint(long time, BatteryStats.HistoryItem record) {
if (record.currentTime <= mEndTimeMs - mTimePeriodMs) {
// Since onDataPoint is invoked sorted by time, so we could use this way to get the
// closet battery level 'mTimePeriodMs' time ago.
mLastPeriodBatteryLevel = record.batteryLevel;
}
mEndBatteryLevel = record.batteryLevel;
}
@Override
public void onDataGap() {
// do nothing
}
@Override
public void onParsingDone() {
mBatteryDrain = mLastPeriodBatteryLevel - mEndBatteryLevel;
}
/**
* Return {@code true} if the battery drain in {@link #mTimePeriodMs} is too much
*/
public boolean isDeviceHeavilyUsed() {
return mBatteryDrain > mThreshold;
}
}

View File

@@ -19,13 +19,14 @@ package com.android.settings.fuelgauge.batterytip.detectors;
import android.content.Context;
import android.os.BatteryStats;
import android.support.annotation.VisibleForTesting;
import android.text.format.DateUtils;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.AppInfo;
import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.batterytip.HighUsageDataParser;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
@@ -43,6 +44,8 @@ public class HighUsageDetector implements BatteryTipDetector {
private List<AppInfo> mHighUsageAppList;
private Context mContext;
@VisibleForTesting
HighUsageDataParser mDataParser;
@VisibleForTesting
BatteryUtils mBatteryUtils;
public HighUsageDetector(Context context, BatteryTipPolicy policy,
@@ -52,32 +55,42 @@ public class HighUsageDetector implements BatteryTipDetector {
mBatteryStatsHelper = batteryStatsHelper;
mHighUsageAppList = new ArrayList<>();
mBatteryUtils = BatteryUtils.getInstance(context);
mDataParser = new HighUsageDataParser(mPolicy.highUsagePeriodMs,
mPolicy.highUsageBatteryDraining);
}
@Override
public BatteryTip detect() {
final long screenUsageTimeMs = mBatteryUtils.calculateScreenUsageTime(mBatteryStatsHelper);
//TODO(b/70570352): Change it to detect whether battery drops 25% in last 2 hours
if (mPolicy.highUsageEnabled && screenUsageTimeMs > DateUtils.HOUR_IN_MILLIS) {
final List<BatterySipper> batterySippers = mBatteryStatsHelper.getUsageList();
for (int i = 0, size = batterySippers.size(); i < size; i++) {
final BatterySipper batterySipper = batterySippers.get(i);
if (!mBatteryUtils.shouldHideSipper(batterySipper)) {
final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs(
BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj,
BatteryStats.STATS_SINCE_CHARGED);
mHighUsageAppList.add(new AppInfo.Builder()
.setPackageName(mBatteryUtils.getPackageName(batterySipper.getUid()))
.setScreenOnTimeMs(foregroundTimeMs)
.build());
if (mPolicy.highUsageEnabled) {
parseBatteryData();
if (mDataParser.isDeviceHeavilyUsed()) {
final List<BatterySipper> batterySippers = mBatteryStatsHelper.getUsageList();
for (int i = 0, size = batterySippers.size(); i < size; i++) {
final BatterySipper batterySipper = batterySippers.get(i);
if (!mBatteryUtils.shouldHideSipper(batterySipper)) {
final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs(
BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj,
BatteryStats.STATS_SINCE_CHARGED);
mHighUsageAppList.add(new AppInfo.Builder()
.setPackageName(
mBatteryUtils.getPackageName(batterySipper.getUid()))
.setScreenOnTimeMs(foregroundTimeMs)
.build());
}
}
}
mHighUsageAppList = mHighUsageAppList.subList(0,
Math.min(mPolicy.highUsageAppCount, mHighUsageAppList.size()));
Collections.sort(mHighUsageAppList, Collections.reverseOrder());
mHighUsageAppList = mHighUsageAppList.subList(0,
Math.min(mPolicy.highUsageAppCount, mHighUsageAppList.size()));
Collections.sort(mHighUsageAppList, Collections.reverseOrder());
}
}
return new HighUsageTip(screenUsageTimeMs, mHighUsageAppList);
}
@VisibleForTesting
void parseBatteryData() {
BatteryInfo.parse(mBatteryStatsHelper.getStats(), mDataParser);
}
}

View File

@@ -280,7 +280,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
.setButton1Text(R.string.forget)
.setButton1Positive(false)
.setButton1OnClickListener(view -> forgetNetwork())
.setButton2Text(R.string.support_sign_in_button_text)
.setButton2Text(R.string.wifi_sign_in_button_text)
.setButton2Positive(true)
.setButton2OnClickListener(view -> signIntoNetwork());

View File

@@ -24,6 +24,7 @@ import android.net.ConnectivityManager;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.provider.Settings;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.PreferenceScreen;
import android.text.BidiFormatter;
@@ -51,7 +52,11 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
private final WifiManager mWifiManager;
private final Lifecycle mLifecycle;
private WifiTetherSwitchBarController mSwitchController;
private MasterSwitchPreference mPreference;
private int mSoftApState;
@VisibleForTesting
MasterSwitchPreference mPreference;
@VisibleForTesting
WifiTetherSoftApManager mWifiTetherSoftApManager;
static {
WIFI_TETHER_INTENT_FILTER = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
@@ -60,6 +65,12 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
}
public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) {
this(context, lifecycle, true /* initSoftApManager */);
}
@VisibleForTesting
WifiTetherPreferenceController(Context context, Lifecycle lifecycle,
boolean initSoftApManager) {
super(context);
mConnectivityManager =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -69,6 +80,9 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
if (lifecycle != null) {
lifecycle.addObserver(this);
}
if (initSoftApManager) {
initWifiTetherSoftApManager();
}
}
@Override
@@ -101,6 +115,9 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
if (mPreference != null) {
mContext.registerReceiver(mReceiver, WIFI_TETHER_INTENT_FILTER);
clearSummaryForAirplaneMode();
if (mWifiTetherSoftApManager != null) {
mWifiTetherSoftApManager.registerSoftApCallback();
}
}
}
@@ -108,9 +125,36 @@ public class WifiTetherPreferenceController extends AbstractPreferenceController
public void onStop() {
if (mPreference != null) {
mContext.unregisterReceiver(mReceiver);
if (mWifiTetherSoftApManager != null) {
mWifiTetherSoftApManager.unRegisterSoftApCallback();
}
}
}
@VisibleForTesting
void initWifiTetherSoftApManager() {
// This manager only handles the number of connected devices, other parts are handled by
// normal BroadcastReceiver in this controller
mWifiTetherSoftApManager = new WifiTetherSoftApManager(mWifiManager,
new WifiTetherSoftApManager.WifiTetherSoftApCallback() {
@Override
public void onStateChanged(int state, int failureReason) {
mSoftApState = state;
}
@Override
public void onNumClientsChanged(int numClients) {
if (mPreference != null
&& mSoftApState == WifiManager.WIFI_AP_STATE_ENABLED) {
// Only show the number of clients when state is on
mPreference.setSummary(mContext.getResources().getQuantityString(
R.plurals.wifi_tether_connected_summary, numClients,
numClients));
}
}
});
}
//
// Everything below is copied from WifiApEnabler
//

View File

@@ -0,0 +1,47 @@
package com.android.settings.wifi.tether;
import android.net.wifi.WifiManager;
import android.os.Handler;
/**
* Wrapper for {@link android.net.wifi.WifiManager.SoftApCallback} to pass the robo test
*/
public class WifiTetherSoftApManager {
private WifiManager mWifiManager;
private WifiTetherSoftApCallback mWifiTetherSoftApCallback;
private WifiManager.SoftApCallback mSoftApCallback = new WifiManager.SoftApCallback() {
@Override
public void onStateChanged(int state, int failureReason) {
mWifiTetherSoftApCallback.onStateChanged(state, failureReason);
}
@Override
public void onNumClientsChanged(int numClients) {
mWifiTetherSoftApCallback.onNumClientsChanged(numClients);
}
};
private Handler mHandler;
WifiTetherSoftApManager(WifiManager wifiManager,
WifiTetherSoftApCallback wifiTetherSoftApCallback) {
mWifiManager = wifiManager;
mWifiTetherSoftApCallback = wifiTetherSoftApCallback;
mHandler = new Handler();
}
public void registerSoftApCallback() {
mWifiManager.registerSoftApCallback(mSoftApCallback, mHandler);
}
public void unRegisterSoftApCallback() {
mWifiManager.unregisterSoftApCallback(mSoftApCallback);
}
public interface WifiTetherSoftApCallback {
void onStateChanged(int state, int failureReason);
void onNumClientsChanged(int numClients);
}
}

View File

@@ -29,11 +29,10 @@ public class RecoverySystemWrapper {
/**
* Returns whether wipe Euicc data successfully or not.
*
* @param isWipeEuicc whether we want to wipe Euicc data or not
* @param packageName the package name of the caller app.
*/
public boolean wipeEuiccData(
Context context, final boolean isWipeEuicc, final String packageName) {
return RecoverySystem.wipeEuiccData(context, isWipeEuicc, packageName);
Context context, final String packageName) {
return RecoverySystem.wipeEuiccData(context, packageName);
}
}

View File

@@ -19,20 +19,29 @@ package com.android.settings;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.Fragment;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.ScrollView;
@@ -46,6 +55,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowAccountManager;
import org.robolectric.shadows.ShadowActivity;
@RunWith(SettingsRobolectricTestRunner.class)
@@ -55,6 +65,9 @@ import org.robolectric.shadows.ShadowActivity;
shadows = {ShadowUtils.class}
)
public class MasterClearTest {
private static final String TEST_ACCOUNT_TYPE = "android.test.account.type";
private static final String TEST_CONFIRMATION_PACKAGE = "android.test.confirmation.pkg";
private static final String TEST_ACCOUNT_NAME = "test@example.com";
@Mock
private MasterClear mMasterClear;
@@ -62,7 +75,18 @@ public class MasterClearTest {
private ScrollView mScrollView;
@Mock
private LinearLayout mLinearLayout;
@Mock
private PackageManager mPackageManager;
@Mock
private AccountManager mAccountManager;
@Mock
private Activity mMockActivity;
private ShadowActivity mShadowActivity;
private ShadowAccountManager mShadowAccountManager;
private Activity mActivity;
private View mContentView;
@@ -86,6 +110,7 @@ public class MasterClearTest {
mMasterClear = spy(new MasterClear());
mActivity = Robolectric.setupActivity(Activity.class);
mShadowActivity = shadowOf(mActivity);
// mShadowAccountManager = shadowOf(AccountManager.get(mActivity));
mContentView = LayoutInflater.from(mActivity).inflate(R.layout.master_clear, null);
// Make scrollView only have one child
@@ -93,6 +118,32 @@ public class MasterClearTest {
when(mScrollView.getChildCount()).thenReturn(1);
}
@Test
public void testShowFinalConfirmation_eraseEsimChecked() {
ActivityForTest testActivity = new ActivityForTest();
when(mMasterClear.getActivity()).thenReturn(testActivity);
mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim);
mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external);
mMasterClear.mEsimStorage.setChecked(true);
mMasterClear.showFinalConfirmation();
assertThat(testActivity.getArgs().getBoolean(MasterClear.ERASE_ESIMS_EXTRA, false))
.isTrue();
}
@Test
public void testShowFinalConfirmation_eraseEsimUnchecked() {
ActivityForTest testActivity = new ActivityForTest();
when(mMasterClear.getActivity()).thenReturn(testActivity);
mMasterClear.mEsimStorage = mContentView.findViewById(R.id.erase_esim);
mMasterClear.mExternalStorage = mContentView.findViewById(R.id.erase_external);
mMasterClear.mEsimStorage.setChecked(false);
mMasterClear.showFinalConfirmation();
assertThat(testActivity.getArgs().getBoolean(MasterClear.ERASE_ESIMS_EXTRA, true))
.isFalse();
}
@Test
public void testShowWipeEuicc_euiccDisabled() {
prepareEuiccState(
@@ -162,9 +213,61 @@ public class MasterClearTest {
@Test
public void testTryShowAccountConfirmation_unsupported() {
doReturn(mActivity).when(mMasterClear).getActivity();
/* Using the default resources, account confirmation shouldn't trigger */
assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
when(mMasterClear.getActivity()).thenReturn(mActivity);
/* Using the default resources, account confirmation shouldn't trigger */
assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
}
@Test
public void testTryShowAccountConfirmation_no_relevant_accounts() {
when(mMasterClear.getActivity()).thenReturn(mMockActivity);
when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
Account[] accounts = new Account[0];
when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
}
@Test
public void testTryShowAccountConfirmation_unresolved() {
when(mMasterClear.getActivity()).thenReturn(mMockActivity);
when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) };
when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
// The package manager should not resolve the confirmation intent targeting the non-existent
// confirmation package.
when(mMockActivity.getPackageManager()).thenReturn(mPackageManager);
assertThat(mMasterClear.tryShowAccountConfirmation()).isFalse();
}
@Test
public void testTryShowAccountConfirmation_ok() {
when(mMasterClear.getActivity()).thenReturn(mMockActivity);
// Only try to show account confirmation if the appropriate resource overlays are available.
when(mMockActivity.getString(R.string.account_type)).thenReturn(TEST_ACCOUNT_TYPE);
when(mMockActivity.getString(R.string.account_confirmation_package)).thenReturn(TEST_CONFIRMATION_PACKAGE);
// Add accounts to trigger the search for a resolving intent.
Account[] accounts = new Account[] { new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE) };
when(mMockActivity.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mAccountManager);
when(mAccountManager.getAccountsByType(TEST_ACCOUNT_TYPE)).thenReturn(accounts);
// The package manager should not resolve the confirmation intent targeting the non-existent
// confirmation package.
when(mMockActivity.getPackageManager()).thenReturn(mPackageManager);
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.packageName = TEST_CONFIRMATION_PACKAGE;
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = activityInfo;
when(mPackageManager.resolveActivity(any(), eq(0))).thenReturn(resolveInfo);
// Finally mock out the startActivityForResultCall
doNothing().when(mMockActivity).startActivityForResult(any(), eq(MasterClear.CREDENTIAL_CONFIRM_REQUEST));
assertThat(mMasterClear.tryShowAccountConfirmation()).isTrue();
}
private void initScrollView(int height, int scrollY, int childBottom) {

View File

@@ -66,7 +66,7 @@ public class ResetNetworkConfirmTest {
public void testResetNetworkData_resetEsim() {
mResetNetworkConfirm.mEraseEsim = true;
doReturn(true)
.when(mRecoverySystem).wipeEuiccData(any(Context.class), anyBoolean(), anyString());
.when(mRecoverySystem).wipeEuiccData(any(Context.class), anyString());
mResetNetworkConfirm.esimFactoryReset(mActivity, "" /* packageName */);
try {
@@ -77,7 +77,7 @@ public class ResetNetworkConfirmTest {
}
Assert.assertNotNull(mResetNetworkConfirm.mEraseEsimTask);
verify(mRecoverySystem).wipeEuiccData(any(Context.class), anyBoolean(), anyString());
verify(mRecoverySystem).wipeEuiccData(any(Context.class), anyString());
}
@Test
@@ -88,6 +88,6 @@ public class ResetNetworkConfirmTest {
Assert.assertNull(mResetNetworkConfirm.mEraseEsimTask);
verify(mRecoverySystem, never())
.wipeEuiccData(any(Context.class), anyBoolean(), anyString());
.wipeEuiccData(any(Context.class), anyString());
}
}

View File

@@ -22,6 +22,7 @@ import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -38,6 +39,7 @@ import android.content.pm.PackageManager;
import android.os.Build;
import android.os.UserManager;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
import android.widget.Button;
import com.android.settings.R;
@@ -92,12 +94,12 @@ public class BackgroundActivityPreferenceControllerTest {
private DevicePolicyManager mDevicePolicyManager;
@Mock
private DevicePolicyManagerWrapper mDevicePolicyManagerWrapper;
@Mock
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private AdvancedPowerUsageDetail mFragment;
@Mock
private PowerWhitelistBackend mPowerWhitelistBackend;
private BackgroundActivityPreferenceController mController;
private SwitchPreference mPreference;
private Preference mPreference;
private Context mShadowContext;
private BatteryUtils mBatteryUtils;
@@ -125,7 +127,8 @@ public class BackgroundActivityPreferenceControllerTest {
mBatteryUtils = spy(new BatteryUtils(mShadowContext));
doNothing().when(mBatteryUtils).setForceAppStandby(anyInt(), anyString(), anyInt());
mPreference = new SwitchPreference(mShadowContext);
mPreference = new Preference(mShadowContext);
mPreference.setKey(BackgroundActivityPreferenceController.KEY_BACKGROUND_ACTIVITY);
mController = spy(new BackgroundActivityPreferenceController(
mContext, mFragment, UID_LOW_SDK, LOW_SDK_PACKAGE, mPowerWhitelistBackend));
mController.mDpm = mDevicePolicyManagerWrapper;
@@ -133,49 +136,33 @@ public class BackgroundActivityPreferenceControllerTest {
}
@Test
public void testOnPreferenceChange_TurnOnCheck_MethodInvoked() {
mController.onPreferenceChange(mPreference, true);
public void testHandlePreferenceTreeClick_restrictApp_showDialog() {
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager).checkOpNoThrow(anyInt(),
anyInt(), anyString());
mController.handlePreferenceTreeClick(mPreference);
verify(mController).showDialog();
}
@Test
public void testHandlePreferenceTreeClick_unRestrictApp_setModeAllowed() {
doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager).checkOpNoThrow(anyInt(),
anyInt(), anyString());
mController.handlePreferenceTreeClick(mPreference);
verify(mBatteryUtils).setForceAppStandby(UID_LOW_SDK, LOW_SDK_PACKAGE,
AppOpsManager.MODE_ALLOWED);
assertThat(mPreference.getSummary())
.isEqualTo(mShadowContext.getText(R.string.background_activity_summary_on));
}
@Test
public void testOnPreferenceChange_TurnOnCheckHighSDK_MethodInvoked() {
mController = new BackgroundActivityPreferenceController(mContext, mFragment, UID_HIGH_SDK,
HIGH_SDK_PACKAGE, mPowerWhitelistBackend);
mController.mBatteryUtils = mBatteryUtils;
mController.onPreferenceChange(mPreference, true);
verify(mBatteryUtils).setForceAppStandby(UID_HIGH_SDK, HIGH_SDK_PACKAGE,
AppOpsManager.MODE_ALLOWED);
assertThat(mPreference.getSummary())
.isEqualTo(mShadowContext.getText(R.string.background_activity_summary_on));
}
@Test
public void testUpdateState_CheckOn_SetCheckedTrue() {
public void testUpdateState_noError_setEnabled() {
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_ALLOWED);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue();
assertThat(mPreference.isEnabled()).isTrue();
verify(mController).updateSummary(mPreference);
}
@Test
public void testUpdateState_CheckOff_SetCheckedFalse() {
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_IGNORED);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isFalse();
assertThat(mPreference.isEnabled()).isTrue();
verify(mController).updateSummary(mPreference);
}
@@ -184,7 +171,6 @@ public class BackgroundActivityPreferenceControllerTest {
public void testUpdateState_whitelisted() {
when(mPowerWhitelistBackend.isWhitelisted(LOW_SDK_PACKAGE)).thenReturn(true);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue();
assertThat(mPreference.isEnabled()).isFalse();
assertThat(mPreference.getSummary()).isEqualTo(
mShadowContext.getText(R.string.background_activity_summary_whitelisted));
@@ -202,27 +188,23 @@ public class BackgroundActivityPreferenceControllerTest {
}
@Test
public void testUpdateSummary_modeDefault_showSummaryOn() {
public void testUpdateSummary_modeDefault_showNotRestricted() {
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_DEFAULT);
final CharSequence expectedSummary = mShadowContext.getText(
R.string.background_activity_summary_on);
mController.updateSummary(mPreference);
assertThat(mPreference.getSummary()).isEqualTo(expectedSummary);
assertThat(mPreference.getSummary()).isEqualTo("No");
}
@Test
public void testUpdateSummary_modeIgnored_showSummaryOff() {
public void testUpdateSummary_modeIgnored_showRestricted() {
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, UID_LOW_SDK,
LOW_SDK_PACKAGE)).thenReturn(AppOpsManager.MODE_IGNORED);
final CharSequence expectedSummary = mShadowContext.getText(
R.string.background_activity_summary_off);
mController.updateSummary(mPreference);
assertThat(mPreference.getSummary()).isEqualTo(expectedSummary);
assertThat(mPreference.getSummary()).isEqualTo("Yes");
}
@Test

View File

@@ -37,6 +37,7 @@ import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
@@ -54,7 +55,7 @@ public class BatteryTipLoaderTest {
BatteryTip.TipType.BATTERY_SAVER,
BatteryTip.TipType.LOW_BATTERY,
BatteryTip.TipType.SUMMARY};
@Mock
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private BatteryStatsHelper mBatteryStatsHelper;
@Mock
private PowerManager mPowerManager;

View File

@@ -24,6 +24,7 @@ import static org.mockito.Mockito.spy;
import android.content.Context;
import android.provider.Settings;
import android.text.format.DateUtils;
import com.android.settings.TestConfig;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -42,6 +43,8 @@ public class BatteryTipPolicyTest {
+ ",battery_saver_tip_enabled=false"
+ ",high_usage_enabled=true"
+ ",high_usage_app_count=5"
+ ",high_usage_period_ms=2000"
+ ",high_usage_battery_draining=30"
+ ",app_restriction_enabled=true"
+ ",reduced_battery_enabled=true"
+ ",reduced_battery_percent=30"
@@ -66,6 +69,8 @@ public class BatteryTipPolicyTest {
assertThat(batteryTipPolicy.batterySaverTipEnabled).isFalse();
assertThat(batteryTipPolicy.highUsageEnabled).isTrue();
assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(5);
assertThat(batteryTipPolicy.highUsagePeriodMs).isEqualTo(2000);
assertThat(batteryTipPolicy.highUsageBatteryDraining).isEqualTo(30);
assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue();
assertThat(batteryTipPolicy.reducedBatteryEnabled).isTrue();
assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(30);
@@ -85,6 +90,8 @@ public class BatteryTipPolicyTest {
assertThat(batteryTipPolicy.batterySaverTipEnabled).isTrue();
assertThat(batteryTipPolicy.highUsageEnabled).isTrue();
assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(3);
assertThat(batteryTipPolicy.highUsagePeriodMs).isEqualTo(2 * DateUtils.HOUR_IN_MILLIS);
assertThat(batteryTipPolicy.highUsageBatteryDraining).isEqualTo(25);
assertThat(batteryTipPolicy.appRestrictionEnabled).isTrue();
assertThat(batteryTipPolicy.reducedBatteryEnabled).isFalse();
assertThat(batteryTipPolicy.reducedBatteryPercent).isEqualTo(50);

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2018 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.fuelgauge.batterytip;
import static com.google.common.truth.Truth.assertThat;
import android.os.BatteryStats;
import android.text.format.DateUtils;
import com.android.settings.TestConfig;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import java.time.Duration;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class HighUsageDataParserTest {
private static final long PERIOD_ONE_MINUTE_MS = Duration.ofMinutes(1).toMillis();
private static final long END_TIME_MS = 2 * PERIOD_ONE_MINUTE_MS;
private static final int THRESHOLD_LOW = 10;
private static final int THRESHOLD_HIGH = 20;
private HighUsageDataParser mDataParser;
private BatteryStats.HistoryItem mFirstItem;
private BatteryStats.HistoryItem mSecondItem;
private BatteryStats.HistoryItem mThirdItem;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mFirstItem = new BatteryStats.HistoryItem();
mFirstItem.batteryLevel = 100;
mFirstItem.currentTime = 0;
mSecondItem = new BatteryStats.HistoryItem();
mSecondItem.batteryLevel = 95;
mSecondItem.currentTime = PERIOD_ONE_MINUTE_MS;
mThirdItem = new BatteryStats.HistoryItem();
mThirdItem.batteryLevel = 80;
mThirdItem.currentTime = END_TIME_MS;
}
@Test
public void testDataParser_thresholdLow_isHeavilyUsed() {
mDataParser = new HighUsageDataParser(PERIOD_ONE_MINUTE_MS, THRESHOLD_LOW);
parseData();
assertThat(mDataParser.isDeviceHeavilyUsed()).isTrue();
}
@Test
public void testDataParser_thresholdHigh_notHeavilyUsed() {
mDataParser = new HighUsageDataParser(PERIOD_ONE_MINUTE_MS, THRESHOLD_HIGH);
parseData();
assertThat(mDataParser.isDeviceHeavilyUsed()).isFalse();
}
private void parseData() {
mDataParser.onParsingStarted(0, END_TIME_MS);
mDataParser.onDataPoint(0, mFirstItem);
mDataParser.onDataPoint(PERIOD_ONE_MINUTE_MS, mSecondItem);
mDataParser.onDataPoint(END_TIME_MS, mThirdItem);
mDataParser.onParsingDone();
}
}

View File

@@ -18,6 +18,8 @@ package com.android.settings.fuelgauge.batterytip.detectors;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -31,6 +33,7 @@ import com.android.settings.TestConfig;
import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.HighUsageDataParser;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
@@ -55,6 +58,8 @@ public class HighUsageDetectorTest {
private BatteryUtils mBatteryUtils;
@Mock
private BatterySipper mBatterySipper;
@Mock
private HighUsageDataParser mDataParser;
private BatteryTipPolicy mPolicy;
private HighUsageDetector mHighUsageDetector;
@@ -66,8 +71,10 @@ public class HighUsageDetectorTest {
mContext = RuntimeEnvironment.application;
mPolicy = spy(new BatteryTipPolicy(mContext));
mHighUsageDetector = new HighUsageDetector(mContext, mPolicy, mBatteryStatsHelper);
mHighUsageDetector = spy(new HighUsageDetector(mContext, mPolicy, mBatteryStatsHelper));
mHighUsageDetector.mBatteryUtils = mBatteryUtils;
mHighUsageDetector.mDataParser = mDataParser;
doNothing().when(mHighUsageDetector).parseBatteryData();
mUsageList = new ArrayList<>();
mUsageList.add(mBatterySipper);
@@ -82,8 +89,7 @@ public class HighUsageDetectorTest {
@Test
public void testDetect_containsHighUsageApp_tipVisible() {
doReturn(2 * DateUtils.HOUR_IN_MILLIS).when(mBatteryUtils).calculateScreenUsageTime(
mBatteryStatsHelper);
doReturn(true).when(mDataParser).isDeviceHeavilyUsed();
doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList();
doReturn(DateUtils.HOUR_IN_MILLIS).when(mBatteryUtils).getProcessTimeMs(
BatteryUtils.StatusType.FOREGROUND, mBatterySipper.uidObj,

View File

@@ -18,11 +18,15 @@ package com.android.settings.wifi.tether;
import static android.arch.lifecycle.Lifecycle.Event.ON_START;
import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -65,6 +69,7 @@ import java.util.ArrayList;
shadows = {
WifiTetherPreferenceControllerTest.ShadowWifiTetherSettings.class,
WifiTetherPreferenceControllerTest.ShadowWifiTetherSwitchBarController.class,
WifiTetherPreferenceControllerTest.ShadowWifiTetherSoftApManager.class
})
public class WifiTetherPreferenceControllerTest {
@@ -94,8 +99,9 @@ public class WifiTetherPreferenceControllerTest {
when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
when(mScreen.findPreference(anyString())).thenReturn(mPreference);
when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[] {"1", "2"});
mController = new WifiTetherPreferenceController(mContext, mLifecycle);
when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
mController = new WifiTetherPreferenceController(mContext, mLifecycle,
false /* initSoftApManager */);
}
@After
@@ -105,8 +111,9 @@ public class WifiTetherPreferenceControllerTest {
@Test
public void isAvailable_noTetherRegex_shouldReturnFalse() {
when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[] {});
mController = new WifiTetherPreferenceController(mContext, mLifecycle);
when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{});
mController = new WifiTetherPreferenceController(mContext, mLifecycle,
false /* initSoftApManager */);
assertThat(mController.isAvailable()).isFalse();
}
@@ -244,6 +251,19 @@ public class WifiTetherPreferenceControllerTest {
}
}
@Implements(WifiTetherSoftApManager.class)
public static final class ShadowWifiTetherSoftApManager {
@Implementation
public void registerSoftApCallback() {
// do nothing
}
@Implementation
public void unRegisterSoftApCallback() {
// do nothing
}
}
@Implements(WifiTetherSwitchBarController.class)
public static final class ShadowWifiTetherSwitchBarController {