1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.service.dataloader;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.RequiresPermission;
22 import android.annotation.SystemApi;
23 import android.app.Service;
24 import android.content.Intent;
25 import android.content.pm.DataLoaderParams;
26 import android.content.pm.DataLoaderParamsParcel;
27 import android.content.pm.FileSystemControlParcel;
28 import android.content.pm.IDataLoader;
29 import android.content.pm.IDataLoaderStatusListener;
30 import android.content.pm.InstallationFile;
31 import android.content.pm.InstallationFileParcel;
32 import android.os.IBinder;
33 import android.os.ParcelFileDescriptor;
34 import android.util.ExceptionUtils;
35 import android.util.Slog;
36 
37 import libcore.io.IoUtils;
38 
39 import java.io.IOException;
40 import java.util.Collection;
41 
42 /**
43  * The base class for implementing data loader service to control data loaders. Expecting
44  * Incremental Service to bind to a children class of this.
45  *
46  * WARNING: This is a system API to aid internal development.
47  * Use at your own risk. It will change or be removed without warning.
48  *
49  * TODO(b/136132412): update with latest API design
50  *
51  * @hide
52  */
53 @SystemApi
54 public abstract class DataLoaderService extends Service {
55     private static final String TAG = "DataLoaderService";
56     private final DataLoaderBinderService mBinder = new DataLoaderBinderService();
57 
58     /**
59      * Managed DataLoader interface. Each instance corresponds to a single installation session.
60      * @hide
61      */
62     @SystemApi
63     public interface DataLoader {
64         /**
65          * A virtual constructor.
66          *
67          * @param dataLoaderParams parameters set in the installation session
68          * @param connector FS API wrapper
69          * @return True if initialization of a Data Loader was successful. False will be reported to
70          * PackageManager and fail the installation
71          */
onCreate(@onNull DataLoaderParams dataLoaderParams, @NonNull FileSystemConnector connector)72         boolean onCreate(@NonNull DataLoaderParams dataLoaderParams,
73                 @NonNull FileSystemConnector connector);
74 
75         /**
76          * Prepare installation image. After this method succeeds installer will validate the files
77          * and continue installation.
78          *
79          * @param addedFiles   list of files created in this installation session.
80          * @param removedFiles list of files removed in this installation session.
81          * @return false if unable to create and populate all addedFiles.
82          */
onPrepareImage(@onNull Collection<InstallationFile> addedFiles, @NonNull Collection<String> removedFiles)83         boolean onPrepareImage(@NonNull Collection<InstallationFile> addedFiles,
84                 @NonNull Collection<String> removedFiles);
85     }
86 
87     /**
88      * DataLoader factory method.
89      *
90      * @return An instance of a DataLoader.
91      * @hide
92      */
93     @SystemApi
onCreateDataLoader(@onNull DataLoaderParams dataLoaderParams)94     public @Nullable DataLoader onCreateDataLoader(@NonNull DataLoaderParams dataLoaderParams) {
95         return null;
96     }
97 
98     /**
99      * @hide
100      */
onBind(@onNull Intent intent)101     public final @NonNull IBinder onBind(@NonNull Intent intent) {
102         return (IBinder) mBinder;
103     }
104 
105     private class DataLoaderBinderService extends IDataLoader.Stub {
106         @Override
create(int id, @NonNull DataLoaderParamsParcel params, @NonNull FileSystemControlParcel control, @NonNull IDataLoaderStatusListener listener)107         public void create(int id, @NonNull DataLoaderParamsParcel params,
108                 @NonNull FileSystemControlParcel control,
109                 @NonNull IDataLoaderStatusListener listener)
110                 throws RuntimeException {
111             try {
112                 nativeCreateDataLoader(id, control, params, listener);
113             } catch (Exception ex) {
114                 Slog.e(TAG, "Failed to create native loader for " + id, ex);
115                 destroy(id);
116                 throw new RuntimeException(ex);
117             } finally {
118                 if (control.incremental != null) {
119                     IoUtils.closeQuietly(control.incremental.cmd);
120                     IoUtils.closeQuietly(control.incremental.pendingReads);
121                     IoUtils.closeQuietly(control.incremental.log);
122                 }
123             }
124         }
125 
126         @Override
start(int id)127         public void start(int id) {
128             if (!nativeStartDataLoader(id)) {
129                 Slog.e(TAG, "Failed to start loader: " + id);
130             }
131         }
132 
133         @Override
stop(int id)134         public void stop(int id) {
135             if (!nativeStopDataLoader(id)) {
136                 Slog.w(TAG, "Failed to stop loader: " + id);
137             }
138         }
139 
140         @Override
destroy(int id)141         public void destroy(int id) {
142             if (!nativeDestroyDataLoader(id)) {
143                 Slog.w(TAG, "Failed to destroy loader: " + id);
144             }
145         }
146 
147         @Override
prepareImage(int id, InstallationFileParcel[] addedFiles, String[] removedFiles)148         public void prepareImage(int id, InstallationFileParcel[] addedFiles,
149                 String[] removedFiles) {
150             if (!nativePrepareImage(id, addedFiles, removedFiles)) {
151                 Slog.w(TAG, "Failed to prepare image for data loader: " + id);
152             }
153         }
154     }
155 
156     /**
157      * Used by the DataLoaderService implementations.
158      *
159      * @hide
160      */
161     @SystemApi
162     public static final class FileSystemConnector {
163         /**
164          * Create a wrapper for a native instance.
165          *
166          * @hide
167          */
FileSystemConnector(long nativeInstance)168         FileSystemConnector(long nativeInstance) {
169             mNativeInstance = nativeInstance;
170         }
171 
172         /**
173          * Write data to an installation file from an arbitrary FD.
174          *
175          * @param name        name of file previously added to the installation session.
176          * @param offsetBytes offset into the file to begin writing at, or 0 to start at the
177          *                    beginning of the file.
178          * @param lengthBytes total size of the file being written, used to preallocate the
179          *                    underlying disk space, or -1 if unknown. The system may clear various
180          *                    caches as needed to allocate this space.
181          * @param incomingFd  FD to read bytes from.
182          * @throws IOException if trouble opening the file for writing, such as lack of disk space
183          *                     or unavailable media.
184          */
185         @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)
writeData(@onNull String name, long offsetBytes, long lengthBytes, @NonNull ParcelFileDescriptor incomingFd)186         public void writeData(@NonNull String name, long offsetBytes, long lengthBytes,
187                 @NonNull ParcelFileDescriptor incomingFd) throws IOException {
188             try {
189                 nativeWriteData(mNativeInstance, name, offsetBytes, lengthBytes, incomingFd);
190             } catch (RuntimeException e) {
191                 ExceptionUtils.maybeUnwrapIOException(e);
192                 throw e;
193             }
194         }
195 
196         private final long mNativeInstance;
197     }
198 
199     /* Native methods */
nativeCreateDataLoader(int storageId, @NonNull FileSystemControlParcel control, @NonNull DataLoaderParamsParcel params, IDataLoaderStatusListener listener)200     private native boolean nativeCreateDataLoader(int storageId,
201             @NonNull FileSystemControlParcel control,
202             @NonNull DataLoaderParamsParcel params,
203             IDataLoaderStatusListener listener);
204 
nativeStartDataLoader(int storageId)205     private native boolean nativeStartDataLoader(int storageId);
206 
nativeStopDataLoader(int storageId)207     private native boolean nativeStopDataLoader(int storageId);
208 
nativeDestroyDataLoader(int storageId)209     private native boolean nativeDestroyDataLoader(int storageId);
210 
nativePrepareImage(int storageId, InstallationFileParcel[] addedFiles, String[] removedFiles)211     private native boolean nativePrepareImage(int storageId,
212             InstallationFileParcel[] addedFiles, String[] removedFiles);
213 
nativeWriteData(long nativeInstance, String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd)214     private static native void nativeWriteData(long nativeInstance, String name, long offsetBytes,
215             long lengthBytes, ParcelFileDescriptor incomingFd);
216 
217 }
218