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