• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.deviceinfo.storage;
18 
19 import static android.content.pm.ApplicationInfo.CATEGORY_AUDIO;
20 import static android.content.pm.ApplicationInfo.CATEGORY_GAME;
21 import static android.content.pm.ApplicationInfo.CATEGORY_IMAGE;
22 import static android.content.pm.ApplicationInfo.CATEGORY_VIDEO;
23 
24 import android.content.Context;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.PackageManager.NameNotFoundException;
27 import android.content.pm.UserInfo;
28 import android.os.UserHandle;
29 import android.os.UserManager;
30 import android.util.ArraySet;
31 import android.util.Log;
32 import android.util.SparseArray;
33 
34 import com.android.settingslib.applications.StorageStatsSource;
35 import com.android.settingslib.utils.AsyncLoader;
36 import com.android.settingslib.wrapper.PackageManagerWrapper;
37 
38 import java.io.IOException;
39 import java.util.Collections;
40 import java.util.Comparator;
41 import java.util.List;
42 
43 /**
44  * StorageAsyncLoader is a Loader which loads categorized app information and external stats for all
45  * users
46  */
47 public class StorageAsyncLoader
48         extends AsyncLoader<SparseArray<StorageAsyncLoader.AppsStorageResult>> {
49     private UserManager mUserManager;
50     private static final String TAG = "StorageAsyncLoader";
51 
52     private String mUuid;
53     private StorageStatsSource mStatsManager;
54     private PackageManagerWrapper mPackageManager;
55     private ArraySet<String> mSeenPackages;
56 
StorageAsyncLoader(Context context, UserManager userManager, String uuid, StorageStatsSource source, PackageManagerWrapper pm)57     public StorageAsyncLoader(Context context, UserManager userManager,
58             String uuid, StorageStatsSource source, PackageManagerWrapper pm) {
59         super(context);
60         mUserManager = userManager;
61         mUuid = uuid;
62         mStatsManager = source;
63         mPackageManager = pm;
64     }
65 
66     @Override
loadInBackground()67     public SparseArray<AppsStorageResult> loadInBackground() {
68         return loadApps();
69     }
70 
loadApps()71     private SparseArray<AppsStorageResult> loadApps() {
72         mSeenPackages = new ArraySet<>();
73         SparseArray<AppsStorageResult> result = new SparseArray<>();
74         List<UserInfo> infos = mUserManager.getUsers();
75         // Sort the users by user id ascending.
76         Collections.sort(
77                 infos,
78                 new Comparator<UserInfo>() {
79                     @Override
80                     public int compare(UserInfo userInfo, UserInfo otherUser) {
81                         return Integer.compare(userInfo.id, otherUser.id);
82                     }
83                 });
84         for (int i = 0, userCount = infos.size(); i < userCount; i++) {
85             UserInfo info = infos.get(i);
86             result.put(info.id, getStorageResultForUser(info.id));
87         }
88         return result;
89     }
90 
getStorageResultForUser(int userId)91     private AppsStorageResult getStorageResultForUser(int userId) {
92         Log.d(TAG, "Loading apps");
93         List<ApplicationInfo> applicationInfos =
94                 mPackageManager.getInstalledApplicationsAsUser(0, userId);
95         AppsStorageResult result = new AppsStorageResult();
96         UserHandle myUser = UserHandle.of(userId);
97         for (int i = 0, size = applicationInfos.size(); i < size; i++) {
98             ApplicationInfo app = applicationInfos.get(i);
99 
100             StorageStatsSource.AppStorageStats stats;
101             try {
102                 stats = mStatsManager.getStatsForPackage(mUuid, app.packageName, myUser);
103             } catch (NameNotFoundException | IOException e) {
104                 // This may happen if the package was removed during our calculation.
105                 Log.w(TAG, "App unexpectedly not found", e);
106                 continue;
107             }
108 
109             final long dataSize = stats.getDataBytes();
110             final long cacheQuota = mStatsManager.getCacheQuotaBytes(mUuid, app.uid);
111             final long cacheBytes = stats.getCacheBytes();
112             long blamedSize = dataSize;
113             // Technically, we could overages as freeable on the storage settings screen.
114             // If the app is using more cache than its quota, we would accidentally subtract the
115             // overage from the system size (because it shows up as unused) during our attribution.
116             // Thus, we cap the attribution at the quota size.
117             if (cacheQuota < cacheBytes) {
118                 blamedSize = blamedSize - cacheBytes + cacheQuota;
119             }
120 
121             // This isn't quite right because it slams the first user by user id with the whole code
122             // size, but this ensures that we count all apps seen once.
123             if (!mSeenPackages.contains(app.packageName)) {
124                 blamedSize += stats.getCodeBytes();
125                 mSeenPackages.add(app.packageName);
126             }
127 
128             switch (app.category) {
129                 case CATEGORY_GAME:
130                     result.gamesSize += blamedSize;
131                     break;
132                 case CATEGORY_AUDIO:
133                     result.musicAppsSize += blamedSize;
134                     break;
135                 case CATEGORY_VIDEO:
136                     result.videoAppsSize += blamedSize;
137                     break;
138                 case CATEGORY_IMAGE:
139                     result.photosAppsSize += blamedSize;
140                     break;
141                 default:
142                     // The deprecated game flag does not set the category.
143                     if ((app.flags & ApplicationInfo.FLAG_IS_GAME) != 0) {
144                         result.gamesSize += blamedSize;
145                         break;
146                     }
147                     result.otherAppsSize += blamedSize;
148                     break;
149             }
150         }
151 
152         Log.d(TAG, "Loading external stats");
153         try {
154             result.externalStats = mStatsManager.getExternalStorageStats(mUuid,
155                     UserHandle.of(userId));
156         } catch (IOException e) {
157             Log.w(TAG, e);
158         }
159         Log.d(TAG, "Obtaining result completed");
160         return result;
161     }
162 
163     @Override
onDiscardResult(SparseArray<AppsStorageResult> result)164     protected void onDiscardResult(SparseArray<AppsStorageResult> result) {
165     }
166 
167     public static class AppsStorageResult {
168         public long gamesSize;
169         public long musicAppsSize;
170         public long photosAppsSize;
171         public long videoAppsSize;
172         public long otherAppsSize;
173         public long cacheSize;
174         public StorageStatsSource.ExternalStorageStats externalStats;
175     }
176 
177     /**
178      * ResultHandler defines a destination of data which can handle a result from
179      * {@link StorageAsyncLoader}.
180      */
181     public interface ResultHandler {
handleResult(SparseArray<AppsStorageResult> result)182         void handleResult(SparseArray<AppsStorageResult> result);
183     }
184 }
185