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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.usage.StorageStatsManager;
22 import android.car.userlib.CarUserManagerHelper;
23 import android.content.Context;
24 import android.os.Bundle;
25 import android.os.storage.StorageManager;
26 import android.os.storage.VolumeInfo;
27 import android.util.SparseArray;
28 
29 import androidx.loader.app.LoaderManager;
30 import androidx.loader.content.Loader;
31 
32 import com.android.settingslib.applications.StorageStatsSource;
33 import com.android.settingslib.deviceinfo.PrivateStorageInfo;
34 import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 /**
40  * Class to manage all the callbacks needed to calculate the total volume, storage used by each app
41  * category and notifying the listeners when the data is loaded.
42  */
43 public class StorageSettingsManager {
44 
45     /**
46      * Callback that is called once the volume of data is loaded for the mounted device.
47      */
48     public interface VolumeListener {
49         /**
50          * Called when the data is successfully loaded from {@link VolumeSizeCallback} and the
51          * total and used size for the mounted device is calculated from {@link AppsStorageResult}
52          */
onDataLoaded(SparseArray<StorageAsyncLoader.AppsStorageResult> result, long usedSizeBytes, long totalSizeBytes)53         void onDataLoaded(SparseArray<StorageAsyncLoader.AppsStorageResult> result,
54                 long usedSizeBytes, long totalSizeBytes);
55     }
56 
57     private static final int STORAGE_JOB_ID = 0;
58     private static final int VOLUME_SIZE_JOB_ID = 1;
59 
60     private final Context mContext;
61     private final VolumeInfo mVolumeInfo;
62 
63     private List<VolumeListener> mVolumeListeners = new ArrayList<>();
64     private PrivateStorageInfo mPrivateStorageInfo;
65     private SparseArray<StorageAsyncLoader.AppsStorageResult> mAppsStorageResultSparseArray;
66 
StorageSettingsManager(Context context, VolumeInfo volume)67     StorageSettingsManager(Context context, VolumeInfo volume) {
68         mContext = context;
69         mVolumeInfo = volume;
70     }
71 
72     /**
73      * Registers a listener that will be notified once the data is loaded.
74      */
registerListener(VolumeListener volumeListener)75     public void registerListener(VolumeListener volumeListener) {
76         if (!mVolumeListeners.contains(volumeListener)) {
77             mVolumeListeners.add(volumeListener);
78         }
79     }
80 
81     /**
82      * Unregisters the listener.
83      */
unregisterlistener(VolumeListener volumeListener)84     public void unregisterlistener(VolumeListener volumeListener) {
85         mVolumeListeners.remove(volumeListener);
86     }
87 
88     /**
89      * Start calculating the storage and volume.
90      */
startLoading(LoaderManager loaderManager)91     public void startLoading(LoaderManager loaderManager) {
92         loaderManager.restartLoader(STORAGE_JOB_ID, Bundle.EMPTY, new AppsStorageResult());
93         loaderManager.restartLoader(VOLUME_SIZE_JOB_ID, Bundle.EMPTY, new VolumeSizeCallback());
94     }
95 
onReceivedSizes()96     private void onReceivedSizes() {
97         if (mAppsStorageResultSparseArray != null && mPrivateStorageInfo != null) {
98             long privateUsedBytes = mPrivateStorageInfo.totalBytes - mPrivateStorageInfo.freeBytes;
99             for (VolumeListener listener : mVolumeListeners) {
100                 listener.onDataLoaded(mAppsStorageResultSparseArray, privateUsedBytes,
101                         mPrivateStorageInfo.totalBytes);
102             }
103         }
104     }
105 
106     /**
107      * Callback to get the storage volume information for the device that is mounted.
108      */
109     private class VolumeSizeCallback
110             implements LoaderManager.LoaderCallbacks<PrivateStorageInfo> {
111 
112         @Override
onCreateLoader(int id, Bundle args)113         public Loader<PrivateStorageInfo> onCreateLoader(int id, Bundle args) {
114             StorageManager sm = mContext.getSystemService(StorageManager.class);
115             StorageManagerVolumeProvider smvp = new StorageManagerVolumeProvider(sm);
116             StorageStatsManager stats = mContext.getSystemService(StorageStatsManager.class);
117             return new VolumeSizesLoader(mContext, smvp, stats, mVolumeInfo);
118         }
119 
120         @Override
onLoadFinished( Loader<PrivateStorageInfo> loader, PrivateStorageInfo privateStorageInfo)121         public void onLoadFinished(
122                 Loader<PrivateStorageInfo> loader, PrivateStorageInfo privateStorageInfo) {
123             if (privateStorageInfo == null) {
124                 return;
125             }
126             mPrivateStorageInfo = privateStorageInfo;
127             onReceivedSizes();
128         }
129 
130         @Override
onLoaderReset(Loader<PrivateStorageInfo> loader)131         public void onLoaderReset(Loader<PrivateStorageInfo> loader) {
132         }
133     }
134 
135     /**
136      * Callback to calculate how much space each category of applications is using.
137      */
138     private class AppsStorageResult implements
139             LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>> {
140 
141         @NonNull
142         @Override
onCreateLoader(int id, @Nullable Bundle args)143         public Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> onCreateLoader(int id,
144                 @Nullable Bundle args) {
145             return new StorageAsyncLoader(mContext, new CarUserManagerHelper(mContext),
146                     new StorageStatsSource(mContext));
147         }
148 
149         @Override
onLoadFinished( @onNull Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader, SparseArray<StorageAsyncLoader.AppsStorageResult> data)150         public void onLoadFinished(
151                 @NonNull Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader,
152                 SparseArray<StorageAsyncLoader.AppsStorageResult> data) {
153             mAppsStorageResultSparseArray = data;
154             onReceivedSizes();
155         }
156 
157         @Override
onLoaderReset( @onNull Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader)158         public void onLoaderReset(
159                 @NonNull Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader) {
160         }
161     }
162 }
163