diff --git a/Android.bp b/Android.bp index 890547bff9ef3..5c99b8cdec6a6 100644 --- a/Android.bp +++ b/Android.bp @@ -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", diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index fe774f8f639a8..85027d9a1e18d 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -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 diff --git a/core/java/android/os/incremental/IIncrementalManager.aidl b/core/java/android/os/incremental/IIncrementalManager.aidl index d6446d485af51..f84d7efe06b1f 100644 --- a/core/java/android/os/incremental/IIncrementalManager.aidl +++ b/core/java/android/os/incremental/IIncrementalManager.aidl @@ -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); } diff --git a/core/java/android/os/incremental/IIncrementalManagerNative.aidl b/core/java/android/os/incremental/IIncrementalManagerNative.aidl new file mode 100644 index 0000000000000..d9c7c6b5cc212 --- /dev/null +++ b/core/java/android/os/incremental/IIncrementalManagerNative.aidl @@ -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); +} diff --git a/core/java/android/os/incremental/IIncrementalServiceProxy.aidl b/core/java/android/os/incremental/IIncrementalServiceProxy.aidl deleted file mode 100644 index ffff52e5aac90..0000000000000 --- a/core/java/android/os/incremental/IIncrementalServiceProxy.aidl +++ /dev/null @@ -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); -} diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 5aabf86e17e6d..c30f5589a8353 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -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: * *
- * IncrementalManager manager = (IncrementalManager) getSystemService(Context.INCREMENTAL_MANAGER);
+ * IncrementalManager manager = (IncrementalManager) getSystemService(Context.INCREMENTAL_SERVICE);
* IncrementalStorage storage = manager.openStorage("/path/to/incremental/dir");
*
*
* @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+ * 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; } } diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java index 2bf89ed7f7e80..275086832c515 100644 --- a/core/java/android/os/incremental/IncrementalStorage.java +++ b/core/java/android/os/incremental/IncrementalStorage.java @@ -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: * *
- * 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");
*
@@ -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);
}
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerService.java b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
new file mode 100644
index 0000000000000..1b1a292936397
--- /dev/null
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
@@ -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);
+ }
+}
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
new file mode 100644
index 0000000000000..d35e806b2685c
--- /dev/null
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
@@ -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