blob: f8273c039e74adf98984fef93dc59faa75375e24 [file] [log] [blame]
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -07001/*
2 * Copyright (C) 2014 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 com.android.server.pm;
18
Jeff Sharkeyf0600952014-08-07 17:31:53 -070019import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070020import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
Jeff Sharkey742e7902014-08-16 19:09:13 -070021import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -070022import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070023import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
24import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
25import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070026import static android.system.OsConstants.O_CREAT;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070027import static android.system.OsConstants.O_RDONLY;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070028import static android.system.OsConstants.O_WRONLY;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070029
Jeff Sharkeya0907432014-08-15 10:23:11 -070030import android.content.Context;
Jeff Sharkeyfbd0e9f2014-08-06 16:34:34 -070031import android.content.Intent;
Jeff Sharkeya0907432014-08-15 10:23:11 -070032import android.content.IntentSender;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070033import android.content.pm.ApplicationInfo;
34import android.content.pm.IPackageInstallObserver2;
35import android.content.pm.IPackageInstallerSession;
Jeff Sharkey7328a1b2014-08-07 14:01:43 -070036import android.content.pm.PackageInstaller;
Jeff Sharkeya0907432014-08-15 10:23:11 -070037import android.content.pm.PackageInstaller.SessionInfo;
38import android.content.pm.PackageInstaller.SessionParams;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070039import android.content.pm.PackageManager;
40import android.content.pm.PackageParser;
Jeff Sharkeyc4858a22014-06-16 10:51:20 -070041import android.content.pm.PackageParser.ApkLite;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -070042import android.content.pm.PackageParser.PackageLite;
Jeff Sharkey275e0852014-06-17 18:18:49 -070043import android.content.pm.PackageParser.PackageParserException;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070044import android.content.pm.Signature;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070045import android.os.Bundle;
Jeff Sharkey78cc3402014-05-22 10:52:49 -070046import android.os.FileBridge;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070047import android.os.FileUtils;
48import android.os.Handler;
49import android.os.Looper;
50import android.os.Message;
51import android.os.ParcelFileDescriptor;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -070052import android.os.Process;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070053import android.os.RemoteException;
Jeff Sharkey57dcf5b2014-06-18 17:46:05 -070054import android.os.UserHandle;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070055import android.system.ErrnoException;
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070056import android.system.Os;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070057import android.system.OsConstants;
58import android.system.StructStat;
59import android.util.ArraySet;
Jeff Sharkeya1031142014-07-12 18:09:46 -070060import android.util.ExceptionUtils;
61import android.util.MathUtils;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070062import android.util.Slog;
63
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070064import com.android.internal.annotations.GuardedBy;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -070065import com.android.internal.content.NativeLibraryHelper;
Jeff Sharkey742e7902014-08-16 19:09:13 -070066import com.android.internal.content.PackageHelper;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070067import com.android.internal.util.ArrayUtils;
Jeff Sharkeya1031142014-07-12 18:09:46 -070068import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070069import com.android.internal.util.Preconditions;
Jeff Sharkeya0907432014-08-15 10:23:11 -070070import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070071
Jeff Sharkey941a8ba2014-08-20 16:26:32 -070072import libcore.io.IoUtils;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070073import libcore.io.Libcore;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070074
75import java.io.File;
76import java.io.FileDescriptor;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070077import java.io.IOException;
78import java.util.ArrayList;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -070079import java.util.List;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070080import java.util.concurrent.atomic.AtomicInteger;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070081
82public class PackageInstallerSession extends IPackageInstallerSession.Stub {
83 private static final String TAG = "PackageInstaller";
Jeff Sharkey9a445772014-07-16 11:32:08 -070084 private static final boolean LOGD = true;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070085
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070086 private static final int MSG_COMMIT = 0;
87
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070088 // TODO: enforce INSTALL_ALLOW_TEST
89 // TODO: enforce INSTALL_ALLOW_DOWNGRADE
Jeff Sharkeyec55ef02014-07-08 11:28:00 -070090
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -070091 // TODO: treat INHERIT_EXISTING as installExistingPackage()
92
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070093 private final PackageInstallerService.InternalCallback mCallback;
Jeff Sharkeya0907432014-08-15 10:23:11 -070094 private final Context mContext;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -070095 private final PackageManagerService mPm;
96 private final Handler mHandler;
97
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070098 final int sessionId;
99 final int userId;
100 final String installerPackageName;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700101 final SessionParams params;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700102 final long createdMillis;
Jeff Sharkey742e7902014-08-16 19:09:13 -0700103
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700104 /** Staging location where client data is written. */
105 final File stageDir;
106 final String stageCid;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700107
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700108 /** Note that UID is not persisted; it's always derived at runtime. */
109 final int installerUid;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700110
Jeff Sharkey742e7902014-08-16 19:09:13 -0700111 private final AtomicInteger mOpenCount = new AtomicInteger();
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700112
113 private final Object mLock = new Object();
114
115 @GuardedBy("mLock")
116 private float mClientProgress = 0;
117 @GuardedBy("mLock")
118 private float mProgress = 0;
119 @GuardedBy("mLock")
120 private float mReportedProgress = -1;
121
122 @GuardedBy("mLock")
123 private boolean mSealed = false;
124 @GuardedBy("mLock")
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700125 private boolean mPermissionsAccepted = false;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700126 @GuardedBy("mLock")
127 private boolean mDestroyed = false;
128
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700129 private int mFinalStatus;
130 private String mFinalMessage;
131
Jeff Sharkey742e7902014-08-16 19:09:13 -0700132 @GuardedBy("mLock")
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700133 private ArrayList<FileBridge> mBridges = new ArrayList<>();
134
135 @GuardedBy("mLock")
136 private IPackageInstallObserver2 mRemoteObserver;
137
138 /** Fields derived from commit parsing */
139 private String mPackageName;
140 private int mVersionCode;
141 private Signature[] mSignatures;
142
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700143 /**
144 * Path to the validated base APK for this session, which may point at an
145 * APK inside the session (when the session defines the base), or it may
146 * point at the existing base APK (when adding splits to an existing app).
147 * <p>
148 * This is used when confirming permissions, since we can't fully stage the
149 * session inside an ASEC before confirming with user.
150 */
151 @GuardedBy("mLock")
152 private File mResolvedBaseFile;
153
154 @GuardedBy("mLock")
155 private File mResolvedStageDir;
156
157 @GuardedBy("mLock")
158 private final List<File> mResolvedStagedFiles = new ArrayList<>();
159 @GuardedBy("mLock")
160 private final List<File> mResolvedInheritedFiles = new ArrayList<>();
161
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700162 private final Handler.Callback mHandlerCallback = new Handler.Callback() {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700163 @Override
164 public boolean handleMessage(Message msg) {
165 synchronized (mLock) {
166 if (msg.obj != null) {
167 mRemoteObserver = (IPackageInstallObserver2) msg.obj;
168 }
169
170 try {
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700171 commitLocked();
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700172 } catch (PackageManagerException e) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700173 final String completeMsg = ExceptionUtils.getCompleteMessage(e);
174 Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700175 destroyInternal();
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700176 dispatchSessionFinished(e.error, completeMsg, null);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700177 }
178
179 return true;
180 }
181 }
182 };
183
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700184 public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
Jeff Sharkeya0907432014-08-15 10:23:11 -0700185 Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
186 String installerPackageName, SessionParams params, long createdMillis,
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700187 File stageDir, String stageCid, boolean sealed) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700188 mCallback = callback;
Jeff Sharkeya0907432014-08-15 10:23:11 -0700189 mContext = context;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700190 mPm = pm;
191 mHandler = new Handler(looper, mHandlerCallback);
192
193 this.sessionId = sessionId;
194 this.userId = userId;
195 this.installerPackageName = installerPackageName;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700196 this.params = params;
197 this.createdMillis = createdMillis;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700198 this.stageDir = stageDir;
199 this.stageCid = stageCid;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700200
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700201 if ((stageDir == null) == (stageCid == null)) {
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700202 throw new IllegalArgumentException(
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700203 "Exactly one of stageDir or stageCid stage must be set");
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700204 }
205
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700206 mSealed = sealed;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700207
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700208 // Always derived at runtime
209 installerUid = mPm.getPackageUid(installerPackageName, userId);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700210
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700211 if (mPm.checkPermission(android.Manifest.permission.INSTALL_PACKAGES,
212 installerPackageName) == PackageManager.PERMISSION_GRANTED) {
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700213 mPermissionsAccepted = true;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700214 } else {
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700215 mPermissionsAccepted = false;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700216 }
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700217
218 computeProgressLocked();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700219 }
220
Jeff Sharkeya0907432014-08-15 10:23:11 -0700221 public SessionInfo generateInfo() {
222 final SessionInfo info = new SessionInfo();
Jeff Sharkey742e7902014-08-16 19:09:13 -0700223 synchronized (mLock) {
224 info.sessionId = sessionId;
225 info.installerPackageName = installerPackageName;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700226 info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
227 mResolvedBaseFile.getAbsolutePath() : null;
Jeff Sharkey742e7902014-08-16 19:09:13 -0700228 info.progress = mProgress;
229 info.sealed = mSealed;
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700230 info.active = mOpenCount.get() > 0;
Jeff Sharkeybb580672014-07-10 12:10:25 -0700231
Jeff Sharkey742e7902014-08-16 19:09:13 -0700232 info.mode = params.mode;
233 info.sizeBytes = params.sizeBytes;
234 info.appPackageName = params.appPackageName;
235 info.appIcon = params.appIcon;
236 info.appLabel = params.appLabel;
237 }
Jeff Sharkeybb580672014-07-10 12:10:25 -0700238 return info;
239 }
240
Jeff Sharkey742e7902014-08-16 19:09:13 -0700241 public boolean isSealed() {
242 synchronized (mLock) {
243 return mSealed;
244 }
245 }
246
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700247 private void assertNotSealed(String cookie) {
248 synchronized (mLock) {
249 if (mSealed) {
250 throw new SecurityException(cookie + " not allowed after commit");
251 }
252 }
253 }
254
Jeff Sharkey742e7902014-08-16 19:09:13 -0700255 /**
256 * Resolve the actual location where staged data should be written. This
257 * might point at an ASEC mount point, which is why we delay path resolution
258 * until someone actively works with the session.
259 */
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700260 private File resolveStageDir() throws IOException {
Jeff Sharkey742e7902014-08-16 19:09:13 -0700261 synchronized (mLock) {
262 if (mResolvedStageDir == null) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700263 if (stageDir != null) {
264 mResolvedStageDir = stageDir;
Jeff Sharkey742e7902014-08-16 19:09:13 -0700265 } else {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700266 final String path = PackageHelper.getSdDir(stageCid);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700267 if (path != null) {
268 mResolvedStageDir = new File(path);
269 } else {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700270 throw new IOException("Failed to resolve path to container " + stageCid);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700271 }
272 }
273 }
274 return mResolvedStageDir;
275 }
276 }
277
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700278 @Override
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700279 public void setClientProgress(float progress) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700280 synchronized (mLock) {
281 mClientProgress = progress;
282 computeProgressLocked();
283 }
284 maybePublishProgress();
Jeff Sharkeya1031142014-07-12 18:09:46 -0700285 }
286
287 @Override
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700288 public void addClientProgress(float progress) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700289 synchronized (mLock) {
290 mClientProgress += progress;
291 computeProgressLocked();
292 }
293 maybePublishProgress();
294 }
295
296 private void computeProgressLocked() {
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700297 if (mProgress <= 0.8f) {
298 mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f);
299 }
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700300 }
301
302 private void maybePublishProgress() {
303 // Only publish when meaningful change
304 if (Math.abs(mProgress - mReportedProgress) > 0.01) {
305 mReportedProgress = mProgress;
306 mCallback.onSessionProgressChanged(this, mProgress);
307 }
308 }
309
310 @Override
Jeff Sharkeya0907432014-08-15 10:23:11 -0700311 public String[] getNames() {
312 assertNotSealed("getNames");
Jeff Sharkey742e7902014-08-16 19:09:13 -0700313 try {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700314 return resolveStageDir().list();
Jeff Sharkey742e7902014-08-16 19:09:13 -0700315 } catch (IOException e) {
316 throw ExceptionUtils.wrap(e);
317 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700318 }
319
320 @Override
321 public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
Jeff Sharkeya1031142014-07-12 18:09:46 -0700322 try {
323 return openWriteInternal(name, offsetBytes, lengthBytes);
324 } catch (IOException e) {
325 throw ExceptionUtils.wrap(e);
326 }
327 }
328
329 private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
330 throws IOException {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700331 // Quick sanity check of state, and allocate a pipe for ourselves. We
332 // then do heavy disk allocation outside the lock, but this open pipe
333 // will block any attempted install transitions.
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700334 final FileBridge bridge;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700335 synchronized (mLock) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700336 assertNotSealed("openWrite");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700337
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700338 bridge = new FileBridge();
339 mBridges.add(bridge);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700340 }
341
342 try {
343 // Use installer provided name for now; we always rename later
344 if (!FileUtils.isValidExtFilename(name)) {
345 throw new IllegalArgumentException("Invalid name: " + name);
346 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700347 final File target = new File(resolveStageDir(), name);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700348
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700349 // TODO: this should delegate to DCS so the system process avoids
350 // holding open FDs into containers.
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700351 final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700352 O_CREAT | O_WRONLY, 0644);
353 Os.chmod(target.getAbsolutePath(), 0644);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700354
355 // If caller specified a total length, allocate it for them. Free up
356 // cache space to grow, if needed.
357 if (lengthBytes > 0) {
358 final StructStat stat = Libcore.os.fstat(targetFd);
359 final long deltaBytes = lengthBytes - stat.st_size;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700360 // Only need to free up space when writing to internal stage
361 if (stageDir != null && deltaBytes > 0) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700362 mPm.freeStorage(deltaBytes);
363 }
364 Libcore.os.posix_fallocate(targetFd, 0, lengthBytes);
365 }
366
367 if (offsetBytes > 0) {
368 Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
369 }
370
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700371 bridge.setTargetFile(targetFd);
372 bridge.start();
373 return new ParcelFileDescriptor(bridge.getClientSocket());
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700374
375 } catch (ErrnoException e) {
Jeff Sharkeya1031142014-07-12 18:09:46 -0700376 throw e.rethrowAsIOException();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700377 }
378 }
379
380 @Override
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700381 public ParcelFileDescriptor openRead(String name) {
382 try {
383 return openReadInternal(name);
384 } catch (IOException e) {
385 throw ExceptionUtils.wrap(e);
386 }
387 }
388
389 private ParcelFileDescriptor openReadInternal(String name) throws IOException {
390 assertNotSealed("openRead");
391
392 try {
393 if (!FileUtils.isValidExtFilename(name)) {
394 throw new IllegalArgumentException("Invalid name: " + name);
395 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700396 final File target = new File(resolveStageDir(), name);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700397
398 final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0);
399 return new ParcelFileDescriptor(targetFd);
400
401 } catch (ErrnoException e) {
402 throw e.rethrowAsIOException();
403 }
404 }
405
406 @Override
Jeff Sharkeya0907432014-08-15 10:23:11 -0700407 public void commit(IntentSender statusReceiver) {
408 Preconditions.checkNotNull(statusReceiver);
409
410 final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700411 statusReceiver, sessionId);
Jeff Sharkeya0907432014-08-15 10:23:11 -0700412 mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700413 }
414
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700415 private void commitLocked() throws PackageManagerException {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700416 if (mDestroyed) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700417 throw new PackageManagerException(INSTALL_FAILED_ALREADY_EXISTS, "Invalid session");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700418 }
419
420 // Verify that all writers are hands-off
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700421 if (!mSealed) {
Jeff Sharkey78cc3402014-05-22 10:52:49 -0700422 for (FileBridge bridge : mBridges) {
423 if (!bridge.isClosed()) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700424 throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700425 "Files still open");
426 }
427 }
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700428 mSealed = true;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700429
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700430 // Persist the fact that we've sealed ourselves to prevent mutations
431 // of any hard links we create below.
432 mCallback.onSessionSealed(this);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700433 }
434
Jeff Sharkey742e7902014-08-16 19:09:13 -0700435 try {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700436 resolveStageDir();
Jeff Sharkey742e7902014-08-16 19:09:13 -0700437 } catch (IOException e) {
438 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700439 "Failed to resolve stage location", e);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700440 }
441
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700442 // Verify that stage looks sane with respect to existing application.
443 // This currently only ensures packageName, versionCode, and certificate
444 // consistency.
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700445 validateInstallLocked();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700446
447 Preconditions.checkNotNull(mPackageName);
448 Preconditions.checkNotNull(mSignatures);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700449 Preconditions.checkNotNull(mResolvedBaseFile);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700450
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700451 if (!mPermissionsAccepted) {
452 // User needs to accept permissions; give installer an intent they
453 // can use to involve user.
454 final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
455 intent.setPackage("com.android.packageinstaller");
456 intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
457 try {
458 mRemoteObserver.onUserActionRequired(intent);
459 } catch (RemoteException ignored) {
460 }
461 return;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700462 }
463
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700464 if (stageCid != null) {
465 // Figure out the final installed size and resize the container once
466 // and for all. Internally the parser handles straddling between two
467 // locations when inheriting.
468 final long finalSize = calculateInstalledSize();
469 resizeContainer(stageCid, finalSize);
470 }
471
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700472 // Inherit any packages and native libraries from existing install that
473 // haven't been overridden.
Jeff Sharkeya0907432014-08-15 10:23:11 -0700474 if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700475 try {
476 if (stageCid != null) {
477 // TODO: this should delegate to DCS so the system process
478 // avoids holding open FDs into containers.
479 copyFiles(mResolvedInheritedFiles, resolveStageDir());
480 } else {
481 linkFiles(mResolvedInheritedFiles, resolveStageDir());
482 }
483 } catch (IOException e) {
484 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
485 "Failed to inherit existing install", e);
486 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700487 }
488
Jeff Sharkeya1031142014-07-12 18:09:46 -0700489 // TODO: surface more granular state from dexopt
Jeff Sharkeyec9bad22014-09-05 09:45:20 -0700490 mProgress = 0.9f;
491 maybePublishProgress();
Jeff Sharkeya1031142014-07-12 18:09:46 -0700492
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700493 // Unpack native libraries
494 extractNativeLibraries(mResolvedStageDir, params.abiOverride);
495
496 // Container is ready to go, let's seal it up!
497 if (stageCid != null) {
498 finalizeAndFixContainer(stageCid);
499 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700500
501 // We've reached point of no return; call into PMS to install the stage.
502 // Regardless of success or failure we always destroy session.
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700503 final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
504 @Override
Jeff Sharkeyfbd0e9f2014-08-06 16:34:34 -0700505 public void onUserActionRequired(Intent intent) {
506 throw new IllegalStateException();
507 }
508
509 @Override
510 public void onPackageInstalled(String basePackageName, int returnCode, String msg,
511 Bundle extras) {
Jeff Sharkeya1031142014-07-12 18:09:46 -0700512 destroyInternal();
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700513 dispatchSessionFinished(returnCode, msg, extras);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700514 }
515 };
516
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700517 mPm.installStage(mPackageName, stageDir, stageCid, localObserver,
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700518 params, installerPackageName, installerUid, new UserHandle(userId));
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700519 }
520
521 /**
522 * Validate install by confirming that all application packages are have
523 * consistent package name, version code, and signing certificates.
524 * <p>
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700525 * Clears and populates {@link #mResolvedBaseFile},
526 * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
527 * <p>
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700528 * Renames package files in stage to match split names defined inside.
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700529 * <p>
530 * Note that upgrade compatibility is still performed by
531 * {@link PackageManagerService}.
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700532 */
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700533 private void validateInstallLocked() throws PackageManagerException {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700534 mPackageName = null;
535 mVersionCode = -1;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700536 mSignatures = null;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700537
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700538 mResolvedBaseFile = null;
539 mResolvedStagedFiles.clear();
540 mResolvedInheritedFiles.clear();
541
542 final File[] files = mResolvedStageDir.listFiles();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700543 if (ArrayUtils.isEmpty(files)) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700544 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700545 }
546
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700547 // Verify that all staged packages are internally consistent
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700548 final ArraySet<String> stagedSplits = new ArraySet<>();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700549 for (File file : files) {
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700550
551 // Installers can't stage directories, so it's fine to ignore
552 // entries like "lost+found".
553 if (file.isDirectory()) continue;
554
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700555 final ApkLite apk;
Jeff Sharkey275e0852014-06-17 18:18:49 -0700556 try {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700557 apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
Jeff Sharkey275e0852014-06-17 18:18:49 -0700558 } catch (PackageParserException e) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700559 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
Jeff Sharkey275e0852014-06-17 18:18:49 -0700560 "Failed to parse " + file + ": " + e);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700561 }
562
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700563 if (!stagedSplits.add(apk.splitName)) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700564 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700565 "Split " + apk.splitName + " was defined multiple times");
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700566 }
567
568 // Use first package to define unknown values
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700569 if (mPackageName == null) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700570 mPackageName = apk.packageName;
571 mVersionCode = apk.versionCode;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700572 }
Jeff Sharkeyec55ef02014-07-08 11:28:00 -0700573 if (mSignatures == null) {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700574 mSignatures = apk.signatures;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700575 }
576
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700577 assertApkConsistent(String.valueOf(file), apk);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700578
579 // Take this opportunity to enforce uniform naming
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700580 final String targetName;
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700581 if (apk.splitName == null) {
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700582 targetName = "base.apk";
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700583 } else {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700584 targetName = "split_" + apk.splitName + ".apk";
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700585 }
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700586 if (!FileUtils.isValidExtFilename(targetName)) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700587 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700588 "Invalid filename: " + targetName);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700589 }
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700590
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700591 final File targetFile = new File(mResolvedStageDir, targetName);
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700592 if (!file.equals(targetFile)) {
593 file.renameTo(targetFile);
594 }
595
596 // Base is coming from session
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700597 if (apk.splitName == null) {
598 mResolvedBaseFile = targetFile;
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700599 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700600
601 mResolvedStagedFiles.add(targetFile);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700602 }
603
Jeff Sharkeya0907432014-08-15 10:23:11 -0700604 if (params.mode == SessionParams.MODE_FULL_INSTALL) {
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700605 // Full installs must include a base package
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700606 if (!stagedSplits.contains(null)) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700607 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700608 "Full install must include a base package");
609 }
610
611 } else {
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700612 // Partial installs must be consistent with existing install
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700613 final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
614 if (app == null) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700615 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700616 "Missing existing base package for " + mPackageName);
617 }
618
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700619 final PackageLite existing;
620 final ApkLite existingBase;
Jeff Sharkey275e0852014-06-17 18:18:49 -0700621 try {
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700622 existing = PackageParser.parsePackageLite(new File(app.getCodePath()), 0);
623 existingBase = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
Jeff Sharkeybb7b7be2014-08-19 16:18:28 -0700624 PackageParser.PARSE_COLLECT_CERTIFICATES);
Jeff Sharkey275e0852014-06-17 18:18:49 -0700625 } catch (PackageParserException e) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700626 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700627 "Failed to parse existing package " + app.getCodePath() + ": " + e);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700628 }
629
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700630 assertApkConsistent("Existing base", existingBase);
631
632 // Inherit base if not overridden
633 if (mResolvedBaseFile == null) {
634 mResolvedBaseFile = new File(app.getBaseCodePath());
635 mResolvedInheritedFiles.add(mResolvedBaseFile);
636 }
637
638 // Inherit splits if not overridden
639 if (!ArrayUtils.isEmpty(existing.splitNames)) {
640 for (int i = 0; i < existing.splitNames.length; i++) {
641 final String splitName = existing.splitNames[i];
642 final File splitFile = new File(existing.splitCodePaths[i]);
643
644 if (!stagedSplits.contains(splitName)) {
645 mResolvedInheritedFiles.add(splitFile);
646 }
647 }
648 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700649 }
650 }
651
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700652 private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException {
653 if (!mPackageName.equals(apk.packageName)) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700654 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700655 + apk.packageName + " inconsistent with " + mPackageName);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700656 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700657 if (mVersionCode != apk.versionCode) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700658 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700659 + " version code " + apk.versionCode + " inconsistent with "
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700660 + mVersionCode);
661 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700662 if (!Signature.areExactMatch(mSignatures, apk.signatures)) {
Jeff Sharkeye0b0bef2014-07-12 15:37:47 -0700663 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700664 tag + " signatures are inconsistent");
665 }
666 }
667
668 /**
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700669 * Calculate the final install footprint size, combining both staged and
670 * existing APKs together and including unpacked native code from both.
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700671 */
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700672 private long calculateInstalledSize() throws PackageManagerException {
673 Preconditions.checkNotNull(mResolvedBaseFile);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700674
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700675 final ApkLite baseApk;
676 try {
677 baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0);
678 } catch (PackageParserException e) {
679 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
680 "Failed to parse base package " + mResolvedBaseFile + ": " + e);
681 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700682
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700683 final List<String> splitPaths = new ArrayList<>();
684 for (File file : mResolvedStagedFiles) {
685 if (mResolvedBaseFile.equals(file)) continue;
686 splitPaths.add(file.getAbsolutePath());
687 }
688 for (File file : mResolvedInheritedFiles) {
689 if (mResolvedBaseFile.equals(file)) continue;
690 splitPaths.add(file.getAbsolutePath());
691 }
692
693 // This is kind of hacky; we're creating a half-parsed package that is
694 // straddled between the inherited and staged APKs.
695 final PackageLite pkg = new PackageLite(null, baseApk, null,
696 splitPaths.toArray(new String[splitPaths.size()]));
697 final boolean isForwardLocked =
698 (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
699
700 try {
701 return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride);
702 } catch (IOException e) {
703 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
704 "Failed to calculate install size", e);
705 }
706 }
707
708 private static void linkFiles(List<File> fromFiles, File toDir) throws IOException {
709 for (File fromFile : fromFiles) {
710 final File toFile = new File(toDir, fromFile.getName());
711 try {
712 if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile);
713 Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath());
714 } catch (ErrnoException e) {
715 throw new IOException("Failed to link " + fromFile + " to " + toFile, e);
716 }
717 }
718 Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
719 }
720
721 private static void copyFiles(List<File> fromFiles, File toDir) throws IOException {
722 // Remove any partial files from previous attempt
723 for (File file : toDir.listFiles()) {
724 if (file.getName().endsWith(".tmp")) {
725 file.delete();
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700726 }
727 }
Jeff Sharkey9a445772014-07-16 11:32:08 -0700728
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700729 for (File fromFile : fromFiles) {
730 final File tmpFile = File.createTempFile("inherit", ".tmp", toDir);
731 if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile);
732 if (!FileUtils.copyFile(fromFile, tmpFile)) {
733 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
734 }
735
736 final File toFile = new File(toDir, fromFile.getName());
737 if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile);
738 if (!tmpFile.renameTo(toFile)) {
739 throw new IOException("Failed to rename " + tmpFile + " to " + toFile);
740 }
741 }
742 Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
743 }
744
745 private static void extractNativeLibraries(File packageDir, String abiOverride)
746 throws PackageManagerException {
747 if (LOGD) Slog.v(TAG, "extractNativeLibraries()");
748
749 // Always start from a clean slate
750 final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
751 NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
752
753 NativeLibraryHelper.Handle handle = null;
754 try {
755 handle = NativeLibraryHelper.Handle.create(packageDir);
756 final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
757 abiOverride);
758 if (res != PackageManager.INSTALL_SUCCEEDED) {
759 throw new PackageManagerException(res,
760 "Failed to extract native libraries, res=" + res);
761 }
762 } catch (IOException e) {
763 throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
764 "Failed to extract native libraries", e);
765 } finally {
766 IoUtils.closeQuietly(handle);
767 }
768 }
769
770 private static void resizeContainer(String cid, long targetSize)
771 throws PackageManagerException {
772 String path = PackageHelper.getSdDir(cid);
773 if (path == null) {
774 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
775 "Failed to find mounted " + cid);
776 }
777
778 final long currentSize = new File(path).getTotalSpace();
779 if (currentSize > targetSize) {
780 Slog.w(TAG, "Current size " + currentSize + " is larger than target size "
781 + targetSize + "; skipping resize");
782 return;
783 }
784
785 if (!PackageHelper.unMountSdDir(cid)) {
786 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
787 "Failed to unmount " + cid + " before resize");
788 }
789
790 if (!PackageHelper.resizeSdDir(targetSize, cid,
791 PackageManagerService.getEncryptKey())) {
792 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
793 "Failed to resize " + cid + " to " + targetSize + " bytes");
794 }
795
796 path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
797 Process.SYSTEM_UID, false);
798 if (path == null) {
799 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
800 "Failed to mount " + cid + " after resize");
801 }
802 }
803
804 private void finalizeAndFixContainer(String cid) throws PackageManagerException {
805 if (!PackageHelper.finalizeSdDir(cid)) {
806 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
807 "Failed to finalize container " + cid);
808 }
809
810 final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
811 UserHandle.USER_OWNER);
812 final int gid = UserHandle.getSharedAppGid(uid);
813 if (!PackageHelper.fixSdPermissions(cid, gid, null)) {
814 throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
815 "Failed to fix permissions on container " + cid);
816 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700817 }
818
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700819 void setPermissionsResult(boolean accepted) {
820 if (!mSealed) {
821 throw new SecurityException("Must be sealed to accept permissions");
822 }
823
824 if (accepted) {
825 // Mark and kick off another install pass
826 mPermissionsAccepted = true;
827 mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
828 } else {
829 destroyInternal();
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700830 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700831 }
832 }
833
Jeff Sharkey742e7902014-08-16 19:09:13 -0700834 public void open() {
835 if (mOpenCount.getAndIncrement() == 0) {
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700836 mCallback.onSessionActiveChanged(this, true);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700837 }
838 }
839
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700840 @Override
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700841 public void close() {
Jeff Sharkey742e7902014-08-16 19:09:13 -0700842 if (mOpenCount.decrementAndGet() == 0) {
Jeff Sharkeybc7bce32014-09-05 15:53:05 -0700843 mCallback.onSessionActiveChanged(this, false);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700844 }
Jeff Sharkey16c8e3f2014-07-24 17:08:17 -0700845 }
846
847 @Override
848 public void abandon() {
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700849 destroyInternal();
Jeff Sharkeyf0600952014-08-07 17:31:53 -0700850 dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700851 }
852
853 private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
854 mFinalStatus = returnCode;
855 mFinalMessage = msg;
856
857 if (mRemoteObserver != null) {
858 try {
859 mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras);
860 } catch (RemoteException ignored) {
861 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700862 }
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700863
864 final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
865 mCallback.onSessionFinished(this, success);
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700866 }
Jeff Sharkeya1031142014-07-12 18:09:46 -0700867
868 private void destroyInternal() {
869 synchronized (mLock) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700870 mSealed = true;
871 mDestroyed = true;
Jeff Sharkeya1031142014-07-12 18:09:46 -0700872 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700873 if (stageDir != null) {
874 FileUtils.deleteContents(stageDir);
875 stageDir.delete();
Jeff Sharkey742e7902014-08-16 19:09:13 -0700876 }
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700877 if (stageCid != null) {
878 PackageHelper.destroySdDir(stageCid);
Jeff Sharkey742e7902014-08-16 19:09:13 -0700879 }
Jeff Sharkeya1031142014-07-12 18:09:46 -0700880 }
881
882 void dump(IndentingPrintWriter pw) {
Jeff Sharkey742e7902014-08-16 19:09:13 -0700883 synchronized (mLock) {
884 dumpLocked(pw);
885 }
886 }
887
888 private void dumpLocked(IndentingPrintWriter pw) {
Jeff Sharkeya1031142014-07-12 18:09:46 -0700889 pw.println("Session " + sessionId + ":");
890 pw.increaseIndent();
891
892 pw.printPair("userId", userId);
893 pw.printPair("installerPackageName", installerPackageName);
894 pw.printPair("installerUid", installerUid);
895 pw.printPair("createdMillis", createdMillis);
Jeff Sharkey941a8ba2014-08-20 16:26:32 -0700896 pw.printPair("stageDir", stageDir);
897 pw.printPair("stageCid", stageCid);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700898 pw.println();
899
900 params.dump(pw);
901
902 pw.printPair("mClientProgress", mClientProgress);
903 pw.printPair("mProgress", mProgress);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700904 pw.printPair("mSealed", mSealed);
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700905 pw.printPair("mPermissionsAccepted", mPermissionsAccepted);
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -0700906 pw.printPair("mDestroyed", mDestroyed);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700907 pw.printPair("mBridges", mBridges.size());
Jeff Sharkey7328a1b2014-08-07 14:01:43 -0700908 pw.printPair("mFinalStatus", mFinalStatus);
909 pw.printPair("mFinalMessage", mFinalMessage);
Jeff Sharkeya1031142014-07-12 18:09:46 -0700910 pw.println();
911
912 pw.decreaseIndent();
913 }
Jeff Sharkey3a44f3f2014-04-28 17:36:31 -0700914}