diff options
| author | TreeHugger Robot <treehugger-gerrit@google.com> | 2019-12-18 21:38:46 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2019-12-18 21:38:46 +0000 |
| commit | d8492dbfb2f43fbf349c6444bdbcbfef9af37d05 (patch) | |
| tree | b057fe20106e15546f5f025c3fc34fed96b81131 /core/java | |
| parent | 3095609f4e37ea737a81921af5e1d758d326fad4 (diff) | |
| parent | ea14d19106b06cc5d28fad2c779b914caeeeb31f (diff) | |
Merge "Migrating Incremental* APIs to PackageManager APIs."
Diffstat (limited to 'core/java')
13 files changed, 391 insertions, 593 deletions
diff --git a/core/java/android/os/incremental/IncrementalDataLoaderParams.java b/core/java/android/content/pm/DataLoaderParams.java index 701f1cc8de02..b163861c53b5 100644 --- a/core/java/android/os/incremental/IncrementalDataLoaderParams.java +++ b/core/java/android/content/pm/DataLoaderParams.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.os.incremental; +package android.content.pm; import android.annotation.NonNull; import android.annotation.Nullable; @@ -29,12 +29,12 @@ import java.util.stream.Collectors; * Hide for now. * @hide */ -public class IncrementalDataLoaderParams { - @NonNull private final IncrementalDataLoaderParamsParcel mData; +public class DataLoaderParams { + @NonNull private final DataLoaderParamsParcel mData; - public IncrementalDataLoaderParams(@NonNull String url, @NonNull String packageName, + public DataLoaderParams(@NonNull String url, @NonNull String packageName, @Nullable Map<String, ParcelFileDescriptor> namedFds) { - IncrementalDataLoaderParamsParcel data = new IncrementalDataLoaderParamsParcel(); + DataLoaderParamsParcel data = new DataLoaderParamsParcel(); data.staticArgs = url; data.packageName = packageName; if (namedFds == null || namedFds.isEmpty()) { @@ -52,7 +52,7 @@ public class IncrementalDataLoaderParams { mData = data; } - public IncrementalDataLoaderParams(@NonNull IncrementalDataLoaderParamsParcel data) { + public DataLoaderParams(@NonNull DataLoaderParamsParcel data) { mData = data; } @@ -70,7 +70,7 @@ public class IncrementalDataLoaderParams { return mData.packageName; } - public final @NonNull IncrementalDataLoaderParamsParcel getData() { + public final @NonNull DataLoaderParamsParcel getData() { return mData; } diff --git a/core/java/android/os/incremental/IncrementalDataLoaderParamsParcel.aidl b/core/java/android/content/pm/DataLoaderParamsParcel.aidl index cd988dcace5b..33163980b915 100644 --- a/core/java/android/os/incremental/IncrementalDataLoaderParamsParcel.aidl +++ b/core/java/android/content/pm/DataLoaderParamsParcel.aidl @@ -14,15 +14,15 @@ * limitations under the License. */ -package android.os.incremental; +package android.content.pm; -import android.os.incremental.NamedParcelFileDescriptor; +import android.content.pm.NamedParcelFileDescriptor; /** * Class for holding data loader configuration parameters. * @hide */ -parcelable IncrementalDataLoaderParamsParcel { +parcelable DataLoaderParamsParcel { @utf8InCpp String packageName; @utf8InCpp String staticArgs; NamedParcelFileDescriptor[] dynamicArgs; diff --git a/core/java/android/content/pm/FileSystemControlParcel.aidl b/core/java/android/content/pm/FileSystemControlParcel.aidl new file mode 100644 index 000000000000..f00feaeb2f5a --- /dev/null +++ b/core/java/android/content/pm/FileSystemControlParcel.aidl @@ -0,0 +1,31 @@ +/* + * 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; + +import android.content.pm.IPackageInstallerSessionFileSystemConnector; +import android.os.incremental.IncrementalFileSystemControlParcel; + +/** + * Wraps info needed for DataLoader to provide data. + * @hide + */ +parcelable FileSystemControlParcel { + // Incremental FS control descriptors. + @nullable IncrementalFileSystemControlParcel incremental; + // Callback-based installation connector. + @nullable IPackageInstallerSessionFileSystemConnector callback; +} diff --git a/core/java/android/content/pm/IDataLoader.aidl b/core/java/android/content/pm/IDataLoader.aidl index 60cc9ba9e141..c65bd6acbaf8 100644 --- a/core/java/android/content/pm/IDataLoader.aidl +++ b/core/java/android/content/pm/IDataLoader.aidl @@ -30,5 +30,4 @@ oneway interface IDataLoader { void start(in List<InstallationFile> fileInfos); void stop(); void destroy(); - void onFileCreated(long inode, in byte[] metadata); } diff --git a/core/java/android/content/pm/IPackageInstallerSessionFileSystemConnector.aidl b/core/java/android/content/pm/IPackageInstallerSessionFileSystemConnector.aidl new file mode 100644 index 000000000000..4b2f29e5ae78 --- /dev/null +++ b/core/java/android/content/pm/IPackageInstallerSessionFileSystemConnector.aidl @@ -0,0 +1,24 @@ +/* + * 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; + +import android.os.ParcelFileDescriptor; + +/** {@hide} */ +interface IPackageInstallerSessionFileSystemConnector { + void writeData(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd); +} diff --git a/core/java/android/os/incremental/NamedParcelFileDescriptor.aidl b/core/java/android/content/pm/NamedParcelFileDescriptor.aidl index 038ced1744a1..68dd5f54654b 100644 --- a/core/java/android/os/incremental/NamedParcelFileDescriptor.aidl +++ b/core/java/android/content/pm/NamedParcelFileDescriptor.aidl @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.os.incremental; +package android.content.pm; import android.os.ParcelFileDescriptor; diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 898631e9a3b1..218c8765f3bb 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -50,8 +50,6 @@ import android.os.ParcelableException; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; -import android.os.incremental.IncrementalDataLoaderParams; -import android.os.incremental.IncrementalDataLoaderParamsParcel; import android.system.ErrnoException; import android.system.Os; import android.util.ArraySet; @@ -1459,7 +1457,7 @@ public class PackageInstaller { /** {@hide} */ public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST; /** {@hide} */ - public IncrementalDataLoaderParams incrementalParams; + public DataLoaderParams incrementalParams; /** TODO(b/146080380): add a class name to make it fully compatible with ComponentName. * {@hide} */ public String dataLoaderPackageName; @@ -1496,10 +1494,10 @@ public class PackageInstaller { isMultiPackage = source.readBoolean(); isStaged = source.readBoolean(); requiredInstalledVersionCode = source.readLong(); - IncrementalDataLoaderParamsParcel dataLoaderParamsParcel = source.readParcelable( - IncrementalDataLoaderParamsParcel.class.getClassLoader()); + DataLoaderParamsParcel dataLoaderParamsParcel = source.readParcelable( + DataLoaderParamsParcel.class.getClassLoader()); if (dataLoaderParamsParcel != null) { - incrementalParams = new IncrementalDataLoaderParams( + incrementalParams = new DataLoaderParams( dataLoaderParamsParcel); } dataLoaderPackageName = source.readString(); @@ -1863,7 +1861,7 @@ public class PackageInstaller { * {@hide} */ @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) - public void setIncrementalParams(@NonNull IncrementalDataLoaderParams incrementalParams) { + public void setIncrementalParams(@NonNull DataLoaderParams incrementalParams) { this.incrementalParams = incrementalParams; } diff --git a/core/java/android/os/incremental/IIncrementalManager.aidl b/core/java/android/os/incremental/IIncrementalManager.aidl index f84d7efe06b1..17a310a5beb0 100644 --- a/core/java/android/os/incremental/IIncrementalManager.aidl +++ b/core/java/android/os/incremental/IIncrementalManager.aidl @@ -16,8 +16,8 @@ package android.os.incremental; -import android.os.incremental.IncrementalFileSystemControlParcel; -import android.os.incremental.IncrementalDataLoaderParamsParcel; +import android.content.pm.FileSystemControlParcel; +import android.content.pm.DataLoaderParamsParcel; import android.content.pm.IDataLoaderStatusListener; /** @@ -27,8 +27,8 @@ import android.content.pm.IDataLoaderStatusListener; */ interface IIncrementalManager { boolean prepareDataLoader(int mountId, - in IncrementalFileSystemControlParcel control, - in IncrementalDataLoaderParamsParcel params, + in FileSystemControlParcel control, + in DataLoaderParamsParcel params, in IDataLoaderStatusListener listener); boolean startDataLoader(int mountId); void showHealthBlockedUI(int mountId); diff --git a/core/java/android/os/incremental/IIncrementalManagerNative.aidl b/core/java/android/os/incremental/IIncrementalManagerNative.aidl index d9c7c6b5cc21..14215b1ea84d 100644 --- a/core/java/android/os/incremental/IIncrementalManagerNative.aidl +++ b/core/java/android/os/incremental/IIncrementalManagerNative.aidl @@ -16,7 +16,7 @@ package android.os.incremental; -import android.os.incremental.IncrementalDataLoaderParamsParcel; +import android.content.pm.DataLoaderParamsParcel; /** @hide */ interface IIncrementalManagerNative { @@ -32,7 +32,7 @@ interface IIncrementalManagerNative { * Opens or creates a storage given a target path and data loader params. Returns the storage ID. */ int openStorage(in @utf8InCpp String path); - int createStorage(in @utf8InCpp String path, in IncrementalDataLoaderParamsParcel params, int createMode); + int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode); int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode); /** diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index 5bd0748b8d97..7987efd6b387 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -35,6 +35,7 @@ import static dalvik.system.VMRuntime.getInstructionSet; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.DataLoaderParams; import android.content.pm.InstallationFile; import android.os.IVold; import android.os.RemoteException; @@ -82,12 +83,12 @@ public final class IncrementalFileStorages { public IncrementalFileStorages(@NonNull String packageName, @NonNull File stageDir, @NonNull IncrementalManager incrementalManager, - @NonNull IncrementalDataLoaderParams incrementalDataLoaderParams) { + @NonNull DataLoaderParams dataLoaderParams) { mPackageName = packageName; mStageDir = stageDir; mIncrementalManager = incrementalManager; - if (incrementalDataLoaderParams.getPackageName().equals("local")) { - final String incrementalPath = incrementalDataLoaderParams.getStaticArgs(); + if (dataLoaderParams.getPackageName().equals("local")) { + final String incrementalPath = dataLoaderParams.getStaticArgs(); mDefaultStorage = mIncrementalManager.openStorage(incrementalPath); mDefaultDir = incrementalPath; return; @@ -97,7 +98,7 @@ public final class IncrementalFileStorages { return; } mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir, - incrementalDataLoaderParams, + dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false); } diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index c30f5589a835..c72228708b4c 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; import android.content.Context; +import android.content.pm.DataLoaderParams; import android.os.RemoteException; import android.system.ErrnoException; import android.system.Os; @@ -104,7 +105,7 @@ public final class IncrementalManager { */ @Nullable public IncrementalStorage createStorage(@NonNull String path, - @NonNull IncrementalDataLoaderParams params, @CreateMode int createMode, + @NonNull DataLoaderParams params, @CreateMode int createMode, boolean autoStartDataLoader) { try { final int id = mNativeService.createStorage(path, params.getData(), createMode); diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java new file mode 100644 index 000000000000..373e1e5f979f --- /dev/null +++ b/core/java/android/service/dataloader/DataLoaderService.java @@ -0,0 +1,307 @@ +/* + * 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.service.dataloader; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.Service; +import android.content.Intent; +import android.content.pm.DataLoaderParams; +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.List; + +/** + * The base class for implementing data loader service to control data loaders. Expecting + * Incremental Service to bind to a children class of this. + * + * @hide + * + * Hide for now, should be @SystemApi + * TODO(b/136132412): update with latest API design + */ +public abstract class DataLoaderService extends Service { + private static final String TAG = "IncrementalDataLoaderService"; + private final DataLoaderBinderService mBinder = new DataLoaderBinderService(); + + public static final int DATA_LOADER_READY = + IDataLoaderStatusListener.DATA_LOADER_READY; + public static final int DATA_LOADER_NOT_READY = + IDataLoaderStatusListener.DATA_LOADER_NOT_READY; + public static final int DATA_LOADER_RUNNING = + IDataLoaderStatusListener.DATA_LOADER_RUNNING; + public static final int DATA_LOADER_STOPPED = + IDataLoaderStatusListener.DATA_LOADER_STOPPED; + public static final int DATA_LOADER_SLOW_CONNECTION = + IDataLoaderStatusListener.DATA_LOADER_SLOW_CONNECTION; + public static final int DATA_LOADER_NO_CONNECTION = + IDataLoaderStatusListener.DATA_LOADER_NO_CONNECTION; + public static final int DATA_LOADER_CONNECTION_OK = + IDataLoaderStatusListener.DATA_LOADER_CONNECTION_OK; + + @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. + */ + public abstract static class 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. + * + * @param params Data loader configuration parameters. + * @param connector IncFS API wrapper. + * @param listener Used for reporting internal state to IncrementalService. + * @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. + */ + public abstract boolean onCreate(@NonNull DataLoaderParams params, + @NonNull FileSystemConnector connector, + @NonNull StatusListener listener); + + /** + * 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. + * + * @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. + */ + 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(); + } + + /** + * DataLoader factory method. + * + * @return An instance of a DataLoader. + */ + public abstract @Nullable DataLoader onCreateDataLoader(); + + /** + * @hide + */ + public final @NonNull IBinder onBind(@NonNull Intent intent) { + return (IBinder) mBinder; + } + + private class DataLoaderBinderService extends IDataLoader.Stub { + private int mId; + + @Override + public void create(int id, @NonNull Bundle options, + @NonNull IDataLoaderStatusListener listener) + throws IllegalArgumentException, RuntimeException { + mId = id; + final DataLoaderParamsParcel params = options.getParcelable("params"); + if (params == null) { + throw new IllegalArgumentException("Must specify Incremental data loader params"); + } + final FileSystemControlParcel control = + options.getParcelable("control"); + if (control == null) { + throw new IllegalArgumentException("Must specify Incremental control parcel"); + } + mStatusListener = listener; + try { + if (!nativeCreateDataLoader(id, control, params, listener)) { + Slog.e(TAG, "Failed to create native loader for " + mId); + } + } catch (Exception 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.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); + } + } + } + } + + @Override + public void start(List<InstallationFile> fileInfos) { + if (!nativeStartDataLoader(mId)) { + Slog.e(TAG, "Failed to start loader: loader not found for " + mId); + } + } + + @Override + public void stop() { + if (!nativeStopDataLoader(mId)) { + Slog.w(TAG, "Failed to stop loader: loader not found for " + mId); + } + } + + @Override + public void destroy() { + if (!nativeDestroyDataLoader(mId)) { + Slog.w(TAG, "Failed to destroy loader: loader not found for " + mId); + } + } + } + + /** + * + * Used by the DataLoaderService implementations. + * + * @hide + * + * TODO(b/136132412) Should be @SystemApi + */ + public static final class FileSystemConnector { + /** + * Creates a wrapper for an installation session connector. + * @hide + */ + FileSystemConnector(IPackageInstallerSessionFileSystemConnector connector) { + mConnector = connector; + } + + /** + * 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. + */ + public void writeData(String name, long offsetBytes, long lengthBytes, + ParcelFileDescriptor incomingFd) throws IOException { + try { + mConnector.writeData(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 + * TODO(b/136132412) Should be @SystemApi + */ + 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, + @NonNull DataLoaderParamsParcel params, + IDataLoaderStatusListener listener); + + private native boolean nativeStartDataLoader(int storageId); + + private native boolean nativeStopDataLoader(int storageId); + + private native boolean nativeDestroyDataLoader(int storageId); + + private static native boolean nativeReportStatus(long nativeInstance, int status); +} diff --git a/core/java/android/service/incremental/IncrementalDataLoaderService.java b/core/java/android/service/incremental/IncrementalDataLoaderService.java deleted file mode 100644 index c4a06c8f53db..000000000000 --- a/core/java/android/service/incremental/IncrementalDataLoaderService.java +++ /dev/null @@ -1,563 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.service.incremental; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.Service; -import android.content.Intent; -import android.content.pm.IDataLoader; -import android.content.pm.IDataLoaderStatusListener; -import android.content.pm.InstallationFile; -import android.os.Bundle; -import android.os.IBinder; -import android.os.incremental.IncrementalDataLoaderParams; -import android.os.incremental.IncrementalDataLoaderParamsParcel; -import android.os.incremental.IncrementalFileSystemControlParcel; -import android.os.incremental.NamedParcelFileDescriptor; -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; - - -/** - * The base class for implementing data loader service to control data loaders. Expecting - * Incremental Service to bind to a children class of this. - * - * @hide - * - * Hide for now, should be @SystemApi - * TODO(b/136132412): update with latest API design - */ -public abstract class IncrementalDataLoaderService extends Service { - private static final String TAG = "IncrementalDataLoaderService"; - private final DataLoaderBinderService mBinder = new DataLoaderBinderService(); - - public static final int DATA_LOADER_READY = - IDataLoaderStatusListener.DATA_LOADER_READY; - public static final int DATA_LOADER_NOT_READY = - IDataLoaderStatusListener.DATA_LOADER_NOT_READY; - public static final int DATA_LOADER_RUNNING = - IDataLoaderStatusListener.DATA_LOADER_RUNNING; - public static final int DATA_LOADER_STOPPED = - IDataLoaderStatusListener.DATA_LOADER_STOPPED; - public static final int DATA_LOADER_SLOW_CONNECTION = - IDataLoaderStatusListener.DATA_LOADER_SLOW_CONNECTION; - public static final int DATA_LOADER_NO_CONNECTION = - IDataLoaderStatusListener.DATA_LOADER_NO_CONNECTION; - public static final int DATA_LOADER_CONNECTION_OK = - IDataLoaderStatusListener.DATA_LOADER_CONNECTION_OK; - - @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 { - } - - /** - * Incremental FileSystem block size. - **/ - public static final int BLOCK_SIZE = 4096; - - /** - * Data compression types - */ - public static final int COMPRESSION_NONE = 0; - public static final int COMPRESSION_LZ4 = 1; - - /** - * @hide - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({COMPRESSION_NONE, COMPRESSION_LZ4}) - public @interface CompressionType { - } - - /** - * Managed DataLoader interface. Each instance corresponds to a single Incremental File System - * instance. - */ - public abstract static class 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. - * - * @param params Data loader configuration parameters. - * @param connector IncFS API wrapper. - * @param listener Used for reporting internal state to IncrementalService. - * @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. - */ - public abstract boolean onCreate(@NonNull IncrementalDataLoaderParams params, - @NonNull FileSystemConnector connector, - @NonNull StatusListener listener); - - /** - * 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. - * - * @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. - */ - 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(); - - /** - * IFS reports a pending read each time the page needs to be loaded, e.g. missing. - * - * @param pendingReads array of blocks to load. - * - * TODO(b/136132412): avoid using collections - */ - public abstract void onPendingReads( - @NonNull Collection<FileSystemConnector.PendingReadInfo> pendingReads); - - /** - * IFS tracks all reads and reports them using onPageReads. - * - * @param reads array of blocks. - * - * TODO(b/136132412): avoid using collections - */ - public abstract void onPageReads(@NonNull Collection<FileSystemConnector.ReadInfo> reads); - - /** - * IFS informs data loader that a new file has been created. - * <p> - * This can be used to prepare the data loader before it starts loading data. For example, - * the data loader can keep a list of newly created files, so that it knows what files to - * download from the server. - * - * @param inode The inode value of the new file. - * @param metadata The metadata of the new file. - */ - public abstract void onFileCreated(long inode, byte[] metadata); - } - - /** - * DataLoader factory method. - * - * @return An instance of a DataLoader. - */ - public abstract @Nullable DataLoader onCreateDataLoader(); - - /** - * @hide - */ - public final @NonNull IBinder onBind(@NonNull Intent intent) { - return (IBinder) mBinder; - } - - private class DataLoaderBinderService extends IDataLoader.Stub { - private int mId; - - @Override - public void create(int id, @NonNull Bundle options, - @NonNull IDataLoaderStatusListener listener) - throws IllegalArgumentException, RuntimeException { - mId = id; - final IncrementalDataLoaderParamsParcel params = options.getParcelable("params"); - if (params == null) { - throw new IllegalArgumentException("Must specify Incremental data loader params"); - } - final IncrementalFileSystemControlParcel control = - options.getParcelable("control"); - if (control == null) { - throw new IllegalArgumentException("Must specify Incremental control parcel"); - } - mStatusListener = listener; - try { - if (!nativeCreateDataLoader(id, control, params, listener)) { - Slog.e(TAG, "Failed to create native loader for " + mId); - } - } catch (Exception ex) { - destroy(); - throw new RuntimeException(ex); - } finally { - // Closing FDs. - if (control.cmd != null) { - try { - control.cmd.close(); - } catch (IOException e) { - Slog.e(TAG, "Failed to close IncFs CMD file descriptor " + e); - } - } - if (control.log != null) { - try { - control.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); - } - } - } - } - - @Override - public void start(List<InstallationFile> fileInfos) { - if (!nativeStartDataLoader(mId)) { - Slog.e(TAG, "Failed to start loader: loader not found for " + mId); - } - } - - @Override - public void stop() { - if (!nativeStopDataLoader(mId)) { - Slog.w(TAG, "Failed to stop loader: loader not found for " + mId); - } - } - - @Override - public void destroy() { - if (!nativeDestroyDataLoader(mId)) { - Slog.w(TAG, "Failed to destroy loader: loader not found for " + mId); - } - } - - @Override - // TODO(b/136132412): remove this - public void onFileCreated(long inode, byte[] metadata) { - if (!nativeOnFileCreated(mId, inode, metadata)) { - Slog.w(TAG, "Failed to handle onFileCreated for storage:" + mId - + " inode:" + inode); - } - } - } - - /** - * IncFs API wrapper for writing pages and getting page missing info. Non-hidden methods are - * expected to be called by the IncrementalDataLoaderService implemented by developers. - * - * @hide - * - * TODO(b/136132412) Should be @SystemApi - */ - public static final class FileSystemConnector { - /** - * Defines a block address. A block is the unit of data chunk that IncFs operates with. - * - * @hide - */ - public static class BlockAddress { - /** - * Linux inode uniquely identifies file within a single IFS instance. - */ - private final long mFileIno; - /** - * Index of a 4K block within a file. - */ - private final int mBlockIndex; - - public BlockAddress(long fileIno, int blockIndex) { - this.mFileIno = fileIno; - this.mBlockIndex = blockIndex; - } - - public long getFileIno() { - return mFileIno; - } - - public int getBlockIndex() { - return mBlockIndex; - } - } - - /** - * A block is the unit of data chunk that IncFs operates with. - * - * @hide - */ - public static class Block extends BlockAddress { - /** - * Data content of the block. - */ - private final @NonNull byte[] mDataBytes; - - public Block(long fileIno, int blockIndex, @NonNull byte[] dataBytes) { - super(fileIno, blockIndex); - this.mDataBytes = dataBytes; - } - } - - /** - * Defines a page/block inside a file. - */ - public static class DataBlock extends Block { - /** - * Compression type of the data block. - */ - private final @CompressionType int mCompressionType; - - public DataBlock(long fileIno, int blockIndex, @NonNull byte[] dataBytes, - @CompressionType int compressionType) { - super(fileIno, blockIndex, dataBytes); - this.mCompressionType = compressionType; - } - } - - /** - * Defines a hash block for a certain file. A hash block index is the index in an array of - * hashes which is the 1-d representation of the hash tree. One DataBlock might be - * associated with multiple HashBlocks. - */ - public static class HashBlock extends Block { - public HashBlock(long fileIno, int blockIndex, @NonNull byte[] dataBytes) { - super(fileIno, blockIndex, dataBytes); - } - } - - /** - * Information about a page that is pending to be read. - */ - public static class PendingReadInfo extends BlockAddress { - PendingReadInfo(long fileIno, int blockIndex) { - super(fileIno, blockIndex); - } - } - - /** - * Information about a page that is read. - */ - public static class ReadInfo extends BlockAddress { - /** - * A monotonically increasing read timestamp. - */ - private final long mTimePoint; - /** - * Number of blocks read starting from blockIndex. - */ - private final int mBlockCount; - - ReadInfo(long timePoint, long fileIno, int firstBlockIndex, int blockCount) { - super(fileIno, firstBlockIndex); - this.mTimePoint = timePoint; - this.mBlockCount = blockCount; - } - - public long getTimePoint() { - return mTimePoint; - } - - public int getBlockCount() { - return mBlockCount; - } - } - - /** - * Defines the dynamic information about an IncFs file. - */ - public static class FileInfo { - /** - * BitSet to show if any block is available at each block index. - */ - private final @NonNull - byte[] mBlockBitmap; - - /** - * @hide - */ - public FileInfo(@NonNull byte[] blockBitmap) { - this.mBlockBitmap = blockBitmap; - } - } - - /** - * Creates a wrapper for a native instance. - */ - FileSystemConnector(long nativeInstance) { - mNativeInstance = nativeInstance; - } - - /** - * Checks whether a range in a file if loaded. - * - * @param node inode of the file. - * @param start The starting offset of the range. - * @param end The ending offset of the range. - * @return True if the file is fully loaded. - */ - public boolean isFileRangeLoaded(long node, long start, long end) { - return nativeIsFileRangeLoadedNode(mNativeInstance, node, start, end); - } - - /** - * Gets the metadata of a file. - * - * @param node inode of the file. - * @return The metadata object. - */ - @NonNull - public byte[] getFileMetadata(long node) throws IOException { - final byte[] metadata = nativeGetFileMetadataNode(mNativeInstance, node); - if (metadata == null || metadata.length == 0) { - throw new IOException( - "IncrementalFileSystem failed to obtain metadata for node: " + node); - } - return metadata; - } - - /** - * Gets the dynamic information of a file, such as page bitmaps. Can be used to get missing - * page indices by the FileSystemConnector. - * - * @param node inode of the file. - * @return Dynamic file info. - */ - @NonNull - public FileInfo getDynamicFileInfo(long node) throws IOException { - final byte[] blockBitmap = nativeGetFileInfoNode(mNativeInstance, node); - if (blockBitmap == null || blockBitmap.length == 0) { - throw new IOException( - "IncrementalFileSystem failed to obtain dynamic file info for node: " - + node); - } - return new FileInfo(blockBitmap); - } - - /** - * Writes a page's data and/or hashes. - * - * @param dataBlocks the DataBlock objects that contain data block index and data bytes. - * @param hashBlocks the HashBlock objects that contain hash indices and hash bytes. - * - * TODO(b/136132412): change API to avoid dynamic allocation of data block objects - */ - public void writeMissingData(@NonNull DataBlock[] dataBlocks, - @Nullable HashBlock[] hashBlocks) throws IOException { - if (!nativeWriteMissingData(mNativeInstance, dataBlocks, hashBlocks)) { - throw new IOException("IncrementalFileSystem failed to write missing data."); - } - } - - /** - * Writes the signer block of a file. Expecting the connector to call this when it got - * signing data from data loader. - * - * @param node the file to be written to. - * @param signerData the raw signer data byte array. - */ - public void writeSignerData(long node, @NonNull byte[] signerData) - throws IOException { - if (!nativeWriteSignerDataNode(mNativeInstance, node, signerData)) { - throw new IOException( - "IncrementalFileSystem failed to write signer data of node " + node); - } - } - - private final long mNativeInstance; - } - - /** - * Wrapper for native reporting DataLoader statuses. - * - * @hide - * - * TODO(b/136132412) Should be @SystemApi - */ - 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 IncrementalFileSystemControlParcel control, - @NonNull IncrementalDataLoaderParamsParcel params, - IDataLoaderStatusListener listener); - - private native boolean nativeStartDataLoader(int storageId); - - private native boolean nativeStopDataLoader(int storageId); - - private native boolean nativeDestroyDataLoader(int storageId); - - private static native boolean nativeOnFileCreated(int storageId, - long inode, byte[] metadata); - - private static native boolean nativeIsFileRangeLoadedNode( - long nativeInstance, long node, long start, long end); - - private static native boolean nativeWriteMissingData( - long nativeInstance, FileSystemConnector.DataBlock[] dataBlocks, - FileSystemConnector.HashBlock[] hashBlocks); - - private static native boolean nativeWriteSignerDataNode( - long nativeInstance, long node, byte[] signerData); - - private static native byte[] nativeGetFileMetadataNode( - long nativeInstance, long node); - - private static native byte[] nativeGetFileInfoNode( - long nativeInstance, long node); - - private static native boolean nativeReportStatus(long nativeInstance, int status); -} |
