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