Merge "[incremental] Java service and shell command handler"
This commit is contained in:
committed by
Android (Google) Code Review
commit
247901bb4f
@@ -810,8 +810,8 @@ cc_library {
|
||||
filegroup {
|
||||
name: "incremental_aidl",
|
||||
srcs: [
|
||||
"core/java/android/os/incremental/IIncrementalManagerNative.aidl",
|
||||
"core/java/android/os/incremental/IIncrementalManager.aidl",
|
||||
"core/java/android/os/incremental/IIncrementalServiceProxy.aidl",
|
||||
"core/java/android/os/incremental/IncrementalDataLoaderParamsParcel.aidl",
|
||||
"core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl",
|
||||
"core/java/android/os/incremental/NamedParcelFileDescriptor.aidl",
|
||||
|
||||
@@ -4991,7 +4991,7 @@ public abstract class Context {
|
||||
* {@link android.os.incremental.IncrementalManager}.
|
||||
* @hide
|
||||
*/
|
||||
public static final String INCREMENTAL_SERVICE = "incremental";
|
||||
public static final String INCREMENTAL_SERVICE = "incremental_service";
|
||||
|
||||
/**
|
||||
* Determine whether the given permission is allowed for a particular
|
||||
|
||||
@@ -16,89 +16,22 @@
|
||||
|
||||
package android.os.incremental;
|
||||
|
||||
import android.os.incremental.IncrementalFileSystemControlParcel;
|
||||
import android.os.incremental.IncrementalDataLoaderParamsParcel;
|
||||
import android.content.pm.IDataLoaderStatusListener;
|
||||
|
||||
/** @hide */
|
||||
/**
|
||||
* Binder service to receive calls from native Incremental Service and handle Java tasks such as
|
||||
* looking up data loader service package names, binding and talking to the data loader service.
|
||||
* @hide
|
||||
*/
|
||||
interface IIncrementalManager {
|
||||
/**
|
||||
* A set of flags for the |createMode| parameters when creating a new Incremental storage.
|
||||
*/
|
||||
const int CREATE_MODE_TEMPORARY_BIND = 1;
|
||||
const int CREATE_MODE_PERMANENT_BIND = 2;
|
||||
const int CREATE_MODE_CREATE = 4;
|
||||
const int CREATE_MODE_OPEN_EXISTING = 8;
|
||||
|
||||
/**
|
||||
* Opens or creates a storage given a target path and data loader params. Returns the storage ID.
|
||||
*/
|
||||
int openStorage(in @utf8InCpp String path);
|
||||
int createStorage(in @utf8InCpp String path, in IncrementalDataLoaderParamsParcel params, int createMode);
|
||||
int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode);
|
||||
|
||||
/**
|
||||
* Bind-mounts a path under a storage to a full path. Can be permanent or temporary.
|
||||
*/
|
||||
const int BIND_TEMPORARY = 0;
|
||||
const int BIND_PERMANENT = 1;
|
||||
int makeBindMount(int storageId, in @utf8InCpp String pathUnderStorage, in @utf8InCpp String targetFullPath, int bindType);
|
||||
|
||||
/**
|
||||
* Deletes an existing bind mount on a path under a storage. Returns 0 on success, and -errno on failure.
|
||||
*/
|
||||
int deleteBindMount(int storageId, in @utf8InCpp String targetFullPath);
|
||||
|
||||
/**
|
||||
* Creates a directory under a storage. The target directory is specified by its relative path under the storage.
|
||||
*/
|
||||
int makeDirectory(int storageId, in @utf8InCpp String pathUnderStorage);
|
||||
|
||||
/**
|
||||
* Recursively creates a directory under a storage. The target directory is specified by its relative path under the storage.
|
||||
* All the parent directories of the target directory will be created if they do not exist already.
|
||||
*/
|
||||
int makeDirectories(int storageId, in @utf8InCpp String pathUnderStorage);
|
||||
|
||||
/**
|
||||
* Creates a file under a storage, specifying its name, size and metadata.
|
||||
*/
|
||||
int makeFile(int storageId, in @utf8InCpp String pathUnderStorage, long size, in byte[] metadata);
|
||||
|
||||
/**
|
||||
* Creates a file under a storage. Content of the file is from a range inside another file.
|
||||
* Both files are specified by relative paths under storage.
|
||||
*/
|
||||
int makeFileFromRange(int storageId, in @utf8InCpp String targetPathUnderStorage, in @utf8InCpp String sourcePathUnderStorage, long start, long end);
|
||||
|
||||
/**
|
||||
* Creates a hard link between two files in two storage instances.
|
||||
* Source and dest specified by parent storage IDs and their relative paths under the storage.
|
||||
* The source and dest storage instances should be in the same fs mount.
|
||||
* Note: destStorageId can be the same as sourceStorageId.
|
||||
*/
|
||||
int makeLink(int sourceStorageId, in @utf8InCpp String sourcePathUnderStorage, int destStorageId, in @utf8InCpp String destPathUnderStorage);
|
||||
|
||||
/**
|
||||
* Deletes a hard link in a storage, specified by the relative path of the link target under storage.
|
||||
*/
|
||||
int unlink(int storageId, in @utf8InCpp String pathUnderStorage);
|
||||
|
||||
/**
|
||||
* Checks if a file's certain range is loaded. File is specified by relative file path under storage.
|
||||
*/
|
||||
boolean isFileRangeLoaded(int storageId, in @utf8InCpp String pathUnderStorage, long start, long end);
|
||||
|
||||
/**
|
||||
* Reads the metadata of a file. File is specified by relative path under storage.
|
||||
*/
|
||||
byte[] getFileMetadata(int storageId, in @utf8InCpp String pathUnderStorage);
|
||||
|
||||
/**
|
||||
* Starts loading data for a storage.
|
||||
*/
|
||||
boolean startLoading(int storageId);
|
||||
|
||||
/**
|
||||
* Deletes a storage given its ID. Deletes its bind mounts and unmount it. Stop its data loader.
|
||||
*/
|
||||
void deleteStorage(int storageId);
|
||||
boolean prepareDataLoader(int mountId,
|
||||
in IncrementalFileSystemControlParcel control,
|
||||
in IncrementalDataLoaderParamsParcel params,
|
||||
in IDataLoaderStatusListener listener);
|
||||
boolean startDataLoader(int mountId);
|
||||
void showHealthBlockedUI(int mountId);
|
||||
void destroyDataLoader(int mountId);
|
||||
void newFileForDataLoader(int mountId, long inode, in byte[] metadata);
|
||||
}
|
||||
|
||||
104
core/java/android/os/incremental/IIncrementalManagerNative.aidl
Normal file
104
core/java/android/os/incremental/IIncrementalManagerNative.aidl
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.os.incremental.IncrementalDataLoaderParamsParcel;
|
||||
|
||||
/** @hide */
|
||||
interface IIncrementalManagerNative {
|
||||
/**
|
||||
* A set of flags for the |createMode| parameters when creating a new Incremental storage.
|
||||
*/
|
||||
const int CREATE_MODE_TEMPORARY_BIND = 1;
|
||||
const int CREATE_MODE_PERMANENT_BIND = 2;
|
||||
const int CREATE_MODE_CREATE = 4;
|
||||
const int CREATE_MODE_OPEN_EXISTING = 8;
|
||||
|
||||
/**
|
||||
* Opens or creates a storage given a target path and data loader params. Returns the storage ID.
|
||||
*/
|
||||
int openStorage(in @utf8InCpp String path);
|
||||
int createStorage(in @utf8InCpp String path, in IncrementalDataLoaderParamsParcel params, int createMode);
|
||||
int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode);
|
||||
|
||||
/**
|
||||
* Bind-mounts a path under a storage to a full path. Can be permanent or temporary.
|
||||
*/
|
||||
const int BIND_TEMPORARY = 0;
|
||||
const int BIND_PERMANENT = 1;
|
||||
int makeBindMount(int storageId, in @utf8InCpp String pathUnderStorage, in @utf8InCpp String targetFullPath, int bindType);
|
||||
|
||||
/**
|
||||
* Deletes an existing bind mount on a path under a storage. Returns 0 on success, and -errno on failure.
|
||||
*/
|
||||
int deleteBindMount(int storageId, in @utf8InCpp String targetFullPath);
|
||||
|
||||
/**
|
||||
* Creates a directory under a storage. The target directory is specified by its relative path under the storage.
|
||||
*/
|
||||
int makeDirectory(int storageId, in @utf8InCpp String pathUnderStorage);
|
||||
|
||||
/**
|
||||
* Recursively creates a directory under a storage. The target directory is specified by its relative path under the storage.
|
||||
* All the parent directories of the target directory will be created if they do not exist already.
|
||||
*/
|
||||
int makeDirectories(int storageId, in @utf8InCpp String pathUnderStorage);
|
||||
|
||||
/**
|
||||
* Creates a file under a storage, specifying its name, size and metadata.
|
||||
*/
|
||||
int makeFile(int storageId, in @utf8InCpp String pathUnderStorage, long size, in byte[] metadata);
|
||||
|
||||
/**
|
||||
* Creates a file under a storage. Content of the file is from a range inside another file.
|
||||
* Both files are specified by relative paths under storage.
|
||||
*/
|
||||
int makeFileFromRange(int storageId, in @utf8InCpp String targetPathUnderStorage, in @utf8InCpp String sourcePathUnderStorage, long start, long end);
|
||||
|
||||
/**
|
||||
* Creates a hard link between two files in two storage instances.
|
||||
* Source and dest specified by parent storage IDs and their relative paths under the storage.
|
||||
* The source and dest storage instances should be in the same fs mount.
|
||||
* Note: destStorageId can be the same as sourceStorageId.
|
||||
*/
|
||||
int makeLink(int sourceStorageId, in @utf8InCpp String sourcePathUnderStorage, int destStorageId, in @utf8InCpp String destPathUnderStorage);
|
||||
|
||||
/**
|
||||
* Deletes a hard link in a storage, specified by the relative path of the link target under storage.
|
||||
*/
|
||||
int unlink(int storageId, in @utf8InCpp String pathUnderStorage);
|
||||
|
||||
/**
|
||||
* Checks if a file's certain range is loaded. File is specified by relative file path under storage.
|
||||
*/
|
||||
boolean isFileRangeLoaded(int storageId, in @utf8InCpp String pathUnderStorage, long start, long end);
|
||||
|
||||
/**
|
||||
* Reads the metadata of a file. File is specified by relative path under storage.
|
||||
*/
|
||||
byte[] getFileMetadata(int storageId, in @utf8InCpp String pathUnderStorage);
|
||||
|
||||
/**
|
||||
* Starts loading data for a storage.
|
||||
*/
|
||||
boolean startLoading(int storageId);
|
||||
|
||||
/**
|
||||
* Deletes a storage given its ID. Deletes its bind mounts and unmount it. Stop its data loader.
|
||||
*/
|
||||
void deleteStorage(int storageId);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* 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.os.incremental.IncrementalFileSystemControlParcel;
|
||||
import android.os.incremental.IncrementalDataLoaderParamsParcel;
|
||||
import android.content.pm.IDataLoaderStatusListener;
|
||||
|
||||
/**
|
||||
* Binder service to receive calls from native Incremental Service and handle Java tasks such as
|
||||
* looking up data loader service package names, binding and talking to the data loader service.
|
||||
* @hide
|
||||
*/
|
||||
interface IIncrementalServiceProxy {
|
||||
boolean prepareDataLoader(int mountId,
|
||||
in IncrementalFileSystemControlParcel control,
|
||||
in IncrementalDataLoaderParamsParcel params,
|
||||
in IDataLoaderStatusListener listener);
|
||||
boolean startDataLoader(int mountId);
|
||||
void showHealthBlockedUI(int mountId);
|
||||
void destroyDataLoader(int mountId);
|
||||
void newFileForDataLoader(int mountId, long inode, in byte[] metadata);
|
||||
}
|
||||
@@ -36,31 +36,28 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* Provides operations to open or create an IncrementalStorage, using IIncrementalManager service.
|
||||
* Example Usage:
|
||||
* Provides operations to open or create an IncrementalStorage, using IIncrementalManagerNative
|
||||
* service. Example Usage:
|
||||
*
|
||||
* <blockquote><pre>
|
||||
* IncrementalManager manager = (IncrementalManager) getSystemService(Context.INCREMENTAL_MANAGER);
|
||||
* IncrementalManager manager = (IncrementalManager) getSystemService(Context.INCREMENTAL_SERVICE);
|
||||
* IncrementalStorage storage = manager.openStorage("/path/to/incremental/dir");
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemService(Context.INCREMENTAL_SERVICE)
|
||||
public class IncrementalManager {
|
||||
public final class IncrementalManager {
|
||||
private static final String TAG = "IncrementalManager";
|
||||
private final IIncrementalManager mService;
|
||||
@GuardedBy("mStorages")
|
||||
private final SparseArray<IncrementalStorage> mStorages = new SparseArray<>();
|
||||
|
||||
public static final int CREATE_MODE_TEMPORARY_BIND =
|
||||
IIncrementalManager.CREATE_MODE_TEMPORARY_BIND;
|
||||
IIncrementalManagerNative.CREATE_MODE_TEMPORARY_BIND;
|
||||
public static final int CREATE_MODE_PERMANENT_BIND =
|
||||
IIncrementalManager.CREATE_MODE_PERMANENT_BIND;
|
||||
IIncrementalManagerNative.CREATE_MODE_PERMANENT_BIND;
|
||||
public static final int CREATE_MODE_CREATE =
|
||||
IIncrementalManager.CREATE_MODE_CREATE;
|
||||
IIncrementalManagerNative.CREATE_MODE_CREATE;
|
||||
public static final int CREATE_MODE_OPEN_EXISTING =
|
||||
IIncrementalManager.CREATE_MODE_OPEN_EXISTING;
|
||||
IIncrementalManagerNative.CREATE_MODE_OPEN_EXISTING;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(prefix = {"CREATE_MODE_"}, value = {
|
||||
@@ -72,8 +69,12 @@ public class IncrementalManager {
|
||||
public @interface CreateMode {
|
||||
}
|
||||
|
||||
public IncrementalManager(@NonNull IIncrementalManager is) {
|
||||
mService = is;
|
||||
private final @Nullable IIncrementalManagerNative mNativeService;
|
||||
@GuardedBy("mStorages")
|
||||
private final SparseArray<IncrementalStorage> mStorages = new SparseArray<>();
|
||||
|
||||
public IncrementalManager(IIncrementalManagerNative nativeService) {
|
||||
mNativeService = nativeService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,6 +83,7 @@ public class IncrementalManager {
|
||||
* @param storageId The storage ID to identify the storage object.
|
||||
* @return IncrementalStorage object corresponding to storage ID.
|
||||
*/
|
||||
// TODO(b/136132412): remove this
|
||||
@Nullable
|
||||
public IncrementalStorage getStorage(int storageId) {
|
||||
synchronized (mStorages) {
|
||||
@@ -95,8 +97,8 @@ public class IncrementalManager {
|
||||
*
|
||||
* @param path Absolute path to mount Incremental File System on.
|
||||
* @param params IncrementalDataLoaderParams object to configure data loading.
|
||||
* @param createMode Mode for opening an old Incremental File System mount or
|
||||
* creating a new mount.
|
||||
* @param createMode Mode for opening an old Incremental File System mount or creating
|
||||
* a new mount.
|
||||
* @param autoStartDataLoader Set true to immediately start data loader after creating storage.
|
||||
* @return IncrementalStorage object corresponding to the mounted directory.
|
||||
*/
|
||||
@@ -105,11 +107,11 @@ public class IncrementalManager {
|
||||
@NonNull IncrementalDataLoaderParams params, @CreateMode int createMode,
|
||||
boolean autoStartDataLoader) {
|
||||
try {
|
||||
final int id = mService.createStorage(path, params.getData(), createMode);
|
||||
final int id = mNativeService.createStorage(path, params.getData(), createMode);
|
||||
if (id < 0) {
|
||||
return null;
|
||||
}
|
||||
final IncrementalStorage storage = new IncrementalStorage(mService, id);
|
||||
final IncrementalStorage storage = new IncrementalStorage(mNativeService, id);
|
||||
synchronized (mStorages) {
|
||||
mStorages.put(id, storage);
|
||||
}
|
||||
@@ -123,8 +125,8 @@ public class IncrementalManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an existing Incremental File System mounted directory and returns an
|
||||
* IncrementalStorage object.
|
||||
* Opens an existing Incremental File System mounted directory and returns an IncrementalStorage
|
||||
* object.
|
||||
*
|
||||
* @param path Absolute target path that Incremental File System has been mounted on.
|
||||
* @return IncrementalStorage object corresponding to the mounted directory.
|
||||
@@ -132,11 +134,11 @@ public class IncrementalManager {
|
||||
@Nullable
|
||||
public IncrementalStorage openStorage(@NonNull String path) {
|
||||
try {
|
||||
final int id = mService.openStorage(path);
|
||||
final int id = mNativeService.openStorage(path);
|
||||
if (id < 0) {
|
||||
return null;
|
||||
}
|
||||
final IncrementalStorage storage = new IncrementalStorage(mService, id);
|
||||
final IncrementalStorage storage = new IncrementalStorage(mNativeService, id);
|
||||
synchronized (mStorages) {
|
||||
mStorages.put(id, storage);
|
||||
}
|
||||
@@ -155,11 +157,12 @@ public class IncrementalManager {
|
||||
public IncrementalStorage createStorage(@NonNull String path,
|
||||
@NonNull IncrementalStorage linkedStorage, @CreateMode int createMode) {
|
||||
try {
|
||||
final int id = mService.createLinkedStorage(path, linkedStorage.getId(), createMode);
|
||||
final int id = mNativeService.createLinkedStorage(
|
||||
path, linkedStorage.getId(), createMode);
|
||||
if (id < 0) {
|
||||
return null;
|
||||
}
|
||||
final IncrementalStorage storage = new IncrementalStorage(mService, id);
|
||||
final IncrementalStorage storage = new IncrementalStorage(mNativeService, id);
|
||||
synchronized (mStorages) {
|
||||
mStorages.put(id, storage);
|
||||
}
|
||||
@@ -175,6 +178,7 @@ public class IncrementalManager {
|
||||
* @param file Target file to search storage for.
|
||||
* @return Absolute path which is a bind-mount point of Incremental File System.
|
||||
*/
|
||||
@Nullable
|
||||
private Path getStoragePathForFile(File file) {
|
||||
File currentPath = new File(file.getParent());
|
||||
while (currentPath.getParent() != null) {
|
||||
@@ -198,18 +202,19 @@ public class IncrementalManager {
|
||||
* </li>
|
||||
* </ol>
|
||||
*
|
||||
* @param sourcePath Absolute path to the source. Should be the same type as the destPath
|
||||
* (file or dir). Expected to already exist and is an Incremental path.
|
||||
* @param destPath Absolute path to the destination.
|
||||
* @throws IllegalArgumentException when 1) source does not exist,
|
||||
* or 2) source and dest type mismatch (one is file and the other is dir),
|
||||
* or 3) source path is not on Incremental File System,
|
||||
* @throws IOException when 1) cannot find the root path of the Incremental storage of source,
|
||||
* or 2) cannot retrieve the Incremental storage instance of the source,
|
||||
* or 3) renaming a file, but dest is not on the same Incremental Storage,
|
||||
* or 4) renaming a dir, dest dir does not exist but fails to be created.
|
||||
*
|
||||
* TODO(b/136132412): add unit tests
|
||||
* @param sourcePath Absolute path to the source. Should be the same type as the destPath (file
|
||||
* or dir). Expected to already exist and is an Incremental path.
|
||||
* @param destPath Absolute path to the destination.
|
||||
* @throws IllegalArgumentException when 1) source does not exist, or 2) source and dest type
|
||||
* mismatch (one is file and the other is dir), or 3) source
|
||||
* path is not on Incremental File System,
|
||||
* @throws IOException when 1) cannot find the root path of the Incremental storage
|
||||
* of source, or 2) cannot retrieve the Incremental storage
|
||||
* instance of the source, or 3) renaming a file, but dest is
|
||||
* not on the same Incremental Storage, or 4) renaming a dir,
|
||||
* dest dir does not exist but fails to be created.
|
||||
* <p>
|
||||
* TODO(b/136132412): add unit tests
|
||||
*/
|
||||
public void rename(@NonNull String sourcePath, @NonNull String destPath) throws IOException {
|
||||
final File source = new File(sourcePath);
|
||||
@@ -243,6 +248,7 @@ public class IncrementalManager {
|
||||
if (storage == null) {
|
||||
throw new IOException("Failed to retrieve storage from Incremental Service.");
|
||||
}
|
||||
|
||||
if (source.isFile()) {
|
||||
renameFile(storage, storagePath, source, dest);
|
||||
} else {
|
||||
@@ -304,11 +310,11 @@ public class IncrementalManager {
|
||||
*/
|
||||
public void closeStorage(@NonNull String path) {
|
||||
try {
|
||||
final int id = mService.openStorage(path);
|
||||
final int id = mNativeService.openStorage(path);
|
||||
if (id < 0) {
|
||||
return;
|
||||
}
|
||||
mService.deleteStorage(id);
|
||||
mNativeService.deleteStorage(id);
|
||||
synchronized (mStorages) {
|
||||
mStorages.remove(id);
|
||||
}
|
||||
@@ -321,7 +327,7 @@ public class IncrementalManager {
|
||||
* Checks if path is mounted on Incremental File System.
|
||||
*/
|
||||
public static boolean isIncrementalPath(@NonNull String path) {
|
||||
// TODO(b/136132412): implement native method
|
||||
// TODO(b/136132412): add jni implementation
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Provides operations on an Incremental File System directory, using IncrementalService. Example
|
||||
* usage:
|
||||
* Provides operations on an Incremental File System directory, using IncrementalServiceNative.
|
||||
* Example usage:
|
||||
*
|
||||
* <blockquote><pre>
|
||||
* IncrementalManager manager = (IncrementalManager) getSystemService(Context.INCREMENTAL_MANAGER);
|
||||
* IncrementalManager manager = (IncrementalManager) getSystemService(Context.INCREMENTAL_SERVICE);
|
||||
* IncrementalStorage storage = manager.openStorage("/path/to/incremental/dir");
|
||||
* storage.makeDirectory("subdir");
|
||||
* </pre></blockquote>
|
||||
@@ -38,10 +38,10 @@ import java.io.IOException;
|
||||
public final class IncrementalStorage {
|
||||
private static final String TAG = "IncrementalStorage";
|
||||
private final int mId;
|
||||
private final IIncrementalManager mService;
|
||||
private final IIncrementalManagerNative mService;
|
||||
|
||||
|
||||
public IncrementalStorage(@NonNull IIncrementalManager is, int id) {
|
||||
public IncrementalStorage(@NonNull IIncrementalManagerNative is, int id) {
|
||||
mService = is;
|
||||
mId = id;
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public final class IncrementalStorage {
|
||||
throws IOException {
|
||||
try {
|
||||
int res = mService.makeBindMount(mId, sourcePathUnderStorage, targetPath,
|
||||
IIncrementalManager.BIND_TEMPORARY);
|
||||
IIncrementalManagerNative.BIND_TEMPORARY);
|
||||
if (res < 0) {
|
||||
throw new IOException("bind() failed with errno " + -res);
|
||||
}
|
||||
@@ -103,7 +103,7 @@ public final class IncrementalStorage {
|
||||
throws IOException {
|
||||
try {
|
||||
int res = mService.makeBindMount(mId, sourcePathUnderStorage, targetPath,
|
||||
IIncrementalManager.BIND_PERMANENT);
|
||||
IIncrementalManagerNative.BIND_PERMANENT);
|
||||
if (res < 0) {
|
||||
throw new IOException("bind() permanent failed with errno " + -res);
|
||||
}
|
||||
@@ -274,7 +274,8 @@ public final class IncrementalStorage {
|
||||
throw new IOException("moveDir() requires that destination dir already exists.");
|
||||
}
|
||||
try {
|
||||
int res = mService.makeBindMount(mId, "", destPath, IIncrementalManager.BIND_PERMANENT);
|
||||
int res = mService.makeBindMount(mId, "", destPath,
|
||||
IIncrementalManagerNative.BIND_PERMANENT);
|
||||
if (res < 0) {
|
||||
throw new IOException("moveDir() failed at making bind mount, errno " + -res);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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 com.android.server.incremental;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.pm.DataLoaderManager;
|
||||
import android.content.pm.IDataLoader;
|
||||
import android.content.pm.IDataLoaderStatusListener;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.ShellCallback;
|
||||
import android.os.incremental.IIncrementalManager;
|
||||
import android.os.incremental.IncrementalDataLoaderParamsParcel;
|
||||
import android.os.incremental.IncrementalFileSystemControlParcel;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
|
||||
/**
|
||||
* This service has the following purposes:
|
||||
* 1) Starts the IIncrementalManager binder service.
|
||||
* 1) Starts the native IIncrementalManagerService binder service.
|
||||
* 2) Handles shell commands for "incremental" service.
|
||||
* 3) Handles binder calls from the native IIncrementalManagerService binder service and pass
|
||||
* them to a data loader binder service.
|
||||
*/
|
||||
|
||||
public class IncrementalManagerService extends IIncrementalManager.Stub {
|
||||
private static final String TAG = "IncrementalManagerService";
|
||||
private static final String BINDER_SERVICE_NAME = "incremental";
|
||||
// DataLoaderManagerService should have been started before us
|
||||
private @NonNull DataLoaderManager mDataLoaderManager;
|
||||
private long mNativeInstance;
|
||||
private final @NonNull Context mContext;
|
||||
|
||||
/**
|
||||
* Starts IIncrementalManager binder service and register to Service Manager.
|
||||
* Starts the native IIncrementalManagerNative binder service.
|
||||
*/
|
||||
public static IncrementalManagerService start(Context context) {
|
||||
IncrementalManagerService self = new IncrementalManagerService(context);
|
||||
if (self.mNativeInstance == 0) {
|
||||
return null;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
private IncrementalManagerService(Context context) {
|
||||
mContext = context;
|
||||
mDataLoaderManager = mContext.getSystemService(DataLoaderManager.class);
|
||||
ServiceManager.addService(BINDER_SERVICE_NAME, this);
|
||||
// Starts and register IIncrementalManagerNative service
|
||||
// TODO(b/136132412): add jni implementation
|
||||
}
|
||||
/**
|
||||
* Notifies native IIncrementalManager service that system is ready.
|
||||
*/
|
||||
public void systemReady() {
|
||||
// TODO(b/136132412): add jni implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds data loader service provider and binds to it. This requires PackageManager.
|
||||
*/
|
||||
@Override
|
||||
public boolean prepareDataLoader(int mountId, IncrementalFileSystemControlParcel control,
|
||||
IncrementalDataLoaderParamsParcel params,
|
||||
IDataLoaderStatusListener listener) {
|
||||
Bundle dataLoaderParams = new Bundle();
|
||||
dataLoaderParams.putCharSequence("packageName", params.packageName);
|
||||
dataLoaderParams.putParcelable("control", control);
|
||||
dataLoaderParams.putParcelable("params", params);
|
||||
DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class);
|
||||
if (dataLoaderManager == null) {
|
||||
Slog.e(TAG, "Failed to find data loader manager service");
|
||||
return false;
|
||||
}
|
||||
if (!dataLoaderManager.initializeDataLoader(mountId, dataLoaderParams, listener)) {
|
||||
Slog.e(TAG, "Failed to initialize data loader");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean startDataLoader(int mountId) {
|
||||
IDataLoader dataLoader = mDataLoaderManager.getDataLoader(mountId);
|
||||
if (dataLoader == null) {
|
||||
Slog.e(TAG, "Start failed to retrieve data loader for ID=" + mountId);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
// TODO: fix file list
|
||||
dataLoader.start(null);
|
||||
return true;
|
||||
} catch (RemoteException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyDataLoader(int mountId) {
|
||||
IDataLoader dataLoader = mDataLoaderManager.getDataLoader(mountId);
|
||||
if (dataLoader == null) {
|
||||
Slog.e(TAG, "Destroy failed to retrieve data loader for ID=" + mountId);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
dataLoader.destroy();
|
||||
} catch (RemoteException ex) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this
|
||||
@Override
|
||||
public void newFileForDataLoader(int mountId, long inode, byte[] metadata) {
|
||||
IDataLoader dataLoader = mDataLoaderManager.getDataLoader(mountId);
|
||||
if (dataLoader == null) {
|
||||
Slog.e(TAG, "Failed to retrieve data loader for ID=" + mountId);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
dataLoader.onFileCreated(inode, metadata);
|
||||
} catch (RemoteException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showHealthBlockedUI(int mountId) {
|
||||
// TODO(b/136132412): implement this
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
|
||||
FileDescriptor err, @NonNull String[] args, ShellCallback callback,
|
||||
@NonNull ResultReceiver resultReceiver) {
|
||||
(new IncrementalManagerShellCommand(mContext)).exec(
|
||||
this, in, out, err, args, callback, resultReceiver);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* 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 com.android.server.incremental;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.IIntentReceiver;
|
||||
import android.content.IIntentSender;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.InstallationFile;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.ShellCommand;
|
||||
import android.os.incremental.IncrementalDataLoaderParams;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Defines actions to handle adb commands like "adb abb incremental ...".
|
||||
*/
|
||||
public final class IncrementalManagerShellCommand extends ShellCommand {
|
||||
private static final String TAG = "IncrementalShellCommand";
|
||||
// Assuming the adb data loader is always installed on the device
|
||||
private static final String LOADER_PACKAGE_NAME = "com.android.incremental.nativeadb";
|
||||
private final @NonNull Context mContext;
|
||||
|
||||
private static final int ERROR_INVALID_ARGUMENTS = -1;
|
||||
private static final int ERROR_DATA_LOADER_INIT = -2;
|
||||
private static final int ERROR_COMMAND_EXECUTION = -3;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({ERROR_INVALID_ARGUMENTS, ERROR_DATA_LOADER_INIT, ERROR_COMMAND_EXECUTION})
|
||||
public @interface IncrementalShellCommandErrorCode {
|
||||
}
|
||||
|
||||
IncrementalManagerShellCommand(@NonNull Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onCommand(@Nullable String cmd) {
|
||||
if (cmd == null) {
|
||||
return handleDefaultCommands(null);
|
||||
}
|
||||
switch (cmd) {
|
||||
case "install-start":
|
||||
return runInstallStart();
|
||||
case "install-finish":
|
||||
return runInstallFinish();
|
||||
default:
|
||||
return handleDefaultCommands(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHelp() {
|
||||
PrintWriter pw = getOutPrintWriter();
|
||||
pw.println("Incremental Service Commands:");
|
||||
pw.println(" help");
|
||||
pw.println(" Print this help text.");
|
||||
pw.println(" install-start");
|
||||
pw.println(" Opens an installation session");
|
||||
pw.println(" install-finish SESSION_ID --file NAME:SIZE:INDEX --file NAME:SIZE:INDEX ...");
|
||||
pw.println(" Commits an installation session specified by session ID for an APK ");
|
||||
pw.println(" or a bundle of splits. Configures lib dirs or OBB files if specified.");
|
||||
}
|
||||
|
||||
private int runInstallStart() {
|
||||
final PrintWriter pw = getOutPrintWriter();
|
||||
final PackageInstaller packageInstaller =
|
||||
mContext.getPackageManager().getPackageInstaller();
|
||||
if (packageInstaller == null) {
|
||||
pw.println("Failed to get PackageInstaller.");
|
||||
return ERROR_COMMAND_EXECUTION;
|
||||
}
|
||||
|
||||
final Map<String, ParcelFileDescriptor> dataLoaderDynamicArgs = getDataLoaderDynamicArgs();
|
||||
if (dataLoaderDynamicArgs == null) {
|
||||
pw.println("File names and sizes don't match.");
|
||||
return ERROR_DATA_LOADER_INIT;
|
||||
}
|
||||
final IncrementalDataLoaderParams params = new IncrementalDataLoaderParams(
|
||||
"", LOADER_PACKAGE_NAME, dataLoaderDynamicArgs);
|
||||
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
|
||||
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
|
||||
sessionParams.installFlags |= PackageManager.INSTALL_ALL_USERS;
|
||||
// Replace existing if same package is already installed
|
||||
sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
|
||||
sessionParams.setIncrementalParams(params);
|
||||
|
||||
try {
|
||||
int sessionId = packageInstaller.createSession(sessionParams);
|
||||
pw.println("Successfully opened session: sessionId = " + sessionId);
|
||||
} catch (Exception ex) {
|
||||
pw.println("Failed to create session.");
|
||||
return ERROR_COMMAND_EXECUTION;
|
||||
} finally {
|
||||
try {
|
||||
for (Map.Entry<String, ParcelFileDescriptor> nfd
|
||||
: dataLoaderDynamicArgs.entrySet()) {
|
||||
nfd.getValue().close();
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int runInstallFinish() {
|
||||
final PrintWriter pw = getOutPrintWriter();
|
||||
final int sessionId = parseInt(getNextArgRequired());
|
||||
final List<InstallationFile> installationFiles = parseFileArgs(pw);
|
||||
if (installationFiles == null) {
|
||||
pw.println("Must specify at least one file to install.");
|
||||
return ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
final int numFiles = installationFiles.size();
|
||||
if (numFiles == 0) {
|
||||
pw.println("Must specify at least one file to install.");
|
||||
return ERROR_INVALID_ARGUMENTS;
|
||||
}
|
||||
|
||||
final PackageInstaller packageInstaller = mContext.getPackageManager()
|
||||
.getPackageInstaller();
|
||||
if (packageInstaller == null) {
|
||||
pw.println("Failed to get PackageInstaller.");
|
||||
return ERROR_COMMAND_EXECUTION;
|
||||
}
|
||||
|
||||
final LocalIntentReceiver localReceiver = new LocalIntentReceiver();
|
||||
boolean success = false;
|
||||
|
||||
PackageInstaller.Session session = null;
|
||||
try {
|
||||
session = packageInstaller.openSession(sessionId);
|
||||
for (int i = 0; i < numFiles; i++) {
|
||||
InstallationFile file = installationFiles.get(i);
|
||||
session.addFile(file.getName(), file.getSize(), file.getMetadata());
|
||||
}
|
||||
session.commit(localReceiver.getIntentSender());
|
||||
final Intent result = localReceiver.getResult();
|
||||
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
|
||||
PackageInstaller.STATUS_FAILURE);
|
||||
if (status == PackageInstaller.STATUS_SUCCESS) {
|
||||
success = true;
|
||||
pw.println("Success");
|
||||
return 0;
|
||||
} else {
|
||||
pw.println("Failure ["
|
||||
+ result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
|
||||
return ERROR_COMMAND_EXECUTION;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace(pw);
|
||||
return ERROR_COMMAND_EXECUTION;
|
||||
} finally {
|
||||
if (!success) {
|
||||
try {
|
||||
if (session != null) {
|
||||
session.abandon();
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class LocalIntentReceiver {
|
||||
private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
|
||||
|
||||
private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
|
||||
@Override
|
||||
public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
|
||||
IIntentReceiver finishedReceiver, String requiredPermission,
|
||||
Bundle options) {
|
||||
try {
|
||||
mResult.offer(intent, 5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public IntentSender getIntentSender() {
|
||||
return new IntentSender((IIntentSender) mLocalSender);
|
||||
}
|
||||
|
||||
public Intent getResult() {
|
||||
try {
|
||||
return mResult.take();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Helpers. */
|
||||
private Map<String, ParcelFileDescriptor> getDataLoaderDynamicArgs() {
|
||||
Map<String, ParcelFileDescriptor> dataLoaderDynamicArgs = new HashMap<>();
|
||||
final FileDescriptor outFd = getOutFileDescriptor();
|
||||
final FileDescriptor inFd = getInFileDescriptor();
|
||||
try {
|
||||
dataLoaderDynamicArgs.put("inFd", ParcelFileDescriptor.dup(inFd));
|
||||
dataLoaderDynamicArgs.put("outFd", ParcelFileDescriptor.dup(outFd));
|
||||
return dataLoaderDynamicArgs;
|
||||
} catch (Exception ex) {
|
||||
Slog.e(TAG, "Failed to dup FDs");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private long parseLong(String arg) {
|
||||
long result = -1;
|
||||
try {
|
||||
result = Long.parseLong(arg);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int parseInt(String arg) {
|
||||
int result = -1;
|
||||
try {
|
||||
result = Integer.parseInt(arg);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<InstallationFile> parseFileArgs(PrintWriter pw) {
|
||||
List<InstallationFile> fileList = new ArrayList<>();
|
||||
String opt;
|
||||
while ((opt = getNextOption()) != null) {
|
||||
switch (opt) {
|
||||
case "--file": {
|
||||
final String fileArgs = getNextArgRequired();
|
||||
final String[] args = fileArgs.split(":");
|
||||
if (args.length != 3) {
|
||||
pw.println("Invalid file args: " + fileArgs);
|
||||
return null;
|
||||
}
|
||||
final String name = args[0];
|
||||
final long size = parseLong(args[1]);
|
||||
if (size < 0) {
|
||||
pw.println("Invalid file size in: " + fileArgs);
|
||||
return null;
|
||||
}
|
||||
final long index = parseLong(args[2]);
|
||||
if (index < 0) {
|
||||
pw.println("Invalid file index in: " + fileArgs);
|
||||
return null;
|
||||
}
|
||||
final byte[] metadata = String.valueOf(index).getBytes(StandardCharsets.UTF_8);
|
||||
fileList.add(new InstallationFile(name, size, metadata));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fileList;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user