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