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