1 /* 2 * Copyright (C) 2019 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.car.settings.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; 27 import android.content.pm.PackageManager.NameNotFoundException; 28 import android.content.pm.UserInfo; 29 import android.os.UserHandle; 30 import android.util.ArraySet; 31 import android.util.SparseArray; 32 33 import com.android.car.settings.common.AsyncLoader; 34 import com.android.car.settings.common.Logger; 35 import com.android.car.settings.profiles.ProfileHelper; 36 import com.android.settingslib.applications.StorageStatsSource; 37 38 import java.io.IOException; 39 import java.util.List; 40 41 /** 42 * {@link StorageAsyncLoader} is a Loader which loads categorized app information and external stats 43 * for all users. 44 * 45 * <p>Class is taken from {@link com.android.settings.deviceinfo.storage.StorageAsyncLoader} 46 */ 47 public class StorageAsyncLoader 48 extends AsyncLoader<SparseArray<StorageAsyncLoader.AppsStorageResult>> { 49 private static final Logger LOG = new Logger(StorageAsyncLoader.class); 50 51 private final StorageStatsSource mStatsManager; 52 private final PackageManager mPackageManager; 53 private final ProfileHelper mProfileHelper; 54 StorageAsyncLoader(Context context, StorageStatsSource source)55 public StorageAsyncLoader(Context context, StorageStatsSource source) { 56 super(context); 57 mStatsManager = source; 58 mPackageManager = context.getPackageManager(); 59 mProfileHelper = ProfileHelper.getInstance(context); 60 } 61 62 @Override loadInBackground()63 public SparseArray<AppsStorageResult> loadInBackground() { 64 ArraySet<String> seenPackages = new ArraySet<>(); 65 SparseArray<AppsStorageResult> result = new SparseArray<>(); 66 List<UserInfo> infos = mProfileHelper.getAllProfiles(); 67 for (int i = 0, userCount = infos.size(); i < userCount; i++) { 68 UserInfo info = infos.get(i); 69 result.put(info.id, getStorageResultForUser(info.id, seenPackages)); 70 } 71 return result; 72 } 73 getStorageResultForUser(int userId, ArraySet<String> seenPackages)74 private AppsStorageResult getStorageResultForUser(int userId, ArraySet<String> seenPackages) { 75 LOG.d("Loading apps"); 76 List<ApplicationInfo> applicationInfos = 77 mPackageManager.getInstalledApplicationsAsUser(/* getAllInstalledApplications= */ 0, 78 userId); 79 UserHandle myUser = UserHandle.of(userId); 80 long gameAppSize = 0; 81 long musicAppsSize = 0; 82 long videoAppsSize = 0; 83 long photosAppsSize = 0; 84 long otherAppsSize = 0; 85 for (int i = 0, size = applicationInfos.size(); i < size; i++) { 86 ApplicationInfo app = applicationInfos.get(i); 87 StorageStatsSource.AppStorageStats stats; 88 try { 89 stats = mStatsManager.getStatsForPackage(/* volumeUuid= */ null, app.packageName, 90 myUser); 91 } catch (NameNotFoundException | IOException e) { 92 // This may happen if the package was removed during our calculation. 93 LOG.w("App unexpectedly not found", e); 94 continue; 95 } 96 97 long dataSize = stats.getDataBytes(); 98 long cacheQuota = mStatsManager.getCacheQuotaBytes(/* volumeUuid= */ null, app.uid); 99 long cacheBytes = stats.getCacheBytes(); 100 long blamedSize = dataSize; 101 // Technically, we could show overages as freeable on the storage settings screen. 102 // If the app is using more cache than its quota, we would accidentally subtract the 103 // overage from the system size (because it shows up as unused) during our attribution. 104 // Thus, we cap the attribution at the quota size. 105 if (cacheQuota < cacheBytes) { 106 blamedSize = blamedSize - cacheBytes + cacheQuota; 107 } 108 109 // This isn't quite right because it slams the first user by user id with the whole code 110 // size, but this ensures that we count all apps seen once. 111 if (!seenPackages.contains(app.packageName)) { 112 blamedSize += stats.getCodeBytes(); 113 seenPackages.add(app.packageName); 114 } 115 116 switch (app.category) { 117 case CATEGORY_GAME: 118 gameAppSize += blamedSize; 119 break; 120 case CATEGORY_AUDIO: 121 musicAppsSize += blamedSize; 122 break; 123 case CATEGORY_VIDEO: 124 videoAppsSize += blamedSize; 125 break; 126 case CATEGORY_IMAGE: 127 photosAppsSize += blamedSize; 128 break; 129 default: 130 // The deprecated game flag does not set the category. 131 if ((app.flags & ApplicationInfo.FLAG_IS_GAME) != 0) { 132 gameAppSize += blamedSize; 133 break; 134 } 135 otherAppsSize += blamedSize; 136 break; 137 } 138 } 139 140 AppsStorageResult result = new AppsStorageResult(gameAppSize, musicAppsSize, photosAppsSize, 141 videoAppsSize, otherAppsSize); 142 143 LOG.d("Loading external stats"); 144 try { 145 result.mStorageStats = mStatsManager.getExternalStorageStats(null, 146 UserHandle.of(userId)); 147 } catch (IOException e) { 148 LOG.w("External stats not loaded" + e); 149 } 150 LOG.d("Obtaining result completed"); 151 return result; 152 } 153 154 /** 155 * Class to hold the result for different categories for storage. 156 */ 157 public static class AppsStorageResult { 158 private final long mGamesSize; 159 private final long mMusicAppsSize; 160 private final long mPhotosAppsSize; 161 private final long mVideoAppsSize; 162 private final long mOtherAppsSize; 163 private long mCacheSize; 164 private StorageStatsSource.ExternalStorageStats mStorageStats; 165 AppsStorageResult(long gamesSize, long musicAppsSize, long photosAppsSize, long videoAppsSize, long otherAppsSize)166 AppsStorageResult(long gamesSize, long musicAppsSize, long photosAppsSize, 167 long videoAppsSize, long otherAppsSize) { 168 mGamesSize = gamesSize; 169 mMusicAppsSize = musicAppsSize; 170 mPhotosAppsSize = photosAppsSize; 171 mVideoAppsSize = videoAppsSize; 172 mOtherAppsSize = otherAppsSize; 173 } 174 175 /** 176 * Returns the size in bytes used by the applications of category {@link CATEGORY_GAME}. 177 */ getGamesSize()178 public long getGamesSize() { 179 return mGamesSize; 180 } 181 182 /** 183 * Returns the size in bytes used by the applications of category {@link CATEGORY_AUDIO}. 184 */ getMusicAppsSize()185 public long getMusicAppsSize() { 186 return mMusicAppsSize; 187 } 188 189 /** 190 * Returns the size in bytes used by the applications of category {@link CATEGORY_IMAGE}. 191 */ getPhotosAppsSize()192 public long getPhotosAppsSize() { 193 return mPhotosAppsSize; 194 } 195 196 /** 197 * Returns the size in bytes used by the applications of category {@link CATEGORY_VIDEO}. 198 */ getVideoAppsSize()199 public long getVideoAppsSize() { 200 return mVideoAppsSize; 201 } 202 203 /** 204 * Returns the size in bytes used by the applications not assigned to one of the other 205 * categories. 206 */ getOtherAppsSize()207 public long getOtherAppsSize() { 208 return mOtherAppsSize; 209 } 210 211 /** 212 * Returns the cached size in bytes. 213 */ getCacheSize()214 public long getCacheSize() { 215 return mCacheSize; 216 } 217 218 /** 219 * Sets the storage cached size. 220 */ setCacheSize(long cacheSize)221 public void setCacheSize(long cacheSize) { 222 this.mCacheSize = cacheSize; 223 } 224 225 /** 226 * Returns the size in bytes for external storage of mounted device. 227 */ getExternalStats()228 public StorageStatsSource.ExternalStorageStats getExternalStats() { 229 return mStorageStats; 230 } 231 232 /** 233 * Sets the size in bytes for the external storage. 234 */ setExternalStats( StorageStatsSource.ExternalStorageStats externalStats)235 public void setExternalStats( 236 StorageStatsSource.ExternalStorageStats externalStats) { 237 this.mStorageStats = externalStats; 238 } 239 } 240 } 241