Migrating Incremental* APIs to PackageManager APIs.
Step 2, merging Data Loader params.
Test: builds and flashes
Bug: b/136132412
Change-Id: I2102554316dadcdcb49790c133ece110c43c29b3
diff --git a/Android.bp b/Android.bp
index 536f688..742a70e5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -833,6 +833,7 @@
name: "dataloader_aidl",
srcs: [
"core/java/android/content/pm/DataLoaderParamsParcel.aidl",
+ "core/java/android/content/pm/DataLoaderType.aidl",
"core/java/android/content/pm/FileSystemControlParcel.aidl",
"core/java/android/content/pm/IDataLoaderStatusListener.aidl",
"core/java/android/content/pm/IPackageInstallerSessionFileSystemConnector.aidl",
diff --git a/api/system-current.txt b/api/system-current.txt
index 23dd652..dc27f22 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1934,10 +1934,12 @@
}
public class DataLoaderParams {
- ctor public DataLoaderParams(@NonNull String, @NonNull String, @Nullable java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>);
+ method @NonNull public static final android.content.pm.DataLoaderParams forIncremental(@NonNull android.content.ComponentName, @NonNull String, @Nullable java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>);
+ method @NonNull public static final android.content.pm.DataLoaderParams forStreaming(@NonNull android.content.ComponentName, @NonNull String);
+ method @NonNull public final String getArguments();
+ method @NonNull public final android.content.ComponentName getComponentName();
method @NonNull public final java.util.Map<java.lang.String,android.os.ParcelFileDescriptor> getDynamicArgs();
- method @NonNull public final String getPackageName();
- method @NonNull public final String getStaticArgs();
+ method @NonNull public final int getType();
}
public final class InstantAppInfo implements android.os.Parcelable {
@@ -2050,11 +2052,11 @@
public static class PackageInstaller.SessionParams implements android.os.Parcelable {
method @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public void setAllocateAggressive(boolean);
method @Deprecated public void setAllowDowngrade(boolean);
+ method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams);
method public void setDontKillApp(boolean);
method public void setEnableRollback(boolean);
method public void setEnableRollback(boolean, int);
method @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) public void setGrantedRuntimePermissions(String[]);
- method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setIncrementalParams(@NonNull android.content.pm.DataLoaderParams);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex();
method public void setInstallAsInstantApp(boolean);
method public void setInstallAsVirtualPreload();
diff --git a/core/java/android/content/pm/DataLoaderParams.java b/core/java/android/content/pm/DataLoaderParams.java
index af4b99a..60d7bb3 100644
--- a/core/java/android/content/pm/DataLoaderParams.java
+++ b/core/java/android/content/pm/DataLoaderParams.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.ComponentName;
import android.os.ParcelFileDescriptor;
import java.util.Arrays;
@@ -26,7 +27,7 @@
import java.util.stream.Collectors;
/**
- * This class represents the parameters used to configure an Incremental Data Loader.
+ * This class represents the parameters used to configure a Data Loader.
*
* WARNING: This is a system API to aid internal development.
* Use at your own risk. It will change or be removed without warning.
@@ -34,13 +35,41 @@
*/
@SystemApi
public class DataLoaderParams {
- @NonNull private final DataLoaderParamsParcel mData;
+ @NonNull
+ private final DataLoaderParamsParcel mData;
- public DataLoaderParams(@NonNull String url, @NonNull String packageName,
+ /**
+ * Creates and populates set of Data Loader parameters for Streaming installation.
+ *
+ * @param componentName Data Loader component supporting Streaming installation.
+ * @param arguments free form installation arguments
+ */
+ public static final @NonNull DataLoaderParams forStreaming(@NonNull ComponentName componentName,
+ @NonNull String arguments) {
+ return new DataLoaderParams(DataLoaderType.STREAMING, componentName, arguments, null);
+ }
+
+ /**
+ * Creates and populates set of Data Loader parameters for Incremental installation.
+ *
+ * @param componentName Data Loader component supporting Incremental installation.
+ * @param arguments free form installation arguments
+ * @param namedFds TODO(b/146080380) remove
+ */
+ public static final @NonNull DataLoaderParams forIncremental(
+ @NonNull ComponentName componentName, @NonNull String arguments,
@Nullable Map<String, ParcelFileDescriptor> namedFds) {
+ return new DataLoaderParams(DataLoaderType.INCREMENTAL, componentName, arguments, namedFds);
+ }
+
+ /** @hide */
+ public DataLoaderParams(@NonNull @DataLoaderType int type, @NonNull ComponentName componentName,
+ @NonNull String arguments, @Nullable Map<String, ParcelFileDescriptor> namedFds) {
DataLoaderParamsParcel data = new DataLoaderParamsParcel();
- data.staticArgs = url;
- data.packageName = packageName;
+ data.type = type;
+ data.packageName = componentName.getPackageName();
+ data.className = componentName.getClassName();
+ data.arguments = arguments;
if (namedFds == null || namedFds.isEmpty()) {
data.dynamicArgs = new NamedParcelFileDescriptor[0];
} else {
@@ -56,39 +85,42 @@
mData = data;
}
- /**
- * @hide
- */
- public DataLoaderParams(@NonNull DataLoaderParamsParcel data) {
+ /** @hide */
+ DataLoaderParams(@NonNull DataLoaderParamsParcel data) {
mData = data;
}
- /**
- * @return static server's URL
- */
- public final @NonNull String getStaticArgs() {
- return mData.staticArgs;
- }
-
- /**
- * @return data loader's package name
- */
- public final @NonNull String getPackageName() {
- return mData.packageName;
- }
-
- /**
- * @hide
- */
+ /** @hide */
public final @NonNull DataLoaderParamsParcel getData() {
return mData;
}
/**
- * @return data loader's dynamic arguments such as file descriptors
+ * @return data loader type
+ */
+ public final @NonNull @DataLoaderType int getType() {
+ return mData.type;
+ }
+
+ /**
+ * @return data loader's component name
+ */
+ public final @NonNull ComponentName getComponentName() {
+ return new ComponentName(mData.packageName, mData.className);
+ }
+
+ /**
+ * @return data loader's arguments
+ */
+ public final @NonNull String getArguments() {
+ return mData.arguments;
+ }
+
+ /**
+ * @return data loader's dynamic arguments such as file descriptors TODO: remove
*/
public final @NonNull Map<String, ParcelFileDescriptor> getDynamicArgs() {
return Arrays.stream(mData.dynamicArgs).collect(
- Collectors.toMap(p->p.name, p->p.fd));
+ Collectors.toMap(p -> p.name, p -> p.fd));
}
}
diff --git a/core/java/android/content/pm/DataLoaderParamsParcel.aidl b/core/java/android/content/pm/DataLoaderParamsParcel.aidl
index 3316398..e05843b 100644
--- a/core/java/android/content/pm/DataLoaderParamsParcel.aidl
+++ b/core/java/android/content/pm/DataLoaderParamsParcel.aidl
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.content.pm.DataLoaderType;
import android.content.pm.NamedParcelFileDescriptor;
/**
@@ -23,7 +24,9 @@
* @hide
*/
parcelable DataLoaderParamsParcel {
+ DataLoaderType type;
@utf8InCpp String packageName;
- @utf8InCpp String staticArgs;
+ @utf8InCpp String className;
+ @utf8InCpp String arguments;
NamedParcelFileDescriptor[] dynamicArgs;
}
diff --git a/core/java/android/content/pm/DataLoaderType.aidl b/core/java/android/content/pm/DataLoaderType.aidl
new file mode 100644
index 0000000..7d726f5
--- /dev/null
+++ b/core/java/android/content/pm/DataLoaderType.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.content.pm;
+
+/**
+ * Types of Data Loader for an installation session.
+ * @hide
+ */
+@Backing(type="int")
+enum DataLoaderType {
+ /**
+ * Default value, legacy installation.
+ */
+ NONE = 0,
+ /**
+ * Streaming installation using data loader.
+ */
+ STREAMING = 1,
+ /**
+ * Streaming installation using Incremental FileSystem.
+ */
+ INCREMENTAL = 2,
+}
diff --git a/core/java/android/content/pm/IDataLoader.aidl b/core/java/android/content/pm/IDataLoader.aidl
index c65bd6a..b5baa93 100644
--- a/core/java/android/content/pm/IDataLoader.aidl
+++ b/core/java/android/content/pm/IDataLoader.aidl
@@ -27,7 +27,9 @@
*/
oneway interface IDataLoader {
void create(int id, in Bundle params, IDataLoaderStatusListener listener);
- void start(in List<InstallationFile> fileInfos);
+ void start();
void stop();
void destroy();
+
+ void prepareImage(in List<InstallationFile> addedFiles, in List<String> removedFiles);
}
diff --git a/core/java/android/content/pm/IDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
index a60d6ee..5011faa 100644
--- a/core/java/android/content/pm/IDataLoaderStatusListener.aidl
+++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
@@ -22,13 +22,18 @@
*/
oneway interface IDataLoaderStatusListener {
/** Data loader status */
- const int DATA_LOADER_READY = 0;
- const int DATA_LOADER_NOT_READY = 1;
- const int DATA_LOADER_RUNNING = 2;
+ const int DATA_LOADER_CREATED = 0;
+ const int DATA_LOADER_DESTROYED = 1;
+
+ const int DATA_LOADER_STARTED = 2;
const int DATA_LOADER_STOPPED = 3;
- const int DATA_LOADER_SLOW_CONNECTION = 4;
- const int DATA_LOADER_NO_CONNECTION = 5;
- const int DATA_LOADER_CONNECTION_OK = 6;
+
+ const int DATA_LOADER_IMAGE_READY = 4;
+ const int DATA_LOADER_IMAGE_NOT_READY = 5;
+
+ const int DATA_LOADER_SLOW_CONNECTION = 6;
+ const int DATA_LOADER_NO_CONNECTION = 7;
+ const int DATA_LOADER_CONNECTION_OK = 8;
/** Data loader status callback */
void onStatusChanged(in int dataLoaderId, in int status);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 3d6d849..e4a0bc0 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1461,10 +1461,7 @@
/** {@hide} */
public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
/** {@hide} */
- public DataLoaderParams incrementalParams;
- /** TODO(b/146080380): add a class name to make it fully compatible with ComponentName.
- * {@hide} */
- public String dataLoaderPackageName;
+ public DataLoaderParams dataLoaderParams;
/** {@hide} */
public int rollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE;
@@ -1503,10 +1500,8 @@
DataLoaderParamsParcel dataLoaderParamsParcel = source.readParcelable(
DataLoaderParamsParcel.class.getClassLoader());
if (dataLoaderParamsParcel != null) {
- incrementalParams = new DataLoaderParams(
- dataLoaderParamsParcel);
+ dataLoaderParams = new DataLoaderParams(dataLoaderParamsParcel);
}
- dataLoaderPackageName = source.readString();
rollbackDataPolicy = source.readInt();
}
@@ -1531,8 +1526,7 @@
ret.isMultiPackage = isMultiPackage;
ret.isStaged = isStaged;
ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
- ret.incrementalParams = incrementalParams;
- ret.dataLoaderPackageName = dataLoaderPackageName;
+ ret.dataLoaderParams = dataLoaderParams;
ret.rollbackDataPolicy = rollbackDataPolicy;
return ret;
}
@@ -1893,29 +1887,18 @@
}
/**
- * Set Incremental data loader params.
+ * Set the data loader params for the session.
+ * This also switches installation into data provider mode and disallow direct writes into
+ * staging folder.
+ *
* WARNING: This is a system API to aid internal development.
* Use at your own risk. It will change or be removed without warning.
* {@hide}
*/
@SystemApi
@RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
- public void setIncrementalParams(@NonNull DataLoaderParams incrementalParams) {
- this.incrementalParams = incrementalParams;
- }
-
- /**
- * Set the data provider params for the session.
- * This also switches installation into callback mode and disallow direct writes into
- * staging folder.
- * TODO(b/146080380): unify dataprovider params with Incremental.
- *
- * @param dataLoaderPackageName name of the dataLoader package
- * {@hide}
- */
- @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
- public void setDataLoaderPackageName(String dataLoaderPackageName) {
- this.dataLoaderPackageName = dataLoaderPackageName;
+ public void setDataLoaderParams(@NonNull DataLoaderParams dataLoaderParams) {
+ this.dataLoaderParams = dataLoaderParams;
}
/** {@hide} */
@@ -1938,7 +1921,7 @@
pw.printPair("isMultiPackage", isMultiPackage);
pw.printPair("isStaged", isStaged);
pw.printPair("requiredInstalledVersionCode", requiredInstalledVersionCode);
- pw.printPair("dataLoaderPackageName", dataLoaderPackageName);
+ pw.printPair("dataLoaderParams", dataLoaderParams);
pw.printPair("rollbackDataPolicy", rollbackDataPolicy);
pw.println();
}
@@ -1969,12 +1952,11 @@
dest.writeBoolean(isMultiPackage);
dest.writeBoolean(isStaged);
dest.writeLong(requiredInstalledVersionCode);
- if (incrementalParams != null) {
- dest.writeParcelable(incrementalParams.getData(), flags);
+ if (dataLoaderParams != null) {
+ dest.writeParcelable(dataLoaderParams.getData(), flags);
} else {
dest.writeParcelable(null, flags);
}
- dest.writeString(dataLoaderPackageName);
dest.writeInt(rollbackDataPolicy);
}
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index fb94fc9..2138d553 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -87,8 +87,8 @@
mPackageName = packageName;
mStageDir = stageDir;
mIncrementalManager = incrementalManager;
- if (dataLoaderParams.getPackageName().equals("local")) {
- final String incrementalPath = dataLoaderParams.getStaticArgs();
+ if (dataLoaderParams.getComponentName().getPackageName().equals("local")) {
+ final String incrementalPath = dataLoaderParams.getArguments();
mDefaultStorage = mIncrementalManager.openStorage(incrementalPath);
mDefaultDir = incrementalPath;
return;
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index 54a4fa6..75f252e 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -16,7 +16,6 @@
package android.service.dataloader;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -27,19 +26,16 @@
import android.content.pm.FileSystemControlParcel;
import android.content.pm.IDataLoader;
import android.content.pm.IDataLoaderStatusListener;
-import android.content.pm.IPackageInstallerSessionFileSystemConnector;
import android.content.pm.InstallationFile;
import android.content.pm.NamedParcelFileDescriptor;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
import android.util.ExceptionUtils;
import android.util.Slog;
import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
import java.util.List;
/**
@@ -55,88 +51,35 @@
*/
@SystemApi
public abstract class DataLoaderService extends Service {
- private static final String TAG = "IncrementalDataLoaderService";
+ private static final String TAG = "DataLoaderService";
private final DataLoaderBinderService mBinder = new DataLoaderBinderService();
- /** @hide */
- public static final int DATA_LOADER_READY =
- IDataLoaderStatusListener.DATA_LOADER_READY;
- /** @hide */
- public static final int DATA_LOADER_NOT_READY =
- IDataLoaderStatusListener.DATA_LOADER_NOT_READY;
- /** @hide */
- public static final int DATA_LOADER_RUNNING =
- IDataLoaderStatusListener.DATA_LOADER_RUNNING;
- /** @hide */
- public static final int DATA_LOADER_STOPPED =
- IDataLoaderStatusListener.DATA_LOADER_STOPPED;
- /** @hide */
- public static final int DATA_LOADER_SLOW_CONNECTION =
- IDataLoaderStatusListener.DATA_LOADER_SLOW_CONNECTION;
- /** @hide */
- public static final int DATA_LOADER_NO_CONNECTION =
- IDataLoaderStatusListener.DATA_LOADER_NO_CONNECTION;
- /** @hide */
- public static final int DATA_LOADER_CONNECTION_OK =
- IDataLoaderStatusListener.DATA_LOADER_CONNECTION_OK;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"DATA_LOADER_"}, value = {
- DATA_LOADER_READY,
- DATA_LOADER_NOT_READY,
- DATA_LOADER_RUNNING,
- DATA_LOADER_STOPPED,
- DATA_LOADER_SLOW_CONNECTION,
- DATA_LOADER_NO_CONNECTION,
- DATA_LOADER_CONNECTION_OK
- })
- public @interface DataLoaderStatus {
- }
-
/**
- * Managed DataLoader interface. Each instance corresponds to a single Incremental File System
- * instance.
+ * Managed DataLoader interface. Each instance corresponds to a single installation session.
* @hide
*/
- public abstract static class DataLoader {
+ public interface DataLoader {
/**
- * A virtual constructor used to do simple initialization. Not ready to serve any data yet.
- * All heavy-lifting has to be done in onStart.
+ * A virtual constructor.
*
- * @param params Data loader configuration parameters.
- * @param connector IncFS API wrapper.
- * @param listener Used for reporting internal state to IncrementalService.
+ * @param dataLoaderParams parameters set in the installation session
+ * @param connector FS API wrapper
* @return True if initialization of a Data Loader was successful. False will be reported to
- * IncrementalService and can cause an unmount of an IFS instance.
+ * PackageManager and fail the installation
*/
- public abstract boolean onCreate(@NonNull DataLoaderParams params,
- @NonNull FileSystemConnector connector,
- @NonNull StatusListener listener);
+ boolean onCreate(@NonNull DataLoaderParams dataLoaderParams,
+ @NonNull FileSystemConnector connector);
/**
- * Start the data loader. After this method returns data loader is considered to be ready to
- * receive callbacks from IFS, supply data via connector and send status updates via
- * callbacks.
+ * Prepare installation image. After this method succeeds installer will validate the files
+ * and continue installation.
*
- * @return True if Data Loader was able to start. False will be reported to
- * IncrementalService and can cause an unmount of an IFS instance.
+ * @param addedFiles list of files created in this installation session.
+ * @param removedFiles list of files removed in this installation session.
+ * @return false if unable to create and populate all addedFiles.
*/
- public abstract boolean onStart();
-
- /**
- * Stop the data loader. Use to stop any additional threads and free up resources. Data
- * loader is not longer responsible for supplying data. Start/Stop pair can be called
- * multiple times e.g. if IFS detects corruption and data needs to be re-loaded.
- */
- public abstract void onStop();
-
- /**
- * Virtual destructor. Use to cleanup all internal state. After this method returns, the
- * data loader can no longer use connector or callbacks. For any additional operations with
- * this instance of IFS a new DataLoader will be created using createDataLoader method.
- */
- public abstract void onDestroy();
+ boolean onPrepareImage(Collection<InstallationFile> addedFiles,
+ Collection<String> removedFiles);
}
/**
@@ -145,7 +88,9 @@
* @return An instance of a DataLoader.
* @hide
*/
- public abstract @Nullable DataLoader onCreateDataLoader();
+ public @Nullable DataLoader onCreateDataLoader() {
+ return null;
+ }
/**
* @hide
@@ -160,148 +105,125 @@
@Override
public void create(int id, @NonNull Bundle options,
@NonNull IDataLoaderStatusListener listener)
- throws IllegalArgumentException, RuntimeException {
+ throws IllegalArgumentException, RuntimeException {
mId = id;
- final DataLoaderParamsParcel params = options.getParcelable("params");
+ final DataLoaderParamsParcel params = options.getParcelable("params");
if (params == null) {
- throw new IllegalArgumentException("Must specify Incremental data loader params");
+ throw new IllegalArgumentException("Must specify data loader params");
}
- final FileSystemControlParcel control =
- options.getParcelable("control");
+ final FileSystemControlParcel control = options.getParcelable("control");
if (control == null) {
- throw new IllegalArgumentException("Must specify Incremental control parcel");
+ throw new IllegalArgumentException("Must specify control parcel");
}
- mStatusListener = listener;
try {
if (!nativeCreateDataLoader(id, control, params, listener)) {
Slog.e(TAG, "Failed to create native loader for " + mId);
}
} catch (Exception ex) {
+ Slog.e(TAG, "Failed to create native loader for " + mId, ex);
destroy();
throw new RuntimeException(ex);
} finally {
// Closing FDs.
- if (control.incremental.cmd != null) {
- try {
- control.incremental.cmd.close();
- } catch (IOException e) {
- Slog.e(TAG, "Failed to close IncFs CMD file descriptor " + e);
+ if (control.incremental != null) {
+ if (control.incremental.cmd != null) {
+ try {
+ control.incremental.cmd.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to close IncFs CMD file descriptor " + e);
+ }
+ }
+ if (control.incremental.log != null) {
+ try {
+ control.incremental.log.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to close IncFs LOG file descriptor " + e);
+ }
}
}
- if (control.incremental.log != null) {
- try {
- control.incremental.log.close();
- } catch (IOException e) {
- Slog.e(TAG, "Failed to close IncFs LOG file descriptor " + e);
- }
- }
- NamedParcelFileDescriptor[] fds = params.dynamicArgs;
- for (NamedParcelFileDescriptor nfd : fds) {
- try {
- nfd.fd.close();
- } catch (IOException e) {
- Slog.e(TAG,
- "Failed to close DynamicArgs parcel file descriptor " + e);
+ if (params.dynamicArgs != null) {
+ NamedParcelFileDescriptor[] fds = params.dynamicArgs;
+ for (NamedParcelFileDescriptor nfd : fds) {
+ try {
+ nfd.fd.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to close DynamicArgs parcel file descriptor " + e);
+ }
}
}
}
}
@Override
- public void start(List<InstallationFile> fileInfos) {
+ public void start() {
if (!nativeStartDataLoader(mId)) {
- Slog.e(TAG, "Failed to start loader: loader not found for " + mId);
+ Slog.e(TAG, "Failed to start loader: " + mId);
}
}
@Override
public void stop() {
if (!nativeStopDataLoader(mId)) {
- Slog.w(TAG, "Failed to stop loader: loader not found for " + mId);
+ Slog.w(TAG, "Failed to stop loader: " + mId);
}
}
@Override
public void destroy() {
if (!nativeDestroyDataLoader(mId)) {
- Slog.w(TAG, "Failed to destroy loader: loader not found for " + mId);
+ Slog.w(TAG, "Failed to destroy loader: " + mId);
+ }
+ }
+
+ @Override
+ public void prepareImage(List<InstallationFile> addedFiles, List<String> removedFiles) {
+ if (!nativePrepareImage(mId, addedFiles, removedFiles)) {
+ Slog.w(TAG, "Failed to destroy loader: " + mId);
}
}
}
/**
- *
* Used by the DataLoaderService implementations.
*
* @hide
*/
public static final class FileSystemConnector {
/**
- * Creates a wrapper for an installation session connector.
+ * Create a wrapper for a native instance.
+ *
* @hide
*/
- FileSystemConnector(IPackageInstallerSessionFileSystemConnector connector) {
- mConnector = connector;
+ FileSystemConnector(long nativeInstance) {
+ mNativeInstance = nativeInstance;
}
/**
* Write data to an installation file from an arbitrary FD.
*
- * @param name name of file previously added to the installation session.
- * @param offsetBytes offset into the file to begin writing at, or 0 to
- * start at the beginning of the file.
- * @param lengthBytes total size of the file being written, used to
- * preallocate the underlying disk space, or -1 if unknown.
- * The system may clear various caches as needed to allocate
- * this space.
- * @param incomingFd FD to read bytes from.
- * @throws IOException if trouble opening the file for writing, such as
- * lack of disk space or unavailable media.
+ * @param name name of file previously added to the installation session.
+ * @param offsetBytes offset into the file to begin writing at, or 0 to start at the
+ * beginning of the file.
+ * @param lengthBytes total size of the file being written, used to preallocate the
+ * underlying disk space, or -1 if unknown. The system may clear various
+ * caches as needed to allocate this space.
+ * @param incomingFd FD to read bytes from.
+ * @throws IOException if trouble opening the file for writing, such as lack of disk space
+ * or unavailable media.
*/
public void writeData(String name, long offsetBytes, long lengthBytes,
ParcelFileDescriptor incomingFd) throws IOException {
try {
- mConnector.writeData(name, offsetBytes, lengthBytes, incomingFd);
+ nativeWriteData(mNativeInstance, name, offsetBytes, lengthBytes, incomingFd);
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
}
}
- private final IPackageInstallerSessionFileSystemConnector mConnector;
- }
-
- /**
- * Wrapper for native reporting DataLoader statuses.
- * @hide
- */
- public static final class StatusListener {
- /**
- * Creates a wrapper for a native instance.
- * @hide
- */
- StatusListener(long nativeInstance) {
- mNativeInstance = nativeInstance;
- }
-
- /**
- * Report the status of DataLoader. Used for system-wide notifications e.g., disabling
- * applications which rely on this data loader to function properly.
- *
- * @param status status to report.
- * @return True if status was reported successfully.
- */
- public boolean onStatusChanged(@DataLoaderStatus int status) {
- return nativeReportStatus(mNativeInstance, status);
- }
-
private final long mNativeInstance;
}
- private IDataLoaderStatusListener mStatusListener = null;
-
/* Native methods */
private native boolean nativeCreateDataLoader(int storageId,
@NonNull FileSystemControlParcel control,
@@ -314,5 +236,10 @@
private native boolean nativeDestroyDataLoader(int storageId);
- private static native boolean nativeReportStatus(long nativeInstance, int status);
+ private native boolean nativePrepareImage(int storageId,
+ Collection<InstallationFile> addedFiles, Collection<String> removedFiles);
+
+ private static native void nativeWriteData(long nativeInstance, String name, long offsetBytes,
+ long lengthBytes, ParcelFileDescriptor incomingFd);
+
}
diff --git a/core/jni/android_service_DataLoaderService.cpp b/core/jni/android_service_DataLoaderService.cpp
index 381b386..a62d127 100644
--- a/core/jni/android_service_DataLoaderService.cpp
+++ b/core/jni/android_service_DataLoaderService.cpp
@@ -51,13 +51,19 @@
}
-static jboolean nativeReportStatus(JNIEnv* env,
- jobject clazz,
- jlong self,
- jint status) {
- auto listener = (DataLoaderStatusListenerPtr)self;
- return DataLoader_StatusListener_reportStatus(listener,
- (DataLoaderStatus)status);
+static jboolean nativePrepareImage(JNIEnv* env, jobject thiz, jint storageId, jobject addedFiles, jobject removedFiles) {
+ return DataLoaderService_OnPrepareImage(storageId, addedFiles, removedFiles);
+}
+
+static void nativeWriteData(JNIEnv* env,
+ jobject clazz,
+ jlong self,
+ jstring name,
+ jlong offsetBytes,
+ jlong lengthBytes,
+ jobject incomingFd) {
+ auto connector = (DataLoaderFilesystemConnectorPtr)self;
+ return DataLoader_FilesystemConnector_writeData(connector, name, offsetBytes, lengthBytes, incomingFd);
}
static const JNINativeMethod dlc_method_table[] = {
@@ -69,7 +75,8 @@
{"nativeStartDataLoader", "(I)Z", (void*)nativeStartDataLoader},
{"nativeStopDataLoader", "(I)Z", (void*)nativeStopDataLoader},
{"nativeDestroyDataLoader", "(I)Z", (void*)nativeDestroyDataLoader},
- {"nativeReportStatus", "(JI)Z", (void*)nativeReportStatus},
+ {"nativePrepareImage", "(ILjava/util/Collection;Ljava/util/Collection;)Z", (void*)nativePrepareImage},
+ {"nativeWriteData", "(JLjava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V", (void*)nativeWriteData},
};
} // namespace
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1165d2d..44a902c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4113,7 +4113,7 @@
<permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"
android:protectionLevel="signature|privileged" />
- <!-- Must be required by intent filter verifier receiver, to ensure that only the
+ <!-- Must be required by intent filter verifier rintent-filtereceiver, to ensure that only the
system can interact with it.
@hide
-->
@@ -5143,6 +5143,12 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
-</application>
+ <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader">
+ <intent-filter>
+ <action android:name="android.intent.action.LOAD_DATA" />
+ </intent-filter>
+ </service>
+
+ </application>
</manifest>
diff --git a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
index de92fcd5..4e49302 100644
--- a/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
+++ b/packages/Incremental/NativeAdbDataLoader/jni/com_android_incremental_nativeadb_DataLoaderService.cpp
@@ -177,8 +177,7 @@
android::dataloader::ServiceParamsPtr) final {
CHECK(ifs) << "ifs can't be null";
CHECK(statusListener) << "statusListener can't be null";
- ALOGE("[AdbDataLoader] onCreate: %s/%s/%d", params.staticArgs().c_str(),
- params.packageName().c_str(), (int)params.dynamicArgs().size());
+ ALOGE("[AdbDataLoader] onCreate: %d/%s/%s/%s/%d", params.type(), params.packageName().c_str(), params.className().c_str(), params.arguments().c_str(), (int)params.dynamicArgs().size());
if (params.dynamicArgs().empty()) {
ALOGE("[AdbDataLoader] Invalid DataLoaderParams. Need in/out FDs.");
@@ -204,7 +203,7 @@
}
std::string logFile;
- if (const auto packageName = extractPackageName(params.staticArgs()); !packageName.empty()) {
+ if (const auto packageName = extractPackageName(params.arguments()); !packageName.empty()) {
logFile = android::base::GetProperty("adb.readlog." + packageName, "");
}
if (logFile.empty()) {
@@ -288,8 +287,7 @@
"inode=%d. Ignore.",
static_cast<int>(ino));
mRequestedFiles.erase(fileId);
- mStatusListener->reportStatus(
- INCREMENTAL_DATA_LOADER_NO_CONNECTION);
+ mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
}
}
sendRequest(mOutFd, BLOCK_MISSING, fileId, blockIdx);
@@ -337,7 +335,7 @@
}
if (res < 0) {
ALOGE("[AdbDataLoader] failed to poll. Abort.");
- mStatusListener->reportStatus(INCREMENTAL_DATA_LOADER_NO_CONNECTION);
+ mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
break;
}
if (res == mEventFd) {
@@ -346,7 +344,7 @@
}
if (!readChunk(mInFd, data)) {
ALOGE("[AdbDataLoader] failed to read a message. Abort.");
- mStatusListener->reportStatus(INCREMENTAL_DATA_LOADER_NO_CONNECTION);
+ mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
break;
}
auto remainingData = std::span(data);
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerService.java b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
index 3049522..d673ec8 100644
--- a/services/core/java/com/android/server/incremental/IncrementalManagerService.java
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.incremental;
import android.annotation.NonNull;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.DataLoaderManager;
import android.content.pm.DataLoaderParamsParcel;
@@ -85,7 +86,8 @@
DataLoaderParamsParcel params,
IDataLoaderStatusListener listener) {
Bundle dataLoaderParams = new Bundle();
- dataLoaderParams.putCharSequence("packageName", params.packageName);
+ dataLoaderParams.putParcelable("componentName",
+ new ComponentName(params.packageName, params.className));
dataLoaderParams.putParcelable("control", control);
dataLoaderParams.putParcelable("params", params);
DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class);
@@ -109,8 +111,7 @@
return false;
}
try {
- // TODO: fix file list
- dataLoader.start(null);
+ dataLoader.start();
return true;
} catch (RemoteException ex) {
return false;
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
index 6a8434a..a68f777 100644
--- a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
@@ -111,14 +112,14 @@
pw.println("File names and sizes don't match.");
return ERROR_DATA_LOADER_INIT;
}
- final DataLoaderParams params = new DataLoaderParams(
- "", LOADER_PACKAGE_NAME, dataLoaderDynamicArgs);
+ final DataLoaderParams params = DataLoaderParams.forIncremental(
+ new ComponentName(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);
+ sessionParams.setDataLoaderParams(params);
try {
int sessionId = packageInstaller.createSession(sessionParams);
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index 0719797..0dfea7f 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -76,13 +76,12 @@
return false;
}
}
- CharSequence packageNameSeq = params.getCharSequence("packageName");
- if (packageNameSeq == null) {
- Slog.e(TAG, "Must specify package name.");
+ ComponentName componentName = params.getParcelable("componentName");
+ if (componentName == null) {
+ Slog.e(TAG, "Must specify component name.");
return false;
}
- String packageName = packageNameSeq.toString();
- ComponentName dataLoaderComponent = getDataLoaderServiceName(packageName);
+ ComponentName dataLoaderComponent = resolveDataLoaderComponentName(componentName);
if (dataLoaderComponent == null) {
return false;
}
@@ -103,22 +102,23 @@
/**
* Find the ComponentName of the data loader service provider, given its package name.
*
- * @param packageName the package name of the provider.
+ * @param componentName the name of the provider.
* @return ComponentName of the data loader service provider. Null if provider not found.
*/
- private @Nullable ComponentName getDataLoaderServiceName(String packageName) {
+ private @Nullable ComponentName resolveDataLoaderComponentName(
+ ComponentName componentName) {
final PackageManager pm = mContext.getPackageManager();
if (pm == null) {
Slog.e(TAG, "PackageManager is not available.");
return null;
}
Intent intent = new Intent(Intent.ACTION_LOAD_DATA);
- intent.setPackage(packageName);
+ intent.setComponent(componentName);
List<ResolveInfo> services =
pm.queryIntentServicesAsUser(intent, 0, UserHandle.getCallingUserId());
if (services == null || services.isEmpty()) {
Slog.e(TAG,
- "Failed to find data loader service provider in package " + packageName);
+ "Failed to find data loader service provider in " + componentName);
return null;
}
@@ -128,23 +128,21 @@
int numServices = services.size();
for (int i = 0; i < numServices; i++) {
ResolveInfo ri = services.get(i);
- ComponentName componentName = new ComponentName(
+ ComponentName resolved = new ComponentName(
ri.serviceInfo.packageName, ri.serviceInfo.name);
// There should only be one matching provider inside the given package.
// If there's more than one, return the first one found.
try {
- ApplicationInfo ai = pm.getApplicationInfo(componentName.getPackageName(), 0);
+ ApplicationInfo ai = pm.getApplicationInfo(resolved.getPackageName(), 0);
if (checkLoader && !ai.isPrivilegedApp()) {
Slog.w(TAG,
- "Data loader: " + componentName.getPackageName()
- + " is not a privileged app, skipping.");
+ "Data loader: " + resolved + " is not a privileged app, skipping.");
continue;
}
- return componentName;
+ return resolved;
} catch (PackageManager.NameNotFoundException ex) {
Slog.w(TAG,
- "Privileged data loader: " + componentName.getPackageName()
- + " not found, skipping.");
+ "Privileged data loader: " + resolved + " not found, skipping.");
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index c12395e..ac183dc 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static android.content.pm.DataLoaderType.INCREMENTAL;
+import static android.content.pm.DataLoaderType.STREAMING;
import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -49,12 +51,18 @@
import android.annotation.Nullable;
import android.app.admin.DevicePolicyEventLogger;
import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
+import android.content.pm.DataLoaderManager;
+import android.content.pm.DataLoaderParams;
+import android.content.pm.FileSystemControlParcel;
+import android.content.pm.IDataLoader;
+import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstallerSession;
import android.content.pm.IPackageInstallerSessionFileSystemConnector;
@@ -84,6 +92,7 @@
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.Process;
+import android.os.RemoteException;
import android.os.RevocableFileDescriptor;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -127,12 +136,12 @@
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final String TAG = "PackageInstallerSession";
@@ -189,7 +198,11 @@
private static final String ATTR_VOLUME_UUID = "volumeUuid";
private static final String ATTR_NAME = "name";
private static final String ATTR_INSTALL_REASON = "installRason";
- private static final String ATTR_DATA_LOADER_PACKAGE_NAME = "dataLoaderPackageName";
+ private static final String ATTR_IS_DATALOADER = "isDataLoader";
+ private static final String ATTR_DATALOADER_TYPE = "dataLoaderType";
+ private static final String ATTR_DATALOADER_PACKAGE_NAME = "dataLoaderPackageName";
+ private static final String ATTR_DATALOADER_CLASS_NAME = "dataLoaderClassName";
+ private static final String ATTR_DATALOADER_ARGUMENTS = "dataLoaderArguments";
private static final String ATTR_LENGTH_BYTES = "lengthBytes";
private static final String ATTR_METADATA = "metadata";
@@ -414,7 +427,15 @@
};
private boolean isDataLoaderInstallation() {
- return !TextUtils.isEmpty(params.dataLoaderPackageName);
+ return params.dataLoaderParams != null;
+ }
+
+ private boolean isStreamingInstallation() {
+ return isDataLoaderInstallation() && params.dataLoaderParams.getType() == STREAMING;
+ }
+
+ private boolean isIncrementalInstallation() {
+ return isDataLoaderInstallation() && params.dataLoaderParams.getType() == INCREMENTAL;
}
/**
@@ -525,14 +546,13 @@
stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
// TODO(b/136132412): sanity check if session should not be incremental
- if (!params.isStaged && params.incrementalParams != null
- && !params.incrementalParams.getPackageName().isEmpty()) {
+ if (!params.isStaged && isIncrementalInstallation()) {
IncrementalManager incrementalManager = (IncrementalManager) mContext.getSystemService(
Context.INCREMENTAL_SERVICE);
if (incrementalManager != null) {
mIncrementalFileStorages =
new IncrementalFileStorages(mPackageName, stageDir, incrementalManager,
- params.incrementalParams);
+ params.dataLoaderParams);
}
}
}
@@ -714,7 +734,7 @@
public void removeSplit(String splitName) {
if (isDataLoaderInstallation()) {
throw new IllegalStateException(
- "Cannot remove splits in a callback installation session.");
+ "Cannot remove splits in a data loader installation session.");
}
if (TextUtils.isEmpty(params.appPackageName)) {
throw new IllegalStateException("Must specify package name to remove a split");
@@ -753,7 +773,7 @@
private void assertCanWrite(boolean reverseMode) {
if (isDataLoaderInstallation()) {
throw new IllegalStateException(
- "Cannot write regular files in a callback installation session.");
+ "Cannot write regular files in a data loader installation session.");
}
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
@@ -894,7 +914,7 @@
public ParcelFileDescriptor openRead(String name) {
if (isDataLoaderInstallation()) {
throw new IllegalStateException(
- "Cannot read regular files in a callback installation session.");
+ "Cannot read regular files in a data loader installation session.");
}
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
@@ -1663,7 +1683,7 @@
computeProgressLocked(true);
// Unpack native libraries for non-incremental installation
- if (params.incrementalParams == null) {
+ if (isIncrementalInstallation()) {
extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
}
}
@@ -2382,7 +2402,7 @@
}
if (!isDataLoaderInstallation()) {
throw new IllegalStateException(
- "Cannot add files to non-callback installation session.");
+ "Cannot add files to non-data loader installation session.");
}
// Use installer provided name for now; we always rename later
if (!FileUtils.isValidExtFilename(name)) {
@@ -2401,7 +2421,7 @@
public void removeFile(String name) {
if (!isDataLoaderInstallation()) {
throw new IllegalStateException(
- "Cannot add files to non-callback installation session.");
+ "Cannot add files to non-data loader installation session.");
}
if (TextUtils.isEmpty(params.appPackageName)) {
throw new IllegalStateException("Must specify package name to remove a split");
@@ -2415,76 +2435,121 @@
}
}
+ static class Notificator {
+ private int mValue = 0;
+
+ void setValue(int value) {
+ synchronized (this) {
+ mValue = value;
+ this.notify();
+ }
+ }
+ int waitForValue() {
+ synchronized (this) {
+ while (mValue == 0) {
+ try {
+ this.wait();
+ } catch (InterruptedException e) {
+ // Happens if someone interrupts your thread.
+ }
+ }
+ return mValue;
+ }
+ }
+ }
+
/**
* Makes sure files are present in staging location.
*/
private void prepareDataLoader()
throws PackageManagerException, StreamingException {
- if (!isDataLoaderInstallation()) {
+ if (!isStreamingInstallation()) {
return;
}
FileSystemConnector connector = new FileSystemConnector();
- FileInfo[] addedFiles = mFiles.stream().filter(
- file -> sAddedFilter.accept(new File(file.name))).toArray(FileInfo[]::new);
- String[] removedFiles = mFiles.stream().filter(
+ List<InstallationFile> addedFiles = mFiles.stream().filter(
+ file -> sAddedFilter.accept(new File(file.name))).map(
+ file -> new InstallationFile(
+ file.name, file.lengthBytes, file.metadata)).collect(
+ Collectors.toList());
+ List<String> removedFiles = mFiles.stream().filter(
file -> sRemovedFilter.accept(new File(file.name))).map(
- file -> file.name.substring(0,
- file.name.length() - REMOVE_MARKER_EXTENSION.length())).toArray(
- String[]::new);
+ file -> file.name.substring(
+ 0, file.name.length() - REMOVE_MARKER_EXTENSION.length())).collect(
+ Collectors.toList());
- DataLoader dataLoader = new DataLoader();
- try {
- dataLoader.onCreate(connector);
-
- if (!dataLoader.onPrepareImage(addedFiles, removedFiles)) {
- throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
- "Failed to prepare image.");
- }
- } catch (IOException e) {
- throw new StreamingException(e);
- } finally {
- dataLoader.onDestroy();
- }
- }
-
- static class DataLoader {
- private ParcelFileDescriptor mInFd = null;
- private FileSystemConnector mConnector = null;
-
- void onCreate(FileSystemConnector connector) throws IOException {
- mConnector = connector;
+ DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class);
+ if (dataLoaderManager == null) {
+ throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "Failed to find data loader manager service");
}
- void onDestroy() {
- IoUtils.closeQuietly(mInFd);
- }
+ // TODO(b/146080380): make this code async.
+ final Notificator created = new Notificator();
+ final Notificator started = new Notificator();
+ final Notificator imageReady = new Notificator();
- private static final String STDIN_PATH = "-";
- boolean onPrepareImage(FileInfo[] addedFiles, String[] removedFiles) throws IOException {
- for (FileInfo fileInfo : addedFiles) {
- String filePath = new String(fileInfo.metadata, StandardCharsets.UTF_8);
- if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) {
- if (mInFd == null) {
- Slog.e(TAG, "Invalid stdin file descriptor.");
- return false;
+ IDataLoaderStatusListener listener = new IDataLoaderStatusListener.Stub() {
+ @Override
+ public void onStatusChanged(int dataLoaderId, int status) {
+ switch (status) {
+ case IDataLoaderStatusListener.DATA_LOADER_CREATED: {
+ created.setValue(1);
+ break;
}
- ParcelFileDescriptor inFd = ParcelFileDescriptor.dup(mInFd.getFileDescriptor());
- mConnector.writeData(fileInfo.name, 0, fileInfo.lengthBytes, inFd);
- } else {
- File localFile = new File(filePath);
- ParcelFileDescriptor incomingFd = null;
- try {
- incomingFd = ParcelFileDescriptor.open(localFile,
- ParcelFileDescriptor.MODE_READ_ONLY);
- mConnector.writeData(fileInfo.name, 0, localFile.length(), incomingFd);
- } finally {
- IoUtils.closeQuietly(incomingFd);
+ case IDataLoaderStatusListener.DATA_LOADER_STARTED: {
+ started.setValue(1);
+ break;
+ }
+ case IDataLoaderStatusListener.DATA_LOADER_IMAGE_READY: {
+ imageReady.setValue(1);
+ break;
+ }
+ case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: {
+ imageReady.setValue(2);
+ break;
}
}
}
- return true;
+ };
+
+ final DataLoaderParams params = this.params.dataLoaderParams;
+
+ final FileSystemControlParcel control = new FileSystemControlParcel();
+ control.callback = connector;
+
+ Bundle dataLoaderParams = new Bundle();
+ dataLoaderParams.putParcelable("componentName", params.getComponentName());
+ dataLoaderParams.putParcelable("control", control);
+ dataLoaderParams.putParcelable("params", params.getData());
+
+ if (!dataLoaderManager.initializeDataLoader(sessionId, dataLoaderParams, listener)) {
+ throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "Failed to initialize data loader");
+ }
+ created.waitForValue();
+
+ IDataLoader dataLoader = dataLoaderManager.getDataLoader(sessionId);
+ if (dataLoader == null) {
+ throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "Failure to obtain data loader");
+ }
+
+ try {
+ dataLoader.start();
+ started.waitForValue();
+
+ dataLoader.prepareImage(addedFiles, removedFiles);
+ if (imageReady.waitForValue() == 2) {
+ throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "Failed to prepare image.");
+ }
+
+ dataLoader.destroy();
+ } catch (RemoteException e) {
+ throw new StreamingException(e);
}
}
@@ -2846,7 +2911,17 @@
writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
- writeStringAttribute(out, ATTR_DATA_LOADER_PACKAGE_NAME, params.dataLoaderPackageName);
+ final boolean isDataLoader = params.dataLoaderParams != null;
+ writeBooleanAttribute(out, ATTR_IS_DATALOADER, isDataLoader);
+ if (isDataLoader) {
+ writeIntAttribute(out, ATTR_DATALOADER_TYPE, params.dataLoaderParams.getType());
+ writeStringAttribute(out, ATTR_DATALOADER_PACKAGE_NAME,
+ params.dataLoaderParams.getComponentName().getPackageName());
+ writeStringAttribute(out, ATTR_DATALOADER_CLASS_NAME,
+ params.dataLoaderParams.getComponentName().getClassName());
+ writeStringAttribute(out, ATTR_DATALOADER_ARGUMENTS,
+ params.dataLoaderParams.getArguments());
+ }
writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
writeWhitelistedRestrictedPermissionsLocked(out,
@@ -2957,7 +3032,15 @@
params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
- params.dataLoaderPackageName = readStringAttribute(in, ATTR_DATA_LOADER_PACKAGE_NAME);
+ if (readBooleanAttribute(in, ATTR_IS_DATALOADER)) {
+ params.dataLoaderParams = new DataLoaderParams(
+ readIntAttribute(in, ATTR_DATALOADER_TYPE),
+ new ComponentName(
+ readStringAttribute(in, ATTR_DATALOADER_PACKAGE_NAME),
+ readStringAttribute(in, ATTR_DATALOADER_CLASS_NAME)),
+ readStringAttribute(in, ATTR_DATALOADER_ARGUMENTS),
+ null);
+ }
final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
if (appIconFile.exists()) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index dfffbd6..10e2780 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -34,6 +34,7 @@
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
+import android.content.pm.DataLoaderParams;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstaller;
@@ -136,7 +137,9 @@
private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/";
private static final int DEFAULT_WAIT_MS = 60 * 1000;
- private static final String PM_SHELL_DATALOADER = "com.android.pm.dataloader";
+ private static final String DATA_LOADER_PACKAGE = "android";
+ private static final String DATA_LOADER_CLASS =
+ "com.android.server.pm.PackageManagerShellCommandDataLoader";
final IPackageManager mInterface;
final IPermissionManager mPermissionManager;
@@ -1159,8 +1162,10 @@
private int runStreamingInstall() throws RemoteException {
final InstallParams params = makeInstallParams();
- if (TextUtils.isEmpty(params.sessionParams.dataLoaderPackageName)) {
- params.sessionParams.setDataLoaderPackageName(PM_SHELL_DATALOADER);
+ if (params.sessionParams.dataLoaderParams == null) {
+ final DataLoaderParams dataLoaderParams = DataLoaderParams.forStreaming(
+ new ComponentName(DATA_LOADER_PACKAGE, DATA_LOADER_CLASS), "");
+ params.sessionParams.setDataLoaderParams(dataLoaderParams);
}
return doRunInstall(params);
}
@@ -1171,7 +1176,7 @@
private int doRunInstall(final InstallParams params) throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
- final boolean streaming = !TextUtils.isEmpty(params.sessionParams.dataLoaderPackageName);
+ final boolean streaming = params.sessionParams.dataLoaderParams != null;
ArrayList<String> inPaths = getRemainingArgs();
if (inPaths.isEmpty()) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
new file mode 100644
index 0000000..1ee9ab8
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -0,0 +1,92 @@
+/*
+ * 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.pm;
+
+import android.annotation.NonNull;
+import android.content.pm.DataLoaderParams;
+import android.content.pm.InstallationFile;
+import android.os.ParcelFileDescriptor;
+import android.service.dataloader.DataLoaderService;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+
+/**
+ * Callback data loader for PackageManagerShellCommand installations.
+ */
+public class PackageManagerShellCommandDataLoader extends DataLoaderService {
+ public static final String TAG = "PackageManagerShellCommandDataLoader";
+
+ static class DataLoader implements DataLoaderService.DataLoader {
+ private ParcelFileDescriptor mInFd = null;
+ private FileSystemConnector mConnector = null;
+
+ private static final String STDIN_PATH = "-";
+
+ @Override
+ public boolean onCreate(@NonNull DataLoaderParams dataLoaderParams,
+ @NonNull FileSystemConnector connector) {
+ mConnector = connector;
+ return true;
+ }
+ @Override
+ public boolean onPrepareImage(Collection<InstallationFile> addedFiles,
+ Collection<String> removedFiles) {
+ try {
+ for (InstallationFile fileInfo : addedFiles) {
+ String filePath = new String(fileInfo.getMetadata(), StandardCharsets.UTF_8);
+ if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) {
+ // TODO(b/146080380): add support for STDIN installations.
+ if (mInFd == null) {
+ Slog.e(TAG, "Invalid stdin file descriptor.");
+ return false;
+ }
+ ParcelFileDescriptor inFd = ParcelFileDescriptor.dup(
+ mInFd.getFileDescriptor());
+ mConnector.writeData(fileInfo.getName(), 0, fileInfo.getSize(), inFd);
+ } else {
+ File localFile = new File(filePath);
+ ParcelFileDescriptor incomingFd = null;
+ try {
+ // TODO(b/146080380): open files via callback into shell command.
+ incomingFd = ParcelFileDescriptor.open(localFile,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ mConnector.writeData(fileInfo.getName(), 0, localFile.length(),
+ incomingFd);
+ } finally {
+ IoUtils.closeQuietly(incomingFd);
+ }
+ }
+ }
+ return true;
+ } catch (IOException e) {
+ return false;
+ }
+ }
+ }
+
+ @Override
+ public DataLoaderService.DataLoader onCreateDataLoader() {
+ return new DataLoader();
+ }
+}
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index c43328f..afce260 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -351,10 +351,13 @@
{
metadata::Mount m;
m.mutable_storage()->set_id(ifs->mountId);
+ m.mutable_loader()->set_type((int)dataLoaderParams.type);
m.mutable_loader()->set_package_name(dataLoaderParams.packageName);
- m.mutable_loader()->set_arguments(dataLoaderParams.staticArgs);
+ m.mutable_loader()->set_class_name(dataLoaderParams.className);
+ m.mutable_loader()->set_arguments(dataLoaderParams.arguments);
const auto metadata = m.SerializeAsString();
m.mutable_loader()->release_arguments();
+ m.mutable_loader()->release_class_name();
m.mutable_loader()->release_package_name();
if (auto err = mIncFs->makeFile(ifs->control, constants().infoMdName, INCFS_ROOT_INODE, 0,
metadata);
@@ -794,7 +797,7 @@
}
bool started = false;
std::unique_lock l(ifs->lock);
- if (ifs->dataLoaderStatus != IDataLoaderStatusListener::DATA_LOADER_READY) {
+ if (ifs->dataLoaderStatus != IDataLoaderStatusListener::DATA_LOADER_CREATED) {
if (ifs->dataLoaderReady.wait_for(l, Seconds(5)) == std::cv_status::timeout) {
LOG(ERROR) << "Timeout waiting for data loader to be ready";
return false;
@@ -917,8 +920,10 @@
}
DataLoaderParamsParcel dlParams;
+ dlParams.type = (DataLoaderType)m.loader().type();
dlParams.packageName = std::move(*m.mutable_loader()->mutable_package_name());
- dlParams.staticArgs = std::move(*m.mutable_loader()->mutable_arguments());
+ dlParams.className = std::move(*m.mutable_loader()->mutable_class_name());
+ dlParams.arguments = std::move(*m.mutable_loader()->mutable_arguments());
if (!prepareDataLoader(*ifs, &dlParams)) {
deleteStorage(*ifs);
return false;
@@ -955,7 +960,7 @@
}
std::unique_lock l(ifs.lock);
- if (ifs.dataLoaderStatus == IDataLoaderStatusListener::DATA_LOADER_READY) {
+ if (ifs.dataLoaderStatus == IDataLoaderStatusListener::DATA_LOADER_CREATED) {
LOG(INFO) << "Skipped data loader preparation because it already exists";
return true;
}
@@ -1008,20 +1013,20 @@
}
break;
}
- case IDataLoaderStatusListener::DATA_LOADER_READY: {
+ case IDataLoaderStatusListener::DATA_LOADER_CONNECTION_OK: {
+ ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STARTED;
+ break;
+ }
+ case IDataLoaderStatusListener::DATA_LOADER_CREATED: {
ifs->dataLoaderReady.notify_one();
break;
}
- case IDataLoaderStatusListener::DATA_LOADER_NOT_READY: {
+ case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: {
ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STOPPED;
incrementalService.deleteStorageLocked(*ifs, std::move(l));
break;
}
- case IDataLoaderStatusListener::DATA_LOADER_RUNNING: {
- break;
- }
- case IDataLoaderStatusListener::DATA_LOADER_CONNECTION_OK: {
- ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_RUNNING;
+ case IDataLoaderStatusListener::DATA_LOADER_STARTED: {
break;
}
case IDataLoaderStatusListener::DATA_LOADER_STOPPED: {
diff --git a/services/incremental/Metadata.proto b/services/incremental/Metadata.proto
index 0ff3c32..79f1bf8 100644
--- a/services/incremental/Metadata.proto
+++ b/services/incremental/Metadata.proto
@@ -10,7 +10,9 @@
message DataLoader {
string package_name = 1;
+ string class_name = 3;
string arguments = 2;
+ int32 type = 4;
}
message Storage {
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index f6b123d..ca1e1a9 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -138,10 +138,10 @@
.WillByDefault(Invoke(this, &MockIncrementalManager::startDataLoaderOk));
}
void setDataLoaderStatusNotReady() {
- mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_NOT_READY);
+ mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
}
void setDataLoaderStatusReady() {
- mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_READY);
+ mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_CREATED);
}
private:
@@ -235,7 +235,7 @@
MockServiceManager serviceManager = MockServiceManager(mVold, mIncrementalManager, mIncFs);
mIncrementalService = std::make_unique<IncrementalService>(serviceManager, mRootDir.path);
mDataLoaderParcel.packageName = "com.test";
- mDataLoaderParcel.staticArgs = "uri";
+ mDataLoaderParcel.arguments = "uri";
mIncrementalService->onSystemReady();
}