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