PackageInstallerSession.java revision 0e4ac4c8ca8cee0936d983fdda04b76c2f40f343
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.internal.util.XmlUtils.readBitmapAttribute;
29import static com.android.internal.util.XmlUtils.readBooleanAttribute;
30import static com.android.internal.util.XmlUtils.readIntAttribute;
31import static com.android.internal.util.XmlUtils.readLongAttribute;
32import static com.android.internal.util.XmlUtils.readStringAttribute;
33import static com.android.internal.util.XmlUtils.readUriAttribute;
34import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
35import static com.android.internal.util.XmlUtils.writeIntAttribute;
36import static com.android.internal.util.XmlUtils.writeLongAttribute;
37import static com.android.internal.util.XmlUtils.writeStringAttribute;
38import static com.android.internal.util.XmlUtils.writeUriAttribute;
39import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid;
40import static com.android.server.pm.PackageInstallerService.prepareStageDir;
41
42import android.Manifest;
43import android.annotation.NonNull;
44import android.annotation.Nullable;
45import android.app.admin.DevicePolicyManager;
46import android.content.Context;
47import android.content.Intent;
48import android.content.IntentSender;
49import android.content.pm.ApplicationInfo;
50import android.content.pm.IPackageInstallObserver2;
51import android.content.pm.IPackageInstallerSession;
52import android.content.pm.PackageInfo;
53import android.content.pm.PackageInstaller;
54import android.content.pm.PackageInstaller.SessionInfo;
55import android.content.pm.PackageInstaller.SessionParams;
56import android.content.pm.PackageManager;
57import android.content.pm.PackageParser;
58import android.content.pm.PackageParser.ApkLite;
59import android.content.pm.PackageParser.PackageLite;
60import android.content.pm.PackageParser.PackageParserException;
61import android.content.pm.Signature;
62import android.graphics.Bitmap;
63import android.graphics.BitmapFactory;
64import android.os.Binder;
65import android.os.Bundle;
66import android.os.FileBridge;
67import android.os.FileUtils;
68import android.os.Handler;
69import android.os.Looper;
70import android.os.Message;
71import android.os.ParcelFileDescriptor;
72import android.os.ParcelableException;
73import android.os.Process;
74import android.os.RemoteException;
75import android.os.RevocableFileDescriptor;
76import android.os.UserHandle;
77import android.os.storage.StorageManager;
78import android.system.ErrnoException;
79import android.system.Os;
80import android.system.OsConstants;
81import android.system.StructStat;
82import android.text.TextUtils;
83import android.util.ArraySet;
84import android.util.ExceptionUtils;
85import android.util.MathUtils;
86import android.util.Slog;
87
88import com.android.internal.annotations.GuardedBy;
89import com.android.internal.content.NativeLibraryHelper;
90import com.android.internal.content.PackageHelper;
91import com.android.internal.os.SomeArgs;
92import com.android.internal.util.ArrayUtils;
93import com.android.internal.util.IndentingPrintWriter;
94import com.android.internal.util.Preconditions;
95import com.android.server.pm.Installer.InstallerException;
96import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
97
98import libcore.io.IoUtils;
99import libcore.io.Libcore;
100
101import org.xmlpull.v1.XmlPullParser;
102import org.xmlpull.v1.XmlPullParserException;
103import org.xmlpull.v1.XmlSerializer;
104
105import java.io.File;
106import java.io.FileDescriptor;
107import java.io.FileFilter;
108import java.io.FileOutputStream;
109import java.io.IOException;
110import java.security.cert.Certificate;
111import java.util.ArrayList;
112import java.util.Arrays;
113import java.util.List;
114import java.util.concurrent.atomic.AtomicInteger;
115
116public class PackageInstallerSession extends IPackageInstallerSession.Stub {
117    private static final String TAG = "PackageInstaller";
118    private static final boolean LOGD = true;
119    private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed";
120
121    private static final int MSG_COMMIT = 0;
122    private static final int MSG_ON_PACKAGE_INSTALLED = 1;
123
124    /** XML constants used for persisting a session */
125    static final String TAG_SESSION = "session";
126    private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
127    private static final String ATTR_SESSION_ID = "sessionId";
128    private static final String ATTR_USER_ID = "userId";
129    private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
130    private static final String ATTR_INSTALLER_UID = "installerUid";
131    private static final String ATTR_CREATED_MILLIS = "createdMillis";
132    private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
133    private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
134    private static final String ATTR_PREPARED = "prepared";
135    private static final String ATTR_SEALED = "sealed";
136    private static final String ATTR_MODE = "mode";
137    private static final String ATTR_INSTALL_FLAGS = "installFlags";
138    private static final String ATTR_INSTALL_LOCATION = "installLocation";
139    private static final String ATTR_SIZE_BYTES = "sizeBytes";
140    private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
141    @Deprecated
142    private static final String ATTR_APP_ICON = "appIcon";
143    private static final String ATTR_APP_LABEL = "appLabel";
144    private static final String ATTR_ORIGINATING_URI = "originatingUri";
145    private static final String ATTR_ORIGINATING_UID = "originatingUid";
146    private static final String ATTR_REFERRER_URI = "referrerUri";
147    private static final String ATTR_ABI_OVERRIDE = "abiOverride";
148    private static final String ATTR_VOLUME_UUID = "volumeUuid";
149    private static final String ATTR_NAME = "name";
150    private static final String ATTR_INSTALL_REASON = "installRason";
151
152    // TODO: enforce INSTALL_ALLOW_TEST
153    // TODO: enforce INSTALL_ALLOW_DOWNGRADE
154
155    private final PackageInstallerService.InternalCallback mCallback;
156    private final Context mContext;
157    private final PackageManagerService mPm;
158    private final Handler mHandler;
159
160    final int sessionId;
161    final int userId;
162    final SessionParams params;
163    final long createdMillis;
164    final int defaultContainerGid;
165
166    /** Staging location where client data is written. */
167    final File stageDir;
168    final String stageCid;
169
170    private final AtomicInteger mActiveCount = new AtomicInteger();
171
172    private final Object mLock = new Object();
173
174    /** Uid of the creator of this session. */
175    private final int mOriginalInstallerUid;
176
177    /** Package of the owner of the installer session */
178    @GuardedBy("mLock")
179    private String mInstallerPackageName;
180
181    /** Uid of the owner of the installer session */
182    @GuardedBy("mLock")
183    private int mInstallerUid;
184
185    @GuardedBy("mLock")
186    private float mClientProgress = 0;
187    @GuardedBy("mLock")
188    private float mInternalProgress = 0;
189
190    @GuardedBy("mLock")
191    private float mProgress = 0;
192    @GuardedBy("mLock")
193    private float mReportedProgress = -1;
194
195    /** State of the session. */
196    @GuardedBy("mLock")
197    private boolean mPrepared = false;
198    @GuardedBy("mLock")
199    private boolean mSealed = false;
200    @GuardedBy("mLock")
201    private boolean mCommitted = false;
202    @GuardedBy("mLock")
203    private boolean mRelinquished = false;
204    @GuardedBy("mLock")
205    private boolean mDestroyed = false;
206
207    /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */
208    @GuardedBy("mLock")
209    private boolean mPermissionsManuallyAccepted = false;
210
211    @GuardedBy("mLock")
212    private int mFinalStatus;
213    @GuardedBy("mLock")
214    private String mFinalMessage;
215
216    @GuardedBy("mLock")
217    private final ArrayList<RevocableFileDescriptor> mFds = new ArrayList<>();
218    @GuardedBy("mLock")
219    private final ArrayList<FileBridge> mBridges = new ArrayList<>();
220
221    @GuardedBy("mLock")
222    private IPackageInstallObserver2 mRemoteObserver;
223
224    /** Fields derived from commit parsing */
225    @GuardedBy("mLock")
226    private String mPackageName;
227    @GuardedBy("mLock")
228    private int mVersionCode;
229    @GuardedBy("mLock")
230    private Signature[] mSignatures;
231    @GuardedBy("mLock")
232    private Certificate[][] mCertificates;
233
234    /**
235     * Path to the validated base APK for this session, which may point at an
236     * APK inside the session (when the session defines the base), or it may
237     * point at the existing base APK (when adding splits to an existing app).
238     * <p>
239     * This is used when confirming permissions, since we can't fully stage the
240     * session inside an ASEC before confirming with user.
241     */
242    @GuardedBy("mLock")
243    private File mResolvedBaseFile;
244
245    @GuardedBy("mLock")
246    private File mResolvedStageDir;
247
248    @GuardedBy("mLock")
249    private final List<File> mResolvedStagedFiles = new ArrayList<>();
250    @GuardedBy("mLock")
251    private final List<File> mResolvedInheritedFiles = new ArrayList<>();
252    @GuardedBy("mLock")
253    private final List<String> mResolvedInstructionSets = new ArrayList<>();
254    @GuardedBy("mLock")
255    private File mInheritedFilesBase;
256
257    private static final FileFilter sAddedFilter = new FileFilter() {
258        @Override
259        public boolean accept(File file) {
260            // Installers can't stage directories, so it's fine to ignore
261            // entries like "lost+found".
262            if (file.isDirectory()) return false;
263            if (file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
264            return true;
265        }
266    };
267    private static final FileFilter sRemovedFilter = new FileFilter() {
268        @Override
269        public boolean accept(File file) {
270            if (file.isDirectory()) return false;
271            if (!file.getName().endsWith(REMOVE_SPLIT_MARKER_EXTENSION)) return false;
272            return true;
273        }
274    };
275
276    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
277        @Override
278        public boolean handleMessage(Message msg) {
279            switch (msg.what) {
280                case MSG_COMMIT:
281                    synchronized (mLock) {
282                        try {
283                            commitLocked();
284                        } catch (PackageManagerException e) {
285                            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
286                            Slog.e(TAG,
287                                    "Commit of session " + sessionId + " failed: " + completeMsg);
288                            destroyInternal();
289                            dispatchSessionFinished(e.error, completeMsg, null);
290                        }
291                    }
292
293                    break;
294                case MSG_ON_PACKAGE_INSTALLED:
295                    final SomeArgs args = (SomeArgs) msg.obj;
296                    final String packageName = (String) args.arg1;
297                    final String message = (String) args.arg2;
298                    final Bundle extras = (Bundle) args.arg3;
299                    final IPackageInstallObserver2 observer = (IPackageInstallObserver2) args.arg4;
300                    final int returnCode = args.argi1;
301                    args.recycle();
302
303                    try {
304                        observer.onPackageInstalled(packageName, returnCode, message, extras);
305                    } catch (RemoteException ignored) {
306                    }
307
308                    break;
309            }
310
311            return true;
312        }
313    };
314
315    /**
316     * @return {@code true} iff the installing is app an device owner?
317     */
318    private boolean isInstallerDeviceOwnerLocked() {
319        DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
320                Context.DEVICE_POLICY_SERVICE);
321
322        return (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
323                mInstallerPackageName);
324    }
325
326    /**
327     * Checks if the permissions still need to be confirmed.
328     *
329     * <p>This is dependant on the identity of the installer, hence this cannot be cached if the
330     * installer might still {@link #transfer(String) change}.
331     *
332     * @return {@code true} iff we need to ask to confirm the permissions?
333     */
334    private boolean needToAskForPermissionsLocked() {
335        if (mPermissionsManuallyAccepted) {
336            return false;
337        }
338
339        final boolean isInstallPermissionGranted =
340                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
341                        mInstallerUid) == PackageManager.PERMISSION_GRANTED);
342        final boolean isSelfUpdatePermissionGranted =
343                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
344                        mInstallerUid) == PackageManager.PERMISSION_GRANTED);
345        final boolean isPermissionGranted = isInstallPermissionGranted
346                || (isSelfUpdatePermissionGranted
347                    && mPm.getPackageUid(mPackageName, 0, userId) == mInstallerUid);
348        final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
349        final boolean forcePermissionPrompt =
350                (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
351
352        // Device owners are allowed to silently install packages, so the permission check is
353        // waived if the installer is the device owner.
354        return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
355                || isInstallerDeviceOwnerLocked());
356    }
357
358    public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
359            Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
360            String installerPackageName, int installerUid, SessionParams params, long createdMillis,
361            File stageDir, String stageCid, boolean prepared, boolean sealed) {
362        mCallback = callback;
363        mContext = context;
364        mPm = pm;
365        mHandler = new Handler(looper, mHandlerCallback);
366
367        this.sessionId = sessionId;
368        this.userId = userId;
369        mOriginalInstallerUid = installerUid;
370        mInstallerPackageName = installerPackageName;
371        mInstallerUid = installerUid;
372        this.params = params;
373        this.createdMillis = createdMillis;
374        this.stageDir = stageDir;
375        this.stageCid = stageCid;
376
377        if ((stageDir == null) == (stageCid == null)) {
378            throw new IllegalArgumentException(
379                    "Exactly one of stageDir or stageCid stage must be set");
380        }
381
382        mPrepared = prepared;
383
384        if (sealed) {
385            synchronized (mLock) {
386                try {
387                    sealAndValidateLocked();
388                } catch (PackageManagerException | IOException e) {
389                    destroyInternal();
390                    throw new IllegalArgumentException(e);
391                }
392            }
393        }
394
395        final long identity = Binder.clearCallingIdentity();
396        try {
397            final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
398                    PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
399            defaultContainerGid = UserHandle.getSharedAppGid(uid);
400        } finally {
401            Binder.restoreCallingIdentity(identity);
402        }
403    }
404
405    public SessionInfo generateInfo() {
406        return generateInfo(true);
407    }
408
409    public SessionInfo generateInfo(boolean includeIcon) {
410        final SessionInfo info = new SessionInfo();
411        synchronized (mLock) {
412            info.sessionId = sessionId;
413            info.installerPackageName = mInstallerPackageName;
414            info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
415                    mResolvedBaseFile.getAbsolutePath() : null;
416            info.progress = mProgress;
417            info.sealed = mSealed;
418            info.active = mActiveCount.get() > 0;
419
420            info.mode = params.mode;
421            info.installReason = params.installReason;
422            info.sizeBytes = params.sizeBytes;
423            info.appPackageName = params.appPackageName;
424            if (includeIcon) {
425                info.appIcon = params.appIcon;
426            }
427            info.appLabel = params.appLabel;
428
429            info.installLocation = params.installLocation;
430            info.originatingUri = params.originatingUri;
431            info.originatingUid = params.originatingUid;
432            info.referrerUri = params.referrerUri;
433            info.grantedRuntimePermissions = params.grantedRuntimePermissions;
434            info.installFlags = params.installFlags;
435        }
436        return info;
437    }
438
439    public boolean isPrepared() {
440        synchronized (mLock) {
441            return mPrepared;
442        }
443    }
444
445    public boolean isSealed() {
446        synchronized (mLock) {
447            return mSealed;
448        }
449    }
450
451    private void assertPreparedAndNotSealedLocked(String cookie) {
452        assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
453        if (mSealed) {
454            throw new SecurityException(cookie + " not allowed after sealing");
455        }
456    }
457
458    private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) {
459        assertPreparedAndNotDestroyedLocked(cookie);
460        if (mCommitted) {
461            throw new SecurityException(cookie + " not allowed after commit");
462        }
463    }
464
465    private void assertPreparedAndNotDestroyedLocked(String cookie) {
466        if (!mPrepared) {
467            throw new IllegalStateException(cookie + " before prepared");
468        }
469        if (mDestroyed) {
470            throw new SecurityException(cookie + " not allowed after destruction");
471        }
472    }
473
474    /**
475     * Resolve the actual location where staged data should be written. This
476     * might point at an ASEC mount point, which is why we delay path resolution
477     * until someone actively works with the session.
478     */
479    private File resolveStageDirLocked() throws IOException {
480        if (mResolvedStageDir == null) {
481            if (stageDir != null) {
482                mResolvedStageDir = stageDir;
483            } else {
484                final String path = PackageHelper.getSdDir(stageCid);
485                if (path != null) {
486                    mResolvedStageDir = new File(path);
487                } else {
488                    throw new IOException("Failed to resolve path to container " + stageCid);
489                }
490            }
491        }
492        return mResolvedStageDir;
493    }
494
495    @Override
496    public void setClientProgress(float progress) {
497        synchronized (mLock) {
498            assertCallerIsOwnerOrRootLocked();
499
500            // Always publish first staging movement
501            final boolean forcePublish = (mClientProgress == 0);
502            mClientProgress = progress;
503            computeProgressLocked(forcePublish);
504        }
505    }
506
507    @Override
508    public void addClientProgress(float progress) {
509        synchronized (mLock) {
510            assertCallerIsOwnerOrRootLocked();
511
512            setClientProgress(mClientProgress + progress);
513        }
514    }
515
516    private void computeProgressLocked(boolean forcePublish) {
517        mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
518                + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
519
520        // Only publish when meaningful change
521        if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) {
522            mReportedProgress = mProgress;
523            mCallback.onSessionProgressChanged(this, mProgress);
524        }
525    }
526
527    @Override
528    public String[] getNames() {
529        synchronized (mLock) {
530            assertCallerIsOwnerOrRootLocked();
531            assertPreparedAndNotCommittedOrDestroyedLocked("getNames");
532
533            try {
534                return resolveStageDirLocked().list();
535            } catch (IOException e) {
536                throw ExceptionUtils.wrap(e);
537            }
538        }
539    }
540
541    @Override
542    public void removeSplit(String splitName) {
543        if (TextUtils.isEmpty(params.appPackageName)) {
544            throw new IllegalStateException("Must specify package name to remove a split");
545        }
546
547        synchronized (mLock) {
548            assertCallerIsOwnerOrRootLocked();
549            assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit");
550
551            try {
552                createRemoveSplitMarkerLocked(splitName);
553            } catch (IOException e) {
554                throw ExceptionUtils.wrap(e);
555            }
556        }
557    }
558
559    private void createRemoveSplitMarkerLocked(String splitName) throws IOException {
560        try {
561            final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION;
562            if (!FileUtils.isValidExtFilename(markerName)) {
563                throw new IllegalArgumentException("Invalid marker: " + markerName);
564            }
565            final File target = new File(resolveStageDirLocked(), markerName);
566            target.createNewFile();
567            Os.chmod(target.getAbsolutePath(), 0 /*mode*/);
568        } catch (ErrnoException e) {
569            throw e.rethrowAsIOException();
570        }
571    }
572
573    @Override
574    public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
575        try {
576            return openWriteInternal(name, offsetBytes, lengthBytes);
577        } catch (IOException e) {
578            throw ExceptionUtils.wrap(e);
579        }
580    }
581
582    private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
583            throws IOException {
584        // Quick sanity check of state, and allocate a pipe for ourselves. We
585        // then do heavy disk allocation outside the lock, but this open pipe
586        // will block any attempted install transitions.
587        final RevocableFileDescriptor fd;
588        final FileBridge bridge;
589        final File stageDir;
590        synchronized (mLock) {
591            assertCallerIsOwnerOrRootLocked();
592            assertPreparedAndNotSealedLocked("openWrite");
593
594            if (PackageInstaller.ENABLE_REVOCABLE_FD) {
595                fd = new RevocableFileDescriptor();
596                bridge = null;
597                mFds.add(fd);
598            } else {
599                fd = null;
600                bridge = new FileBridge();
601                mBridges.add(bridge);
602            }
603
604            stageDir = resolveStageDirLocked();
605        }
606
607        try {
608            // Use installer provided name for now; we always rename later
609            if (!FileUtils.isValidExtFilename(name)) {
610                throw new IllegalArgumentException("Invalid name: " + name);
611            }
612            final File target;
613            final long identity = Binder.clearCallingIdentity();
614            try {
615                target = new File(stageDir, name);
616            } finally {
617                Binder.restoreCallingIdentity(identity);
618            }
619
620            // TODO: this should delegate to DCS so the system process avoids
621            // holding open FDs into containers.
622            final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
623                    O_CREAT | O_WRONLY, 0644);
624            Os.chmod(target.getAbsolutePath(), 0644);
625
626            // If caller specified a total length, allocate it for them. Free up
627            // cache space to grow, if needed.
628            if (stageDir != null && lengthBytes > 0) {
629                mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes,
630                        PackageHelper.translateAllocateFlags(params.installFlags));
631            }
632
633            if (offsetBytes > 0) {
634                Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
635            }
636
637            if (PackageInstaller.ENABLE_REVOCABLE_FD) {
638                fd.init(mContext, targetFd);
639                return fd.getRevocableFileDescriptor();
640            } else {
641                bridge.setTargetFile(targetFd);
642                bridge.start();
643                return new ParcelFileDescriptor(bridge.getClientSocket());
644            }
645
646        } catch (ErrnoException e) {
647            throw e.rethrowAsIOException();
648        }
649    }
650
651    @Override
652    public ParcelFileDescriptor openRead(String name) {
653        synchronized (mLock) {
654            assertCallerIsOwnerOrRootLocked();
655            assertPreparedAndNotCommittedOrDestroyedLocked("openRead");
656            try {
657                return openReadInternalLocked(name);
658            } catch (IOException e) {
659                throw ExceptionUtils.wrap(e);
660            }
661        }
662    }
663
664    private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException {
665        try {
666            if (!FileUtils.isValidExtFilename(name)) {
667                throw new IllegalArgumentException("Invalid name: " + name);
668            }
669            final File target = new File(resolveStageDirLocked(), name);
670            final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0);
671            return new ParcelFileDescriptor(targetFd);
672        } catch (ErrnoException e) {
673            throw e.rethrowAsIOException();
674        }
675    }
676
677    /**
678     * Check if the caller is the owner of this session. Otherwise throw a
679     * {@link SecurityException}.
680     */
681    private void assertCallerIsOwnerOrRootLocked() {
682        final int callingUid = Binder.getCallingUid();
683        if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) {
684            throw new SecurityException("Session does not belong to uid " + callingUid);
685        }
686    }
687
688    /**
689     * If anybody is reading or writing data of the session, throw an {@link SecurityException}.
690     */
691    private void assertNoWriteFileTransfersOpenLocked() {
692        // Verify that all writers are hands-off
693        for (RevocableFileDescriptor fd : mFds) {
694            if (!fd.isRevoked()) {
695                throw new SecurityException("Files still open");
696            }
697        }
698        for (FileBridge bridge : mBridges) {
699            if (!bridge.isClosed()) {
700                throw new SecurityException("Files still open");
701            }
702        }
703    }
704
705    @Override
706    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
707        Preconditions.checkNotNull(statusReceiver);
708
709        final boolean wasSealed;
710        synchronized (mLock) {
711            assertCallerIsOwnerOrRootLocked();
712            assertPreparedAndNotDestroyedLocked("commit");
713
714            final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
715                    mContext, statusReceiver, sessionId, isInstallerDeviceOwnerLocked(), userId);
716            mRemoteObserver = adapter.getBinder();
717
718            if (forTransfer) {
719                mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
720
721                if (mInstallerUid == mOriginalInstallerUid) {
722                    throw new IllegalArgumentException("Session has not been transferred");
723                }
724            } else {
725                if (mInstallerUid != mOriginalInstallerUid) {
726                    throw new IllegalArgumentException("Session has been transferred");
727                }
728            }
729
730            wasSealed = mSealed;
731            if (!mSealed) {
732                try {
733                    sealAndValidateLocked();
734                } catch (IOException e) {
735                    throw new IllegalArgumentException(e);
736                } catch (PackageManagerException e) {
737                    // Do now throw an exception here to stay compatible with O and older
738                    destroyInternal();
739                    dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
740                    return;
741                }
742            }
743
744            // Client staging is fully done at this point
745            mClientProgress = 1f;
746            computeProgressLocked(true);
747
748            // This ongoing commit should keep session active, even though client
749            // will probably close their end.
750            mActiveCount.incrementAndGet();
751
752            mCommitted = true;
753            mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
754        }
755
756        if (!wasSealed) {
757            // Persist the fact that we've sealed ourselves to prevent
758            // mutations of any hard links we create. We do this without holding
759            // the session lock, since otherwise it's a lock inversion.
760            mCallback.onSessionSealedBlocking(this);
761        }
762    }
763
764    /**
765     * Seal the session to prevent further modification and validate the contents of it.
766     *
767     * <p>The session will be sealed after calling this method even if it failed.
768     *
769     * @throws PackageManagerException if the session was sealed but something went wrong. If the
770     *                                 session was sealed this is the only possible exception.
771     */
772    private void sealAndValidateLocked() throws PackageManagerException, IOException {
773        assertNoWriteFileTransfersOpenLocked();
774        assertPreparedAndNotDestroyedLocked("sealing of session");
775
776        final PackageInfo pkgInfo = mPm.getPackageInfo(
777                params.appPackageName, PackageManager.GET_SIGNATURES
778                        | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
779
780        resolveStageDirLocked();
781
782        mSealed = true;
783
784        // Verify that stage looks sane with respect to existing application.
785        // This currently only ensures packageName, versionCode, and certificate
786        // consistency.
787        try {
788            validateInstallLocked(pkgInfo);
789        } catch (PackageManagerException e) {
790            throw e;
791        } catch (Throwable e) {
792            // Convert all exceptions into package manager exceptions as only those are handled
793            // in the code above
794            throw new PackageManagerException(e);
795        }
796
797        // Read transfers from the original owner stay open, but as the session's data
798        // cannot be modified anymore, there is no leak of information.
799    }
800
801    @Override
802    public void transfer(String packageName) {
803        Preconditions.checkNotNull(packageName);
804
805        ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId);
806        if (newOwnerAppInfo == null) {
807            throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
808        }
809
810        if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission(
811                Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) {
812            throw new SecurityException("Destination package " + packageName + " does not have "
813                    + "the " + Manifest.permission.INSTALL_PACKAGES + " permission");
814        }
815
816        // Only install flags that can be verified by the app the session is transferred to are
817        // allowed. The parameters can be read via PackageInstaller.SessionInfo.
818        if (!params.areHiddenOptionsSet()) {
819            throw new SecurityException("Can only transfer sessions that use public options");
820        }
821
822        synchronized (mLock) {
823            assertCallerIsOwnerOrRootLocked();
824            assertPreparedAndNotSealedLocked("transfer");
825
826            try {
827                sealAndValidateLocked();
828            } catch (IOException e) {
829                throw new IllegalStateException(e);
830            } catch (PackageManagerException e) {
831                // Session is sealed but could not be verified, we need to destroy it
832                destroyInternal();
833                dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
834
835                throw new IllegalArgumentException("Package is not valid", e);
836            }
837
838            if (!mPackageName.equals(mInstallerPackageName)) {
839                throw new SecurityException("Can only transfer sessions that update the original "
840                        + "installer");
841            }
842
843            mInstallerPackageName = packageName;
844            mInstallerUid = newOwnerAppInfo.uid;
845        }
846
847        // Persist the fact that we've sealed ourselves to prevent
848        // mutations of any hard links we create. We do this without holding
849        // the session lock, since otherwise it's a lock inversion.
850        mCallback.onSessionSealedBlocking(this);
851    }
852
853    private void commitLocked()
854            throws PackageManagerException {
855        if (mDestroyed) {
856            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
857        }
858        if (!mSealed) {
859            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
860        }
861
862        Preconditions.checkNotNull(mPackageName);
863        Preconditions.checkNotNull(mSignatures);
864        Preconditions.checkNotNull(mResolvedBaseFile);
865
866        if (needToAskForPermissionsLocked()) {
867            // User needs to accept permissions; give installer an intent they
868            // can use to involve user.
869            final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
870            intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
871            intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
872            try {
873                mRemoteObserver.onUserActionRequired(intent);
874            } catch (RemoteException ignored) {
875            }
876
877            // Commit was keeping session marked as active until now; release
878            // that extra refcount so session appears idle.
879            closeInternal(false);
880            return;
881        }
882
883        if (stageCid != null) {
884            // Figure out the final installed size and resize the container once
885            // and for all. Internally the parser handles straddling between two
886            // locations when inheriting.
887            final long finalSize = calculateInstalledSize();
888            resizeContainer(stageCid, finalSize);
889        }
890
891        // Inherit any packages and native libraries from existing install that
892        // haven't been overridden.
893        if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
894            try {
895                final List<File> fromFiles = mResolvedInheritedFiles;
896                final File toDir = resolveStageDirLocked();
897
898                if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
899                if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
900                    throw new IllegalStateException("mInheritedFilesBase == null");
901                }
902
903                if (isLinkPossible(fromFiles, toDir)) {
904                    if (!mResolvedInstructionSets.isEmpty()) {
905                        final File oatDir = new File(toDir, "oat");
906                        createOatDirs(mResolvedInstructionSets, oatDir);
907                    }
908                    linkFiles(fromFiles, toDir, mInheritedFilesBase);
909                } else {
910                    // TODO: this should delegate to DCS so the system process
911                    // avoids holding open FDs into containers.
912                    copyFiles(fromFiles, toDir);
913                }
914            } catch (IOException e) {
915                throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
916                        "Failed to inherit existing install", e);
917            }
918        }
919
920        // TODO: surface more granular state from dexopt
921        mInternalProgress = 0.5f;
922        computeProgressLocked(true);
923
924        // Unpack native libraries
925        extractNativeLibraries(mResolvedStageDir, params.abiOverride);
926
927        // Container is ready to go, let's seal it up!
928        if (stageCid != null) {
929            finalizeAndFixContainer(stageCid);
930        }
931
932        // We've reached point of no return; call into PMS to install the stage.
933        // Regardless of success or failure we always destroy session.
934        final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
935            @Override
936            public void onUserActionRequired(Intent intent) {
937                throw new IllegalStateException();
938            }
939
940            @Override
941            public void onPackageInstalled(String basePackageName, int returnCode, String msg,
942                    Bundle extras) {
943                destroyInternal();
944                dispatchSessionFinished(returnCode, msg, extras);
945            }
946        };
947
948        final UserHandle user;
949        if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
950            user = UserHandle.ALL;
951        } else {
952            user = new UserHandle(userId);
953        }
954
955        mRelinquished = true;
956        mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
957                mInstallerPackageName, mInstallerUid, user, mCertificates);
958    }
959
960    /**
961     * Validate install by confirming that all application packages are have
962     * consistent package name, version code, and signing certificates.
963     * <p>
964     * Clears and populates {@link #mResolvedBaseFile},
965     * {@link #mResolvedStagedFiles}, and {@link #mResolvedInheritedFiles}.
966     * <p>
967     * Renames package files in stage to match split names defined inside.
968     * <p>
969     * Note that upgrade compatibility is still performed by
970     * {@link PackageManagerService}.
971     */
972    private void validateInstallLocked(@Nullable PackageInfo pkgInfo)
973            throws PackageManagerException {
974        mPackageName = null;
975        mVersionCode = -1;
976        mSignatures = null;
977
978        mResolvedBaseFile = null;
979        mResolvedStagedFiles.clear();
980        mResolvedInheritedFiles.clear();
981
982        try {
983            resolveStageDirLocked();
984        } catch (IOException e) {
985            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
986                    "Failed to resolve stage location", e);
987        }
988
989        final File[] removedFiles = mResolvedStageDir.listFiles(sRemovedFilter);
990        final List<String> removeSplitList = new ArrayList<>();
991        if (!ArrayUtils.isEmpty(removedFiles)) {
992            for (File removedFile : removedFiles) {
993                final String fileName = removedFile.getName();
994                final String splitName = fileName.substring(
995                        0, fileName.length() - REMOVE_SPLIT_MARKER_EXTENSION.length());
996                removeSplitList.add(splitName);
997            }
998        }
999
1000        final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
1001        if (ArrayUtils.isEmpty(addedFiles) && removeSplitList.size() == 0) {
1002            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
1003        }
1004        // Verify that all staged packages are internally consistent
1005        final ArraySet<String> stagedSplits = new ArraySet<>();
1006        for (File addedFile : addedFiles) {
1007            final ApkLite apk;
1008            try {
1009                int flags = PackageParser.PARSE_COLLECT_CERTIFICATES;
1010                if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
1011                    flags |= PackageParser.PARSE_IS_EPHEMERAL;
1012                }
1013                apk = PackageParser.parseApkLite(addedFile, flags);
1014            } catch (PackageParserException e) {
1015                throw PackageManagerException.from(e);
1016            }
1017
1018            if (!stagedSplits.add(apk.splitName)) {
1019                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1020                        "Split " + apk.splitName + " was defined multiple times");
1021            }
1022
1023            // Use first package to define unknown values
1024            if (mPackageName == null) {
1025                mPackageName = apk.packageName;
1026                mVersionCode = apk.versionCode;
1027            }
1028            if (mSignatures == null) {
1029                mSignatures = apk.signatures;
1030                mCertificates = apk.certificates;
1031            }
1032
1033            assertApkConsistentLocked(String.valueOf(addedFile), apk);
1034
1035            // Take this opportunity to enforce uniform naming
1036            final String targetName;
1037            if (apk.splitName == null) {
1038                targetName = "base.apk";
1039            } else {
1040                targetName = "split_" + apk.splitName + ".apk";
1041            }
1042            if (!FileUtils.isValidExtFilename(targetName)) {
1043                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1044                        "Invalid filename: " + targetName);
1045            }
1046
1047            final File targetFile = new File(mResolvedStageDir, targetName);
1048            if (!addedFile.equals(targetFile)) {
1049                addedFile.renameTo(targetFile);
1050            }
1051
1052            // Base is coming from session
1053            if (apk.splitName == null) {
1054                mResolvedBaseFile = targetFile;
1055            }
1056
1057            mResolvedStagedFiles.add(targetFile);
1058        }
1059
1060        if (removeSplitList.size() > 0) {
1061            if (pkgInfo == null) {
1062                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1063                        "Missing existing base package for " + mPackageName);
1064            }
1065
1066            // validate split names marked for removal
1067            for (String splitName : removeSplitList) {
1068                if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) {
1069                    throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1070                            "Split not found: " + splitName);
1071                }
1072            }
1073
1074            // ensure we've got appropriate package name, version code and signatures
1075            if (mPackageName == null) {
1076                mPackageName = pkgInfo.packageName;
1077                mVersionCode = pkgInfo.versionCode;
1078            }
1079            if (mSignatures == null) {
1080                mSignatures = pkgInfo.signatures;
1081            }
1082        }
1083
1084        if (params.mode == SessionParams.MODE_FULL_INSTALL) {
1085            // Full installs must include a base package
1086            if (!stagedSplits.contains(null)) {
1087                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1088                        "Full install must include a base package");
1089            }
1090
1091        } else {
1092            // Partial installs must be consistent with existing install
1093            if (pkgInfo == null || pkgInfo.applicationInfo == null) {
1094                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1095                        "Missing existing base package for " + mPackageName);
1096            }
1097
1098            final PackageLite existing;
1099            final ApkLite existingBase;
1100            ApplicationInfo appInfo = pkgInfo.applicationInfo;
1101            try {
1102                existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0);
1103                existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()),
1104                        PackageParser.PARSE_COLLECT_CERTIFICATES);
1105            } catch (PackageParserException e) {
1106                throw PackageManagerException.from(e);
1107            }
1108
1109            assertApkConsistentLocked("Existing base", existingBase);
1110
1111            // Inherit base if not overridden
1112            if (mResolvedBaseFile == null) {
1113                mResolvedBaseFile = new File(appInfo.getBaseCodePath());
1114                mResolvedInheritedFiles.add(mResolvedBaseFile);
1115            }
1116
1117            // Inherit splits if not overridden
1118            if (!ArrayUtils.isEmpty(existing.splitNames)) {
1119                for (int i = 0; i < existing.splitNames.length; i++) {
1120                    final String splitName = existing.splitNames[i];
1121                    final File splitFile = new File(existing.splitCodePaths[i]);
1122                    final boolean splitRemoved = removeSplitList.contains(splitName);
1123                    if (!stagedSplits.contains(splitName) && !splitRemoved) {
1124                        mResolvedInheritedFiles.add(splitFile);
1125                    }
1126                }
1127            }
1128
1129            // Inherit compiled oat directory.
1130            final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
1131            mInheritedFilesBase = packageInstallDir;
1132            final File oatDir = new File(packageInstallDir, "oat");
1133            if (oatDir.exists()) {
1134                final File[] archSubdirs = oatDir.listFiles();
1135
1136                // Keep track of all instruction sets we've seen compiled output for.
1137                // If we're linking (and not copying) inherited files, we can recreate the
1138                // instruction set hierarchy and link compiled output.
1139                if (archSubdirs != null && archSubdirs.length > 0) {
1140                    final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
1141                    for (File archSubDir : archSubdirs) {
1142                        // Skip any directory that isn't an ISA subdir.
1143                        if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
1144                            continue;
1145                        }
1146
1147                        mResolvedInstructionSets.add(archSubDir.getName());
1148                        List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
1149
1150                        // Only add compiled files associated with the base.
1151                        // Once b/62269291 is resolved, we can add all compiled files again.
1152                        for (File oatFile : oatFiles) {
1153                            if (oatFile.getName().equals("base.art")
1154                                    || oatFile.getName().equals("base.odex")
1155                                    || oatFile.getName().equals("base.vdex")) {
1156                                mResolvedInheritedFiles.add(oatFile);
1157                            }
1158                        }
1159                    }
1160                }
1161            }
1162        }
1163    }
1164
1165    private void assertApkConsistentLocked(String tag, ApkLite apk)
1166            throws PackageManagerException {
1167        if (!mPackageName.equals(apk.packageName)) {
1168            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
1169                    + apk.packageName + " inconsistent with " + mPackageName);
1170        }
1171        if (params.appPackageName != null && !params.appPackageName.equals(apk.packageName)) {
1172            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
1173                    + " specified package " + params.appPackageName
1174                    + " inconsistent with " + apk.packageName);
1175        }
1176        if (mVersionCode != apk.versionCode) {
1177            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
1178                    + " version code " + apk.versionCode + " inconsistent with "
1179                    + mVersionCode);
1180        }
1181        if (!Signature.areExactMatch(mSignatures, apk.signatures)) {
1182            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1183                    tag + " signatures are inconsistent");
1184        }
1185    }
1186
1187    /**
1188     * Calculate the final install footprint size, combining both staged and
1189     * existing APKs together and including unpacked native code from both.
1190     */
1191    private long calculateInstalledSize() throws PackageManagerException {
1192        Preconditions.checkNotNull(mResolvedBaseFile);
1193
1194        final ApkLite baseApk;
1195        try {
1196            baseApk = PackageParser.parseApkLite(mResolvedBaseFile, 0);
1197        } catch (PackageParserException e) {
1198            throw PackageManagerException.from(e);
1199        }
1200
1201        final List<String> splitPaths = new ArrayList<>();
1202        for (File file : mResolvedStagedFiles) {
1203            if (mResolvedBaseFile.equals(file)) continue;
1204            splitPaths.add(file.getAbsolutePath());
1205        }
1206        for (File file : mResolvedInheritedFiles) {
1207            if (mResolvedBaseFile.equals(file)) continue;
1208            splitPaths.add(file.getAbsolutePath());
1209        }
1210
1211        // This is kind of hacky; we're creating a half-parsed package that is
1212        // straddled between the inherited and staged APKs.
1213        final PackageLite pkg = new PackageLite(null, baseApk, null, null, null, null,
1214                splitPaths.toArray(new String[splitPaths.size()]), null);
1215        final boolean isForwardLocked =
1216                (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
1217
1218        try {
1219            return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride);
1220        } catch (IOException e) {
1221            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
1222                    "Failed to calculate install size", e);
1223        }
1224    }
1225
1226    /**
1227     * Determine if creating hard links between source and destination is
1228     * possible. That is, do they all live on the same underlying device.
1229     */
1230    private boolean isLinkPossible(List<File> fromFiles, File toDir) {
1231        try {
1232            final StructStat toStat = Os.stat(toDir.getAbsolutePath());
1233            for (File fromFile : fromFiles) {
1234                final StructStat fromStat = Os.stat(fromFile.getAbsolutePath());
1235                if (fromStat.st_dev != toStat.st_dev) {
1236                    return false;
1237                }
1238            }
1239        } catch (ErrnoException e) {
1240            Slog.w(TAG, "Failed to detect if linking possible: " + e);
1241            return false;
1242        }
1243        return true;
1244    }
1245
1246    /**
1247     * @return the uid of the owner this session
1248     */
1249    public int getInstallerUid() {
1250        synchronized (mLock) {
1251            return mInstallerUid;
1252        }
1253    }
1254
1255    private static String getRelativePath(File file, File base) throws IOException {
1256        final String pathStr = file.getAbsolutePath();
1257        final String baseStr = base.getAbsolutePath();
1258        // Don't allow relative paths.
1259        if (pathStr.contains("/.") ) {
1260            throw new IOException("Invalid path (was relative) : " + pathStr);
1261        }
1262
1263        if (pathStr.startsWith(baseStr)) {
1264            return pathStr.substring(baseStr.length());
1265        }
1266
1267        throw new IOException("File: " + pathStr + " outside base: " + baseStr);
1268    }
1269
1270    private void createOatDirs(List<String> instructionSets, File fromDir)
1271            throws PackageManagerException {
1272        for (String instructionSet : instructionSets) {
1273            try {
1274                mPm.mInstaller.createOatDir(fromDir.getAbsolutePath(), instructionSet);
1275            } catch (InstallerException e) {
1276                throw PackageManagerException.from(e);
1277            }
1278        }
1279    }
1280
1281    private void linkFiles(List<File> fromFiles, File toDir, File fromDir)
1282            throws IOException {
1283        for (File fromFile : fromFiles) {
1284            final String relativePath = getRelativePath(fromFile, fromDir);
1285            try {
1286                mPm.mInstaller.linkFile(relativePath, fromDir.getAbsolutePath(),
1287                        toDir.getAbsolutePath());
1288            } catch (InstallerException e) {
1289                throw new IOException("failed linkOrCreateDir(" + relativePath + ", "
1290                        + fromDir + ", " + toDir + ")", e);
1291            }
1292        }
1293
1294        Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
1295    }
1296
1297    private static void copyFiles(List<File> fromFiles, File toDir) throws IOException {
1298        // Remove any partial files from previous attempt
1299        for (File file : toDir.listFiles()) {
1300            if (file.getName().endsWith(".tmp")) {
1301                file.delete();
1302            }
1303        }
1304
1305        for (File fromFile : fromFiles) {
1306            final File tmpFile = File.createTempFile("inherit", ".tmp", toDir);
1307            if (LOGD) Slog.d(TAG, "Copying " + fromFile + " to " + tmpFile);
1308            if (!FileUtils.copyFile(fromFile, tmpFile)) {
1309                throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
1310            }
1311            try {
1312                Os.chmod(tmpFile.getAbsolutePath(), 0644);
1313            } catch (ErrnoException e) {
1314                throw new IOException("Failed to chmod " + tmpFile);
1315            }
1316            final File toFile = new File(toDir, fromFile.getName());
1317            if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile);
1318            if (!tmpFile.renameTo(toFile)) {
1319                throw new IOException("Failed to rename " + tmpFile + " to " + toFile);
1320            }
1321        }
1322        Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
1323    }
1324
1325    private static void extractNativeLibraries(File packageDir, String abiOverride)
1326            throws PackageManagerException {
1327        // Always start from a clean slate
1328        final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
1329        NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
1330
1331        NativeLibraryHelper.Handle handle = null;
1332        try {
1333            handle = NativeLibraryHelper.Handle.create(packageDir);
1334            final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
1335                    abiOverride);
1336            if (res != PackageManager.INSTALL_SUCCEEDED) {
1337                throw new PackageManagerException(res,
1338                        "Failed to extract native libraries, res=" + res);
1339            }
1340        } catch (IOException e) {
1341            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
1342                    "Failed to extract native libraries", e);
1343        } finally {
1344            IoUtils.closeQuietly(handle);
1345        }
1346    }
1347
1348    private static void resizeContainer(String cid, long targetSize)
1349            throws PackageManagerException {
1350        String path = PackageHelper.getSdDir(cid);
1351        if (path == null) {
1352            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1353                    "Failed to find mounted " + cid);
1354        }
1355
1356        final long currentSize = new File(path).getTotalSpace();
1357        if (currentSize > targetSize) {
1358            Slog.w(TAG, "Current size " + currentSize + " is larger than target size "
1359                    + targetSize + "; skipping resize");
1360            return;
1361        }
1362
1363        if (!PackageHelper.unMountSdDir(cid)) {
1364            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1365                    "Failed to unmount " + cid + " before resize");
1366        }
1367
1368        if (!PackageHelper.resizeSdDir(targetSize, cid,
1369                PackageManagerService.getEncryptKey())) {
1370            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1371                    "Failed to resize " + cid + " to " + targetSize + " bytes");
1372        }
1373
1374        path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
1375                Process.SYSTEM_UID, false);
1376        if (path == null) {
1377            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1378                    "Failed to mount " + cid + " after resize");
1379        }
1380    }
1381
1382    private void finalizeAndFixContainer(String cid) throws PackageManagerException {
1383        if (!PackageHelper.finalizeSdDir(cid)) {
1384            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1385                    "Failed to finalize container " + cid);
1386        }
1387
1388        if (!PackageHelper.fixSdPermissions(cid, defaultContainerGid, null)) {
1389            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
1390                    "Failed to fix permissions on container " + cid);
1391        }
1392    }
1393
1394    void setPermissionsResult(boolean accepted) {
1395        if (!mSealed) {
1396            throw new SecurityException("Must be sealed to accept permissions");
1397        }
1398
1399        if (accepted) {
1400            // Mark and kick off another install pass
1401            synchronized (mLock) {
1402                mPermissionsManuallyAccepted = true;
1403                mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
1404            }
1405        } else {
1406            destroyInternal();
1407            dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
1408        }
1409    }
1410
1411    public void open() throws IOException {
1412        if (mActiveCount.getAndIncrement() == 0) {
1413            mCallback.onSessionActiveChanged(this, true);
1414        }
1415
1416        boolean wasPrepared;
1417        synchronized (mLock) {
1418            wasPrepared = mPrepared;
1419            if (!mPrepared) {
1420                if (stageDir != null) {
1421                    prepareStageDir(stageDir);
1422                } else if (stageCid != null) {
1423                    final long identity = Binder.clearCallingIdentity();
1424                    try {
1425                        prepareExternalStageCid(stageCid, params.sizeBytes);
1426                    } finally {
1427                        Binder.restoreCallingIdentity(identity);
1428                    }
1429
1430                    // TODO: deliver more granular progress for ASEC allocation
1431                    mInternalProgress = 0.25f;
1432                    computeProgressLocked(true);
1433                } else {
1434                    throw new IllegalArgumentException(
1435                            "Exactly one of stageDir or stageCid stage must be set");
1436                }
1437
1438                mPrepared = true;
1439            }
1440        }
1441
1442        if (!wasPrepared) {
1443            mCallback.onSessionPrepared(this);
1444        }
1445    }
1446
1447    @Override
1448    public void close() {
1449        closeInternal(true);
1450    }
1451
1452    private void closeInternal(boolean checkCaller) {
1453        int activeCount;
1454        synchronized (mLock) {
1455            if (checkCaller) {
1456                assertCallerIsOwnerOrRootLocked();
1457            }
1458
1459            activeCount = mActiveCount.decrementAndGet();
1460        }
1461
1462        if (activeCount == 0) {
1463            mCallback.onSessionActiveChanged(this, false);
1464        }
1465    }
1466
1467    @Override
1468    public void abandon() {
1469        synchronized (mLock) {
1470            assertCallerIsOwnerOrRootLocked();
1471
1472            if (mRelinquished) {
1473                Slog.d(TAG, "Ignoring abandon after commit relinquished control");
1474                return;
1475            }
1476            destroyInternal();
1477        }
1478
1479        dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
1480    }
1481
1482    private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
1483        final IPackageInstallObserver2 observer;
1484        final String packageName;
1485        synchronized (mLock) {
1486            mFinalStatus = returnCode;
1487            mFinalMessage = msg;
1488
1489            observer = mRemoteObserver;
1490            packageName = mPackageName;
1491        }
1492
1493        if (observer != null) {
1494            // Execute observer.onPackageInstalled on different tread as we don't want callers
1495            // inside the system server have to worry about catching the callbacks while they are
1496            // calling into the session
1497            final SomeArgs args = SomeArgs.obtain();
1498            args.arg1 = packageName;
1499            args.arg2 = msg;
1500            args.arg3 = extras;
1501            args.arg4 = observer;
1502            args.argi1 = returnCode;
1503
1504            mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
1505        }
1506
1507        final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
1508
1509        // Send broadcast to default launcher only if it's a new install
1510        final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING);
1511        if (success && isNewInstall) {
1512            mPm.sendSessionCommitBroadcast(generateInfo(), userId);
1513        }
1514
1515        mCallback.onSessionFinished(this, success);
1516    }
1517
1518    private void destroyInternal() {
1519        synchronized (mLock) {
1520            mSealed = true;
1521            mDestroyed = true;
1522
1523            // Force shut down all bridges
1524            for (RevocableFileDescriptor fd : mFds) {
1525                fd.revoke();
1526            }
1527            for (FileBridge bridge : mBridges) {
1528                bridge.forceClose();
1529            }
1530        }
1531        if (stageDir != null) {
1532            try {
1533                mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
1534            } catch (InstallerException ignored) {
1535            }
1536        }
1537        if (stageCid != null) {
1538            PackageHelper.destroySdDir(stageCid);
1539        }
1540    }
1541
1542    void dump(IndentingPrintWriter pw) {
1543        synchronized (mLock) {
1544            dumpLocked(pw);
1545        }
1546    }
1547
1548    private void dumpLocked(IndentingPrintWriter pw) {
1549        pw.println("Session " + sessionId + ":");
1550        pw.increaseIndent();
1551
1552        pw.printPair("userId", userId);
1553        pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid);
1554        pw.printPair("mInstallerPackageName", mInstallerPackageName);
1555        pw.printPair("mInstallerUid", mInstallerUid);
1556        pw.printPair("createdMillis", createdMillis);
1557        pw.printPair("stageDir", stageDir);
1558        pw.printPair("stageCid", stageCid);
1559        pw.println();
1560
1561        params.dump(pw);
1562
1563        pw.printPair("mClientProgress", mClientProgress);
1564        pw.printPair("mProgress", mProgress);
1565        pw.printPair("mSealed", mSealed);
1566        pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted);
1567        pw.printPair("mRelinquished", mRelinquished);
1568        pw.printPair("mDestroyed", mDestroyed);
1569        pw.printPair("mFds", mFds.size());
1570        pw.printPair("mBridges", mBridges.size());
1571        pw.printPair("mFinalStatus", mFinalStatus);
1572        pw.printPair("mFinalMessage", mFinalMessage);
1573        pw.println();
1574
1575        pw.decreaseIndent();
1576    }
1577
1578    private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out,
1579            String[] grantedRuntimePermissions) throws IOException {
1580        if (grantedRuntimePermissions != null) {
1581            for (String permission : grantedRuntimePermissions) {
1582                out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
1583                writeStringAttribute(out, ATTR_NAME, permission);
1584                out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
1585            }
1586        }
1587    }
1588
1589    private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) {
1590        return new File(sessionsDir, "app_icon." + sessionId + ".png");
1591    }
1592
1593    /**
1594     * Write this session to a {@link XmlSerializer}.
1595     *
1596     * @param out Where to write the session to
1597     * @param sessionsDir The directory containing the sessions
1598     */
1599    void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException {
1600        synchronized (mLock) {
1601            if (mDestroyed) {
1602                return;
1603            }
1604
1605            out.startTag(null, TAG_SESSION);
1606
1607            writeIntAttribute(out, ATTR_SESSION_ID, sessionId);
1608            writeIntAttribute(out, ATTR_USER_ID, userId);
1609            writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
1610                    mInstallerPackageName);
1611            writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid);
1612            writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis);
1613            if (stageDir != null) {
1614                writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
1615                        stageDir.getAbsolutePath());
1616            }
1617            if (stageCid != null) {
1618                writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
1619            }
1620            writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
1621            writeBooleanAttribute(out, ATTR_SEALED, isSealed());
1622
1623            writeIntAttribute(out, ATTR_MODE, params.mode);
1624            writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
1625            writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
1626            writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
1627            writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
1628            writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
1629            writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
1630            writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
1631            writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
1632            writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
1633            writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
1634            writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
1635
1636            // Persist app icon if changed since last written
1637            File appIconFile = buildAppIconFile(sessionId, sessionsDir);
1638            if (params.appIcon == null && appIconFile.exists()) {
1639                appIconFile.delete();
1640            } else if (params.appIcon != null
1641                    && appIconFile.lastModified() != params.appIconLastModified) {
1642                if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
1643                FileOutputStream os = null;
1644                try {
1645                    os = new FileOutputStream(appIconFile);
1646                    params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os);
1647                } catch (IOException e) {
1648                    Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
1649                } finally {
1650                    IoUtils.closeQuietly(os);
1651                }
1652
1653                params.appIconLastModified = appIconFile.lastModified();
1654            }
1655
1656            writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
1657        }
1658
1659        out.endTag(null, TAG_SESSION);
1660    }
1661
1662    private static String[] readGrantedRuntimePermissions(XmlPullParser in)
1663            throws IOException, XmlPullParserException {
1664        List<String> permissions = null;
1665
1666        final int outerDepth = in.getDepth();
1667        int type;
1668        while ((type = in.next()) != XmlPullParser.END_DOCUMENT
1669                && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
1670            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1671                continue;
1672            }
1673            if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
1674                String permission = readStringAttribute(in, ATTR_NAME);
1675                if (permissions == null) {
1676                    permissions = new ArrayList<>();
1677                }
1678                permissions.add(permission);
1679            }
1680        }
1681
1682        if (permissions == null) {
1683            return null;
1684        }
1685
1686        String[] permissionsArray = new String[permissions.size()];
1687        permissions.toArray(permissionsArray);
1688        return permissionsArray;
1689    }
1690
1691    /**
1692     * Read new session from a {@link XmlPullParser xml description} and create it.
1693     *
1694     * @param in The source of the description
1695     * @param callback Callback the session uses to notify about changes of it's state
1696     * @param context Context to be used by the session
1697     * @param pm PackageManager to use by the session
1698     * @param installerThread Thread to be used for callbacks of this session
1699     * @param sessionsDir The directory the sessions are stored in
1700     *
1701     * @return The newly created session
1702     */
1703    public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
1704            @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
1705            @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir)
1706            throws IOException, XmlPullParserException {
1707        final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
1708        final int userId = readIntAttribute(in, ATTR_USER_ID);
1709        final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
1710        final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid(
1711                installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
1712        final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
1713        final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
1714        final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
1715        final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
1716        final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
1717        final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
1718
1719        final SessionParams params = new SessionParams(
1720                SessionParams.MODE_INVALID);
1721        params.mode = readIntAttribute(in, ATTR_MODE);
1722        params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
1723        params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
1724        params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
1725        params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
1726        params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
1727        params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
1728        params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
1729        params.originatingUid =
1730                readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
1731        params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
1732        params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
1733        params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
1734        params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
1735        params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
1736
1737        final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
1738        if (appIconFile.exists()) {
1739            params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
1740            params.appIconLastModified = appIconFile.lastModified();
1741        }
1742
1743        return new PackageInstallerSession(callback, context, pm,
1744                installerThread, sessionId, userId, installerPackageName, installerUid,
1745                params, createdMillis, stageDir, stageCid, prepared, sealed);
1746    }
1747}
1748