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