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