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