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