• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.os.incremental;
18 
19 /**
20  * Set up files and directories used in an installation session. Currently only used by Incremental
21  * Installation. For Incremental installation, the expected outcome of this function is: 0) All the
22  * files are in defaultStorage 1) All APK files are in the same directory, bound to mApkStorage, and
23  * bound to the InstallerSession's stage dir. The files are linked from mApkStorage to
24  * defaultStorage. 2) All lib files are in the sub directories as their names suggest, and in the
25  * same parent directory as the APK files. The files are linked from mApkStorage to defaultStorage.
26  * 3) OBB files are in another directory that is different from APK files and lib files, bound to
27  * mObbStorage. The files are linked from mObbStorage to defaultStorage.
28  *
29  * @throws IllegalStateException the session is not an Incremental installation session.
30  */
31 
32 import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
33 
34 import android.annotation.NonNull;
35 import android.annotation.Nullable;
36 import android.content.Context;
37 import android.content.pm.DataLoaderParams;
38 import android.content.pm.IDataLoaderStatusListener;
39 import android.content.pm.IPackageLoadingProgressCallback;
40 import android.content.pm.InstallationFileParcel;
41 
42 import java.io.File;
43 import java.io.IOException;
44 import java.util.List;
45 import java.util.UUID;
46 
47 /**
48  * This class manages storage instances used during a package installation session.
49  * @hide
50  */
51 public final class IncrementalFileStorages {
52     private static final String TAG = "IncrementalFileStorages";
53 
54     private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
55 
56     private @NonNull final IncrementalManager mIncrementalManager;
57     private @NonNull final File mStageDir;
58     private @Nullable IncrementalStorage mInheritedStorage;
59     private @Nullable IncrementalStorage mDefaultStorage;
60 
61     /**
62      * Set up files and directories used in an installation session. Only used by Incremental.
63      * All the files will be created in defaultStorage.
64      *
65      * @throws IllegalStateException the session is not an Incremental installation session.
66      * @throws IOException if fails to setup files or directories.
67      */
initialize(Context context, @NonNull File stageDir, @Nullable File inheritedDir, @NonNull DataLoaderParams dataLoaderParams, @Nullable IDataLoaderStatusListener statusListener, @Nullable StorageHealthCheckParams healthCheckParams, @Nullable IStorageHealthListener healthListener, @NonNull List<InstallationFileParcel> addedFiles, @NonNull PerUidReadTimeouts[] perUidReadTimeouts, @Nullable IPackageLoadingProgressCallback progressCallback)68     public static IncrementalFileStorages initialize(Context context,
69             @NonNull File stageDir,
70             @Nullable File inheritedDir,
71             @NonNull DataLoaderParams dataLoaderParams,
72             @Nullable IDataLoaderStatusListener statusListener,
73             @Nullable StorageHealthCheckParams healthCheckParams,
74             @Nullable IStorageHealthListener healthListener,
75             @NonNull List<InstallationFileParcel> addedFiles,
76             @NonNull PerUidReadTimeouts[] perUidReadTimeouts,
77             @Nullable IPackageLoadingProgressCallback progressCallback) throws IOException {
78         IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
79                 Context.INCREMENTAL_SERVICE);
80         if (incrementalManager == null) {
81             throw new IOException("Failed to obtain incrementalManager.");
82         }
83 
84         final IncrementalFileStorages result = new IncrementalFileStorages(stageDir, inheritedDir,
85                 incrementalManager, dataLoaderParams);
86         for (InstallationFileParcel file : addedFiles) {
87             if (file.location == LOCATION_DATA_APP) {
88                 try {
89                     result.addApkFile(file);
90                 } catch (IOException e) {
91                     throw new IOException(
92                             "Failed to add file to IncFS: " + file.name + ", reason: ", e);
93                 }
94             } else {
95                 throw new IOException("Unknown file location: " + file.location);
96             }
97         }
98         // Register progress loading callback after files have been added
99         if (progressCallback != null) {
100             incrementalManager.registerLoadingProgressCallback(stageDir.getAbsolutePath(),
101                     progressCallback);
102         }
103         result.startLoading(dataLoaderParams, statusListener, healthCheckParams, healthListener,
104                 perUidReadTimeouts);
105 
106         return result;
107     }
108 
IncrementalFileStorages(@onNull File stageDir, @Nullable File inheritedDir, @NonNull IncrementalManager incrementalManager, @NonNull DataLoaderParams dataLoaderParams)109     private IncrementalFileStorages(@NonNull File stageDir,
110             @Nullable File inheritedDir,
111             @NonNull IncrementalManager incrementalManager,
112             @NonNull DataLoaderParams dataLoaderParams) throws IOException {
113         try {
114             mStageDir = stageDir;
115             mIncrementalManager = incrementalManager;
116             if (inheritedDir != null && IncrementalManager.isIncrementalPath(
117                     inheritedDir.getAbsolutePath())) {
118                 mInheritedStorage = mIncrementalManager.openStorage(
119                         inheritedDir.getAbsolutePath());
120                 if (mInheritedStorage != null) {
121                     boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals(
122                             dataLoaderParams.getComponentName().getPackageName());
123                     if (systemDataLoader && !mInheritedStorage.isFullyLoaded()) {
124                         // System data loader does not support incomplete storages.
125                         throw new IOException("Inherited storage has missing pages.");
126                     }
127 
128                     mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
129                             mInheritedStorage, IncrementalManager.CREATE_MODE_CREATE
130                                     | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
131                     if (mDefaultStorage == null) {
132                         throw new IOException(
133                                 "Couldn't create linked incremental storage at " + stageDir);
134                     }
135                     return;
136                 }
137             }
138 
139             mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
140                     dataLoaderParams, IncrementalManager.CREATE_MODE_CREATE
141                             | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
142             if (mDefaultStorage == null) {
143                 throw new IOException(
144                         "Couldn't create incremental storage at " + stageDir);
145             }
146         } catch (IOException e) {
147             cleanUp();
148             throw e;
149         }
150     }
151 
addApkFile(@onNull InstallationFileParcel apk)152     private void addApkFile(@NonNull InstallationFileParcel apk) throws IOException {
153         final String apkName = apk.name;
154         final File targetFile = new File(mStageDir, apkName);
155         if (!targetFile.exists()) {
156             mDefaultStorage.makeFile(apkName, apk.size, null, apk.metadata, apk.signature, null);
157         }
158     }
159 
160     /**
161      * Starts or re-starts loading of data.
162      */
startLoading( @onNull DataLoaderParams dataLoaderParams, @Nullable IDataLoaderStatusListener statusListener, @Nullable StorageHealthCheckParams healthCheckParams, @Nullable IStorageHealthListener healthListener, @NonNull PerUidReadTimeouts[] perUidReadTimeouts)163     public void startLoading(
164             @NonNull DataLoaderParams dataLoaderParams,
165             @Nullable IDataLoaderStatusListener statusListener,
166             @Nullable StorageHealthCheckParams healthCheckParams,
167             @Nullable IStorageHealthListener healthListener,
168             @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
169         if (!mDefaultStorage.startLoading(dataLoaderParams, statusListener, healthCheckParams,
170                 healthListener, perUidReadTimeouts)) {
171             throw new IOException(
172                     "Failed to start or restart loading data for Incremental installation.");
173         }
174     }
175 
176     /**
177      * Creates file in default storage and sets its content.
178      */
makeFile(@onNull String name, @NonNull byte[] content)179     public void makeFile(@NonNull String name, @NonNull byte[] content) throws IOException {
180         mDefaultStorage.makeFile(name, content.length, UUID.randomUUID(), null, null, content);
181     }
182 
183     /**
184      * Creates a hardlink from inherited storage to default.
185      */
makeLink(@onNull String relativePath, @NonNull String fromBase, @NonNull String toBase)186     public boolean makeLink(@NonNull String relativePath, @NonNull String fromBase,
187             @NonNull String toBase) throws IOException {
188         if (mInheritedStorage == null) {
189             return false;
190         }
191         final File sourcePath = new File(fromBase, relativePath);
192         final File destPath = new File(toBase, relativePath);
193         mInheritedStorage.makeLink(sourcePath.getAbsolutePath(), mDefaultStorage,
194                 destPath.getAbsolutePath());
195         return true;
196     }
197 
198     /**
199      * Permanently disables readlogs.
200      */
disallowReadLogs()201     public void disallowReadLogs() {
202         mDefaultStorage.disallowReadLogs();
203     }
204 
205     /**
206      * Resets the states and unbinds storage instances for an installation session.
207      */
cleanUpAndMarkComplete()208     public void cleanUpAndMarkComplete() {
209         IncrementalStorage defaultStorage = cleanUp();
210         if (defaultStorage != null) {
211             defaultStorage.onInstallationComplete();
212         }
213     }
214 
cleanUp()215     private IncrementalStorage cleanUp() {
216         IncrementalStorage defaultStorage = mDefaultStorage;
217         mInheritedStorage = null;
218         mDefaultStorage = null;
219         if (defaultStorage == null) {
220             return null;
221         }
222 
223         try {
224             mIncrementalManager.unregisterLoadingProgressCallbacks(mStageDir.getAbsolutePath());
225             defaultStorage.unBind(mStageDir.getAbsolutePath());
226         } catch (IOException ignored) {
227         }
228         return defaultStorage;
229     }
230 }
231