Merge "Provide more context for "App-level permissions""
diff --git a/src/com/android/settings/applications/DefaultAutoFillPreference.java b/src/com/android/settings/applications/DefaultAutoFillPreference.java
index 9ed2e19..d3b1b12 100644
--- a/src/com/android/settings/applications/DefaultAutoFillPreference.java
+++ b/src/com/android/settings/applications/DefaultAutoFillPreference.java
@@ -17,14 +17,12 @@
package com.android.settings.applications;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.os.Parcelable;
import android.provider.Settings;
import android.service.autofill.AutoFillService;
import android.service.autofill.AutoFillServiceInfo;
@@ -130,11 +128,7 @@
for (ResolveInfo resolveInfo : resolveInfos) {
ServiceInfo serviceInfo = resolveInfo.serviceInfo;
AutoFillServiceInfo info = new AutoFillServiceInfo(pm, serviceInfo);
- if (info.getParseError() == null) {
- infos.add(info);
- } else {
- Log.i(TAG, "Invalid AutoFillService " + serviceInfo + ": " + info.getParseError());
- }
+ infos.add(info);
}
return infos;
}
diff --git a/src/com/android/settings/backup/BackupSettingsActivity.java b/src/com/android/settings/backup/BackupSettingsActivity.java
index b195384..b376f6b 100644
--- a/src/com/android/settings/backup/BackupSettingsActivity.java
+++ b/src/com/android/settings/backup/BackupSettingsActivity.java
@@ -17,6 +17,8 @@
package com.android.settings.backup;
import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
@@ -41,8 +43,13 @@
Log.d(TAG,
"No manufacturer settings found, launching the backup settings directly");
}
+ Intent intent = backupHelper.getIntentForBackupSettings();
+ // enable the activity before launching it
+ getPackageManager().setComponentEnabledSetting(intent.getComponent(),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
+
// use startActivityForResult to let the activity check the caller signature
- startActivityForResult(backupHelper.getIntentForBackupSettings(), 1);
+ startActivityForResult(intent, 1);
finish();
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
diff --git a/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java b/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java
index 5ada965..6a0bdcf 100644
--- a/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java
+++ b/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java
@@ -148,20 +148,6 @@
editor.apply();
}
- static void persistDiscoveringTimestamp(final Context context) {
- // Load the shared preferences and edit it on a background
- // thread (but serialized!).
- QueuedWork.queue(new Runnable() {
- public void run() {
- SharedPreferences.Editor editor = getSharedPreferences(context).edit();
- editor.putLong(
- KEY_DISCOVERING_TIMESTAMP,
- System.currentTimeMillis());
- editor.apply();
- }
- }, false);
- }
-
static boolean hasDockAutoConnectSetting(Context context, String addr) {
return getSharedPreferences(context).contains(KEY_DOCK_AUTO_CONNECT + addr);
}
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index 61f3e95..ea196ad 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -17,7 +17,9 @@
package com.android.settings.deviceinfo;
import android.content.Context;
+import android.content.Loader;
import android.os.Bundle;
+import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.provider.SearchIndexableResource;
@@ -27,6 +29,7 @@
import com.android.settings.R;
import com.android.settings.core.PreferenceController;
import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.deviceinfo.storage.AppsAsyncLoader;
import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
import com.android.settings.deviceinfo.storage.StorageSummaryDonutPreferenceController;
import com.android.settings.overlay.FeatureFactory;
@@ -42,6 +45,7 @@
public class StorageDashboardFragment extends DashboardFragment {
private static final String TAG = "StorageDashboardFrag";
+ private static final int APPS_JOB_ID = 0;
private VolumeInfo mVolume;
@@ -54,6 +58,12 @@
}
@Override
+ public void onResume() {
+ super.onResume();
+ getLoaderManager().initLoader(APPS_JOB_ID, Bundle.EMPTY, mPreferenceController);
+ }
+
+ @Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
diff --git a/src/com/android/settings/deviceinfo/storage/AppsAsyncLoader.java b/src/com/android/settings/deviceinfo/storage/AppsAsyncLoader.java
new file mode 100644
index 0000000..cbedb08
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/AppsAsyncLoader.java
@@ -0,0 +1,86 @@
+/*
+ * 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.deviceinfo.storage;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.util.ArraySet;
+
+import com.android.settings.applications.PackageManagerWrapper;
+import com.android.settings.utils.AsyncLoader;
+
+import java.util.List;
+
+/**
+ * AppsAsyncLoader is a Loader which loads app storage information and categories it by the app's
+ * specified categorization.
+ */
+public class AppsAsyncLoader extends AsyncLoader<AppsAsyncLoader.AppsStorageResult> {
+ private int mUserId;
+ private String mUuid;
+ private StorageStatsSource mStatsManager;
+ private PackageManagerWrapper mPackageManager;
+
+ public AppsAsyncLoader(Context context, int userId, String uuid, StorageStatsSource source,
+ PackageManagerWrapper pm) {
+ super(context);
+ mUserId = userId;
+ mUuid = uuid;
+ mStatsManager = source;
+ mPackageManager = pm;
+ }
+
+ @Override
+ public AppsStorageResult loadInBackground() {
+ return loadApps();
+ }
+
+ private AppsStorageResult loadApps() {
+ AppsStorageResult result = new AppsStorageResult();
+ ArraySet<Integer> seenUid = new ArraySet<>(); // some apps share a uid
+
+ List<ApplicationInfo> applicationInfos =
+ mPackageManager.getInstalledApplicationsAsUser(0, mUserId);
+ int size = applicationInfos.size();
+ for (int i = 0; i < size; i++) {
+ ApplicationInfo app = applicationInfos.get(i);
+ if (seenUid.contains(app.uid)) {
+ continue;
+ }
+ seenUid.add(app.uid);
+
+ StorageStatsSource.AppStorageStats stats = mStatsManager.getStatsForUid(mUuid, app.uid);
+ // Note: This omits cache intentionally -- we are not attributing it to the apps.
+ long appSize = stats.getCodeBytes() + stats.getDataBytes();
+ if (app.category == ApplicationInfo.CATEGORY_GAME) {
+ result.gamesSize += appSize;
+ } else {
+ result.otherAppsSize += appSize;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ protected void onDiscardResult(AppsStorageResult result) {
+ }
+
+ public static class AppsStorageResult {
+ public long gamesSize;
+ public long otherAppsSize;
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index 16dcd18..5437dcb 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -17,9 +17,12 @@
package com.android.settings.deviceinfo.storage;
import android.app.Fragment;
+import android.app.LoaderManager;
+import android.app.usage.StorageStatsManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
+import android.content.Loader;
import android.os.Bundle;
import android.os.Environment;
import android.os.UserHandle;
@@ -35,6 +38,7 @@
import com.android.settings.Settings;
import com.android.settings.Utils;
import com.android.settings.applications.ManageApplications;
+import com.android.settings.applications.PackageManagerWrapperImpl;
import com.android.settings.core.PreferenceController;
import com.android.settings.core.lifecycle.Lifecycle;
import com.android.settings.core.lifecycle.LifecycleObserver;
@@ -51,10 +55,12 @@
* categorization breakdown.
*/
public class StorageItemPreferenceController extends PreferenceController
- implements StorageMeasurement.MeasurementReceiver, LifecycleObserver, OnDestroy {
+ implements StorageMeasurement.MeasurementReceiver, LifecycleObserver, OnDestroy,
+ LoaderManager.LoaderCallbacks<AppsAsyncLoader.AppsStorageResult> {
private static final String TAG = "StorageItemPreference";
private static final String IMAGE_MIME_TYPE = "image/*";
+
@VisibleForTesting
static final String PHOTO_KEY = "pref_photos_videos";
@VisibleForTesting
@@ -179,15 +185,6 @@
mAudioPreference.setStorageSize(audioSize);
}
- if (mGamePreference != null) {
- mGamePreference.setStorageSize(0);
- }
-
- final long appSize = details.appsSize.get(mUserId);
- if (mAppPreference != null) {
- mAppPreference.setStorageSize(appSize);
- }
-
if (mSystemPreference != null) {
mSystemPreference.setStorageSize(mSystemSize);
}
@@ -216,6 +213,25 @@
mFilePreference = (StorageItemPreferenceAlternate) screen.findPreference(FILES_KEY);
}
+ @Override
+ public Loader<AppsAsyncLoader.AppsStorageResult> onCreateLoader(int id,
+ Bundle args) {
+ return new AppsAsyncLoader(mContext, UserHandle.myUserId(), mVolume.fsUuid,
+ new StorageStatsSource(mContext),
+ new PackageManagerWrapperImpl(mContext.getPackageManager()));
+ }
+
+ @Override
+ public void onLoadFinished(Loader<AppsAsyncLoader.AppsStorageResult> loader,
+ AppsAsyncLoader.AppsStorageResult data) {
+ mGamePreference.setStorageSize(data.gamesSize);
+ mAppPreference.setStorageSize(data.otherAppsSize);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<AppsAsyncLoader.AppsStorageResult> loader) {
+ }
+
/**
* Begins an asynchronous storage measurement task for the preferences.
*/
diff --git a/src/com/android/settings/deviceinfo/storage/StorageStatsSource.java b/src/com/android/settings/deviceinfo/storage/StorageStatsSource.java
index b6e03fb..98038fd 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageStatsSource.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageStatsSource.java
@@ -16,6 +16,7 @@
package com.android.settings.deviceinfo.storage;
+import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.os.UserHandle;
@@ -24,14 +25,19 @@
* StorageStatsSource wraps the StorageStatsManager for testability purposes.
*/
public class StorageStatsSource {
- private StorageStatsManager mSsm;
+ private StorageStatsManager mStorageStatsManager;
public StorageStatsSource(Context context) {
- mSsm = context.getSystemService(StorageStatsManager.class);
+ mStorageStatsManager = context.getSystemService(StorageStatsManager.class);
}
public ExternalStorageStats getExternalStorageStats(String volumeUuid, UserHandle user) {
- return new ExternalStorageStats(mSsm.queryExternalStatsForUser(volumeUuid, user));
+ return new ExternalStorageStats(
+ mStorageStatsManager.queryExternalStatsForUser(volumeUuid, user));
+ }
+
+ public AppStorageStats getStatsForUid(String volumeUuid, int uid) {
+ return new AppStorageStatsImpl(mStorageStatsManager.queryStatsForUid(volumeUuid, uid));
}
public static class ExternalStorageStats {
@@ -55,4 +61,30 @@
imageBytes = stats.getImageBytes();
}
}
+
+ public interface AppStorageStats {
+ long getCodeBytes();
+ long getDataBytes();
+ long getCacheBytes();
+ }
+
+ public static class AppStorageStatsImpl implements AppStorageStats {
+ private StorageStats mStats;
+
+ public AppStorageStatsImpl(StorageStats stats) {
+ mStats = stats;
+ }
+
+ public long getCodeBytes() {
+ return mStats.getCodeBytes();
+ }
+
+ public long getDataBytes() {
+ return mStats.getDataBytes();
+ }
+
+ public long getCacheBytes() {
+ return mStats.getCacheBytes();
+ }
+ }
}
diff --git a/src/com/android/settings/wifi/ConfigureWifiSettings.java b/src/com/android/settings/wifi/ConfigureWifiSettings.java
index 2b4743f..2a9c869 100644
--- a/src/com/android/settings/wifi/ConfigureWifiSettings.java
+++ b/src/com/android/settings/wifi/ConfigureWifiSettings.java
@@ -52,7 +52,7 @@
@Override
public void onAttach(Context context) {
super.onAttach(context);
- mProgressiveDisclosureMixin.setTileLimit(5);
+ mProgressiveDisclosureMixin.setTileLimit(4);
}
@Override
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
index f7baba3..cfec382 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
@@ -215,11 +215,15 @@
details.mediaSize.put(0, mediaSizes);
mController.setSystemSize(KILOBYTE * 6);
mController.onDetailsChanged(details);
+ AppsAsyncLoader.AppsStorageResult result = new AppsAsyncLoader.AppsStorageResult();
+ result.gamesSize = KILOBYTE * 8;
+ result.otherAppsSize = KILOBYTE * 9;
+ mController.onLoadFinished(null, result);
assertThat(audio.getSummary().toString()).isEqualTo("4.00KB");
assertThat(image.getSummary().toString()).isEqualTo("5.00KB");
- assertThat(games.getSummary().toString()).isEqualTo("0");
- assertThat(apps.getSummary().toString()).isEqualTo("1.00KB");
+ assertThat(games.getSummary().toString()).isEqualTo("8.00KB");
+ assertThat(apps.getSummary().toString()).isEqualTo("9.00KB");
assertThat(system.getSummary().toString()).isEqualTo("6.00KB");
assertThat(files.getSummary().toString()).isEqualTo("5.00KB");
}
diff --git a/tests/unit/src/com/android/settings/deviceinfo/storage/AppAsyncLoaderTest.java b/tests/unit/src/com/android/settings/deviceinfo/storage/AppAsyncLoaderTest.java
new file mode 100644
index 0000000..8d4dd2e
--- /dev/null
+++ b/tests/unit/src/com/android/settings/deviceinfo/storage/AppAsyncLoaderTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.deviceinfo.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+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 java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AppAsyncLoaderTest {
+ @Mock
+ private StorageStatsSource mSource;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+ @Mock
+ private PackageManagerWrapper mPackageManager;
+ ArrayList<ApplicationInfo> mInfo = new ArrayList<>();
+
+ private AppsAsyncLoader mLoader;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mInfo = new ArrayList<>();
+ mLoader = new AppsAsyncLoader(mContext, 1, "id", mSource, mPackageManager);
+ when(mPackageManager.getInstalledApplicationsAsUser(anyInt(), anyInt())).thenReturn(mInfo);
+ }
+
+ @Test
+ public void testLoadingApps() throws Exception {
+ addPackage(1001, 0, 1, 10, ApplicationInfo.CATEGORY_UNDEFINED);
+ addPackage(1002, 0, 100, 1000, ApplicationInfo.CATEGORY_UNDEFINED);
+
+ AppsAsyncLoader.AppsStorageResult result = mLoader.loadInBackground();
+
+ assertThat(result.gamesSize).isEqualTo(0L);
+ assertThat(result.otherAppsSize).isEqualTo(1111L);
+ }
+
+ @Test
+ public void testGamesAreFiltered() throws Exception {
+ addPackage(1001, 0, 1, 10, ApplicationInfo.CATEGORY_GAME);
+
+ AppsAsyncLoader.AppsStorageResult result = mLoader.loadInBackground();
+
+ assertThat(result.gamesSize).isEqualTo(11L);
+ assertThat(result.otherAppsSize).isEqualTo(0);
+ }
+
+ @Test
+ public void testDuplicateUidsAreSkipped() throws Exception {
+ addPackage(1001, 0, 1, 10, ApplicationInfo.CATEGORY_UNDEFINED);
+ addPackage(1001, 0, 1, 10, ApplicationInfo.CATEGORY_UNDEFINED);
+
+ AppsAsyncLoader.AppsStorageResult result = mLoader.loadInBackground();
+
+ assertThat(result.otherAppsSize).isEqualTo(11L);
+ }
+
+ @Test
+ public void testCacheIsIgnored() throws Exception {
+ addPackage(1001, 100, 1, 10, ApplicationInfo.CATEGORY_UNDEFINED);
+
+ AppsAsyncLoader.AppsStorageResult result = mLoader.loadInBackground();
+
+ assertThat(result.otherAppsSize).isEqualTo(11L);
+ }
+
+ private void addPackage(int uid, long cacheSize, long codeSize, long dataSize, int category) {
+ StorageStatsSource.AppStorageStats storageStats =
+ mock(StorageStatsSource.AppStorageStats.class);
+ when(storageStats.getCodeBytes()).thenReturn(codeSize);
+ when(storageStats.getDataBytes()).thenReturn(dataSize);
+ when(mSource.getStatsForUid(anyString(), eq(uid))).thenReturn(storageStats);
+
+ ApplicationInfo info = new ApplicationInfo();
+ info.uid = uid;
+ info.category = category;
+ mInfo.add(info);
+ }
+}