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 android.app.usage;
18 
19 import static android.os.storage.StorageManager.convert;
20 
21 import android.annotation.BytesLong;
22 import android.annotation.NonNull;
23 import android.annotation.RequiresPermission;
24 import android.annotation.SystemService;
25 import android.annotation.TestApi;
26 import android.annotation.WorkerThread;
27 import android.content.Context;
28 import android.content.pm.ApplicationInfo;
29 import android.content.pm.PackageInfo;
30 import android.content.pm.PackageManager;
31 import android.content.pm.ParceledListSlice;
32 import android.os.ParcelableException;
33 import android.os.RemoteException;
34 import android.os.UserHandle;
35 import android.os.storage.CrateInfo;
36 import android.os.storage.StorageManager;
37 
38 import com.android.internal.util.Preconditions;
39 
40 import java.io.File;
41 import java.io.IOException;
42 import java.util.Collection;
43 import java.util.Objects;
44 import java.util.UUID;
45 
46 /**
47  * Access to detailed storage statistics. This provides a summary of how apps,
48  * users, and external/shared storage is utilizing disk space.
49  * <p class="note">
50  * Note: no permissions are required when calling these APIs for your own
51  * package or UID. However, requesting details for any other package requires
52  * the {@code android.Manifest.permission#PACKAGE_USAGE_STATS} permission, which
53  * is a system-level permission that will not be granted to normal apps.
54  * Declaring that permission expresses your intention to use this API and an end
55  * user can then choose to grant this permission through the Settings
56  * application.
57  * </p>
58  */
59 @SystemService(Context.STORAGE_STATS_SERVICE)
60 public class StorageStatsManager {
61     private final Context mContext;
62     private final IStorageStatsManager mService;
63 
64     /** {@hide} */
StorageStatsManager(Context context, IStorageStatsManager service)65     public StorageStatsManager(Context context, IStorageStatsManager service) {
66         mContext = Objects.requireNonNull(context);
67         mService = Objects.requireNonNull(service);
68     }
69 
70     /** {@hide} */
71     @TestApi
isQuotaSupported(@onNull UUID storageUuid)72     public boolean isQuotaSupported(@NonNull UUID storageUuid) {
73         try {
74             return mService.isQuotaSupported(convert(storageUuid), mContext.getOpPackageName());
75         } catch (RemoteException e) {
76             throw e.rethrowFromSystemServer();
77         }
78     }
79 
80     /** @removed */
81     @Deprecated
isQuotaSupported(String uuid)82     public boolean isQuotaSupported(String uuid) {
83         return isQuotaSupported(convert(uuid));
84     }
85 
86     /** {@hide} */
87     @TestApi
isReservedSupported(@onNull UUID storageUuid)88     public boolean isReservedSupported(@NonNull UUID storageUuid) {
89         try {
90             return mService.isReservedSupported(convert(storageUuid), mContext.getOpPackageName());
91         } catch (RemoteException e) {
92             throw e.rethrowFromSystemServer();
93         }
94     }
95 
96     /**
97      * Return the total size of the underlying physical media that is hosting
98      * this storage volume.
99      * <p>
100      * This value is best suited for visual display to end users, since it's
101      * designed to reflect the total storage size advertised in a retail
102      * environment.
103      * <p>
104      * Apps making logical decisions about disk space should always use
105      * {@link File#getTotalSpace()} instead of this value.
106      *
107      * @param storageUuid the UUID of the storage volume you're interested in,
108      *            such as {@link StorageManager#UUID_DEFAULT}.
109      * @throws IOException when the storage device isn't present.
110      */
111     @WorkerThread
getTotalBytes(@onNull UUID storageUuid)112     public @BytesLong long getTotalBytes(@NonNull UUID storageUuid) throws IOException {
113         try {
114             return mService.getTotalBytes(convert(storageUuid), mContext.getOpPackageName());
115         } catch (ParcelableException e) {
116             e.maybeRethrow(IOException.class);
117             throw new RuntimeException(e);
118         } catch (RemoteException e) {
119             throw e.rethrowFromSystemServer();
120         }
121     }
122 
123     /** @removed */
124     @Deprecated
getTotalBytes(String uuid)125     public long getTotalBytes(String uuid) throws IOException {
126         return getTotalBytes(convert(uuid));
127     }
128 
129     /**
130      * Return the free space on the requested storage volume.
131      * <p>
132      * This value is best suited for visual display to end users, since it's
133      * designed to reflect both unused space <em>and</em> and cached space that
134      * could be reclaimed by the system.
135      * <p>
136      * Apps making logical decisions about disk space should always use
137      * {@link StorageManager#getAllocatableBytes(UUID)} instead of this value.
138      *
139      * @param storageUuid the UUID of the storage volume you're interested in,
140      *            such as {@link StorageManager#UUID_DEFAULT}.
141      * @throws IOException when the storage device isn't present.
142      */
143     @WorkerThread
getFreeBytes(@onNull UUID storageUuid)144     public @BytesLong long getFreeBytes(@NonNull UUID storageUuid) throws IOException {
145         try {
146             return mService.getFreeBytes(convert(storageUuid), mContext.getOpPackageName());
147         } catch (ParcelableException e) {
148             e.maybeRethrow(IOException.class);
149             throw new RuntimeException(e);
150         } catch (RemoteException e) {
151             throw e.rethrowFromSystemServer();
152         }
153     }
154 
155     /** @removed */
156     @Deprecated
getFreeBytes(String uuid)157     public long getFreeBytes(String uuid) throws IOException {
158         return getFreeBytes(convert(uuid));
159     }
160 
161     /** {@hide} */
getCacheBytes(@onNull UUID storageUuid)162     public @BytesLong long getCacheBytes(@NonNull UUID storageUuid) throws IOException {
163         try {
164             return mService.getCacheBytes(convert(storageUuid), mContext.getOpPackageName());
165         } catch (ParcelableException e) {
166             e.maybeRethrow(IOException.class);
167             throw new RuntimeException(e);
168         } catch (RemoteException e) {
169             throw e.rethrowFromSystemServer();
170         }
171     }
172 
173     /** {@hide} */
174     @Deprecated
getCacheBytes(String uuid)175     public long getCacheBytes(String uuid) throws IOException {
176         return getCacheBytes(convert(uuid));
177     }
178 
179     /**
180      * Return storage statistics for a specific package on the requested storage
181      * volume.
182      * <p class="note">
183      * Note: no permissions are required when calling this API for your own
184      * package. However, requesting details for any other package requires the
185      * {@code android.Manifest.permission#PACKAGE_USAGE_STATS} permission, which
186      * is a system-level permission that will not be granted to normal apps.
187      * Declaring that permission expresses your intention to use this API and an
188      * end user can then choose to grant this permission through the Settings
189      * application.
190      * </p>
191      * <p class="note">
192      * Note: if the requested package uses the {@code android:sharedUserId}
193      * manifest feature, this call will be forced into a slower manual
194      * calculation path. If possible, consider always using
195      * {@link #queryStatsForUid(UUID, int)}, which is typically faster.
196      * </p>
197      *
198      * @param storageUuid the UUID of the storage volume you're interested in,
199      *            such as {@link StorageManager#UUID_DEFAULT}.
200      * @param packageName the package name you're interested in.
201      * @param user the user you're interested in.
202      * @throws PackageManager.NameNotFoundException when the requested package
203      *             name isn't installed for the requested user.
204      * @throws IOException when the storage device isn't present.
205      * @see ApplicationInfo#storageUuid
206      * @see PackageInfo#packageName
207      */
208     @WorkerThread
queryStatsForPackage(@onNull UUID storageUuid, @NonNull String packageName, @NonNull UserHandle user)209     public @NonNull StorageStats queryStatsForPackage(@NonNull UUID storageUuid,
210             @NonNull String packageName, @NonNull UserHandle user)
211             throws PackageManager.NameNotFoundException, IOException {
212         try {
213             return mService.queryStatsForPackage(convert(storageUuid), packageName,
214                     user.getIdentifier(), mContext.getOpPackageName());
215         } catch (ParcelableException e) {
216             e.maybeRethrow(PackageManager.NameNotFoundException.class);
217             e.maybeRethrow(IOException.class);
218             throw new RuntimeException(e);
219         } catch (RemoteException e) {
220             throw e.rethrowFromSystemServer();
221         }
222     }
223 
224     /** @removed */
225     @Deprecated
queryStatsForPackage(String uuid, String packageName, UserHandle user)226     public StorageStats queryStatsForPackage(String uuid, String packageName,
227             UserHandle user) throws PackageManager.NameNotFoundException, IOException {
228         return queryStatsForPackage(convert(uuid), packageName, user);
229     }
230 
231     /**
232      * Return storage statistics for a specific UID on the requested storage
233      * volume.
234      * <p class="note">
235      * Note: no permissions are required when calling this API for your own UID.
236      * However, requesting details for any other UID requires the
237      * {@code android.Manifest.permission#PACKAGE_USAGE_STATS} permission, which
238      * is a system-level permission that will not be granted to normal apps.
239      * Declaring that permission expresses your intention to use this API and an
240      * end user can then choose to grant this permission through the Settings
241      * application.
242      * </p>
243      *
244      * @param storageUuid the UUID of the storage volume you're interested in,
245      *            such as {@link StorageManager#UUID_DEFAULT}.
246      * @param uid the UID you're interested in.
247      * @throws IOException when the storage device isn't present.
248      * @see ApplicationInfo#storageUuid
249      * @see ApplicationInfo#uid
250      */
251     @WorkerThread
queryStatsForUid(@onNull UUID storageUuid, int uid)252     public @NonNull StorageStats queryStatsForUid(@NonNull UUID storageUuid, int uid)
253             throws IOException {
254         try {
255             return mService.queryStatsForUid(convert(storageUuid), uid,
256                     mContext.getOpPackageName());
257         } catch (ParcelableException e) {
258             e.maybeRethrow(IOException.class);
259             throw new RuntimeException(e);
260         } catch (RemoteException e) {
261             throw e.rethrowFromSystemServer();
262         }
263     }
264 
265     /** @removed */
266     @Deprecated
queryStatsForUid(String uuid, int uid)267     public StorageStats queryStatsForUid(String uuid, int uid) throws IOException {
268         return queryStatsForUid(convert(uuid), uid);
269     }
270 
271     /**
272      * Return storage statistics for a specific {@link UserHandle} on the
273      * requested storage volume.
274      * <p class="note">
275      * Note: this API requires the
276      * {@code android.Manifest.permission#PACKAGE_USAGE_STATS} permission, which
277      * is a system-level permission that will not be granted to normal apps.
278      * Declaring that permission expresses your intention to use this API and an
279      * end user can then choose to grant this permission through the Settings
280      * application.
281      * </p>
282      *
283      * @param storageUuid the UUID of the storage volume you're interested in,
284      *            such as {@link StorageManager#UUID_DEFAULT}.
285      * @param user the user you're interested in.
286      * @throws IOException when the storage device isn't present.
287      * @see android.os.Process#myUserHandle()
288      */
289     @WorkerThread
queryStatsForUser(@onNull UUID storageUuid, @NonNull UserHandle user)290     public @NonNull StorageStats queryStatsForUser(@NonNull UUID storageUuid,
291             @NonNull UserHandle user) throws IOException {
292         try {
293             return mService.queryStatsForUser(convert(storageUuid), user.getIdentifier(),
294                     mContext.getOpPackageName());
295         } catch (ParcelableException e) {
296             e.maybeRethrow(IOException.class);
297             throw new RuntimeException(e);
298         } catch (RemoteException e) {
299             throw e.rethrowFromSystemServer();
300         }
301     }
302 
303     /** @removed */
304     @Deprecated
queryStatsForUser(String uuid, UserHandle user)305     public StorageStats queryStatsForUser(String uuid, UserHandle user) throws IOException {
306         return queryStatsForUser(convert(uuid), user);
307     }
308 
309     /**
310      * Return shared/external storage statistics for a specific
311      * {@link UserHandle} on the requested storage volume.
312      * <p class="note">
313      * Note: this API requires the
314      * {@code android.Manifest.permission#PACKAGE_USAGE_STATS} permission, which
315      * is a system-level permission that will not be granted to normal apps.
316      * Declaring that permission expresses your intention to use this API and an
317      * end user can then choose to grant this permission through the Settings
318      * application.
319      * </p>
320      *
321      * @param storageUuid the UUID of the storage volume you're interested in,
322      *            such as {@link StorageManager#UUID_DEFAULT}.
323      * @throws IOException when the storage device isn't present.
324      * @see android.os.Process#myUserHandle()
325      */
326     @WorkerThread
queryExternalStatsForUser(@onNull UUID storageUuid, @NonNull UserHandle user)327     public @NonNull ExternalStorageStats queryExternalStatsForUser(@NonNull UUID storageUuid,
328             @NonNull UserHandle user) throws IOException {
329         try {
330             return mService.queryExternalStatsForUser(convert(storageUuid), user.getIdentifier(),
331                     mContext.getOpPackageName());
332         } catch (ParcelableException e) {
333             e.maybeRethrow(IOException.class);
334             throw new RuntimeException(e);
335         } catch (RemoteException e) {
336             throw e.rethrowFromSystemServer();
337         }
338     }
339 
340     /** @removed */
341     @Deprecated
queryExternalStatsForUser(String uuid, UserHandle user)342     public ExternalStorageStats queryExternalStatsForUser(String uuid, UserHandle user)
343             throws IOException {
344         return queryExternalStatsForUser(convert(uuid), user);
345     }
346 
347     /** {@hide} */
getCacheQuotaBytes(String volumeUuid, int uid)348     public long getCacheQuotaBytes(String volumeUuid, int uid) {
349         try {
350             return mService.getCacheQuotaBytes(volumeUuid, uid, mContext.getOpPackageName());
351         } catch (RemoteException e) {
352             throw e.rethrowFromSystemServer();
353         }
354     }
355 
356     /**
357      * Return all of crate information for the specified storageUuid, packageName, and
358      * userHandle.
359      *
360      * @param storageUuid the UUID of the storage volume you're interested in,
361      *            such as {@link StorageManager#UUID_DEFAULT}.
362      * @param uid the uid you're interested in.
363      * @return the collection of crate information.
364      * @throws PackageManager.NameNotFoundException when the package name is not found.
365      * @throws IOException cause by IO, not support, or the other reasons.
366      * @hide
367      */
368     @TestApi
369     @WorkerThread
370     @NonNull
queryCratesForUid(@onNull UUID storageUuid, int uid)371     public Collection<CrateInfo> queryCratesForUid(@NonNull UUID storageUuid,
372             int uid) throws IOException, PackageManager.NameNotFoundException {
373         try {
374             ParceledListSlice<CrateInfo> crateInfoList =
375                     mService.queryCratesForUid(convert(storageUuid), uid,
376                             mContext.getOpPackageName());
377             return Objects.requireNonNull(crateInfoList).getList();
378         } catch (ParcelableException e) {
379             e.maybeRethrow(PackageManager.NameNotFoundException.class);
380             e.maybeRethrow(IOException.class);
381             throw new RuntimeException(e);
382         } catch (RemoteException e) {
383             throw e.rethrowFromSystemServer();
384         }
385     }
386 
387     /**
388      * Return all of crates information for the specified storageUuid, packageName, and
389      * userHandle.
390      *
391      * @param storageUuid the UUID of the storage volume you're interested in,
392      *            such as {@link StorageManager#UUID_DEFAULT}.
393      * @param packageName the package name you're interested in.
394      * @param user the user you're interested in.
395      * @return the collection of crate information.
396      * @throws PackageManager.NameNotFoundException when the package name is not found.
397      * @throws IOException cause by IO, not support, or the other reasons.
398      * @hide
399      */
400     @WorkerThread
401     @TestApi
402     @NonNull
queryCratesForPackage(@onNull UUID storageUuid, @NonNull String packageName, @NonNull UserHandle user)403     public Collection<CrateInfo> queryCratesForPackage(@NonNull UUID storageUuid,
404             @NonNull String packageName, @NonNull UserHandle user)
405             throws PackageManager.NameNotFoundException, IOException {
406         try {
407             ParceledListSlice<CrateInfo> crateInfoList =
408                     mService.queryCratesForPackage(convert(storageUuid), packageName,
409                             user.getIdentifier(), mContext.getOpPackageName());
410             return Objects.requireNonNull(crateInfoList).getList();
411         } catch (ParcelableException e) {
412             e.maybeRethrow(PackageManager.NameNotFoundException.class);
413             e.maybeRethrow(IOException.class);
414             throw new RuntimeException(e);
415         } catch (RemoteException e) {
416             throw e.rethrowFromSystemServer();
417         }
418     }
419 
420     /**
421      * Return all of crate information for the specified storageUuid, packageName, and
422      * userHandle.
423      *
424      * @param storageUuid the UUID of the storage volume you're interested in,
425      *            such as {@link StorageManager#UUID_DEFAULT}.
426      * @param user the user you're interested in.
427      * @return the collection of crate information.
428      * @throws PackageManager.NameNotFoundException when the package name is not found.
429      * @throws IOException cause by IO, not support, or the other reasons.
430      * @hide
431      */
432     @WorkerThread
433     @TestApi
434     @RequiresPermission(android.Manifest.permission.MANAGE_CRATES)
435     @NonNull
queryCratesForUser(@onNull UUID storageUuid, @NonNull UserHandle user)436     public Collection<CrateInfo> queryCratesForUser(@NonNull UUID storageUuid,
437             @NonNull UserHandle user) throws PackageManager.NameNotFoundException, IOException {
438         try {
439             ParceledListSlice<CrateInfo> crateInfoList =
440                     mService.queryCratesForUser(convert(storageUuid), user.getIdentifier(),
441                             mContext.getOpPackageName());
442             return Objects.requireNonNull(crateInfoList).getList();
443         } catch (ParcelableException e) {
444             e.maybeRethrow(PackageManager.NameNotFoundException.class);
445             e.maybeRethrow(IOException.class);
446             throw new RuntimeException(e);
447         } catch (RemoteException e) {
448             throw e.rethrowFromSystemServer();
449         }
450     }
451 }
452