blob: 321dc9e2246e17885afe6afc9d4376ef4aa1814e [file] [log] [blame]
Songchun Fan4e758692019-11-29 15:43:27 -08001/*
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
17package android.os.incremental;
18
19/**
Alex Buynytskyyc5682f52020-01-30 13:17:05 -080020 * 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.
Songchun Fan4e758692019-11-29 15:43:27 -080028 *
29 * @throws IllegalStateException the session is not an Incremental installation session.
30 */
31
Alex Buynytskyycd4d3872020-02-08 17:50:50 -080032import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
33
Songchun Fan4e758692019-11-29 15:43:27 -080034import android.annotation.NonNull;
35import android.annotation.Nullable;
Alex Buynytskyyc5682f52020-01-30 13:17:05 -080036import android.content.Context;
Alex Buynytskyyea14d192019-12-13 15:42:18 -080037import android.content.pm.DataLoaderParams;
Alex Buynytskyy04f73912020-02-10 08:34:18 -080038import android.content.pm.IDataLoaderStatusListener;
Songchun Fan6381d612020-02-26 17:59:41 -080039import android.content.pm.InstallationFileParcel;
Alex Buynytskyyc5682f52020-01-30 13:17:05 -080040import android.text.TextUtils;
Songchun Fan4e758692019-11-29 15:43:27 -080041
42import java.io.File;
43import java.io.IOException;
Alex Buynytskyyc5682f52020-01-30 13:17:05 -080044import java.util.List;
Songchun Fan4e758692019-11-29 15:43:27 -080045
46/**
47 * This class manages storage instances used during a package installation session.
48 * @hide
49 */
50public final class IncrementalFileStorages {
51 private static final String TAG = "IncrementalFileStorages";
Alex Buynytskyyc5682f52020-01-30 13:17:05 -080052
Yurii Zubrytskyi360bbdc2020-04-18 21:03:09 -070053 private @NonNull final IncrementalManager mIncrementalManager;
54 private @NonNull final File mStageDir;
Songchun Fan4e758692019-11-29 15:43:27 -080055 private @Nullable IncrementalStorage mDefaultStorage;
Songchun Fan4e758692019-11-29 15:43:27 -080056
57 /**
Songchun Fan54c6aed2020-01-31 16:52:41 -080058 * Set up files and directories used in an installation session. Only used by Incremental.
59 * All the files will be created in defaultStorage.
60 * TODO(b/133435829): code clean up
Songchun Fan4e758692019-11-29 15:43:27 -080061 *
62 * @throws IllegalStateException the session is not an Incremental installation session.
Alex Buynytskyyc5682f52020-01-30 13:17:05 -080063 * @throws IOException if fails to setup files or directories.
Songchun Fan4e758692019-11-29 15:43:27 -080064 */
Alex Buynytskyyc5682f52020-01-30 13:17:05 -080065 public static IncrementalFileStorages initialize(Context context,
Songchun Fan4e758692019-11-29 15:43:27 -080066 @NonNull File stageDir,
Alex Buynytskyyc5682f52020-01-30 13:17:05 -080067 @NonNull DataLoaderParams dataLoaderParams,
Alex Buynytskyy04f73912020-02-10 08:34:18 -080068 @Nullable IDataLoaderStatusListener dataLoaderStatusListener,
Songchun Fan6381d612020-02-26 17:59:41 -080069 List<InstallationFileParcel> addedFiles) throws IOException {
Alex Buynytskyyc5682f52020-01-30 13:17:05 -080070 // TODO(b/136132412): sanity check if session should not be incremental
71 IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
72 Context.INCREMENTAL_SERVICE);
73 if (incrementalManager == null) {
74 // TODO(b/146080380): add incremental-specific error code
75 throw new IOException("Failed to obtain incrementalManager.");
76 }
77
Yurii Zubrytskyi360bbdc2020-04-18 21:03:09 -070078 final IncrementalFileStorages result =
79 new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams,
80 dataLoaderStatusListener);
81 for (InstallationFileParcel file : addedFiles) {
82 if (file.location == LOCATION_DATA_APP) {
83 try {
84 result.addApkFile(file);
85 } catch (IOException e) {
86 // TODO(b/146080380): add incremental-specific error code
87 throw new IOException(
88 "Failed to add file to IncFS: " + file.name + ", reason: ", e);
Alex Buynytskyyc5682f52020-01-30 13:17:05 -080089 }
Yurii Zubrytskyi360bbdc2020-04-18 21:03:09 -070090 } else {
91 throw new IOException("Unknown file location: " + file.location);
Alex Buynytskyyc5682f52020-01-30 13:17:05 -080092 }
Alex Buynytskyyc5682f52020-01-30 13:17:05 -080093 }
Yurii Zubrytskyi360bbdc2020-04-18 21:03:09 -070094
95 if (!result.mDefaultStorage.startLoading()) {
96 // TODO(b/146080380): add incremental-specific error code
97 throw new IOException("Failed to start loading data for Incremental installation.");
98 }
99
100 return result;
Alex Buynytskyyc5682f52020-01-30 13:17:05 -0800101 }
102
103 private IncrementalFileStorages(@NonNull File stageDir,
Songchun Fan4e758692019-11-29 15:43:27 -0800104 @NonNull IncrementalManager incrementalManager,
Alex Buynytskyy04f73912020-02-10 08:34:18 -0800105 @NonNull DataLoaderParams dataLoaderParams,
106 @Nullable IDataLoaderStatusListener dataLoaderStatusListener) throws IOException {
Yurii Zubrytskyi360bbdc2020-04-18 21:03:09 -0700107 try {
108 mStageDir = stageDir;
109 mIncrementalManager = incrementalManager;
110 if (dataLoaderParams.getComponentName().getPackageName().equals("local")) {
111 final String incrementalPath = dataLoaderParams.getArguments();
112 if (TextUtils.isEmpty(incrementalPath)) {
113 throw new IOException("Failed to create storage: incrementalPath is empty");
114 }
115 mDefaultStorage = mIncrementalManager.openStorage(incrementalPath);
116 if (mDefaultStorage == null) {
117 throw new IOException(
118 "Couldn't open incremental storage at " + incrementalPath);
119 }
120 mDefaultStorage.bind(stageDir.getAbsolutePath());
121 } else {
122 mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(),
123 dataLoaderParams,
124 dataLoaderStatusListener,
125 IncrementalManager.CREATE_MODE_CREATE
126 | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false);
127 if (mDefaultStorage == null) {
128 throw new IOException(
129 "Couldn't create incremental storage at " + stageDir);
130 }
Alex Buynytskyyc5682f52020-01-30 13:17:05 -0800131 }
Yurii Zubrytskyi360bbdc2020-04-18 21:03:09 -0700132 } catch (IOException e) {
133 cleanUp();
134 throw e;
Songchun Fan4e758692019-11-29 15:43:27 -0800135 }
136 }
137
Songchun Fan6381d612020-02-26 17:59:41 -0800138 private void addApkFile(@NonNull InstallationFileParcel apk) throws IOException {
139 final String apkName = apk.name;
Alex Buynytskyy04f73912020-02-10 08:34:18 -0800140 final File targetFile = new File(mStageDir, apkName);
Songchun Fan54c6aed2020-01-31 16:52:41 -0800141 if (!targetFile.exists()) {
Songchun Fan6381d612020-02-26 17:59:41 -0800142 mDefaultStorage.makeFile(apkName, apk.size, null, apk.metadata, apk.signature);
Songchun Fan4e758692019-11-29 15:43:27 -0800143 }
Songchun Fan4e758692019-11-29 15:43:27 -0800144 }
145
146 /**
147 * Resets the states and unbinds storage instances for an installation session.
148 * TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept
149 */
150 public void cleanUp() {
Yurii Zubrytskyi360bbdc2020-04-18 21:03:09 -0700151 if (mDefaultStorage == null) {
152 return;
153 }
Alex Buynytskyyc5682f52020-01-30 13:17:05 -0800154
155 try {
Alex Buynytskyyc5682f52020-01-30 13:17:05 -0800156 mDefaultStorage.unBind(mStageDir.getAbsolutePath());
157 } catch (IOException ignored) {
Songchun Fan4e758692019-11-29 15:43:27 -0800158 }
Alex Buynytskyyc5682f52020-01-30 13:17:05 -0800159 mDefaultStorage = null;
Songchun Fan4e758692019-11-29 15:43:27 -0800160 }
Songchun Fan4e758692019-11-29 15:43:27 -0800161}