blob: 275086832c5158dfb8253c809975d7199f5258bd [file] [log] [blame]
/*
* Copyright (C) 2019 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.os.incremental;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
import java.io.File;
import java.io.IOException;
/**
* Provides operations on an Incremental File System directory, using IncrementalServiceNative.
* Example usage:
*
* <blockquote><pre>
* IncrementalManager manager = (IncrementalManager) getSystemService(Context.INCREMENTAL_SERVICE);
* IncrementalStorage storage = manager.openStorage("/path/to/incremental/dir");
* storage.makeDirectory("subdir");
* </pre></blockquote>
*
* @hide
*/
public final class IncrementalStorage {
private static final String TAG = "IncrementalStorage";
private final int mId;
private final IIncrementalManagerNative mService;
public IncrementalStorage(@NonNull IIncrementalManagerNative is, int id) {
mService = is;
mId = id;
}
public int getId() {
return mId;
}
/**
* Temporarily bind-mounts the current storage directory to a target directory. The bind-mount
* will NOT be preserved between device reboots.
*
* @param targetPath Absolute path to the target directory.
*/
public void bind(@NonNull String targetPath) throws IOException {
bind("", targetPath);
}
/**
* Temporarily bind-mounts a subdir under the current storage directory to a target directory.
* The bind-mount will NOT be preserved between device reboots.
*
* @param sourcePathUnderStorage Source path as a relative path under current storage
* directory.
* @param targetPath Absolute path to the target directory.
*/
public void bind(@NonNull String sourcePathUnderStorage, @NonNull String targetPath)
throws IOException {
try {
int res = mService.makeBindMount(mId, sourcePathUnderStorage, targetPath,
IIncrementalManagerNative.BIND_TEMPORARY);
if (res < 0) {
throw new IOException("bind() failed with errno " + -res);
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Permanently bind-mounts the current storage directory to a target directory. The bind-mount
* WILL be preserved between device reboots.
*
* @param targetPath Absolute path to the target directory.
*/
public void bindPermanent(@NonNull String targetPath) throws IOException {
bindPermanent("", targetPath);
}
/**
* Permanently bind-mounts a subdir under the current storage directory to a target directory.
* The bind-mount WILL be preserved between device reboots.
*
* @param sourcePathUnderStorage Relative path under the current storage directory.
* @param targetPath Absolute path to the target directory.
*/
public void bindPermanent(@NonNull String sourcePathUnderStorage, @NonNull String targetPath)
throws IOException {
try {
int res = mService.makeBindMount(mId, sourcePathUnderStorage, targetPath,
IIncrementalManagerNative.BIND_PERMANENT);
if (res < 0) {
throw new IOException("bind() permanent failed with errno " + -res);
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Unbinds a bind mount.
*
* @param targetPath Absolute path to the target directory.
*/
public void unBind(@NonNull String targetPath) throws IOException {
try {
int res = mService.deleteBindMount(mId, targetPath);
if (res < 0) {
throw new IOException("unbind() failed with errno " + -res);
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Creates a sub-directory under the current storage directory.
*
* @param pathUnderStorage Relative path of the sub-directory, e.g., "subdir"
*/
public void makeDirectory(@NonNull String pathUnderStorage) throws IOException {
try {
int res = mService.makeDirectory(mId, pathUnderStorage);
if (res < 0) {
throw new IOException("makeDirectory() failed with errno " + -res);
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Creates a sub-directory under the current storage directory. If its parent dirs do not exist,
* create the parent dirs as well.
*
* @param pathUnderStorage Relative path of the sub-directory, e.g., "subdir/subsubdir"
*/
public void makeDirectories(@NonNull String pathUnderStorage) throws IOException {
try {
int res = mService.makeDirectories(mId, pathUnderStorage);
if (res < 0) {
throw new IOException("makeDirectory() failed with errno " + -res);
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Creates a file under the current storage directory.
*
* @param pathUnderStorage Relative path of the new file.
* @param size Size of the new file in bytes.
* @param metadata Metadata bytes.
*/
public void makeFile(@NonNull String pathUnderStorage, long size,
@Nullable byte[] metadata) throws IOException {
try {
int res = mService.makeFile(mId, pathUnderStorage, size, metadata);
if (res < 0) {
throw new IOException("makeFile() failed with errno " + -res);
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Creates a file in Incremental storage. The content of the file is mapped from a range inside
* a source file in the same storage.
*
* @param destRelativePath Target relative path under storage.
* @param sourceRelativePath Source relative path under storage.
* @param rangeStart Starting offset (in bytes) in the source file.
* @param rangeEnd Ending offset (in bytes) in the source file.
*/
public void makeFileFromRange(@NonNull String destRelativePath,
@NonNull String sourceRelativePath, long rangeStart, long rangeEnd) throws IOException {
try {
int res = mService.makeFileFromRange(mId, destRelativePath, sourceRelativePath,
rangeStart, rangeEnd);
if (res < 0) {
throw new IOException("makeFileFromRange() failed, errno " + -res);
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Creates a hard-link between two paths, which can be under different storages but in the same
* Incremental File System.
*
* @param sourcePathUnderStorage The relative path of the source.
* @param destStorage The target storage of the link target.
* @param destPathUnderStorage The relative path of the target.
*/
public void makeLink(@NonNull String sourcePathUnderStorage, IncrementalStorage destStorage,
@NonNull String destPathUnderStorage) throws IOException {
try {
int res = mService.makeLink(mId, sourcePathUnderStorage, destStorage.getId(),
destPathUnderStorage);
if (res < 0) {
throw new IOException("makeLink() failed with errno " + -res);
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Deletes a hard-link under the current storage directory.
*
* @param pathUnderStorage The relative path of the target.
*/
public void unlink(@NonNull String pathUnderStorage) throws IOException {
try {
int res = mService.unlink(mId, pathUnderStorage);
if (res < 0) {
throw new IOException("unlink() failed with errno " + -res);
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/**
* Rename an old file name to a new file name under the current storage directory.
*
* @param sourcePathUnderStorage Old file path as a relative path to the storage directory.
* @param destPathUnderStorage New file path as a relative path to the storage directory.
*/
public void moveFile(@NonNull String sourcePathUnderStorage,
@NonNull String destPathUnderStorage) throws IOException {
try {
int res = mService.makeLink(mId, sourcePathUnderStorage, mId, destPathUnderStorage);
if (res < 0) {
throw new IOException("moveFile() failed at makeLink(), errno " + -res);
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
try {
mService.unlink(mId, sourcePathUnderStorage);
} catch (RemoteException ignored) {
}
}
/**
* Move a directory, which is bind-mounted to a given storage, to a new location. The bind mount
* will be persistent between reboots.
*
* @param sourcePath The old path of the directory as an absolute path.
* @param destPath The new path of the directory as an absolute path, expected to already
* exist.
*/
public void moveDir(@NonNull String sourcePath, @NonNull String destPath) throws IOException {
if (!new File(destPath).exists()) {
throw new IOException("moveDir() requires that destination dir already exists.");
}
try {
int res = mService.makeBindMount(mId, "", destPath,
IIncrementalManagerNative.BIND_PERMANENT);
if (res < 0) {
throw new IOException("moveDir() failed at making bind mount, errno " + -res);
}
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
try {
mService.deleteBindMount(mId, sourcePath);
} catch (RemoteException ignored) {
}
}
/**
* Checks whether a file under the current storage directory is fully loaded.
*
* @param pathUnderStorage The relative path of the file.
* @return True if the file is fully loaded.
*/
public boolean isFileFullyLoaded(@NonNull String pathUnderStorage) {
return isFileRangeLoaded(pathUnderStorage, 0, -1);
}
/**
* Checks whether a range in a file if loaded.
*
* @param pathUnderStorage The relative path of the file.
* @param start The starting offset of the range.
* @param end The ending offset of the range.
* @return True if the file is fully loaded.
*/
public boolean isFileRangeLoaded(@NonNull String pathUnderStorage, long start, long end) {
try {
return mService.isFileRangeLoaded(mId, pathUnderStorage, start, end);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return false;
}
}
/**
* Returns the metadata object of an IncFs File.
*
* @param pathUnderStorage The relative path of the file.
* @return Byte array that contains metadata bytes.
*/
@Nullable
public byte[] getFileMetadata(@NonNull String pathUnderStorage) {
try {
return mService.getFileMetadata(mId, pathUnderStorage);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
}
}
/**
* Informs the data loader service associated with the current storage to start data loader
*
* @return True if data loader is successfully started.
*/
public boolean startLoading() {
try {
return mService.startLoading(mId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return false;
}
}
}