OK, I've found out how to use the new API. I've prepared a small sample of this, fetching some information about the volume storage and of a specific app (this time of the Play Store, but you can change it if needed) :
public class MainActivity extends AppCompatActivity {
public static final String PACKAGE_NAME = "com.android.vending";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
if (!hasUsageStatsPermission(this))
startActivityForResult(new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY), 1);
else {
final Context context = this;
AsyncTask.execute(new Runnable() {
@TargetApi(VERSION_CODES.O)
@Override
public void run() {
@SuppressLint("WrongConstant") final StorageStatsManager storageStatsManager = (StorageStatsManager) getSystemService(Context.STORAGE_STATS_SERVICE);
final StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
final List<StorageVolume> storageVolumes = storageManager.getStorageVolumes();
final UserHandle user = android.os.Process.myUserHandle();
for (StorageVolume storageVolume : storageVolumes) {
final String uuidStr = storageVolume.getUuid();
final UUID uuid = uuidStr == null ? StorageManager.UUID_DEFAULT : UUID.fromString(uuidStr);
try {
Log.d("AppLog", "storage:" + uuid + " : " + storageVolume.getDescription(context) + " : " + storageVolume.getState());
Log.d("AppLog", "getFreeBytes:" + Formatter.formatShortFileSize(context, storageStatsManager.getFreeBytes(uuid)));
Log.d("AppLog", "getTotalBytes:" + Formatter.formatShortFileSize(context, storageStatsManager.getTotalBytes(uuid)));
Log.d("AppLog", "storage stats for app of package name:" + PACKAGE_NAME + " : ");
final StorageStats storageStats = storageStatsManager.queryStatsForPackage(uuid, PACKAGE_NAME, user);
Log.d("AppLog", "getAppBytes:" + Formatter.formatShortFileSize(context, storageStats.getAppBytes()) +
" getCacheBytes:" + Formatter.formatShortFileSize(context, storageStats.getCacheBytes()) +
" getDataBytes:" + Formatter.formatShortFileSize(context, storageStats.getDataBytes()));
} catch (NameNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
}
}
@TargetApi(VERSION_CODES.M)
public static boolean hasUsageStatsPermission(Context context) {
//http://stackoverflow.com/a/42390614/878126
if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
return false;
AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
final int mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.getPackageName());
boolean granted = false;
if (mode == AppOpsManager.MODE_DEFAULT)
granted = (context.checkCallingOrSelfPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) == PackageManager.PERMISSION_GRANTED);
else
granted = (mode == AppOpsManager.MODE_ALLOWED);
return granted;
}
}
manifest file should have this:
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions"/>
EDIT: note that the part of UUID.fromString(uuidStr)
could fail for SD-cards, and the reason by Google is this:
Android can't maintain quota-style statistics for public volumes (SD
card etc.). Apps will need to build size calculation logic themselves.
Sadly, on Android 11, due to the new restrictions of reaching "Android" folder, even this is impossible. Requested here to change it:
@t0m But even with what they wrote, it's not officially impossible to do what they said to do, as storage permission got very restricted on Android 11, not allowing you to reach Android folder properly. Requested to solve this here:
Please consider starring.