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