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                 if (stageCid != null) {
    501                     // TODO: this should delegate to DCS so the system process
    502                     // avoids holding open FDs into containers.
    503                     copyFiles(mResolvedInheritedFiles, resolveStageDir());
    504                 } else {
    505                     linkFiles(mResolvedInheritedFiles, resolveStageDir());
    506                 }
    507             } catch (IOException e) {
    508                 throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
    509                         "Failed to inherit existing install", e);
    510             }
    511         }
    512 
    513         // TODO: surface more granular state from dexopt
    514         mInternalProgress = 0.5f;
    515         computeProgressLocked(true);
    516 
    517         // Unpack native libraries
    518         extractNativeLibraries(mResolvedStageDir, params.abiOverride);
    519 
    520         // Container is ready to go, let's seal it up!
    521         if (stageCid != null) {
    522             finalizeAndFixContainer(stageCid);
    523         }
    524 
    525         // We've reached point of no return; call into PMS to install the stage.
    526         // Regardless of success or failure we always destroy session.
    527         final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
    528             @Override
    529             public void onUserActionRequired(Intent intent) {
    530                 throw new IllegalStateException();
    531             }
    532 
    533             @Override
    534             public void onPackageInstalled(String basePackageName, int returnCode, String msg,
    535                     Bundle extras) {
    536                 destroyInternal();
    537                 dispatchSessionFinished(returnCode, msg, extras);
    538             }
    539         };
    540 
    541         final UserHandle user;
    542         if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
    543             user = UserHandle.ALL;
    544         } else {
    545             user = new UserHandle(userId);
    546         }
    547 
    548         mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
    549                 installerPackageName, installerUid, user);
    550     }
    551 
    552     /**
    553      * Validate install by confirming that all application packages are have
    554      * consistent package name, version code, and signing certificates.
    555      * <p>
    556      * Clears and populates {@link #mResolvedBaseFile},
    557      * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
    558      * <p>
    559      * Renames package files in stage to match split names defined inside.
    560      * <p>
    561      * Note that upgrade compatibility is still performed by
    562      * {@link PackageManagerService}.
    563      */
    564     private void validateInstallLocked() throws PackageManagerException {
    565         mPackageName = null;
    566         mVersionCode = -1;
    567         mSignatures = null;
    568 
    569         mResolvedBaseFile = null;
    570         mResolvedStagedFiles.clear();
    571         mResolvedInheritedFiles.clear();
    572 
    573         final File[] files = mResolvedStageDir.listFiles();
    574         if (ArrayUtils.isEmpty(files)) {
    575             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
    576         }
    577 
    578         // Verify that all staged packages are internally consistent
    579         final ArraySet<String> stagedSplits = new ArraySet<>();
    580         for (File file : files) {
    581 
    582             // Installers can't stage directories, so it's fine to ignore
    583             // entries like "lost+found".
    584             if (file.isDirectory()) continue;
    585 
    586             final ApkLite apk;
    587             try {
    588                 apk = PackageParser.parseApkLite(file, PackageParser.PARSE_COLLECT_CERTIFICATES);
    589             } catch (PackageParserException e) {
    590                 throw PackageManagerException.from(e);
    591             }
    592 
    593             if (!stagedSplits.add(apk.splitName)) {
    594                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
    595                         "Split " + apk.splitName + " was defined multiple times");
    596             }
    597 
    598             // Use first package to define unknown values
    599             if (mPackageName == null) {
    600                 mPackageName = apk.packageName;
    601                 mVersionCode = apk.versionCode;
    602             }
    603             if (mSignatures == null) {
    604                 mSignatures = apk.signatures;
    605             }
    606 
    607             assertApkConsistent(String.valueOf(file), apk);
    608 
    609             // Take this opportunity to enforce uniform naming
    610             final String targetName;
    611             if (apk.splitName == null) {
    612                 targetName = "base.apk";
    613             } else {
    614                 targetName = "split_" + apk.splitName + ".apk";
    615             }
    616             if (!FileUtils.isValidExtFilename(targetName)) {
    617                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
    618                         "Invalid filename: " + targetName);
    619             }
    620 
    621             final File targetFile = new File(mResolvedStageDir, targetName);
    622             if (!file.equals(targetFile)) {
    623                 file.renameTo(targetFile);
    624             }
    625 
    626             // Base is coming from session
    627             if (apk.splitName == null) {
    628                 mResolvedBaseFile = targetFile;
    629             }
    630 
    631             mResolvedStagedFiles.add(targetFile);
    632         }
    633 
    634         if (params.mode == SessionParams.MODE_FULL_INSTALL) {
    635             // Full installs must include a base package
    636             if (!stagedSplits.contains(null)) {
    637                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
    638                         "Full install must include a base package");
    639             }
    640 
    641         } else {
    642             // Partial installs must be consistent with existing install
    643             final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
    644             if (app == null) {
    645                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
    646                         "Missing existing base package for " + mPackageName);
    647             }
    648 
    649             final PackageLite existing;
    650             final ApkLite existingBase;
    651             try {
    652                 existing = PackageParser.parsePackageLite(new File(app.getCodePath()), 0);
    653                 existingBase = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
    654                         PackageParser.PARSE_COLLECT_CERTIFICATES);
    655             } catch (PackageParserException e) {
    656                 throw PackageManagerException.from(e);
    657             }
    658 
    659             assertApkConsistent("Existing base", existingBase);
    660 
    661             // Inherit base if not overridden
    662             if (mResolvedBaseFile == null) {
    663                 mResolvedBaseFile = new File(app.getBaseCodePath());
    664                 mResolvedInheritedFiles.add(mResolvedBaseFile);
    665             }
    666 
    667             // Inherit splits if not overridden
    668             if (!ArrayUtils.isEmpty(existing.splitNames)) {
    669                 for (int i = 0; i < existing.splitNames.length; i++) {
    670                     final String splitName = existing.splitNames[i];
    671                     final File splitFile = new File(existing.splitCodePaths[i]);
    672 
    673                     if (!stagedSplits.contains(splitName)) {
    674                         mResolvedInheritedFiles.add(splitFile);
    675                     }
    676                 }
    677             }
    678         }
    679     }
    680 
    681     private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException {
    682         if (!mPackageName.equals(apk.packageName)) {
    683             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
    684                     + apk.packageName + " inconsistent with " + mPackageName);
    685         }
    686         if (mVersionCode != apk.versionCode) {
    687             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
    688                     + " version code " + apk.versionCode + " inconsistent with "
    689                     + mVersionCode);
    690         }
    691         if (!Signature.areExactMatch(mSignatures, apk.signatures)) {
    692             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
    693                     tag + " signatures are inconsistent");
    694         }
    695     }
    696 
    697     /**
    698      * Calculate the final install footprint size, combining both staged and
    699      * existing APKs together and including unpacked native code from both.
    700      */
    701     private long calculateInstalledSize() throws PackageManagerException {
    702         Preconditions.checkNotNull(mResolvedBaseFile);
    703 
    704         final ApkLite baseApk;
    705         try {
    706             baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0);
    707         } catch (PackageParserException e) {
    708             throw PackageManagerException.from(e);
    709         }
    710 
    711         final List<String> splitPaths = new ArrayList<>();
    712         for (File file : mResolvedStagedFiles) {
    713             if (mResolvedBaseFile.equals(file)) continue;
    714             splitPaths.add(file.getAbsolutePath());
    715         }
    716         for (File file : mResolvedInheritedFiles) {
    717             if (mResolvedBaseFile.equals(file)) continue;
    718             splitPaths.add(file.getAbsolutePath());
    719         }
    720 
    721         // This is kind of hacky; we're creating a half-parsed package that is
    722         // straddled between the inherited and staged APKs.
    723         final PackageLite pkg = new PackageLite(null, baseApk, null,
    724                 splitPaths.toArray(new String[splitPaths.size()]));
    725         final boolean isForwardLocked =
    726                 (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
    727 
    728         try {
    729             return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride);
    730         } catch (IOException e) {
    731             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
    732                     "Failed to calculate install size", e);
    733         }
    734     }
    735 
    736     private static void linkFiles(List<File> fromFiles, File toDir) throws IOException {
    737         for (File fromFile : fromFiles) {
    738             final File toFile = new File(toDir, fromFile.getName());
    739             try {
    740                 if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile);
    741                 Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath());
    742             } catch (ErrnoException e) {
    743                 throw new IOException("Failed to link " + fromFile + " to " + toFile, e);
    744             }
    745         }
    746         Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
    747     }
    748 
    749     private static void copyFiles(List<File> fromFiles, File toDir) throws IOException {
    750         // Remove any partial files from previous attempt
    751         for (File file : toDir.listFiles()) {
    752             if (file.getName().endsWith(".tmp")) {
    753                 file.delete();
    754             }
    755         }
    756 
    757         for (File fromFile : fromFiles) {
    758             final File tmpFile = File.createTempFile("inherit", ".tmp", toDir);
    759             if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile);
    760             if (!FileUtils.copyFile(fromFile, tmpFile)) {
    761                 throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
    762             }
    763 
    764             final File toFile = new File(toDir, fromFile.getName());
    765             if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile);
    766             if (!tmpFile.renameTo(toFile)) {
    767                 throw new IOException("Failed to rename " + tmpFile + " to " + toFile);
    768             }
    769         }
    770         Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
    771     }
    772 
    773     private static void extractNativeLibraries(File packageDir, String abiOverride)
    774             throws PackageManagerException {
    775         // Always start from a clean slate
    776         final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
    777         NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
    778 
    779         NativeLibraryHelper.Handle handle = null;
    780         try {
    781             handle = NativeLibraryHelper.Handle.create(packageDir);
    782             final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
    783                     abiOverride);
    784             if (res != PackageManager.INSTALL_SUCCEEDED) {
    785                 throw new PackageManagerException(res,
    786                         "Failed to extract native libraries, res=" + res);
    787             }
    788         } catch (IOException e) {
    789             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
    790                     "Failed to extract native libraries", e);
    791         } finally {
    792             IoUtils.closeQuietly(handle);
    793         }
    794     }
    795 
    796     private static void resizeContainer(String cid, long targetSize)
    797             throws PackageManagerException {
    798         String path = PackageHelper.getSdDir(cid);
    799         if (path == null) {
    800             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
    801                     "Failed to find mounted " + cid);
    802         }
    803 
    804         final long currentSize = new File(path).getTotalSpace();
    805         if (currentSize > targetSize) {
    806             Slog.w(TAG, "Current size " + currentSize + " is larger than target size "
    807                     + targetSize + "; skipping resize");
    808             return;
    809         }
    810 
    811         if (!PackageHelper.unMountSdDir(cid)) {
    812             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
    813                     "Failed to unmount " + cid + " before resize");
    814         }
    815 
    816         if (!PackageHelper.resizeSdDir(targetSize, cid,
    817                 PackageManagerService.getEncryptKey())) {
    818             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
    819                     "Failed to resize " + cid + " to " + targetSize + " bytes");
    820         }
    821 
    822         path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
    823                 Process.SYSTEM_UID, false);
    824         if (path == null) {
    825             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
    826                     "Failed to mount " + cid + " after resize");
    827         }
    828     }
    829 
    830     private void finalizeAndFixContainer(String cid) throws PackageManagerException {
    831         if (!PackageHelper.finalizeSdDir(cid)) {
    832             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
    833                     "Failed to finalize container " + cid);
    834         }
    835 
    836         final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
    837                 UserHandle.USER_OWNER);
    838         final int gid = UserHandle.getSharedAppGid(uid);
    839         if (!PackageHelper.fixSdPermissions(cid, gid, null)) {
    840             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
    841                     "Failed to fix permissions on container " + cid);
    842         }
    843     }
    844 
    845     void setPermissionsResult(boolean accepted) {
    846         if (!mSealed) {
    847             throw new SecurityException("Must be sealed to accept permissions");
    848         }
    849 
    850         if (accepted) {
    851             // Mark and kick off another install pass
    852             mPermissionsAccepted = true;
    853             mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
    854         } else {
    855             destroyInternal();
    856             dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
    857         }
    858     }
    859 
    860     public void open() throws IOException {
    861         if (mActiveCount.getAndIncrement() == 0) {
    862             mCallback.onSessionActiveChanged(this, true);
    863         }
    864 
    865         synchronized (mLock) {
    866             if (!mPrepared) {
    867                 if (stageDir != null) {
    868                     prepareInternalStageDir(stageDir);
    869                 } else if (stageCid != null) {
    870                     prepareExternalStageCid(stageCid, params.sizeBytes);
    871 
    872                     // TODO: deliver more granular progress for ASEC allocation
    873                     mInternalProgress = 0.25f;
    874                     computeProgressLocked(true);
    875                 } else {
    876                     throw new IllegalArgumentException(
    877                             "Exactly one of stageDir or stageCid stage must be set");
    878                 }
    879 
    880                 mPrepared = true;
    881                 mCallback.onSessionPrepared(this);
    882             }
    883         }
    884     }
    885 
    886     @Override
    887     public void close() {
    888         if (mActiveCount.decrementAndGet() == 0) {
    889             mCallback.onSessionActiveChanged(this, false);
    890         }
    891     }
    892 
    893     @Override
    894     public void abandon() {
    895         destroyInternal();
    896         dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
    897     }
    898 
    899     private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
    900         mFinalStatus = returnCode;
    901         mFinalMessage = msg;
    902 
    903         if (mRemoteObserver != null) {
    904             try {
    905                 mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras);
    906             } catch (RemoteException ignored) {
    907             }
    908         }
    909 
    910         final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
    911         mCallback.onSessionFinished(this, success);
    912     }
    913 
    914     private void destroyInternal() {
    915         synchronized (mLock) {
    916             mSealed = true;
    917             mDestroyed = true;
    918 
    919             // Force shut down all bridges
    920             for (FileBridge bridge : mBridges) {
    921                 bridge.forceClose();
    922             }
    923         }
    924         if (stageDir != null) {
    925             FileUtils.deleteContents(stageDir);
    926             stageDir.delete();
    927         }
    928         if (stageCid != null) {
    929             PackageHelper.destroySdDir(stageCid);
    930         }
    931     }
    932 
    933     void dump(IndentingPrintWriter pw) {
    934         synchronized (mLock) {
    935             dumpLocked(pw);
    936         }
    937     }
    938 
    939     private void dumpLocked(IndentingPrintWriter pw) {
    940         pw.println("Session " + sessionId + ":");
    941         pw.increaseIndent();
    942 
    943         pw.printPair("userId", userId);
    944         pw.printPair("installerPackageName", installerPackageName);
    945         pw.printPair("installerUid", installerUid);
    946         pw.printPair("createdMillis", createdMillis);
    947         pw.printPair("stageDir", stageDir);
    948         pw.printPair("stageCid", stageCid);
    949         pw.println();
    950 
    951         params.dump(pw);
    952 
    953         pw.printPair("mClientProgress", mClientProgress);
    954         pw.printPair("mProgress", mProgress);
    955         pw.printPair("mSealed", mSealed);
    956         pw.printPair("mPermissionsAccepted", mPermissionsAccepted);
    957         pw.printPair("mDestroyed", mDestroyed);
    958         pw.printPair("mBridges", mBridges.size());
    959         pw.printPair("mFinalStatus", mFinalStatus);
    960         pw.printPair("mFinalMessage", mFinalMessage);
    961         pw.println();
    962 
    963         pw.decreaseIndent();
    964     }
    965 }
    966