PackageInstallerSession.java revision e980804df16c968c14a56b8853886bf5f049f46e
1/*
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
19import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
20import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
21import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
22import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
23import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
24import static android.system.OsConstants.O_CREAT;
25import static android.system.OsConstants.O_RDONLY;
26import static android.system.OsConstants.O_WRONLY;
27import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid;
28import static com.android.server.pm.PackageInstallerService.prepareInternalStageDir;
29
30import android.content.Context;
31import android.content.Intent;
32import android.content.IntentSender;
33import android.content.pm.ApplicationInfo;
34import android.content.pm.IPackageInstallObserver2;
35import android.content.pm.IPackageInstallerSession;
36import android.content.pm.PackageInstaller;
37import android.content.pm.PackageInstaller.SessionInfo;
38import android.content.pm.PackageInstaller.SessionParams;
39import android.content.pm.PackageManager;
40import android.content.pm.PackageParser;
41import android.content.pm.PackageParser.ApkLite;
42import android.content.pm.PackageParser.PackageLite;
43import android.content.pm.PackageParser.PackageParserException;
44import android.content.pm.Signature;
45import android.os.Bundle;
46import android.os.FileBridge;
47import android.os.FileUtils;
48import android.os.Handler;
49import android.os.Looper;
50import android.os.Message;
51import android.os.ParcelFileDescriptor;
52import android.os.Process;
53import android.os.RemoteException;
54import android.os.UserHandle;
55import android.system.ErrnoException;
56import android.system.Os;
57import android.system.OsConstants;
58import android.system.StructStat;
59import android.util.ArraySet;
60import android.util.ExceptionUtils;
61import android.util.MathUtils;
62import android.util.Slog;
63
64import com.android.internal.annotations.GuardedBy;
65import com.android.internal.content.NativeLibraryHelper;
66import com.android.internal.content.PackageHelper;
67import com.android.internal.util.ArrayUtils;
68import com.android.internal.util.IndentingPrintWriter;
69import com.android.internal.util.Preconditions;
70import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
71
72import libcore.io.IoUtils;
73import libcore.io.Libcore;
74
75import java.io.File;
76import java.io.FileDescriptor;
77import java.io.IOException;
78import java.util.ArrayList;
79import java.util.List;
80import java.util.concurrent.atomic.AtomicInteger;
81
82public class PackageInstallerSession extends IPackageInstallerSession.Stub {
83    private static final String TAG = "PackageInstaller";
84    private static final boolean LOGD = true;
85
86    private static final int MSG_COMMIT = 0;
87
88    // TODO: enforce INSTALL_ALLOW_TEST
89    // TODO: enforce INSTALL_ALLOW_DOWNGRADE
90
91    private final PackageInstallerService.InternalCallback mCallback;
92    private final Context mContext;
93    private final PackageManagerService mPm;
94    private final Handler mHandler;
95
96    final int sessionId;
97    final int userId;
98    final String installerPackageName;
99    final int installerUid;
100    final SessionParams params;
101    final long createdMillis;
102
103    /** Staging location where client data is written. */
104    final File stageDir;
105    final String stageCid;
106
107    private final AtomicInteger mActiveCount = new AtomicInteger();
108
109    private final Object mLock = new Object();
110
111    @GuardedBy("mLock")
112    private float mClientProgress = 0;
113    @GuardedBy("mLock")
114    private float mInternalProgress = 0;
115
116    @GuardedBy("mLock")
117    private float mProgress = 0;
118    @GuardedBy("mLock")
119    private float mReportedProgress = -1;
120
121    @GuardedBy("mLock")
122    private boolean mPrepared = false;
123    @GuardedBy("mLock")
124    private boolean mSealed = false;
125    @GuardedBy("mLock")
126    private boolean mPermissionsAccepted = false;
127    @GuardedBy("mLock")
128    private boolean mDestroyed = false;
129
130    private int mFinalStatus;
131    private String mFinalMessage;
132
133    @GuardedBy("mLock")
134    private ArrayList<FileBridge> mBridges = new ArrayList<>();
135
136    @GuardedBy("mLock")
137    private IPackageInstallObserver2 mRemoteObserver;
138
139    /** Fields derived from commit parsing */
140    private String mPackageName;
141    private int mVersionCode;
142    private Signature[] mSignatures;
143
144    /**
145     * Path to the validated base APK for this session, which may point at an
146     * APK inside the session (when the session defines the base), or it may
147     * point at the existing base APK (when adding splits to an existing app).
148     * <p>
149     * This is used when confirming permissions, since we can't fully stage the
150     * session inside an ASEC before confirming with user.
151     */
152    @GuardedBy("mLock")
153    private File mResolvedBaseFile;
154
155    @GuardedBy("mLock")
156    private File mResolvedStageDir;
157
158    @GuardedBy("mLock")
159    private final List<File> mResolvedStagedFiles = new ArrayList<>();
160    @GuardedBy("mLock")
161    private final List<File> mResolvedInheritedFiles = new ArrayList<>();
162
163    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
164        @Override
165        public boolean handleMessage(Message msg) {
166            synchronized (mLock) {
167                if (msg.obj != null) {
168                    mRemoteObserver = (IPackageInstallObserver2) msg.obj;
169                }
170
171                try {
172                    commitLocked();
173                } catch (PackageManagerException e) {
174                    final String completeMsg = ExceptionUtils.getCompleteMessage(e);
175                    Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
176                    destroyInternal();
177                    dispatchSessionFinished(e.error, completeMsg, null);
178                }
179
180                return true;
181            }
182        }
183    };
184
185    public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
186            Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
187            String installerPackageName, int installerUid, SessionParams params, long createdMillis,
188            File stageDir, String stageCid, boolean prepared, boolean sealed) {
189        mCallback = callback;
190        mContext = context;
191        mPm = pm;
192        mHandler = new Handler(looper, mHandlerCallback);
193
194        this.sessionId = sessionId;
195        this.userId = userId;
196        this.installerPackageName = installerPackageName;
197        this.installerUid = installerUid;
198        this.params = params;
199        this.createdMillis = createdMillis;
200        this.stageDir = stageDir;
201        this.stageCid = stageCid;
202
203        if ((stageDir == null) == (stageCid == null)) {
204            throw new IllegalArgumentException(
205                    "Exactly one of stageDir or stageCid stage must be set");
206        }
207
208        mPrepared = prepared;
209        mSealed = sealed;
210
211        if ((mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
212                == PackageManager.PERMISSION_GRANTED) || (installerUid == Process.ROOT_UID)) {
213            mPermissionsAccepted = true;
214        } else {
215            mPermissionsAccepted = false;
216        }
217    }
218
219    public SessionInfo generateInfo() {
220        final SessionInfo info = new SessionInfo();
221        synchronized (mLock) {
222            info.sessionId = sessionId;
223            info.installerPackageName = installerPackageName;
224            info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
225                    mResolvedBaseFile.getAbsolutePath() : null;
226            info.progress = mProgress;
227            info.sealed = mSealed;
228            info.active = mActiveCount.get() > 0;
229
230            info.mode = params.mode;
231            info.sizeBytes = params.sizeBytes;
232            info.appPackageName = params.appPackageName;
233            info.appIcon = params.appIcon;
234            info.appLabel = params.appLabel;
235        }
236        return info;
237    }
238
239    public boolean isPrepared() {
240        synchronized (mLock) {
241            return mPrepared;
242        }
243    }
244
245    public boolean isSealed() {
246        synchronized (mLock) {
247            return mSealed;
248        }
249    }
250
251    private void assertPreparedAndNotSealed(String cookie) {
252        synchronized (mLock) {
253            if (!mPrepared) {
254                throw new IllegalStateException(cookie + " before prepared");
255            }
256            if (mSealed) {
257                throw new SecurityException(cookie + " not allowed after commit");
258            }
259        }
260    }
261
262    /**
263     * Resolve the actual location where staged data should be written. This
264     * might point at an ASEC mount point, which is why we delay path resolution
265     * until someone actively works with the session.
266     */
267    private File resolveStageDir() throws IOException {
268        synchronized (mLock) {
269            if (mResolvedStageDir == null) {
270                if (stageDir != null) {
271                    mResolvedStageDir = stageDir;
272                } else {
273                    final String path = PackageHelper.getSdDir(stageCid);
274                    if (path != null) {
275                        mResolvedStageDir = new File(path);
276                    } else {
277                        throw new IOException("Failed to resolve path to container " + stageCid);
278                    }
279                }
280            }
281            return mResolvedStageDir;
282        }
283    }
284
285    @Override
286    public void setClientProgress(float progress) {
287        synchronized (mLock) {
288            // Always publish first staging movement
289            final boolean forcePublish = (mClientProgress == 0);
290            mClientProgress = progress;
291            computeProgressLocked(forcePublish);
292        }
293    }
294
295    @Override
296    public void addClientProgress(float progress) {
297        synchronized (mLock) {
298            setClientProgress(mClientProgress + progress);
299        }
300    }
301
302    private void computeProgressLocked(boolean forcePublish) {
303        mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
304                + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
305
306        // Only publish when meaningful change
307        if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) {
308            mReportedProgress = mProgress;
309            mCallback.onSessionProgressChanged(this, mProgress);
310        }
311    }
312
313    @Override
314    public String[] getNames() {
315        assertPreparedAndNotSealed("getNames");
316        try {
317            return resolveStageDir().list();
318        } catch (IOException e) {
319            throw ExceptionUtils.wrap(e);
320        }
321    }
322
323    @Override
324    public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
325        try {
326            return openWriteInternal(name, offsetBytes, lengthBytes);
327        } catch (IOException e) {
328            throw ExceptionUtils.wrap(e);
329        }
330    }
331
332    private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
333            throws IOException {
334        // Quick sanity check of state, and allocate a pipe for ourselves. We
335        // then do heavy disk allocation outside the lock, but this open pipe
336        // will block any attempted install transitions.
337        final FileBridge bridge;
338        synchronized (mLock) {
339            assertPreparedAndNotSealed("openWrite");
340
341            bridge = new FileBridge();
342            mBridges.add(bridge);
343        }
344
345        try {
346            // Use installer provided name for now; we always rename later
347            if (!FileUtils.isValidExtFilename(name)) {
348                throw new IllegalArgumentException("Invalid name: " + name);
349            }
350            final File target = new File(resolveStageDir(), name);
351
352            // TODO: this should delegate to DCS so the system process avoids
353            // holding open FDs into containers.
354            final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
355                    O_CREAT | O_WRONLY, 0644);
356            Os.chmod(target.getAbsolutePath(), 0644);
357
358            // If caller specified a total length, allocate it for them. Free up
359            // cache space to grow, if needed.
360            if (lengthBytes > 0) {
361                final StructStat stat = Libcore.os.fstat(targetFd);
362                final long deltaBytes = lengthBytes - stat.st_size;
363                // Only need to free up space when writing to internal stage
364                if (stageDir != null && deltaBytes > 0) {
365                    mPm.freeStorage(deltaBytes);
366                }
367                Libcore.os.posix_fallocate(targetFd, 0, lengthBytes);
368            }
369
370            if (offsetBytes > 0) {
371                Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
372            }
373
374            bridge.setTargetFile(targetFd);
375            bridge.start();
376            return new ParcelFileDescriptor(bridge.getClientSocket());
377
378        } catch (ErrnoException e) {
379            throw e.rethrowAsIOException();
380        }
381    }
382
383    @Override
384    public ParcelFileDescriptor openRead(String name) {
385        try {
386            return openReadInternal(name);
387        } catch (IOException e) {
388            throw ExceptionUtils.wrap(e);
389        }
390    }
391
392    private ParcelFileDescriptor openReadInternal(String name) throws IOException {
393        assertPreparedAndNotSealed("openRead");
394
395        try {
396            if (!FileUtils.isValidExtFilename(name)) {
397                throw new IllegalArgumentException("Invalid name: " + name);
398            }
399            final File target = new File(resolveStageDir(), name);
400
401            final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0);
402            return new ParcelFileDescriptor(targetFd);
403
404        } catch (ErrnoException e) {
405            throw e.rethrowAsIOException();
406        }
407    }
408
409    @Override
410    public void commit(IntentSender statusReceiver) {
411        Preconditions.checkNotNull(statusReceiver);
412
413        synchronized (mLock) {
414            if (!mSealed) {
415                // Verify that all writers are hands-off
416                for (FileBridge bridge : mBridges) {
417                    if (!bridge.isClosed()) {
418                        throw new SecurityException("Files still open");
419                    }
420                }
421
422                // Persist the fact that we've sealed ourselves to prevent
423                // mutations of any hard links we create.
424                mSealed = true;
425                mCallback.onSessionSealed(this);
426            }
427        }
428
429        // Client staging is fully done at this point
430        mClientProgress = 1f;
431        computeProgressLocked(true);
432
433        // This ongoing commit should keep session active, even though client
434        // will probably close their end.
435        mActiveCount.incrementAndGet();
436
437        final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
438                statusReceiver, sessionId);
439        mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
440    }
441
442    private void commitLocked() throws PackageManagerException {
443        if (mDestroyed) {
444            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
445        }
446        if (!mSealed) {
447            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
448        }
449
450        try {
451            resolveStageDir();
452        } catch (IOException e) {
453            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
454                    "Failed to resolve stage location", e);
455        }
456
457        // Verify that stage looks sane with respect to existing application.
458        // This currently only ensures packageName, versionCode, and certificate
459        // consistency.
460        validateInstallLocked();
461
462        Preconditions.checkNotNull(mPackageName);
463        Preconditions.checkNotNull(mSignatures);
464        Preconditions.checkNotNull(mResolvedBaseFile);
465
466        if (!mPermissionsAccepted) {
467            // User needs to accept permissions; give installer an intent they
468            // can use to involve user.
469            final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
470            intent.setPackage("com.android.packageinstaller");
471            intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
472            try {
473                mRemoteObserver.onUserActionRequired(intent);
474            } catch (RemoteException ignored) {
475            }
476
477            // Commit was keeping session marked as active until now; release
478            // that extra refcount so session appears idle.
479            close();
480            return;
481        }
482
483        if (stageCid != null) {
484            // Figure out the final installed size and resize the container once
485            // and for all. Internally the parser handles straddling between two
486            // locations when inheriting.
487            final long finalSize = calculateInstalledSize();
488            resizeContainer(stageCid, finalSize);
489        }
490
491        // Inherit any packages and native libraries from existing install that
492        // haven't been overridden.
493        if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
494            try {
495                if (stageCid != null) {
496                    // TODO: this should delegate to DCS so the system process
497                    // avoids holding open FDs into containers.
498                    copyFiles(mResolvedInheritedFiles, resolveStageDir());
499                } else {
500                    linkFiles(mResolvedInheritedFiles, resolveStageDir());
501                }
502            } catch (IOException e) {
503                throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
504                        "Failed to inherit existing install", e);
505            }
506        }
507
508        // TODO: surface more granular state from dexopt
509        mInternalProgress = 0.5f;
510        computeProgressLocked(true);
511
512        // Unpack native libraries
513        extractNativeLibraries(mResolvedStageDir, params.abiOverride);
514
515        // Container is ready to go, let's seal it up!
516        if (stageCid != null) {
517            finalizeAndFixContainer(stageCid);
518        }
519
520        // We've reached point of no return; call into PMS to install the stage.
521        // Regardless of success or failure we always destroy session.
522        final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
523            @Override
524            public void onUserActionRequired(Intent intent) {
525                throw new IllegalStateException();
526            }
527
528            @Override
529            public void onPackageInstalled(String basePackageName, int returnCode, String msg,
530                    Bundle extras) {
531                destroyInternal();
532                dispatchSessionFinished(returnCode, msg, extras);
533            }
534        };
535
536        final UserHandle user;
537        if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
538            user = UserHandle.ALL;
539        } else {
540            user = new UserHandle(userId);
541        }
542
543        mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
544                installerPackageName, installerUid, user);
545    }
546
547    /**
548     * Validate install by confirming that all application packages are have
549     * consistent package name, version code, and signing certificates.
550     * <p>
551     * Clears and populates {@link #mResolvedBaseFile},
552     * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
553     * <p>
554     * Renames package files in stage to match split names defined inside.
555     * <p>
556     * Note that upgrade compatibility is still performed by
557     * {@link PackageManagerService}.
558     */
559    private void validateInstallLocked() throws PackageManagerException {
560        mPackageName = null;
561        mVersionCode = -1;
562        mSignatures = null;
563
564        mResolvedBaseFile = null;
565        mResolvedStagedFiles.clear();
566        mResolvedInheritedFiles.clear();
567
568        final File[] files = mResolvedStageDir.listFiles();
569        if (ArrayUtils.isEmpty(files)) {
570            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
571        }
572
573        // Verify that all staged packages are internally consistent
574        final ArraySet<String> stagedSplits = new ArraySet<>();
575        for (File file : files) {
576
577            // Installers can't stage directories, so it's fine to ignore
578            // entries like "lost+found".
579            if (file.isDirectory()) continue;
580
581            final ApkLite apk;
582            try {
583                apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
584            } catch (PackageParserException e) {
585                throw PackageManagerException.from(e);
586            }
587
588            if (!stagedSplits.add(apk.splitName)) {
589                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
590                        "Split " + apk.splitName + " was defined multiple times");
591            }
592
593            // Use first package to define unknown values
594            if (mPackageName == null) {
595                mPackageName = apk.packageName;
596                mVersionCode = apk.versionCode;
597            }
598            if (mSignatures == null) {
599                mSignatures = apk.signatures;
600            }
601
602            assertApkConsistent(String.valueOf(file), apk);
603
604            // Take this opportunity to enforce uniform naming
605            final String targetName;
606            if (apk.splitName == null) {
607                targetName = "base.apk";
608            } else {
609                targetName = "split_" + apk.splitName + ".apk";
610            }
611            if (!FileUtils.isValidExtFilename(targetName)) {
612                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
613                        "Invalid filename: " + targetName);
614            }
615
616            final File targetFile = new File(mResolvedStageDir, targetName);
617            if (!file.equals(targetFile)) {
618                file.renameTo(targetFile);
619            }
620
621            // Base is coming from session
622            if (apk.splitName == null) {
623                mResolvedBaseFile = targetFile;
624            }
625
626            mResolvedStagedFiles.add(targetFile);
627        }
628
629        if (params.mode == SessionParams.MODE_FULL_INSTALL) {
630            // Full installs must include a base package
631            if (!stagedSplits.contains(null)) {
632                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
633                        "Full install must include a base package");
634            }
635
636        } else {
637            // Partial installs must be consistent with existing install
638            final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
639            if (app == null) {
640                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
641                        "Missing existing base package for " + mPackageName);
642            }
643
644            final PackageLite existing;
645            final ApkLite existingBase;
646            try {
647                existing = PackageParser.parsePackageLite(new File(app.getCodePath()), 0);
648                existingBase = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
649                        PackageParser.PARSE_COLLECT_CERTIFICATES);
650            } catch (PackageParserException e) {
651                throw PackageManagerException.from(e);
652            }
653
654            assertApkConsistent("Existing base", existingBase);
655
656            // Inherit base if not overridden
657            if (mResolvedBaseFile == null) {
658                mResolvedBaseFile = new File(app.getBaseCodePath());
659                mResolvedInheritedFiles.add(mResolvedBaseFile);
660            }
661
662            // Inherit splits if not overridden
663            if (!ArrayUtils.isEmpty(existing.splitNames)) {
664                for (int i = 0; i < existing.splitNames.length; i++) {
665                    final String splitName = existing.splitNames[i];
666                    final File splitFile = new File(existing.splitCodePaths[i]);
667
668                    if (!stagedSplits.contains(splitName)) {
669                        mResolvedInheritedFiles.add(splitFile);
670                    }
671                }
672            }
673        }
674    }
675
676    private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException {
677        if (!mPackageName.equals(apk.packageName)) {
678            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
679                    + apk.packageName + " inconsistent with " + mPackageName);
680        }
681        if (mVersionCode != apk.versionCode) {
682            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
683                    + " version code " + apk.versionCode + " inconsistent with "
684                    + mVersionCode);
685        }
686        if (!Signature.areExactMatch(mSignatures, apk.signatures)) {
687            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
688                    tag + " signatures are inconsistent");
689        }
690    }
691
692    /**
693     * Calculate the final install footprint size, combining both staged and
694     * existing APKs together and including unpacked native code from both.
695     */
696    private long calculateInstalledSize() throws PackageManagerException {
697        Preconditions.checkNotNull(mResolvedBaseFile);
698
699        final ApkLite baseApk;
700        try {
701            baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0);
702        } catch (PackageParserException e) {
703            throw PackageManagerException.from(e);
704        }
705
706        final List<String> splitPaths = new ArrayList<>();
707        for (File file : mResolvedStagedFiles) {
708            if (mResolvedBaseFile.equals(file)) continue;
709            splitPaths.add(file.getAbsolutePath());
710        }
711        for (File file : mResolvedInheritedFiles) {
712            if (mResolvedBaseFile.equals(file)) continue;
713            splitPaths.add(file.getAbsolutePath());
714        }
715
716        // This is kind of hacky; we're creating a half-parsed package that is
717        // straddled between the inherited and staged APKs.
718        final PackageLite pkg = new PackageLite(null, baseApk, null,
719                splitPaths.toArray(new String[splitPaths.size()]));
720        final boolean isForwardLocked =
721                (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
722
723        try {
724            return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride);
725        } catch (IOException e) {
726            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
727                    "Failed to calculate install size", e);
728        }
729    }
730
731    private static void linkFiles(List<File> fromFiles, File toDir) throws IOException {
732        for (File fromFile : fromFiles) {
733            final File toFile = new File(toDir, fromFile.getName());
734            try {
735                if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile);
736                Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath());
737            } catch (ErrnoException e) {
738                throw new IOException("Failed to link " + fromFile + " to " + toFile, e);
739            }
740        }
741        Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
742    }
743
744    private static void copyFiles(List<File> fromFiles, File toDir) throws IOException {
745        // Remove any partial files from previous attempt
746        for (File file : toDir.listFiles()) {
747            if (file.getName().endsWith(".tmp")) {
748                file.delete();
749            }
750        }
751
752        for (File fromFile : fromFiles) {
753            final File tmpFile = File.createTempFile("inherit", ".tmp", toDir);
754            if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile);
755            if (!FileUtils.copyFile(fromFile, tmpFile)) {
756                throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
757            }
758
759            final File toFile = new File(toDir, fromFile.getName());
760            if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile);
761            if (!tmpFile.renameTo(toFile)) {
762                throw new IOException("Failed to rename " + tmpFile + " to " + toFile);
763            }
764        }
765        Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
766    }
767
768    private static void extractNativeLibraries(File packageDir, String abiOverride)
769            throws PackageManagerException {
770        // Always start from a clean slate
771        final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
772        NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
773
774        NativeLibraryHelper.Handle handle = null;
775        try {
776            handle = NativeLibraryHelper.Handle.create(packageDir);
777            final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
778                    abiOverride);
779            if (res != PackageManager.INSTALL_SUCCEEDED) {
780                throw new PackageManagerException(res,
781                        "Failed to extract native libraries, res=" + res);
782            }
783        } catch (IOException e) {
784            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
785                    "Failed to extract native libraries", e);
786        } finally {
787            IoUtils.closeQuietly(handle);
788        }
789    }
790
791    private static void resizeContainer(String cid, long targetSize)
792            throws PackageManagerException {
793        String path = PackageHelper.getSdDir(cid);
794        if (path == null) {
795            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
796                    "Failed to find mounted " + cid);
797        }
798
799        final long currentSize = new File(path).getTotalSpace();
800        if (currentSize > targetSize) {
801            Slog.w(TAG, "Current size " + currentSize + " is larger than target size "
802                    + targetSize + "; skipping resize");
803            return;
804        }
805
806        if (!PackageHelper.unMountSdDir(cid)) {
807            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
808                    "Failed to unmount " + cid + " before resize");
809        }
810
811        if (!PackageHelper.resizeSdDir(targetSize, cid,
812                PackageManagerService.getEncryptKey())) {
813            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
814                    "Failed to resize " + cid + " to " + targetSize + " bytes");
815        }
816
817        path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
818                Process.SYSTEM_UID, false);
819        if (path == null) {
820            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
821                    "Failed to mount " + cid + " after resize");
822        }
823    }
824
825    private void finalizeAndFixContainer(String cid) throws PackageManagerException {
826        if (!PackageHelper.finalizeSdDir(cid)) {
827            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
828                    "Failed to finalize container " + cid);
829        }
830
831        final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
832                UserHandle.USER_OWNER);
833        final int gid = UserHandle.getSharedAppGid(uid);
834        if (!PackageHelper.fixSdPermissions(cid, gid, null)) {
835            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
836                    "Failed to fix permissions on container " + cid);
837        }
838    }
839
840    void setPermissionsResult(boolean accepted) {
841        if (!mSealed) {
842            throw new SecurityException("Must be sealed to accept permissions");
843        }
844
845        if (accepted) {
846            // Mark and kick off another install pass
847            mPermissionsAccepted = true;
848            mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
849        } else {
850            destroyInternal();
851            dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
852        }
853    }
854
855    public void open() throws IOException {
856        if (mActiveCount.getAndIncrement() == 0) {
857            mCallback.onSessionActiveChanged(this, true);
858        }
859
860        synchronized (mLock) {
861            if (!mPrepared) {
862                if (stageDir != null) {
863                    prepareInternalStageDir(stageDir);
864                } else if (stageCid != null) {
865                    prepareExternalStageCid(stageCid, params.sizeBytes);
866
867                    // TODO: deliver more granular progress for ASEC allocation
868                    mInternalProgress = 0.25f;
869                    computeProgressLocked(true);
870                } else {
871                    throw new IllegalArgumentException(
872                            "Exactly one of stageDir or stageCid stage must be set");
873                }
874
875                mPrepared = true;
876                mCallback.onSessionPrepared(this);
877            }
878        }
879    }
880
881    @Override
882    public void close() {
883        if (mActiveCount.decrementAndGet() == 0) {
884            mCallback.onSessionActiveChanged(this, false);
885        }
886    }
887
888    @Override
889    public void abandon() {
890        destroyInternal();
891        dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
892    }
893
894    private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
895        mFinalStatus = returnCode;
896        mFinalMessage = msg;
897
898        if (mRemoteObserver != null) {
899            try {
900                mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras);
901            } catch (RemoteException ignored) {
902            }
903        }
904
905        final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
906        mCallback.onSessionFinished(this, success);
907    }
908
909    private void destroyInternal() {
910        synchronized (mLock) {
911            mSealed = true;
912            mDestroyed = true;
913
914            // Force shut down all bridges
915            for (FileBridge bridge : mBridges) {
916                bridge.forceClose();
917            }
918        }
919        if (stageDir != null) {
920            FileUtils.deleteContents(stageDir);
921            stageDir.delete();
922        }
923        if (stageCid != null) {
924            PackageHelper.destroySdDir(stageCid);
925        }
926    }
927
928    void dump(IndentingPrintWriter pw) {
929        synchronized (mLock) {
930            dumpLocked(pw);
931        }
932    }
933
934    private void dumpLocked(IndentingPrintWriter pw) {
935        pw.println("Session " + sessionId + ":");
936        pw.increaseIndent();
937
938        pw.printPair("userId", userId);
939        pw.printPair("installerPackageName", installerPackageName);
940        pw.printPair("installerUid", installerUid);
941        pw.printPair("createdMillis", createdMillis);
942        pw.printPair("stageDir", stageDir);
943        pw.printPair("stageCid", stageCid);
944        pw.println();
945
946        params.dump(pw);
947
948        pw.printPair("mClientProgress", mClientProgress);
949        pw.printPair("mProgress", mProgress);
950        pw.printPair("mSealed", mSealed);
951        pw.printPair("mPermissionsAccepted", mPermissionsAccepted);
952        pw.printPair("mDestroyed", mDestroyed);
953        pw.printPair("mBridges", mBridges.size());
954        pw.printPair("mFinalStatus", mFinalStatus);
955        pw.printPair("mFinalMessage", mFinalMessage);
956        pw.println();
957
958        pw.decreaseIndent();
959    }
960}
961