Merge "Keep candidates in radio button picker UI ordered"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ec7183a..f15759e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -8598,8 +8598,8 @@
<!-- Preference menu title for accessing the deletion helper from the storage manager settings. [CHAR LIMIT=30]-->
<string name="deletion_helper_preference_title">Free up space now</string>
- <!-- Preference title for gesture settings [CHAR LIMIT=40]-->
- <string name="gesture_preference_title">Gesture shortcuts</string>
+ <!-- Preference title for gesture settings [CHAR LIMIT=40 BACKUP_MESSAGE_ID:5280023307132819052]-->
+ <string name="gesture_preference_title">Gesture</string>
<!-- Preference summary for gesture settings (phone) [CHAR LIMIT=NONE]-->
<string name="gesture_preference_summary" product="default">Quick gestures to control your phone</string>
diff --git a/res/xml/bluetooth_settings.xml b/res/xml/bluetooth_settings.xml
index 046295b..1ab2b16 100644
--- a/res/xml/bluetooth_settings.xml
+++ b/res/xml/bluetooth_settings.xml
@@ -16,6 +16,7 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
android:title="@string/bluetooth_settings">
<Preference
@@ -25,6 +26,12 @@
android:key="paired_devices"
android:title="@string/bluetooth_paired_device_title"/>
+ <com.android.settings.DividerPreference
+ android:key="bt_received_files"
+ android:title="@string/bluetooth_show_received_files"
+ settings:allowDividerAbove="true"
+ settings:allowDividerBelow="true"/>
+
<com.android.settingslib.widget.FooterPreference/>
</PreferenceScreen>
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index 757d67d..033d81b 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -107,6 +107,8 @@
videoView.setLayoutParams(videoLp);
videoView.invalidate();
videoView.start();
+ mVideoBackgroundView.getViewTreeObserver()
+ .removeOnGlobalLayoutListener(mLayoutListener);
}
};
diff --git a/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPicker.java b/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPicker.java
deleted file mode 100644
index 8ffcb68..0000000
--- a/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPicker.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2017 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.applications.defaultapps;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.provider.Settings;
-import android.service.notification.NotificationAssistantService;
-import android.util.Slog;
-
-import com.android.settings.R;
-import com.android.settings.utils.ManagedServiceSettings;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class DefaultNotificationAssistantPicker extends DefaultAppPickerFragment {
- private static final String TAG = "DefaultNotiAssist";
-
- private final ManagedServiceSettings.Config mConfig = getConfig();
-
- @Override
- public int getMetricsCategory() {
- return 0;
- }
-
- @Override
- protected String getDefaultKey() {
- return Settings.Secure.getString(getContext().getContentResolver(), mConfig.setting);
- }
-
- @Override
- protected boolean setDefaultKey(String value) {
- Settings.Secure.putString(getContext().getContentResolver(), mConfig.setting, value);
- return true;
- }
-
- @Override
- protected List<DefaultAppInfo> getCandidates() {
- List<DefaultAppInfo> candidates = new ArrayList<>();
- final int user = ActivityManager.getCurrentUser();
-
- List<ResolveInfo> installedServices = mPm.queryIntentServicesAsUser(
- new Intent(mConfig.intentAction),
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
- user);
-
- for (int i = 0, count = installedServices.size(); i < count; i++) {
- ResolveInfo resolveInfo = installedServices.get(i);
- ServiceInfo info = resolveInfo.serviceInfo;
-
- if (!mConfig.permission.equals(info.permission)) {
- Slog.w(mConfig.tag, "Skipping " + mConfig.noun + " service "
- + info.packageName + "/" + info.name
- + ": it does not require the permission "
- + mConfig.permission);
- continue;
- }
-
- candidates.add(new DefaultAppInfo(mPm,
- mUserId, new ComponentName(info.packageName, info.name)));
- }
- return candidates;
- }
-
- @Override
- protected boolean shouldShowItemNone() {
- return true;
- }
-
- private ManagedServiceSettings.Config getConfig() {
- final ManagedServiceSettings.Config c = new ManagedServiceSettings.Config();
- c.tag = TAG;
- c.setting = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
- c.intentAction = NotificationAssistantService.SERVICE_INTERFACE;
- c.permission = android.Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
- c.noun = "notification assistant";
- c.warningDialogTitle = R.string.notification_listener_security_warning_title;
- c.warningDialogSummary = R.string.notification_listener_security_warning_summary;
- c.emptyText = R.string.no_notification_listeners;
- return c;
- }
-}
diff --git a/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java
new file mode 100644
index 0000000..c425cdc
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothFilesPreferenceController.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 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.bluetooth;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.core.PreferenceController;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
+import com.android.settings.overlay.FeatureFactory;
+
+/**
+ * Controller that shows received files
+ */
+public class BluetoothFilesPreferenceController extends PreferenceController {
+ private static final String TAG = "BluetoothFilesPrefCtrl";
+
+ public static final String KEY_RECEIVED_FILES = "bt_received_files";
+
+ /* Private intent to show the list of received files */
+ @VisibleForTesting
+ static final String ACTION_OPEN_FILES = "com.android.bluetooth.action.TransferHistory";
+ @VisibleForTesting
+ static final String EXTRA_SHOW_ALL_FILES = "android.btopp.intent.extra.SHOW_ALL";
+ @VisibleForTesting
+ static final String EXTRA_DIRECTION = "direction";
+
+ private MetricsFeatureProvider mMetricsFeatureProvider;
+
+ public BluetoothFilesPreferenceController(Context context) {
+ super(context);
+ mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_RECEIVED_FILES;
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (KEY_RECEIVED_FILES.equals(preference.getKey())) {
+ mMetricsFeatureProvider.action(mContext,
+ MetricsProto.MetricsEvent.ACTION_BLUETOOTH_FILES);
+ Intent intent = new Intent(ACTION_OPEN_FILES);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(EXTRA_DIRECTION, 1 /* DIRECTION_INBOUND */);
+ intent.putExtra(EXTRA_SHOW_ALL_FILES, true);
+ mContext.startActivity(intent);
+ return true;
+ }
+
+ return false;
+ }
+
+
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java
index f8648ef..4703031 100644
--- a/src/com/android/settings/bluetooth/BluetoothSettings.java
+++ b/src/com/android/settings/bluetooth/BluetoothSettings.java
@@ -22,7 +22,6 @@
import android.bluetooth.BluetoothDevice;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.os.Bundle;
@@ -31,13 +30,9 @@
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
-import android.support.v7.preference.PreferenceScreen;
import android.text.Spannable;
import android.text.style.TextAppearanceSpan;
import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
@@ -75,15 +70,6 @@
*/
public class BluetoothSettings extends DeviceListPreferenceFragment implements Indexable {
private static final String TAG = "BluetoothSettings";
-
- private static final int MENU_ID_SHOW_RECEIVED = Menu.FIRST + 1;
-
- /* Private intent to show the list of received files */
- private static final String BTOPP_ACTION_OPEN_RECEIVED_FILES =
- "android.btopp.intent.action.OPEN_RECEIVED_FILES";
- private static final String BTOPP_PACKAGE =
- "com.android.bluetooth";
-
private static final int PAIRED_DEVICE_ORDER = 1;
private static final int PAIRING_PREF_ORDER = 2;
@@ -185,31 +171,6 @@
}
@Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if (mLocalAdapter == null) return;
- // If the user is not allowed to configure bluetooth, do not show the menu.
- if (isUiRestricted()) return;
-
- menu.add(Menu.NONE, MENU_ID_SHOW_RECEIVED, 0, R.string.bluetooth_show_received_files)
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
- super.onCreateOptionsMenu(menu, inflater);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case MENU_ID_SHOW_RECEIVED:
- mMetricsFeatureProvider.action(getActivity(),
- MetricsEvent.ACTION_BLUETOOTH_FILES);
- Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES);
- intent.setPackage(BTOPP_PACKAGE);
- getActivity().sendBroadcast(intent);
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
public String getDeviceListKey() {
return KEY_PAIRED_DEVICES;
}
@@ -233,7 +194,6 @@
mPairedDevicesCategory.addPreference(mPairingPreference);
updateFooterPreference(mFooterPreference);
- getActivity().invalidateOptionsMenu();
mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
return; // not break
@@ -258,9 +218,6 @@
if (messageId != 0) {
getEmptyTextView().setText(messageId);
}
- if (!isUiRestricted()) {
- getActivity().invalidateOptionsMenu();
- }
}
private void setOffMessage() {
@@ -312,16 +269,6 @@
}
@Override
- public void onScanningStateChanged(boolean started) {
- super.onScanningStateChanged(started);
- // Update options' enabled state
- final Activity activity = getActivity();
- if (activity != null) {
- activity.invalidateOptionsMenu();
- }
- }
-
- @Override
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
updateContent(mLocalAdapter.getBluetoothState());
}
@@ -417,6 +364,7 @@
(SettingsActivity) getActivity());
controllers.add(mDeviceNamePrefController);
controllers.add(mPairingPrefController);
+ controllers.add(new BluetoothFilesPreferenceController(context));
return controllers;
}
diff --git a/src/com/android/settings/deviceinfo/StorageProfileFragment.java b/src/com/android/settings/deviceinfo/StorageProfileFragment.java
index dee6793..f5129ed 100644
--- a/src/com/android/settings/deviceinfo/StorageProfileFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageProfileFragment.java
@@ -120,7 +120,6 @@
@Override
public void onLoadFinished(Loader<SparseArray<AppsStorageResult>> loader,
SparseArray<AppsStorageResult> result) {
- scrubAppsFromResult(result.get(mUserId));
mPreferenceController.onLoadFinished(result, mUserId);
}
@@ -132,17 +131,4 @@
void setPreferenceController(StorageItemPreferenceController controller) {
mPreferenceController = controller;
}
-
- private AppsStorageResult scrubAppsFromResult(AppsStorageResult result) {
- if (result == null) {
- return null;
- }
-
- // TODO(b/35927909): Attribute app sizes better than zeroing out for profiles.
- result.gamesSize = 0;
- result.musicAppsSize = 0;
- result.videoAppsSize = 0;
- result.otherAppsSize = 0;
- return result;
- }
}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
index 74474b3..0b685d0 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageAsyncLoader.java
@@ -25,6 +25,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.os.UserHandle;
+import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
@@ -34,6 +35,8 @@
import com.android.settingslib.applications.StorageStatsSource;
import java.io.IOException;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
/**
@@ -48,6 +51,7 @@
private String mUuid;
private StorageStatsSource mStatsManager;
private PackageManagerWrapper mPackageManager;
+ private ArraySet<String> mSeenPackages;
public StorageAsyncLoader(Context context, UserManagerWrapper userManager,
String uuid, StorageStatsSource source, PackageManagerWrapper pm) {
@@ -64,8 +68,18 @@
}
private SparseArray<AppsStorageResult> loadApps() {
+ mSeenPackages = new ArraySet<>();
SparseArray<AppsStorageResult> result = new SparseArray<>();
List<UserInfo> infos = mUserManager.getUsers();
+ // Sort the users by user id ascending.
+ Collections.sort(
+ infos,
+ new Comparator<UserInfo>() {
+ @Override
+ public int compare(UserInfo userInfo, UserInfo otherUser) {
+ return Integer.compare(userInfo.id, otherUser.id);
+ }
+ });
for (int i = 0, userCount = infos.size(); i < userCount; i++) {
UserInfo info = infos.get(i);
result.put(info.id, getStorageResultForUser(info.id));
@@ -93,10 +107,11 @@
long blamedSize = stats.getDataBytes() - stats.getCacheBytes();
- // Only count app code against the current user; we don't want
- // double-counting on multi-user devices.
- if (userId == UserHandle.myUserId()) {
+ // This isn't quite right because it slams the first user by user id with the whole code
+ // size, but this ensures that we count all apps seen once.
+ if (!mSeenPackages.contains(app.packageName)) {
blamedSize += stats.getCodeBytes();
+ mSeenPackages.add(app.packageName);
}
switch (app.category) {
@@ -140,6 +155,7 @@
public long musicAppsSize;
public long videoAppsSize;
public long otherAppsSize;
+ public long cacheSize;
public StorageStatsSource.ExternalStorageStats externalStats;
}
diff --git a/src/com/android/settings/deviceinfo/storage/UserProfileController.java b/src/com/android/settings/deviceinfo/storage/UserProfileController.java
index 18fa7b7..fc297ca 100644
--- a/src/com/android/settings/deviceinfo/storage/UserProfileController.java
+++ b/src/com/android/settings/deviceinfo/storage/UserProfileController.java
@@ -96,7 +96,13 @@
int userId = mUser.id;
StorageAsyncLoader.AppsStorageResult result = stats.get(userId);
if (result != null) {
- setSize(result.externalStats.totalBytes, mTotalSizeBytes);
+ setSize(
+ result.externalStats.totalBytes
+ + result.otherAppsSize
+ + result.videoAppsSize
+ + result.musicAppsSize
+ + result.gamesSize,
+ mTotalSizeBytes);
}
}
diff --git a/src/com/android/settings/fuelgauge/anomaly/Anomaly.java b/src/com/android/settings/fuelgauge/anomaly/Anomaly.java
index 37b52fc..746bd7f 100644
--- a/src/com/android/settings/fuelgauge/anomaly/Anomaly.java
+++ b/src/com/android/settings/fuelgauge/anomaly/Anomaly.java
@@ -35,10 +35,12 @@
public class Anomaly implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
@IntDef({AnomalyType.WAKE_LOCK,
- AnomalyType.WAKEUP_ALARM})
+ AnomalyType.WAKEUP_ALARM,
+ AnomalyType.BLUETOOTH_SCAN})
public @interface AnomalyType {
int WAKE_LOCK = 0;
int WAKEUP_ALARM = 1;
+ int BLUETOOTH_SCAN = 2;
}
@Retention(RetentionPolicy.SOURCE)
@@ -52,7 +54,8 @@
@AnomalyType
public static final int[] ANOMALY_TYPE_LIST =
{AnomalyType.WAKE_LOCK,
- AnomalyType.WAKEUP_ALARM};
+ AnomalyType.WAKEUP_ALARM,
+ AnomalyType.BLUETOOTH_SCAN};
/**
* Type of this this anomaly
diff --git a/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicy.java b/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicy.java
index 7dbae36..647737c 100644
--- a/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicy.java
+++ b/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicy.java
@@ -37,9 +37,13 @@
@VisibleForTesting
static final String KEY_WAKEUP_ALARM_DETECTION_ENABLED = "wakeup_alarm_enabled";
@VisibleForTesting
+ static final String KEY_BLUETOOTH_SCAN_DETECTION_ENABLED = "bluetooth_scan_enabled";
+ @VisibleForTesting
static final String KEY_WAKELOCK_THRESHOLD = "wakelock_threshold";
@VisibleForTesting
static final String KEY_WAKEUP_ALARM_THRESHOLD = "wakeup_alarm_threshold";
+ @VisibleForTesting
+ static final String KEY_BLUETOOTH_SCAN_THRESHOLD = "bluetooth_scan_threshold";
/**
* {@code true} if general anomaly detection is enabled
@@ -66,6 +70,14 @@
public final boolean wakeupAlarmDetectionEnabled;
/**
+ * {@code true} if bluetooth scanning detection is enabled
+ *
+ * @see Settings.Global#ANOMALY_DETECTION_CONSTANTS
+ * @see #KEY_BLUETOOTH_SCAN_THRESHOLD
+ */
+ public final boolean bluetoothScanDetectionEnabled;
+
+ /**
* Threshold for wakelock time in milli seconds
*
* @see Settings.Global#ANOMALY_DETECTION_CONSTANTS
@@ -81,6 +93,14 @@
*/
public final long wakeupAlarmThreshold;
+ /**
+ * Threshold for bluetooth unoptimized scanning time in milli seconds
+ *
+ * @see Settings.Global#ANOMALY_DETECTION_CONSTANTS
+ * @see #KEY_BLUETOOTH_SCAN_THRESHOLD
+ */
+ public final long bluetoothScanThreshold;
+
private final KeyValueListParserWrapper mParserWrapper;
public AnomalyDetectionPolicy(Context context) {
@@ -103,9 +123,13 @@
wakeLockDetectionEnabled = mParserWrapper.getBoolean(KEY_WAKELOCK_DETECTION_ENABLED, true);
wakeupAlarmDetectionEnabled = mParserWrapper.getBoolean(KEY_WAKEUP_ALARM_DETECTION_ENABLED,
true);
+ bluetoothScanDetectionEnabled = mParserWrapper.getBoolean(
+ KEY_BLUETOOTH_SCAN_DETECTION_ENABLED, true);
wakeLockThreshold = mParserWrapper.getLong(KEY_WAKELOCK_THRESHOLD,
DateUtils.HOUR_IN_MILLIS);
wakeupAlarmThreshold = mParserWrapper.getLong(KEY_WAKEUP_ALARM_THRESHOLD, 60);
+ bluetoothScanThreshold = mParserWrapper.getLong(KEY_BLUETOOTH_SCAN_THRESHOLD,
+ 30 * DateUtils.MINUTE_IN_MILLIS);
}
public boolean isAnomalyDetectorEnabled(@Anomaly.AnomalyType int type) {
@@ -114,6 +138,8 @@
return wakeLockDetectionEnabled;
case Anomaly.AnomalyType.WAKEUP_ALARM:
return wakeupAlarmDetectionEnabled;
+ case Anomaly.AnomalyType.BLUETOOTH_SCAN:
+ return bluetoothScanDetectionEnabled;
default:
return false; // Disabled when no this type
}
diff --git a/src/com/android/settings/fuelgauge/anomaly/AnomalyLoader.java b/src/com/android/settings/fuelgauge/anomaly/AnomalyLoader.java
index 264c390..03d4d23 100644
--- a/src/com/android/settings/fuelgauge/anomaly/AnomalyLoader.java
+++ b/src/com/android/settings/fuelgauge/anomaly/AnomalyLoader.java
@@ -22,9 +22,9 @@
import android.os.Bundle;
import android.os.UserManager;
import android.support.annotation.VisibleForTesting;
+import android.util.Log;
import com.android.internal.os.BatteryStatsHelper;
-import com.android.settings.Utils;
import com.android.settings.utils.AsyncLoader;
import java.util.ArrayList;
@@ -35,6 +35,8 @@
* an empty list if there is no anomaly.
*/
public class AnomalyLoader extends AsyncLoader<List<Anomaly>> {
+ private static final String TAG = "AnomalyLoader";
+
private static final boolean USE_FAKE_DATA = false;
private BatteryStatsHelper mBatteryStatsHelper;
private String mPackageName;
@@ -108,9 +110,9 @@
List<Anomaly> generateFakeData() {
final List<Anomaly> anomalies = new ArrayList<>();
final Context context = getContext();
+ final String packageName = "com.android.settings";
+ final CharSequence displayName = "Settings";
try {
- final String packageName = "com.android.settings";
- final CharSequence displayName = "Settings";
final int uid = context.getPackageManager().getPackageUid(packageName, 0);
anomalies.add(new Anomaly.Builder()
@@ -125,8 +127,14 @@
.setPackageName(packageName)
.setDisplayName(displayName)
.build());
+ anomalies.add(new Anomaly.Builder()
+ .setUid(uid)
+ .setType(Anomaly.AnomalyType.BLUETOOTH_SCAN)
+ .setPackageName(packageName)
+ .setDisplayName(displayName)
+ .build());
} catch (PackageManager.NameNotFoundException e) {
- e.printStackTrace();
+ Log.e(TAG, "Cannot find package by name: " + packageName, e);
}
return anomalies;
}
diff --git a/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java b/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java
index d226899..de9f7aa 100644
--- a/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java
+++ b/src/com/android/settings/fuelgauge/anomaly/AnomalyUtils.java
@@ -23,6 +23,7 @@
import com.android.settings.fuelgauge.anomaly.action.BackgroundCheckAction;
import com.android.settings.fuelgauge.anomaly.action.ForceStopAction;
import com.android.settings.fuelgauge.anomaly.checker.AnomalyDetector;
+import com.android.settings.fuelgauge.anomaly.checker.BluetoothScanAnomalyDetector;
import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector;
import com.android.settings.fuelgauge.anomaly.checker.WakeupAlarmAnomalyDetector;
@@ -56,6 +57,7 @@
case Anomaly.AnomalyType.WAKE_LOCK:
return new ForceStopAction(mContext);
case Anomaly.AnomalyType.WAKEUP_ALARM:
+ case Anomaly.AnomalyType.BLUETOOTH_SCAN:
return new BackgroundCheckAction(mContext);
default:
return null;
@@ -74,6 +76,8 @@
return new WakeLockAnomalyDetector(mContext);
case Anomaly.AnomalyType.WAKEUP_ALARM:
return new WakeupAlarmAnomalyDetector(mContext);
+ case Anomaly.AnomalyType.BLUETOOTH_SCAN:
+ return new BluetoothScanAnomalyDetector(mContext);
default:
return null;
}
diff --git a/src/com/android/settings/fuelgauge/anomaly/checker/BluetoothScanAnomalyDetector.java b/src/com/android/settings/fuelgauge/anomaly/checker/BluetoothScanAnomalyDetector.java
new file mode 100644
index 0000000..619386e
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/anomaly/checker/BluetoothScanAnomalyDetector.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 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.anomaly.checker;
+
+import android.content.Context;
+import android.os.BatteryStats;
+import android.os.SystemClock;
+import android.support.annotation.VisibleForTesting;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
+
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.Utils;
+import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.fuelgauge.anomaly.Anomaly;
+import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
+import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
+import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Check whether apps have unoptimized bluetooth scanning in the background
+ */
+public class BluetoothScanAnomalyDetector implements AnomalyDetector {
+ private static final String TAG = "BluetoothScanAnomalyDetector";
+ @VisibleForTesting
+ BatteryUtils mBatteryUtils;
+ @VisibleForTesting
+ AnomalyAction mAnomalyAction;
+ private long mBluetoothScanningThreshold;
+ private Context mContext;
+
+ public BluetoothScanAnomalyDetector(Context context) {
+ this(context, new AnomalyDetectionPolicy(context));
+ }
+
+ @VisibleForTesting
+ BluetoothScanAnomalyDetector(Context context, AnomalyDetectionPolicy policy) {
+ mContext = context;
+ mBatteryUtils = BatteryUtils.getInstance(context);
+ mAnomalyAction = AnomalyUtils.getInstance(context).getAnomalyAction(
+ Anomaly.AnomalyType.BLUETOOTH_SCAN);
+ mBluetoothScanningThreshold = policy.bluetoothScanThreshold;
+ }
+
+ @Override
+ public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper) {
+ // Detect all apps if targetPackageName is null
+ return detectAnomalies(batteryStatsHelper, null /* targetPackageName */);
+ }
+
+ @Override
+ public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper,
+ String targetPackageName) {
+ final List<BatterySipper> batterySippers = batteryStatsHelper.getUsageList();
+ final List<Anomaly> anomalies = new ArrayList<>();
+ final int targetUid = mBatteryUtils.getPackageUid(targetPackageName);
+ final long elapsedRealtimeMs = SystemClock.elapsedRealtime();
+
+ for (int i = 0, size = batterySippers.size(); i < size; i++) {
+ final BatterySipper sipper = batterySippers.get(i);
+ final BatteryStats.Uid uid = sipper.uidObj;
+ if (uid == null
+ || mBatteryUtils.shouldHideSipper(sipper)
+ || (targetUid != BatteryUtils.UID_NULL && targetUid != uid.getUid())) {
+ continue;
+ }
+
+ final long bluetoothTimeMs = getBluetoothUnoptimizedBgTimeMs(uid, elapsedRealtimeMs);
+ if (bluetoothTimeMs > mBluetoothScanningThreshold) {
+ final String packageName = mBatteryUtils.getPackageName(uid.getUid());
+ final CharSequence displayName = Utils.getApplicationLabel(mContext,
+ packageName);
+
+ Anomaly anomaly = new Anomaly.Builder()
+ .setUid(uid.getUid())
+ .setType(Anomaly.AnomalyType.BLUETOOTH_SCAN)
+ .setDisplayName(displayName)
+ .setPackageName(packageName)
+ .build();
+
+ if (mAnomalyAction.isActionActive(anomaly)) {
+ anomalies.add(anomaly);
+ }
+ }
+ }
+
+ return anomalies;
+ }
+
+ @VisibleForTesting
+ public long getBluetoothUnoptimizedBgTimeMs(BatteryStats.Uid uid, long elapsedRealtimeMs) {
+ BatteryStats.Timer timer = uid.getBluetoothUnoptimizedScanBackgroundTimer();
+
+ return timer != null ? timer.getTotalDurationMsLocked(elapsedRealtimeMs) : 0;
+ }
+
+}
diff --git a/src/com/android/settings/fuelgauge/anomaly/checker/WakeLockAnomalyDetector.java b/src/com/android/settings/fuelgauge/anomaly/checker/WakeLockAnomalyDetector.java
index 545bb26..5fa0e41 100644
--- a/src/com/android/settings/fuelgauge/anomaly/checker/WakeLockAnomalyDetector.java
+++ b/src/com/android/settings/fuelgauge/anomaly/checker/WakeLockAnomalyDetector.java
@@ -65,7 +65,8 @@
@Override
public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper) {
- return detectAnomalies(batteryStatsHelper, null);
+ // Detect all apps if targetPackageName is null
+ return detectAnomalies(batteryStatsHelper, null /* targetPackageName */);
}
@Override
diff --git a/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetector.java b/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetector.java
index 3502e73..aadfa0c 100644
--- a/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetector.java
+++ b/src/com/android/settings/fuelgauge/anomaly/checker/WakeupAlarmAnomalyDetector.java
@@ -61,7 +61,8 @@
@Override
public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper) {
- return detectAnomalies(batteryStatsHelper, null);
+ // Detect all apps if targetPackageName is null
+ return detectAnomalies(batteryStatsHelper, null /* targetPackageName */);
}
@Override
diff --git a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
index a78fed7..6a13282 100644
--- a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
+++ b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java
@@ -27,7 +27,9 @@
import android.Manifest;
import android.annotation.Nullable;
import android.app.Activity;
+import android.app.NotificationManager;
import android.content.ComponentName;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
@@ -53,12 +55,14 @@
private int mUserId;
private ComponentName mComponentName;
private TouchOverlayManager mTouchOverlayManager;
+ private NotificationManager mNm;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTouchOverlayManager = new TouchOverlayManager(this);
+ mNm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mComponentName = getIntent().getParcelableExtra(EXTRA_COMPONENT_NAME);
mUserId = getIntent().getIntExtra(EXTRA_USER_ID, UserHandle.USER_NULL);
@@ -94,12 +98,7 @@
return;
}
- final SettingsStringUtil.SettingStringHelper setting =
- new SettingsStringUtil.SettingStringHelper(
- getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
- mUserId);
- setting.write(SettingsStringUtil.ComponentNameSet.add(setting.read(), mComponentName));
+ mNm.setNotificationListenerAccessGranted(mComponentName, true);
finish();
}
diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java
index 2cd728c..37601b3 100644
--- a/src/com/android/settings/notification/NotificationAccessSettings.java
+++ b/src/com/android/settings/notification/NotificationAccessSettings.java
@@ -41,7 +41,6 @@
private static final String TAG = NotificationAccessSettings.class.getSimpleName();
private static final Config CONFIG = getNotificationListenerConfig();
-
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -70,10 +69,11 @@
return CONFIG;
}
+ @Override
protected boolean setEnabled(ComponentName service, String title, boolean enable) {
logSpecialPermissionChange(enable, service.getPackageName());
if (!enable) {
- if (!mServiceListing.isEnabled(service)) {
+ if (!isServiceEnabled(service)) {
return true; // already disabled
}
// show a friendly dialog
@@ -82,10 +82,27 @@
.show(getFragmentManager(), "friendlydialog");
return false;
} else {
- return super.setEnabled(service, title, enable);
+ if (isServiceEnabled(service)) {
+ return true; // already enabled
+ }
+ // show a scary dialog
+ new ScaryWarningDialogFragment()
+ .setServiceInfo(service, title, this)
+ .show(getFragmentManager(), "dialog");
+ return false;
}
}
+ @Override
+ protected boolean isServiceEnabled(ComponentName cn) {
+ return mNm.isNotificationListenerAccessGranted(cn);
+ }
+
+ @Override
+ protected void enable(ComponentName service) {
+ mNm.setNotificationListenerAccessGranted(service, true);
+ }
+
@VisibleForTesting
void logSpecialPermissionChange(boolean enable, String packageName) {
int logCategory = enable ? MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW
@@ -94,17 +111,14 @@
logCategory, packageName);
}
- private static void disable(final Context context, final NotificationAccessSettings parent,
- final ComponentName cn) {
- parent.mServiceListing.setEnabled(cn, false);
+ private static void disable(final NotificationAccessSettings parent, final ComponentName cn) {
+ parent.mNm.setNotificationListenerAccessGranted(cn, false);
AsyncTask.execute(new Runnable() {
@Override
public void run() {
- final NotificationManager mgr = context.getSystemService(NotificationManager.class);
-
- if (!mgr.isNotificationPolicyAccessGrantedForPackage(
+ if (!parent.mNm.isNotificationPolicyAccessGrantedForPackage(
cn.getPackageName())) {
- mgr.removeAutomaticZenRules(cn.getPackageName());
+ parent.mNm.removeAutomaticZenRules(cn.getPackageName());
}
}
});
@@ -145,7 +159,7 @@
.setPositiveButton(R.string.notification_listener_disable_warning_confirm,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
- disable(getContext(), parent, cn);
+ disable(parent, cn);
}
})
.setNegativeButton(R.string.notification_listener_disable_warning_cancel,
diff --git a/src/com/android/settings/notification/ZenAccessSettings.java b/src/com/android/settings/notification/ZenAccessSettings.java
index a41a733..0d37980 100644
--- a/src/com/android/settings/notification/ZenAccessSettings.java
+++ b/src/com/android/settings/notification/ZenAccessSettings.java
@@ -17,7 +17,9 @@
package com.android.settings.notification;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.AlertDialog;
+import android.app.AppGlobals;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.NotificationManager;
@@ -25,14 +27,17 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.RemoteException;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.support.v14.preference.SwitchPreference;
@@ -41,6 +46,7 @@
import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.Log;
import android.view.View;
import android.widget.Toast;
@@ -55,10 +61,9 @@
import java.util.List;
public class ZenAccessSettings extends EmptyTextSettings {
+ private final String TAG = "ZenAccessSettings";
private final SettingObserver mObserver = new SettingObserver();
- private static final String ENABLED_SERVICES_SEPARATOR = ":";
-
private Context mContext;
private PackageManager mPkgMan;
private NotificationManager mNoMan;
@@ -106,7 +111,7 @@
final PreferenceScreen screen = getPreferenceScreen();
screen.removeAll();
final ArrayList<ApplicationInfo> apps = new ArrayList<>();
- final ArraySet<String> requesting = mNoMan.getPackagesRequestingNotificationPolicyAccess();
+ final ArraySet<String> requesting = getPackagesRequestingNotificationPolicyAccess();
if (!requesting.isEmpty()) {
final List<ApplicationInfo> installed = mPkgMan.getInstalledApplications(0);
if (installed != null) {
@@ -117,7 +122,8 @@
}
}
}
- ArraySet<String> autoApproved = getEnabledNotificationListeners();
+ ArraySet<String> autoApproved = new ArraySet<>();
+ autoApproved.addAll(mNoMan.getEnabledNotificationListenerPackages());
requesting.addAll(autoApproved);
Collections.sort(apps, new PackageItemInfo.DisplayNameComparator(mPkgMan));
for (ApplicationInfo app : apps) {
@@ -152,20 +158,25 @@
}
}
- private ArraySet<String> getEnabledNotificationListeners() {
- ArraySet<String> packages = new ArraySet<>();
- String settingValue = Settings.Secure.getString(getContext().getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
- if (!TextUtils.isEmpty(settingValue)) {
- String[] restored = settingValue.split(ENABLED_SERVICES_SEPARATOR);
- for (int i = 0; i < restored.length; i++) {
- ComponentName value = ComponentName.unflattenFromString(restored[i]);
- if (null != value) {
- packages.add(value.getPackageName());
+ private ArraySet<String> getPackagesRequestingNotificationPolicyAccess() {
+ ArraySet<String> requestingPackages = new ArraySet<>();
+ try {
+ final String[] PERM = {
+ android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
+ };
+ final ParceledListSlice list = AppGlobals.getPackageManager()
+ .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
+ ActivityManager.getCurrentUser());
+ final List<PackageInfo> pkgs = list.getList();
+ if (pkgs != null) {
+ for (PackageInfo info : pkgs) {
+ requestingPackages.add(info.packageName);
}
}
+ } catch(RemoteException e) {
+ Log.e(TAG, "Cannot reach packagemanager", e);
}
- return packages;
+ return requestingPackages;
}
private boolean hasAccess(String pkg) {
diff --git a/src/com/android/settings/notification/ZenModeSettings.java b/src/com/android/settings/notification/ZenModeSettings.java
index 854857a..0d9c78f 100644
--- a/src/com/android/settings/notification/ZenModeSettings.java
+++ b/src/com/android/settings/notification/ZenModeSettings.java
@@ -179,8 +179,6 @@
private static ManagedServiceSettings.Config getConditionProviderConfig() {
final ManagedServiceSettings.Config c = new ManagedServiceSettings.Config();
c.tag = TAG;
- c.setting = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES;
- c.secondarySetting = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
c.intentAction = ConditionProviderService.SERVICE_INTERFACE;
c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
c.noun = "condition provider";
diff --git a/src/com/android/settings/utils/ManagedServiceSettings.java b/src/com/android/settings/utils/ManagedServiceSettings.java
index 6e3841d..d76520f 100644
--- a/src/com/android/settings/utils/ManagedServiceSettings.java
+++ b/src/com/android/settings/utils/ManagedServiceSettings.java
@@ -20,6 +20,8 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Fragment;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
@@ -54,6 +56,7 @@
private PackageManager mPm;
private DevicePolicyManager mDpm;
protected ServiceListing mServiceListing;
+ protected NotificationManager mNm;
abstract protected Config getConfig();
@@ -68,6 +71,7 @@
mContext = getActivity();
mPm = mContext.getPackageManager();
mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mServiceListing = new ServiceListing(mContext, mConfig);
mServiceListing.addCallback(new ServiceListing.Callback() {
@Override
@@ -124,7 +128,7 @@
} else {
pref.setTitle(summary);
}
- pref.setChecked(mServiceListing.isEnabled(cn));
+ pref.setChecked(isServiceEnabled(cn));
if (managedProfileId != UserHandle.USER_NULL
&& !mDpm.isNotificationListenerServicePermitted(
service.packageName, managedProfileId)) {
@@ -148,6 +152,10 @@
return UserHandle.myUserId();
}
+ protected boolean isServiceEnabled(ComponentName cn) {
+ return mServiceListing.isEnabled(cn);
+ }
+
protected boolean setEnabled(ComponentName service, String title, boolean enable) {
if (!enable) {
// the simple version: disabling
@@ -165,6 +173,10 @@
}
}
+ protected void enable(ComponentName service) {
+ mServiceListing.setEnabled(service, true);
+ }
+
public static class ScaryWarningDialogFragment extends InstrumentedDialogFragment {
static final String KEY_COMPONENT = "c";
static final String KEY_LABEL = "l";
@@ -202,7 +214,7 @@
.setPositiveButton(R.string.allow,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
- parent.mServiceListing.setEnabled(cn, true);
+ parent.enable(cn);
}
})
.setNegativeButton(R.string.deny,
diff --git a/src/com/android/settings/utils/ZenServiceListing.java b/src/com/android/settings/utils/ZenServiceListing.java
index 167b066..40a4f34 100644
--- a/src/com/android/settings/utils/ZenServiceListing.java
+++ b/src/com/android/settings/utils/ZenServiceListing.java
@@ -16,6 +16,7 @@
package com.android.settings.utils;
import android.app.ActivityManager;
+import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -35,16 +36,16 @@
public class ZenServiceListing {
- private final ContentResolver mContentResolver;
private final Context mContext;
private final ManagedServiceSettings.Config mConfig;
private final Set<ServiceInfo> mApprovedServices = new ArraySet<ServiceInfo>();
private final List<Callback> mZenCallbacks = new ArrayList<>();
+ private final NotificationManager mNm;
public ZenServiceListing(Context context, ManagedServiceSettings.Config config) {
mContext = context;
mConfig = config;
- mContentResolver = context.getContentResolver();
+ mNm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
public ServiceInfo findService(final ComponentName cn) {
@@ -67,23 +68,18 @@
public void reloadApprovedServices() {
mApprovedServices.clear();
- String[] settings = {mConfig.setting, mConfig.secondarySetting};
- for (String setting : settings) {
- if (!TextUtils.isEmpty(setting)) {
- final String flat = Settings.Secure.getString(mContentResolver, setting);
- if (!TextUtils.isEmpty(flat)) {
- final List<String> names = Arrays.asList(flat.split(":"));
- List<ServiceInfo> services = new ArrayList<>();
- getServices(mConfig, services, mContext.getPackageManager());
- for (ServiceInfo service : services) {
- if (matchesApprovedPackage(names, service.getComponentName())) {
- mApprovedServices.add(service);
- }
- }
- }
+ List<String> enabledNotificationListenerPkgs = mNm.getEnabledNotificationListenerPackages();
+ List<ServiceInfo> services = new ArrayList<>();
+ getServices(mConfig, services, mContext.getPackageManager());
+ for (ServiceInfo service : services) {
+ final String servicePackage = service.getComponentName().getPackageName();
+ if (mNm.isNotificationPolicyAccessGrantedForPackage(servicePackage)
+ || enabledNotificationListenerPkgs.contains(servicePackage)) {
+ mApprovedServices.add(service);
}
}
+
if (!mApprovedServices.isEmpty()) {
for (Callback callback : mZenCallbacks) {
callback.onServicesReloaded(mApprovedServices);
@@ -91,25 +87,6 @@
}
}
- // Setting could contain: the component name of the condition provider, the package name of
- // the condition provider, the component name of the notification listener.
- private boolean matchesApprovedPackage(List<String> approved, ComponentName serviceOwner) {
- String flatCn = serviceOwner.flattenToString();
- if (approved.contains(flatCn) || approved.contains(serviceOwner.getPackageName())) {
- return true;
- }
- for (String entry : approved) {
- if (!TextUtils.isEmpty(entry)) {
- ComponentName approvedComponent = ComponentName.unflattenFromString(entry);
- if (approvedComponent != null && approvedComponent.getPackageName().equals(
- serviceOwner.getPackageName())) {
- return true;
- }
- }
- }
- return false;
- }
-
private static int getServices(ManagedServiceSettings.Config c, List<ServiceInfo> list,
PackageManager pm) {
int services = 0;
diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPickerTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPickerTest.java
deleted file mode 100644
index a60b43c..0000000
--- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPickerTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2017 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.applications.defaultapps;
-
-
-import android.app.Activity;
-import android.content.Context;
-import android.os.UserManager;
-import android.provider.Settings;
-
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-import com.android.settings.TestConfig;
-import com.android.settings.applications.PackageManagerWrapper;
-
-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;
-import org.robolectric.annotation.Config;
-import org.robolectric.util.ReflectionHelpers;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
-public class DefaultNotificationAssistantPickerTest {
-
- private static final String TEST_APP_KEY = "com.android.settings/PickerTest";
-
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private Activity mActivity;
- @Mock
- private UserManager mUserManager;
- @Mock
- private PackageManagerWrapper mPackageManager;
-
- private DefaultNotificationAssistantPicker mPicker;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mActivity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
-
- mPicker = spy(new DefaultNotificationAssistantPicker());
- mPicker.onAttach((Context) mActivity);
-
- ReflectionHelpers.setField(mPicker, "mPm", mPackageManager);
- doReturn(RuntimeEnvironment.application).when(mPicker).getContext();
- }
-
- @Test
- public void setDefaultAppKey_shouldUpdateDefault() {
- mPicker.setDefaultKey(TEST_APP_KEY);
-
- assertThat(mPicker.getDefaultKey()).isEqualTo(TEST_APP_KEY);
- }
-
- @Test
- public void getDefaultAppKey_shouldReturnDefault() {
- Settings.Secure.putString(RuntimeEnvironment.application.getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT,
- TEST_APP_KEY);
-
- assertThat(mPicker.getDefaultKey()).isEqualTo(TEST_APP_KEY);
- }
-}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothFilesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothFilesPreferenceControllerTest.java
new file mode 100644
index 0000000..7713aaf
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothFilesPreferenceControllerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 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.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.preference.Preference;
+import android.text.TextUtils;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BluetoothFilesPreferenceControllerTest {
+ private Context mContext;
+ private BluetoothFilesPreferenceController mController;
+ private Preference mPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+ mController = new BluetoothFilesPreferenceController(mContext);
+ mPreference = new Preference(mContext);
+ mPreference.setKey(BluetoothFilesPreferenceController.KEY_RECEIVED_FILES);
+ }
+
+ @Test
+ public void testHandlePreferenceTreeClick_sendBroadcast() {
+ mController.handlePreferenceTreeClick(mPreference);
+
+ final Intent intent = ShadowApplication.getInstance().getNextStartedActivity();
+ assertThat(intent).isNotNull();
+ assertThat(intent.getAction()).isEqualTo(
+ BluetoothFilesPreferenceController.ACTION_OPEN_FILES);
+
+ final Bundle bundle = intent.getExtras();
+ assertThat(bundle.getInt(BluetoothFilesPreferenceController.EXTRA_DIRECTION)).isEqualTo(1);
+ assertThat(bundle.getBoolean(
+ BluetoothFilesPreferenceController.EXTRA_SHOW_ALL_FILES)).isTrue();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/StorageProfileFragmentTest.java b/tests/robotests/src/com/android/settings/deviceinfo/StorageProfileFragmentTest.java
index 44e060f..b0f464c 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/StorageProfileFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/StorageProfileFragmentTest.java
@@ -43,7 +43,7 @@
private ArgumentCaptor<SparseArray<StorageAsyncLoader.AppsStorageResult>> mCaptor;
@Test
- public void verifyAppSizesAreZeroedOut() {
+ public void verifyAppSizesAreNotZeroedOut() {
StorageItemPreferenceController controller = mock(StorageItemPreferenceController.class);
StorageProfileFragment fragment = new StorageProfileFragment();
StorageAsyncLoader.AppsStorageResult result = new StorageAsyncLoader.AppsStorageResult();
@@ -62,10 +62,10 @@
verify(controller).onLoadFinished(mCaptor.capture(), anyInt());
StorageAsyncLoader.AppsStorageResult extractedResult = mCaptor.getValue().get(0);
- assertThat(extractedResult.musicAppsSize).isEqualTo(0);
- assertThat(extractedResult.videoAppsSize).isEqualTo(0);
- assertThat(extractedResult.otherAppsSize).isEqualTo(0);
- assertThat(extractedResult.gamesSize).isEqualTo(0);
+ assertThat(extractedResult.musicAppsSize).isEqualTo(100);
+ assertThat(extractedResult.videoAppsSize).isEqualTo(400);
+ assertThat(extractedResult.otherAppsSize).isEqualTo(200);
+ assertThat(extractedResult.gamesSize).isEqualTo(300);
assertThat(extractedResult.externalStats.audioBytes).isEqualTo(1);
assertThat(extractedResult.externalStats.videoBytes).isEqualTo(2);
assertThat(extractedResult.externalStats.imageBytes).isEqualTo(3);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicyTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicyTest.java
index d5bd53b..169cba8 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicyTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyDetectionPolicyTest.java
@@ -44,7 +44,9 @@
+ ",wakelock_enabled=false"
+ ",wakelock_threshold=3000"
+ ",wakeup_alarm_enabled=true"
- + ",wakeup_alarm_threshold=100";
+ + ",wakeup_alarm_threshold=100"
+ + ",bluetooth_scan_enabled=true"
+ + ",bluetooth_scan_threshold=2000";
private Context mContext;
private KeyValueListParserWrapper mKeyValueListParserWrapper;
@@ -64,6 +66,8 @@
assertThat(anomalyDetectionPolicy.wakeLockThreshold).isEqualTo(3000);
assertThat(anomalyDetectionPolicy.wakeupAlarmDetectionEnabled).isTrue();
assertThat(anomalyDetectionPolicy.wakeupAlarmThreshold).isEqualTo(100);
+ assertThat(anomalyDetectionPolicy.bluetoothScanDetectionEnabled).isTrue();
+ assertThat(anomalyDetectionPolicy.bluetoothScanThreshold).isEqualTo(2000);
}
@Test
@@ -82,6 +86,9 @@
assertThat(anomalyDetectionPolicy.wakeLockThreshold).isEqualTo(DateUtils.HOUR_IN_MILLIS);
assertThat(anomalyDetectionPolicy.wakeupAlarmDetectionEnabled).isTrue();
assertThat(anomalyDetectionPolicy.wakeupAlarmThreshold).isEqualTo(60);
+ assertThat(anomalyDetectionPolicy.bluetoothScanDetectionEnabled).isTrue();
+ assertThat(anomalyDetectionPolicy.bluetoothScanThreshold).isEqualTo(
+ 30 * DateUtils.MINUTE_IN_MILLIS);
}
@Test
@@ -92,6 +99,8 @@
Anomaly.AnomalyType.WAKE_LOCK)).isFalse();
assertThat(anomalyDetectionPolicy.isAnomalyDetectorEnabled(
Anomaly.AnomalyType.WAKEUP_ALARM)).isTrue();
+ assertThat(anomalyDetectionPolicy.isAnomalyDetectorEnabled(
+ Anomaly.AnomalyType.BLUETOOTH_SCAN)).isTrue();
}
private AnomalyDetectionPolicy createAnomalyPolicyWithConfig() {
@@ -104,6 +113,8 @@
AnomalyDetectionPolicy.KEY_WAKELOCK_DETECTION_ENABLED, true);
doReturn(true).when(mKeyValueListParserWrapper).getBoolean(
AnomalyDetectionPolicy.KEY_WAKEUP_ALARM_DETECTION_ENABLED, true);
+ doReturn(true).when(mKeyValueListParserWrapper).getBoolean(
+ AnomalyDetectionPolicy.KEY_BLUETOOTH_SCAN_DETECTION_ENABLED, true);
return new AnomalyDetectionPolicy(mContext, mKeyValueListParserWrapper);
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyLoaderTest.java
index 8119168..48749d5 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/AnomalyLoaderTest.java
@@ -30,6 +30,7 @@
import android.os.UserManager;
import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.fuelgauge.anomaly.checker.BluetoothScanAnomalyDetector;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector;
@@ -62,13 +63,17 @@
@Mock
private WakeupAlarmAnomalyDetector mWakeupAlarmAnomalyDetector;
@Mock
+ private BluetoothScanAnomalyDetector mBluetoothScanAnomalyDetector;
+ @Mock
private AnomalyDetectionPolicy mAnomalyDetectionPolicy;
@Mock
private UserManager mUserManager;
private Anomaly mWakeLockAnomaly;
private Anomaly mWakeupAlarmAnomaly;
+ private Anomaly mBluetoothScanAnomaly;
private List<Anomaly> mWakeLockAnomalies;
private List<Anomaly> mWakeupAlarmAnomalies;
+ private List<Anomaly> mBluetoothScanAnomalies;
private AnomalyLoader mAnomalyLoader;
@Before
@@ -91,6 +96,12 @@
doReturn(mWakeupAlarmAnomalies).when(mWakeupAlarmAnomalyDetector).detectAnomalies(any(),
any());
+ mBluetoothScanAnomalies = new ArrayList<>();
+ mBluetoothScanAnomaly = createAnomaly(Anomaly.AnomalyType.BLUETOOTH_SCAN);
+ mBluetoothScanAnomalies.add(mBluetoothScanAnomaly);
+ doReturn(mBluetoothScanAnomalies).when(mBluetoothScanAnomalyDetector).detectAnomalies(any(),
+ any());
+
mAnomalyLoader = new AnomalyLoader(mContext, mBatteryStatsHelper, null,
mAnomalyDetectionPolicy);
mAnomalyLoader.mAnomalyUtils = spy(new AnomalyUtils(mContext));
@@ -102,10 +113,14 @@
Anomaly.AnomalyType.WAKE_LOCK);
doReturn(mWakeupAlarmAnomalyDetector).when(mAnomalyLoader.mAnomalyUtils).getAnomalyDetector(
Anomaly.AnomalyType.WAKEUP_ALARM);
+ doReturn(mBluetoothScanAnomalyDetector).when(
+ mAnomalyLoader.mAnomalyUtils).getAnomalyDetector(
+ Anomaly.AnomalyType.BLUETOOTH_SCAN);
List<Anomaly> anomalies = mAnomalyLoader.loadInBackground();
- assertThat(anomalies).containsExactly(mWakeLockAnomaly, mWakeupAlarmAnomaly);
+ assertThat(anomalies).containsExactly(mWakeLockAnomaly, mWakeupAlarmAnomaly,
+ mBluetoothScanAnomaly);
}
private Anomaly createAnomaly(@Anomaly.AnomalyType int type) {
@@ -121,6 +136,7 @@
public void testGenerateFakeData() {
List<Anomaly> anomalies = mAnomalyLoader.generateFakeData();
- assertThat(anomalies).containsExactly(mWakeLockAnomaly, mWakeupAlarmAnomaly);
+ assertThat(anomalies).containsExactly(mWakeLockAnomaly, mWakeupAlarmAnomaly,
+ mBluetoothScanAnomaly);
}
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/BluetoothScanAnomalyDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/BluetoothScanAnomalyDetectorTest.java
new file mode 100644
index 0000000..941e9cd
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/anomaly/checker/BluetoothScanAnomalyDetectorTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 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.anomaly.checker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.BatteryStats;
+import android.text.format.DateUtils;
+
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.TestConfig;
+import com.android.settings.fuelgauge.BatteryUtils;
+import com.android.settings.fuelgauge.anomaly.Anomaly;
+import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
+import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BluetoothScanAnomalyDetectorTest {
+ private static final String TARGET_PACKAGE_NAME = "com.android.app";
+ private static final int ANOMALY_UID = 111;
+ private static final int NORMAL_UID = 222;
+ private static final int TARGET_UID = 333;
+ private static final long ANOMALY_BLUETOOTH_SCANNING_TIME = DateUtils.HOUR_IN_MILLIS;
+ private static final long NORMAL_BLUETOOTH_SCANNING_TIME = DateUtils.MINUTE_IN_MILLIS;
+ @Mock
+ private BatteryStatsHelper mBatteryStatsHelper;
+ @Mock
+ private BatterySipper mAnomalySipper;
+ @Mock
+ private BatterySipper mNormalSipper;
+ @Mock
+ private BatterySipper mTargetSipper;
+ @Mock
+ private BatteryStats.Uid mAnomalyUid;
+ @Mock
+ private BatteryStats.Uid mNormalUid;
+ @Mock
+ private BatteryStats.Uid mTargetUid;
+ @Mock
+ private BatteryUtils mBatteryUtils;
+ @Mock
+ private AnomalyDetectionPolicy mPolicy;
+ @Mock
+ private AnomalyAction mAnomalyAction;
+
+ private BluetoothScanAnomalyDetector mBluetoothScanAnomalyDetector;
+ private Context mContext;
+ private List<BatterySipper> mUsageList;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ ReflectionHelpers.setField(mPolicy, "bluetoothScanThreshold",
+ 30 * DateUtils.MINUTE_IN_MILLIS);
+
+ mAnomalySipper.uidObj = mAnomalyUid;
+ doReturn(ANOMALY_UID).when(mAnomalyUid).getUid();
+ mNormalSipper.uidObj = mNormalUid;
+ doReturn(NORMAL_UID).when(mNormalUid).getUid();
+ mTargetSipper.uidObj = mTargetUid;
+ doReturn(TARGET_UID).when(mTargetUid).getUid();
+
+ mUsageList = new ArrayList<>();
+ mUsageList.add(mAnomalySipper);
+ mUsageList.add(mNormalSipper);
+ mUsageList.add(mTargetSipper);
+ doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList();
+
+ mBluetoothScanAnomalyDetector = spy(new BluetoothScanAnomalyDetector(mContext, mPolicy));
+ mBluetoothScanAnomalyDetector.mBatteryUtils = mBatteryUtils;
+ mBluetoothScanAnomalyDetector.mAnomalyAction = mAnomalyAction;
+ doReturn(false).when(mBatteryUtils).shouldHideSipper(any());
+ doReturn(true).when(mAnomalyAction).isActionActive(any());
+
+ doReturn(ANOMALY_BLUETOOTH_SCANNING_TIME).when(
+ mBluetoothScanAnomalyDetector).getBluetoothUnoptimizedBgTimeMs(eq(mAnomalyUid),
+ anyLong());
+ doReturn(ANOMALY_BLUETOOTH_SCANNING_TIME).when(
+ mBluetoothScanAnomalyDetector).getBluetoothUnoptimizedBgTimeMs(eq(mTargetUid),
+ anyLong());
+ doReturn(NORMAL_BLUETOOTH_SCANNING_TIME).when(
+ mBluetoothScanAnomalyDetector).getBluetoothUnoptimizedBgTimeMs(eq(mNormalUid),
+ anyLong());
+ }
+
+ @Test
+ public void testDetectAnomalies_containsAnomaly_detectIt() {
+ doReturn(-1).when(mBatteryUtils).getPackageUid(nullable(String.class));
+ final Anomaly anomaly = createBluetoothAnomaly(ANOMALY_UID);
+ final Anomaly targetAnomaly = createBluetoothAnomaly(TARGET_UID);
+
+ List<Anomaly> mAnomalies = mBluetoothScanAnomalyDetector.detectAnomalies(
+ mBatteryStatsHelper);
+
+ assertThat(mAnomalies).containsExactly(anomaly, targetAnomaly);
+ }
+
+ @Test
+ public void testDetectAnomalies_detectTargetAnomaly_detectIt() {
+ doReturn(TARGET_UID).when(mBatteryUtils).getPackageUid(TARGET_PACKAGE_NAME);
+ final Anomaly targetAnomaly = createBluetoothAnomaly(TARGET_UID);
+
+ List<Anomaly> mAnomalies = mBluetoothScanAnomalyDetector.detectAnomalies(
+ mBatteryStatsHelper, TARGET_PACKAGE_NAME);
+
+ assertThat(mAnomalies).containsExactly(targetAnomaly);
+
+ }
+
+ private Anomaly createBluetoothAnomaly(int uid) {
+ return new Anomaly.Builder()
+ .setUid(uid)
+ .setType(Anomaly.AnomalyType.BLUETOOTH_SCAN)
+ .build();
+ }
+
+}
diff --git a/tests/unit/src/com/android/settings/deviceinfo/storage/StorageAsyncLoaderTest.java b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageAsyncLoaderTest.java
index 79a4595..28bd861 100644
--- a/tests/unit/src/com/android/settings/deviceinfo/storage/StorageAsyncLoaderTest.java
+++ b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageAsyncLoaderTest.java
@@ -27,6 +27,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.support.test.filters.SmallTest;
@@ -74,7 +75,8 @@
MockitoAnnotations.initMocks(this);
mInfo = new ArrayList<>();
mLoader = new StorageAsyncLoader(mContext, mUserManager, "id", mSource, mPackageManager);
- when(mPackageManager.getInstalledApplicationsAsUser(anyInt(), anyInt())).thenReturn(mInfo);
+ when(mPackageManager.getInstalledApplicationsAsUser(eq(PRIMARY_USER_ID), anyInt()))
+ .thenReturn(mInfo);
UserInfo info = new UserInfo();
mUsers = new ArrayList<>();
mUsers.add(info);
@@ -174,13 +176,37 @@
info.category = ApplicationInfo.CATEGORY_UNDEFINED;
mInfo.add(info);
when(mSource.getStatsForPackage(anyString(), anyString(), any(UserHandle.class)))
- .thenThrow(new IllegalStateException());
+ .thenThrow(new NameNotFoundException());
SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground();
// Should not crash.
}
+ @Test
+ public void testPackageIsNotDoubleCounted() throws Exception {
+ UserInfo info = new UserInfo();
+ info.id = SECONDARY_USER_ID;
+ mUsers.add(info);
+ when(mSource.getExternalStorageStats(anyString(), eq(UserHandle.SYSTEM)))
+ .thenReturn(new StorageStatsSource.ExternalStorageStats(9, 2, 3, 4, 0));
+ when(mSource.getExternalStorageStats(anyString(), eq(new UserHandle(SECONDARY_USER_ID))))
+ .thenReturn(new StorageStatsSource.ExternalStorageStats(10, 3, 3, 4, 0));
+ addPackage(PACKAGE_NAME_1, 0, 1, 10, ApplicationInfo.CATEGORY_VIDEO);
+ ArrayList<ApplicationInfo> secondaryUserApps = new ArrayList<>();
+ ApplicationInfo appInfo = new ApplicationInfo();
+ appInfo.packageName = PACKAGE_NAME_1;
+ appInfo.category = ApplicationInfo.CATEGORY_VIDEO;
+ secondaryUserApps.add(appInfo);
+
+ SparseArray<StorageAsyncLoader.AppsStorageResult> result = mLoader.loadInBackground();
+
+ assertThat(result.size()).isEqualTo(2);
+ assertThat(result.get(PRIMARY_USER_ID).videoAppsSize).isEqualTo(11L);
+ // No code size for the second user.
+ assertThat(result.get(SECONDARY_USER_ID).videoAppsSize).isEqualTo(10L);
+ }
+
private ApplicationInfo addPackage(String packageName, long cacheSize, long codeSize,
long dataSize, int category) throws Exception {
StorageStatsSource.AppStorageStats storageStats =
@@ -196,4 +222,5 @@
mInfo.add(info);
return info;
}
+
}