Initial API shape for storage statistics.
This API is designed to provide both UID-level stats and overall
summary data for a given storage device, as identified by UUID.
The use of UID-level granularity might appear a bit clunky, but it
matches other usage statistics (such as network and battery), and it
allows us to implement it using an extremely fast quota kernel
feature.
A future CL will wire up the implementation to installd.
Test: builds, boots
Bug: 32206268
Change-Id: I7b51877682d0370c2402c19346f57809f0e7ac53
diff --git a/Android.mk b/Android.mk
index 7c9397f..8c0c640 100644
--- a/Android.mk
+++ b/Android.mk
@@ -108,6 +108,7 @@
core/java/android/app/backup/IFullBackupRestoreObserver.aidl \
core/java/android/app/backup/IRestoreObserver.aidl \
core/java/android/app/backup/IRestoreSession.aidl \
+ core/java/android/app/usage/IStorageStatsManager.aidl \
core/java/android/app/usage/IUsageStatsManager.aidl \
core/java/android/bluetooth/IBluetooth.aidl \
core/java/android/bluetooth/IBluetoothA2dp.aidl \
@@ -689,6 +690,8 @@
frameworks/base/core/java/android/service/notification/StatusBarNotification.aidl \
frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \
frameworks/base/core/java/android/speech/tts/Voice.aidl \
+ frameworks/base/core/java/android/app/usage/StorageStats.aidl \
+ frameworks/base/core/java/android/app/usage/StorageSummary.aidl \
frameworks/base/core/java/android/app/usage/UsageEvents.aidl \
frameworks/base/core/java/android/app/Notification.aidl \
frameworks/base/core/java/android/app/NotificationManager.aidl \
diff --git a/api/current.txt b/api/current.txt
index b017f9d..3fa06fb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6679,6 +6679,36 @@
method public abstract void onThresholdReached(int, java.lang.String);
}
+ public final class StorageStats implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getCacheBytes();
+ method public long getCacheQuotaBytes();
+ method public long getCodeBytes();
+ method public long getDataBytes();
+ method public int getUid();
+ method public java.lang.String getVolumeUuid();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.usage.StorageStats> CREATOR;
+ }
+
+ public class StorageStatsManager {
+ method public android.app.usage.StorageStats queryStatsForUid(java.lang.String, int);
+ method public android.app.usage.StorageSummary querySummary(java.lang.String);
+ }
+
+ public final class StorageSummary implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getFreeBytes();
+ method public long getSharedAudioBytes();
+ method public long getSharedImagesBytes();
+ method public long getSharedTotalBytes();
+ method public long getSharedVideoBytes();
+ method public long getTotalBytes();
+ method public java.lang.String getVolumeUuid();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.usage.StorageSummary> CREATOR;
+ }
+
public final class UsageEvents implements android.os.Parcelable {
method public int describeContents();
method public boolean getNextEvent(android.app.usage.UsageEvents.Event);
@@ -8415,6 +8445,7 @@
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
field public static final java.lang.String STORAGE_SERVICE = "storage";
+ field public static final java.lang.String STORAGE_STATS_SERVICE = "storagestats";
field public static final java.lang.String SYSTEM_HEALTH_SERVICE = "systemhealth";
field public static final java.lang.String TELECOM_SERVICE = "telecom";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
diff --git a/api/system-current.txt b/api/system-current.txt
index a5534de..7490325 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6992,6 +6992,36 @@
method public abstract void onThresholdReached(int, java.lang.String);
}
+ public final class StorageStats implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getCacheBytes();
+ method public long getCacheQuotaBytes();
+ method public long getCodeBytes();
+ method public long getDataBytes();
+ method public int getUid();
+ method public java.lang.String getVolumeUuid();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.usage.StorageStats> CREATOR;
+ }
+
+ public class StorageStatsManager {
+ method public android.app.usage.StorageStats queryStatsForUid(java.lang.String, int);
+ method public android.app.usage.StorageSummary querySummary(java.lang.String);
+ }
+
+ public final class StorageSummary implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getFreeBytes();
+ method public long getSharedAudioBytes();
+ method public long getSharedImagesBytes();
+ method public long getSharedTotalBytes();
+ method public long getSharedVideoBytes();
+ method public long getTotalBytes();
+ method public java.lang.String getVolumeUuid();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.usage.StorageSummary> CREATOR;
+ }
+
public final class UsageEvents implements android.os.Parcelable {
method public int describeContents();
method public boolean getNextEvent(android.app.usage.UsageEvents.Event);
@@ -8770,6 +8800,7 @@
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
field public static final java.lang.String STORAGE_SERVICE = "storage";
+ field public static final java.lang.String STORAGE_STATS_SERVICE = "storagestats";
field public static final java.lang.String SYSTEM_HEALTH_SERVICE = "systemhealth";
field public static final java.lang.String TELECOM_SERVICE = "telecom";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
diff --git a/api/test-current.txt b/api/test-current.txt
index 5db134a..8ef48ec 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6701,6 +6701,36 @@
method public abstract void onThresholdReached(int, java.lang.String);
}
+ public final class StorageStats implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getCacheBytes();
+ method public long getCacheQuotaBytes();
+ method public long getCodeBytes();
+ method public long getDataBytes();
+ method public int getUid();
+ method public java.lang.String getVolumeUuid();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.usage.StorageStats> CREATOR;
+ }
+
+ public class StorageStatsManager {
+ method public android.app.usage.StorageStats queryStatsForUid(java.lang.String, int);
+ method public android.app.usage.StorageSummary querySummary(java.lang.String);
+ }
+
+ public final class StorageSummary implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getFreeBytes();
+ method public long getSharedAudioBytes();
+ method public long getSharedImagesBytes();
+ method public long getSharedTotalBytes();
+ method public long getSharedVideoBytes();
+ method public long getTotalBytes();
+ method public java.lang.String getVolumeUuid();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.usage.StorageSummary> CREATOR;
+ }
+
public final class UsageEvents implements android.os.Parcelable {
method public int describeContents();
method public boolean getNextEvent(android.app.usage.UsageEvents.Event);
@@ -8439,6 +8469,7 @@
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String SHORTCUT_SERVICE = "shortcut";
field public static final java.lang.String STORAGE_SERVICE = "storage";
+ field public static final java.lang.String STORAGE_STATS_SERVICE = "storagestats";
field public static final java.lang.String SYSTEM_HEALTH_SERVICE = "systemhealth";
field public static final java.lang.String TELECOM_SERVICE = "telecom";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 6bddfba..9387019 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -23,8 +23,10 @@
import android.app.job.IJobScheduler;
import android.app.job.JobScheduler;
import android.app.trust.TrustManager;
+import android.app.usage.IStorageStatsManager;
import android.app.usage.IUsageStatsManager;
import android.app.usage.NetworkStatsManager;
+import android.app.usage.StorageStatsManager;
import android.app.usage.UsageStatsManager;
import android.appwidget.AppWidgetManager;
import android.bluetooth.BluetoothManager;
@@ -434,6 +436,15 @@
return new StorageManager(ctx, ctx.mMainThread.getHandler().getLooper());
}});
+ registerService(Context.STORAGE_STATS_SERVICE, StorageStatsManager.class,
+ new CachedServiceFetcher<StorageStatsManager>() {
+ @Override
+ public StorageStatsManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ IStorageStatsManager service = IStorageStatsManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.STORAGE_STATS_SERVICE));
+ return new StorageStatsManager(ctx, service);
+ }});
+
registerService(Context.TELEPHONY_SERVICE, TelephonyManager.class,
new CachedServiceFetcher<TelephonyManager>() {
@Override
diff --git a/core/java/android/app/usage/IStorageStatsManager.aidl b/core/java/android/app/usage/IStorageStatsManager.aidl
new file mode 100644
index 0000000..96125a1
--- /dev/null
+++ b/core/java/android/app/usage/IStorageStatsManager.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import android.app.usage.StorageStats;
+import android.app.usage.StorageSummary;
+
+/** {@hide} */
+interface IStorageStatsManager {
+ StorageStats queryStats(String volumeUuid, int uid, String callingPackage);
+ StorageSummary querySummary(String volumeUuid, String callingPackage);
+}
diff --git a/core/java/android/app/usage/StorageStats.aidl b/core/java/android/app/usage/StorageStats.aidl
new file mode 100644
index 0000000..fde6267
--- /dev/null
+++ b/core/java/android/app/usage/StorageStats.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+parcelable StorageStats;
diff --git a/core/java/android/app/usage/StorageStats.java b/core/java/android/app/usage/StorageStats.java
new file mode 100644
index 0000000..b2486d8
--- /dev/null
+++ b/core/java/android/app/usage/StorageStats.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Storage statistics for a single UID on a single storage volume.
+ * <p class="note">
+ * Note: multiple packages using the same {@code sharedUserId} in their manifest
+ * will be merged into a single UID.
+ * </p>
+ *
+ * @see StorageStatsManager
+ */
+public final class StorageStats implements Parcelable {
+ /** {@hide} */ public final String volumeUuid;
+ /** {@hide} */ public final int uid;
+
+ /** {@hide} */ public long codeBytes;
+ /** {@hide} */ public long dataBytes;
+ /** {@hide} */ public long cacheQuotaBytes;
+ /** {@hide} */ public long cacheBytes;
+
+ /**
+ * Return the UUID of the storage volume that these statistics refer to. The
+ * value {@code null} indicates the default internal storage.
+ */
+ public String getVolumeUuid() {
+ return volumeUuid;
+ }
+
+ /**
+ * Return the UID that these statistics refer to.
+ *
+ * @see ApplicationInfo#uid
+ * @see PackageManager#getPackagesForUid(int)
+ */
+ public int getUid() {
+ return uid;
+ }
+
+ /**
+ * Return the size of all code associated with {@link #getUid()} on
+ * {@link #getVolumeUuid()}. This includes {@code APK} files and optimized
+ * compiler output.
+ * <p>
+ * If the primary shared storage location is also hosted on
+ * {@link #getVolumeUuid()} then this includes files stored under
+ * {@link Context#getObbDir()}.
+ * <p>
+ * Code is shared between all users on a multiuser device.
+ */
+ public long getCodeBytes() {
+ return codeBytes;
+ }
+
+ /**
+ * Return the size of all data associated with {@link #getUid()} on
+ * {@link #getVolumeUuid()}. This includes files stored under
+ * {@link Context#getDataDir()}, {@link Context#getCacheDir()},
+ * {@link Context#getCodeCacheDir()}.
+ * <p>
+ * If the primary shared storage location is also hosted on
+ * {@link #getVolumeUuid()} then this includes files stored under
+ * {@link Context#getExternalFilesDir(String)} and
+ * {@link Context#getExternalCacheDir()}.
+ * <p>
+ * Data is isolated for each user on a multiuser device.
+ */
+ public long getDataBytes() {
+ return dataBytes;
+ }
+
+ /**
+ * Return the quota for cached data associated with {@link #getUid()} on
+ * {@link #getVolumeUuid()}. This quota value is calculated based on how
+ * frequently the user has interacted with the UID.
+ * <p>
+ * When clearing cached data, the system will first focus on packages whose
+ * cached data is larger than their allocated quota.
+ */
+ public long getCacheQuotaBytes() {
+ return cacheQuotaBytes;
+ }
+
+ /**
+ * Return the size of all cached data associated with {@link #getUid()} on
+ * {@link #getVolumeUuid()}. This includes files stored under
+ * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}.
+ * <p>
+ * If the primary shared storage location is also hosted on
+ * {@link #getVolumeUuid()} then this includes files stored under
+ * {@link Context#getExternalCacheDir()}.
+ * <p>
+ * Cached data is isolated for each user on a multiuser device.
+ */
+ public long getCacheBytes() {
+ return cacheBytes;
+ }
+
+ /** {@hide} */
+ public StorageStats(String uuid, int uid) {
+ this.volumeUuid = uuid;
+ this.uid = uid;
+ }
+
+ /** {@hide} */
+ public StorageStats(Parcel in) {
+ this.volumeUuid = in.readString();
+ this.uid = in.readInt();
+ this.codeBytes = in.readLong();
+ this.dataBytes = in.readLong();
+ this.cacheQuotaBytes = in.readLong();
+ this.cacheBytes = in.readLong();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(volumeUuid);
+ dest.writeInt(uid);
+ dest.writeLong(codeBytes);
+ dest.writeLong(dataBytes);
+ dest.writeLong(cacheQuotaBytes);
+ dest.writeLong(cacheBytes);
+ }
+
+ public static final Creator<StorageStats> CREATOR = new Creator<StorageStats>() {
+ @Override
+ public StorageStats createFromParcel(Parcel in) {
+ return new StorageStats(in);
+ }
+
+ @Override
+ public StorageStats[] newArray(int size) {
+ return new StorageStats[size];
+ }
+ };
+}
diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java
new file mode 100644
index 0000000..cdfbe0c
--- /dev/null
+++ b/core/java/android/app/usage/StorageStatsManager.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import android.annotation.WorkerThread;
+import android.content.Context;
+import android.os.RemoteException;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Provides access to detailed storage statistics.
+ * <p class="note">
+ * Note: this API requires the permission
+ * {@code android.permission.PACKAGE_USAGE_STATS}, which is a system-level
+ * permission that will not be granted to normal apps. However, declaring the
+ * permission expresses your intention to use this API and an end user can then
+ * choose to grant this permission through the Settings application.
+ * </p>
+ */
+public class StorageStatsManager {
+ private final Context mContext;
+ private final IStorageStatsManager mService;
+
+ /** {@hide} */
+ public StorageStatsManager(Context context, IStorageStatsManager service) {
+ mContext = Preconditions.checkNotNull(context);
+ mService = Preconditions.checkNotNull(service);
+ }
+
+ /**
+ * Return detailed statistics for the a specific UID on the requested
+ * storage volume.
+ * <p>
+ * This method may take several seconds to calculate the requested values,
+ * so it should only be called from a worker thread.
+ *
+ * @param volumeUuid the UUID of the storage volume you're interested in, or
+ * {@code null} to specify the default internal storage.
+ * @param uid the UID you're interested in.
+ */
+ @WorkerThread
+ public StorageStats queryStatsForUid(String volumeUuid, int uid) {
+ try {
+ return mService.queryStats(volumeUuid, uid, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return summary statistics for the requested storage volume.
+ * <p>
+ * This method may take several seconds to calculate the requested values,
+ * so it should only be called from a worker thread.
+ *
+ * @param volumeUuid the UUID of the storage volume you're interested in, or
+ * {@code null} to specify the default internal storage.
+ */
+ @WorkerThread
+ public StorageSummary querySummary(String volumeUuid) {
+ try {
+ return mService.querySummary(volumeUuid, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/app/usage/StorageSummary.aidl b/core/java/android/app/usage/StorageSummary.aidl
new file mode 100644
index 0000000..b20e025
--- /dev/null
+++ b/core/java/android/app/usage/StorageSummary.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+parcelable StorageSummary;
diff --git a/core/java/android/app/usage/StorageSummary.java b/core/java/android/app/usage/StorageSummary.java
new file mode 100644
index 0000000..e72c9ec
--- /dev/null
+++ b/core/java/android/app/usage/StorageSummary.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.File;
+
+/**
+ * Storage summary for a single storage volume.
+ * <p>
+ * When presenting this summary to users, the
+ * <p>
+ * Details for specific UIDs are available through {@link StorageStats}.
+ *
+ * @see StorageStatsManager
+ */
+public final class StorageSummary implements Parcelable {
+ /** {@hide} */ public final String volumeUuid;
+
+ /** {@hide} */ public long totalBytes;
+ /** {@hide} */ public long freeBytes;
+
+ /** {@hide} */ public long sharedTotalBytes;
+ /** {@hide} */ public long sharedAudioBytes;
+ /** {@hide} */ public long sharedVideoBytes;
+ /** {@hide} */ public long sharedImagesBytes;
+
+ /**
+ * Return the UUID of the storage volume that these statistics refer to. The
+ * value {@code null} indicates the default internal storage.
+ */
+ public String getVolumeUuid() {
+ return volumeUuid;
+ }
+
+ /**
+ * Return the total size of the storage volume.
+ * <p>
+ * To reduce end user confusion, this value is the total storage size
+ * advertised in a retail environment, which is typically larger than the
+ * actual writable partition total size.
+ */
+ public long getTotalBytes() {
+ return totalBytes;
+ }
+
+ /**
+ * Return the free space on this storage volume.
+ * <p>
+ * The free space is equivalent to {@link File#getFreeSpace()} plus the size
+ * of any cached data that can be automatically deleted by the system as
+ * additional space is needed.
+ */
+ public long getFreeBytes() {
+ return freeBytes;
+ }
+
+ /**
+ * Return the total bytes used by all files in the shared storage hosted on
+ * this volume.
+ *
+ * @return total bytes, or {@code -1} if no shared storage is hosted on this
+ * volume.
+ */
+ public long getSharedTotalBytes() {
+ return sharedTotalBytes;
+ }
+
+ /**
+ * Return the total bytes used by audio files in the shared storage hosted
+ * on this volume.
+ *
+ * @return total bytes, or {@code -1} if no shared storage is hosted on this
+ * volume.
+ */
+ public long getSharedAudioBytes() {
+ return sharedAudioBytes;
+ }
+
+ /**
+ * Return the total bytes used by video files in the shared storage hosted
+ * on this volume.
+ *
+ * @return total bytes, or {@code -1} if no shared storage is hosted on this
+ * volume.
+ */
+ public long getSharedVideoBytes() {
+ return sharedVideoBytes;
+ }
+
+ /**
+ * Return the total bytes used by image files in the shared storage hosted
+ * on this volume.
+ *
+ * @return total bytes, or {@code -1} if no shared storage is hosted on this
+ * volume.
+ */
+ public long getSharedImagesBytes() {
+ return sharedImagesBytes;
+ }
+
+ /** {@hide} */
+ public StorageSummary(String uuid) {
+ this.volumeUuid = uuid;
+ }
+
+ /** {@hide} */
+ public StorageSummary(Parcel in) {
+ this.volumeUuid = in.readString();
+ this.totalBytes = in.readLong();
+ this.freeBytes = in.readLong();
+ this.sharedTotalBytes = in.readLong();
+ this.sharedAudioBytes = in.readLong();
+ this.sharedVideoBytes = in.readLong();
+ this.sharedImagesBytes = in.readLong();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(volumeUuid);
+ dest.writeLong(totalBytes);
+ dest.writeLong(freeBytes);
+ dest.writeLong(sharedTotalBytes);
+ dest.writeLong(sharedAudioBytes);
+ dest.writeLong(sharedVideoBytes);
+ dest.writeLong(sharedImagesBytes);
+ }
+
+ public static final Creator<StorageSummary> CREATOR = new Creator<StorageSummary>() {
+ @Override
+ public StorageSummary createFromParcel(Parcel in) {
+ return new StorageSummary(in);
+ }
+
+ @Override
+ public StorageSummary[] newArray(int size) {
+ return new StorageSummary[size];
+ }
+ };
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9dc60ab..ffe1248 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2680,6 +2680,7 @@
SEARCH_SERVICE,
SENSOR_SERVICE,
STORAGE_SERVICE,
+ STORAGE_STATS_SERVICE,
WALLPAPER_SERVICE,
VIBRATOR_SERVICE,
//@hide: STATUS_BAR_SERVICE,
@@ -3077,6 +3078,16 @@
public static final String STORAGE_SERVICE = "storage";
/**
+ * Use with {@link #getSystemService} to retrieve a {@link
+ * android.app.usage.StorageStatsManager} for accessing system storage
+ * statistics.
+ *
+ * @see #getSystemService
+ * @see android.app.usage.StorageStatsManager
+ */
+ public static final String STORAGE_STATS_SERVICE = "storagestats";
+
+ /**
* Use with {@link #getSystemService} to retrieve a
* com.android.server.WallpaperService for accessing wallpapers.
*
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1d550d2..a22581c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -165,6 +165,8 @@
"com.android.server.LockSettingsService$Lifecycle";
private static final String STORAGE_MANAGER_SERVICE_CLASS =
"com.android.server.StorageManagerService$Lifecycle";
+ private static final String STORAGE_STATS_SERVICE_CLASS =
+ "com.android.server.usage.StorageStatsService$Lifecycle";
private static final String SEARCH_MANAGER_SERVICE_CLASS =
"com.android.server.search.SearchManagerService$Lifecycle";
private static final String THERMAL_OBSERVER_CLASS =
@@ -792,7 +794,7 @@
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
if (!disableStorage &&
- !"0".equals(SystemProperties.get("system_init.startmountservice"))) {
+ !"0".equals(SystemProperties.get("system_init.startmountservice"))) {
traceBeginAndSlog("StartStorageManagerService");
try {
/*
@@ -803,7 +805,15 @@
storageManager = IStorageManager.Stub.asInterface(
ServiceManager.getService("mount"));
} catch (Throwable e) {
- reportWtf("starting StorageManager Service", e);
+ reportWtf("starting StorageManagerService", e);
+ }
+ traceEnd();
+
+ traceBeginAndSlog("StartStorageStatsService");
+ try {
+ mSystemServiceManager.startService(STORAGE_STATS_SERVICE_CLASS);
+ } catch (Throwable e) {
+ reportWtf("starting StorageStatsService", e);
}
traceEnd();
}
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
new file mode 100644
index 0000000..cb9cb121
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usage;
+
+import android.app.AppOpsManager;
+import android.app.usage.IStorageStatsManager;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageSummary;
+import android.content.Context;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+
+import com.android.server.SystemService;
+import com.android.server.pm.Installer;
+
+public class StorageStatsService extends IStorageStatsManager.Stub {
+ private static final String TAG = "StorageStatsService";
+
+ public static class Lifecycle extends SystemService {
+ private StorageStatsService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mService = new StorageStatsService(getContext());
+ publishBinderService(Context.STORAGE_STATS_SERVICE, mService);
+ }
+ }
+
+ private final Context mContext;
+ private final AppOpsManager mAppOps;
+ private final StorageManager mStorage;
+ private final Installer mInstaller;
+
+ public StorageStatsService(Context context) {
+ mContext = context;
+ mAppOps = context.getSystemService(AppOpsManager.class);
+ mStorage = context.getSystemService(StorageManager.class);
+ mInstaller = new Installer(context);
+ }
+
+ private void enforcePermission(int callingUid, String callingPackage) {
+ final int mode = mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS,
+ callingUid, callingPackage);
+ switch (mode) {
+ case AppOpsManager.MODE_ALLOWED:
+ return;
+ case AppOpsManager.MODE_DEFAULT:
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.PACKAGE_USAGE_STATS, TAG);
+ default:
+ throw new SecurityException("Blocked by mode " + mode);
+ }
+ }
+
+ @Override
+ public StorageStats queryStats(String volumeUuid, int uid, String callingPackage) {
+ enforcePermission(Binder.getCallingUid(), callingPackage);
+ if (UserHandle.getUserId(uid) != UserHandle.getCallingUserId()) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS, TAG);
+ }
+
+ // TODO: call installd to collect quota stats
+ return null;
+ }
+
+ @Override
+ public StorageSummary querySummary(String volumeUuid, String callingPackage) {
+ enforcePermission(Binder.getCallingUid(), callingPackage);
+
+ // TODO: call installd to collect quota stats
+ return null;
+ }
+}