Merge "Add freeCache API to ExternalStorageService"

This commit is contained in:
Biswarup Pal
2020-12-16 15:53:15 +00:00
committed by Android (Google) Code Review
8 changed files with 114 additions and 1 deletions

View File

@@ -9777,6 +9777,7 @@ package android.service.storage {
ctor public ExternalStorageService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
method public void onFreeCacheRequested(@NonNull java.util.UUID, long);
method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException;
method public abstract void onVolumeStateChanged(@NonNull android.os.storage.StorageVolume) throws java.io.IOException;
field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2

View File

@@ -101,4 +101,15 @@ public abstract class StorageManagerInternal {
* Return true if uid is external storage service.
*/
public abstract boolean isExternalStorageService(int uid);
/**
* Frees cache held by ExternalStorageService.
*
* <p> Blocks until the service frees the cache or fails in doing so.
*
* @param volumeUuid uuid of the {@link StorageVolume} from which cache needs to be freed,
* null value indicates private internal volume.
* @param bytes number of bytes which need to be freed
*/
public abstract void freeCache(@Nullable String volumeUuid, long bytes);
}

View File

@@ -16,6 +16,7 @@
package android.service.storage;
import android.annotation.BytesLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
@@ -29,6 +30,7 @@ import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import com.android.internal.os.BackgroundThread;
@@ -37,6 +39,7 @@ import java.io.File;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.UUID;
/**
* A service to handle filesystem I/O from other apps.
@@ -147,6 +150,18 @@ public abstract class ExternalStorageService extends Service {
*/
public abstract void onVolumeStateChanged(@NonNull StorageVolume vol) throws IOException;
/**
* Called when any cache held by the ExternalStorageService needs to be freed.
*
* <p> Blocks until the service frees the cache or fails in doing so.
*
* @param volumeUuid uuid of the {@link StorageVolume} from which cache needs to be freed
* @param bytes number of bytes which need to be freed
*/
public void onFreeCacheRequested(@NonNull UUID volumeUuid, @BytesLong long bytes) {
throw new UnsupportedOperationException("onFreeCacheRequested not implemented");
}
@Override
@NonNull
public final IBinder onBind(@NonNull Intent intent) {
@@ -182,6 +197,19 @@ public abstract class ExternalStorageService extends Service {
});
}
@Override
public void freeCache(String sessionId, String volumeUuid, long bytes,
RemoteCallback callback) {
mHandler.post(() -> {
try {
onFreeCacheRequested(StorageManager.convert(volumeUuid), bytes);
sendResult(sessionId, null /* throwable */, callback);
} catch (Throwable t) {
sendResult(sessionId, t, callback);
}
});
}
@Override
public void endSession(String sessionId, RemoteCallback callback) throws RemoteException {
mHandler.post(() -> {

View File

@@ -30,4 +30,6 @@ oneway interface IExternalStorageService
void endSession(@utf8InCpp String sessionId, in RemoteCallback callback);
void notifyVolumeStateChanged(@utf8InCpp String sessionId, in StorageVolume vol,
in RemoteCallback callback);
void freeCache(@utf8InCpp String sessionId, in String volumeUuid, long bytes,
in RemoteCallback callback);
}

View File

@@ -4475,6 +4475,15 @@ class StorageManagerService extends IStorageManager.Stub
return mMediaStoreAuthorityAppId == UserHandle.getAppId(uid);
}
@Override
public void freeCache(String volumeUuid, long freeBytes) {
try {
mStorageSessionController.freeCache(volumeUuid, freeBytes);
} catch (ExternalStorageServiceException e) {
Log.e(TAG, "Failed to free cache of vol : " + volumeUuid, e);
}
}
public boolean hasExternalStorage(int uid, String packageName) {
// No need to check for system uid. This avoids a deadlock between
// PackageManagerService and AppOpsService.

View File

@@ -5417,6 +5417,14 @@ public class PackageManagerService extends IPackageManager.Stub
InstantAppRegistry.DEFAULT_UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD))) {
return;
}
// 11. Free storage service cache
StorageManagerInternal smInternal =
mInjector.getLocalService(StorageManagerInternal.class);
// TODO(b/170481432): Decide what value of bytes needs to be sent instead of
// sending the bytes parameter of freeStorage
smInternal.freeCache(volumeUuid, bytes);
if (file.getUsableSpace() >= bytes) return;
} else {
try {
mInstaller.freeCache(volumeUuid, bytes, 0, 0);

View File

@@ -32,6 +32,7 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.os.storage.StorageVolume;
import android.os.storage.VolumeInfo;
import android.provider.MediaStore;
import android.service.storage.ExternalStorageService;
@@ -133,6 +134,28 @@ public final class StorageSessionController {
}
}
/**
* Frees any cache held by ExternalStorageService.
*
* <p> Blocks until the service frees the cache or fails in doing so.
*
* @param volumeUuid uuid of the {@link StorageVolume} from which cache needs to be freed
* @param bytes number of bytes which need to be freed
* @throws ExternalStorageServiceException if it fails to connect to ExternalStorageService
*/
public void freeCache(String volumeUuid, long bytes)
throws ExternalStorageServiceException {
synchronized (mLock) {
int size = mConnections.size();
for (int i = 0; i < size; i++) {
int key = mConnections.keyAt(i);
StorageUserConnection connection = mConnections.get(key);
if (connection != null) {
connection.freeCache(volumeUuid, bytes);
}
}
}
}
/**
* Removes and returns the {@link StorageUserConnection} for {@code vol}.

View File

@@ -124,6 +124,26 @@ public final class StorageUserConnection {
mActiveConnection.notifyVolumeStateChanged(sessionId, vol);
}
/**
* Frees any cache held by ExternalStorageService.
*
* <p> Blocks until the service frees the cache or fails in doing so.
*
* @param volumeUuid uuid of the {@link StorageVolume} from which cache needs to be freed
* @param bytes number of bytes which need to be freed
* @throws ExternalStorageServiceException if it fails to connect to ExternalStorageService
*/
public void freeCache(String volumeUuid, long bytes)
throws ExternalStorageServiceException {
Objects.requireNonNull(volumeUuid);
synchronized (mSessionsLock) {
for (String sessionId : mSessions.keySet()) {
mActiveConnection.freeCache(sessionId, volumeUuid, bytes);
}
}
}
/**
* Removes a session without ending it or waiting for exit.
*
@@ -214,7 +234,7 @@ public final class StorageUserConnection {
// A list of outstanding futures for async calls, for which we are still waiting
// for a callback. Used to unblock waiters if the service dies.
@GuardedBy("mLock")
private ArrayList<CompletableFuture<Void>> mOutstandingOps = new ArrayList<>();
private final ArrayList<CompletableFuture<Void>> mOutstandingOps = new ArrayList<>();
@Override
public void close() {
@@ -309,6 +329,17 @@ public final class StorageUserConnection {
}
}
public void freeCache(String sessionId, String volumeUuid, long bytes)
throws ExternalStorageServiceException {
try {
waitForAsync((service, callback) ->
service.freeCache(sessionId, volumeUuid, bytes, callback));
} catch (Exception e) {
throw new ExternalStorageServiceException("Failed to free " + bytes
+ " bytes for volumeUuid : " + volumeUuid, e);
}
}
private void setResult(Bundle result, CompletableFuture<Void> future) {
ParcelableException ex = result.getParcelable(EXTRA_ERROR);
if (ex != null) {