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