diff options
22 files changed, 524 insertions, 335 deletions
diff --git a/Android.bp b/Android.bp index 536f6885bd13..742a70e50ebc 100644 --- a/Android.bp +++ b/Android.bp @@ -833,6 +833,7 @@ filegroup { 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 23dd6522da82..dc27f2227013 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1934,10 +1934,12 @@ package android.content.pm { } 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 @@ package android.content.pm { 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 af4b99a5f815..60d7bb3c5918 100644 --- a/core/java/android/content/pm/DataLoaderParams.java +++ b/core/java/android/content/pm/DataLoaderParams.java @@ -19,6 +19,7 @@ package android.content.pm; 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.Map; 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 @@ import java.util.stream.Collectors; */ @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 @@ public class DataLoaderParams { mData = data; } - /** - * @hide - */ - public DataLoaderParams(@NonNull DataLoaderParamsParcel data) { + /** @hide */ + DataLoaderParams(@NonNull DataLoaderParamsParcel data) { mData = data; } + /** @hide */ + public final @NonNull DataLoaderParamsParcel getData() { + return mData; + } + /** - * @return static server's URL + * @return data loader type */ - public final @NonNull String getStaticArgs() { - return mData.staticArgs; + public final @NonNull @DataLoaderType int getType() { + return mData.type; } /** - * @return data loader's package name + * @return data loader's component name */ - public final @NonNull String getPackageName() { - return mData.packageName; + public final @NonNull ComponentName getComponentName() { + return new ComponentName(mData.packageName, mData.className); } /** - * @hide + * @return data loader's arguments */ - public final @NonNull DataLoaderParamsParcel getData() { - return mData; + public final @NonNull String getArguments() { + return mData.arguments; } /** - * @return data loader's dynamic arguments such as file descriptors + * @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 33163980b915..e05843b4d4e9 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 @@ import android.content.pm.NamedParcelFileDescriptor; * @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 000000000000..7d726f5ddd6f --- /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 c65bd6acbaf8..b5baa9379d16 100644 --- a/core/java/android/content/pm/IDataLoader.aidl +++ b/core/java/android/content/pm/IDataLoader.aidl @@ -27,7 +27,9 @@ import java.util.List; */ 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 a60d6ee2d28a..5011faafa2f7 100644 --- a/core/java/android/content/pm/IDataLoaderStatusListener.aidl +++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl @@ -22,13 +22,18 @@ package android.content.pm; */ 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 3d6d849bf03d..e4a0bc0197a1 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1461,10 +1461,7 @@ public class PackageInstaller { /** {@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 @@ public class PackageInstaller { 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 @@ public class PackageInstaller { 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 @@ public class PackageInstaller { } /** - * 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 @@ public class PackageInstaller { 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 @@ public class PackageInstaller { 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 fb94fc9bd719..2138d553102e 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -87,8 +87,8 @@ public final class IncrementalFileStorages { 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 54a4fa6023a1..75f252e23f79 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.DataLoaderParamsParcel; 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 @@ import java.util.List; */ @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. - */ - 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. + * @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 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 @@ public abstract class DataLoaderService extends Service { * @return An instance of a DataLoader. * @hide */ - public abstract @Nullable DataLoader onCreateDataLoader(); + public @Nullable DataLoader onCreateDataLoader() { + return null; + } /** * @hide @@ -160,148 +105,125 @@ public abstract class DataLoaderService extends Service { @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 @@ public abstract class DataLoaderService extends Service { 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 381b386d7f62..a62d127a968f 100644 --- a/core/jni/android_service_DataLoaderService.cpp +++ b/core/jni/android_service_DataLoaderService.cpp @@ -51,13 +51,19 @@ static jboolean nativeDestroyDataLoader(JNIEnv* env, } -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 @@ static const JNINativeMethod dlc_method_table[] = { {"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 1165d2d673d5..44a902c830f2 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 de92fcd5b2e8..4e49302debcd 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 @@ private: 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 @@ private: } 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 @@ private: "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 @@ private: } 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 @@ private: } 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 3049522c2231..d673ec84c47e 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 @@ public class IncrementalManagerService extends IIncrementalManager.Stub { 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 @@ public class IncrementalManagerService extends IIncrementalManager.Stub { 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 6a8434aad88b..a68f77747374 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 @@ package com.android.server.incremental; 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 @@ public final class IncrementalManagerShellCommand extends ShellCommand { 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 0719797ed85a..0dfea7fd5f96 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 @@ public class DataLoaderManagerService extends SystemService { 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 @@ public class DataLoaderManagerService extends SystemService { /** * 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 @@ public class DataLoaderManagerService extends SystemService { 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 c12395e28e28..ac183dcf6b7a 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.NonNull; 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.Message; 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.FileDescriptor; 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { }; 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { computeProgressLocked(true); // Unpack native libraries for non-incremental installation - if (params.incrementalParams == null) { + if (isIncrementalInstallation()) { extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs()); } } @@ -2382,7 +2402,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } 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 class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + 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); + DataLoaderManager dataLoaderManager = mContext.getSystemService(DataLoaderManager.class); + if (dataLoaderManager == null) { + throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, + "Failed to find data loader manager service"); + } - if (!dataLoader.onPrepareImage(addedFiles, removedFiles)) { - throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, - "Failed to prepare image."); + // TODO(b/146080380): make this code async. + final Notificator created = new Notificator(); + final Notificator started = new Notificator(); + final Notificator imageReady = new Notificator(); + + IDataLoaderStatusListener listener = new IDataLoaderStatusListener.Stub() { + @Override + public void onStatusChanged(int dataLoaderId, int status) { + switch (status) { + case IDataLoaderStatusListener.DATA_LOADER_CREATED: { + created.setValue(1); + break; + } + 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; + } + } } - } catch (IOException e) { - throw new StreamingException(e); - } finally { - dataLoader.onDestroy(); - } - } + }; + + final DataLoaderParams params = this.params.dataLoaderParams; - static class DataLoader { - private ParcelFileDescriptor mInFd = null; - private FileSystemConnector mConnector = null; + final FileSystemControlParcel control = new FileSystemControlParcel(); + control.callback = connector; - void onCreate(FileSystemConnector connector) throws IOException { - mConnector = 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(); - void onDestroy() { - IoUtils.closeQuietly(mInFd); + IDataLoader dataLoader = dataLoaderManager.getDataLoader(sessionId); + if (dataLoader == null) { + throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, + "Failure to obtain data loader"); } - 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; - } - 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); - } - } + try { + dataLoader.start(); + started.waitForValue(); + + dataLoader.prepareImage(addedFiles, removedFiles); + if (imageReady.waitForValue() == 2) { + throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, + "Failed to prepare image."); } - return true; + + dataLoader.destroy(); + } catch (RemoteException e) { + throw new StreamingException(e); } } @@ -2846,7 +2911,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 dfffbd6cabef..10e2780863d8 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.IIntentSender; 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 000000000000..1ee9ab8927bb --- /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 c43328fcdf9d..afce260ed41e 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -351,10 +351,13 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint, { 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 IncrementalService::startLoading(StorageId storage) const { } 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 @@ bool IncrementalService::mountExistingImage(std::string_view root, std::string_v } 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 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs, } 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 @@ binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChange } 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 0ff3c3234ffa..79f1bf8d29a9 100644 --- a/services/incremental/Metadata.proto +++ b/services/incremental/Metadata.proto @@ -10,7 +10,9 @@ message BindPoint { 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 f6b123d68212..ca1e1a972047 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -138,10 +138,10 @@ public: .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 @@ public: 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(); } |