PackageInstallerService.java revision af759c52ce01fe6b5144957e38da956af01a217b
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 com.android.internal.util.XmlUtils.readBitmapAttribute;
20import static com.android.internal.util.XmlUtils.readBooleanAttribute;
21import static com.android.internal.util.XmlUtils.readIntAttribute;
22import static com.android.internal.util.XmlUtils.readLongAttribute;
23import static com.android.internal.util.XmlUtils.readStringAttribute;
24import static com.android.internal.util.XmlUtils.readUriAttribute;
25import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
26import static com.android.internal.util.XmlUtils.writeIntAttribute;
27import static com.android.internal.util.XmlUtils.writeLongAttribute;
28import static com.android.internal.util.XmlUtils.writeStringAttribute;
29import static com.android.internal.util.XmlUtils.writeUriAttribute;
30import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
31import static org.xmlpull.v1.XmlPullParser.START_TAG;
32
33import android.Manifest;
34import android.app.ActivityManager;
35import android.app.AppGlobals;
36import android.app.AppOpsManager;
37import android.app.Notification;
38import android.app.NotificationManager;
39import android.app.PackageDeleteObserver;
40import android.app.PackageInstallObserver;
41import android.app.admin.DevicePolicyManager;
42import android.content.Context;
43import android.content.Intent;
44import android.content.IntentSender;
45import android.content.IntentSender.SendIntentException;
46import android.content.pm.IPackageInstaller;
47import android.content.pm.IPackageInstallerCallback;
48import android.content.pm.IPackageInstallerSession;
49import android.content.pm.PackageInfo;
50import android.content.pm.PackageInstaller;
51import android.content.pm.PackageInstaller.SessionInfo;
52import android.content.pm.PackageInstaller.SessionParams;
53import android.content.pm.PackageManager;
54import android.content.pm.ParceledListSlice;
55import android.content.pm.VersionedPackage;
56import android.graphics.Bitmap;
57import android.graphics.Bitmap.CompressFormat;
58import android.graphics.BitmapFactory;
59import android.net.Uri;
60import android.os.Binder;
61import android.os.Bundle;
62import android.os.Environment;
63import android.os.Handler;
64import android.os.HandlerThread;
65import android.os.Looper;
66import android.os.Message;
67import android.os.Process;
68import android.os.RemoteCallbackList;
69import android.os.RemoteException;
70import android.os.SELinux;
71import android.os.UserHandle;
72import android.os.UserManager;
73import android.os.storage.StorageManager;
74import android.system.ErrnoException;
75import android.system.Os;
76import android.text.TextUtils;
77import android.text.format.DateUtils;
78import android.util.ArraySet;
79import android.util.AtomicFile;
80import android.util.ExceptionUtils;
81import android.util.Slog;
82import android.util.SparseArray;
83import android.util.SparseBooleanArray;
84import android.util.Xml;
85
86import libcore.io.IoUtils;
87
88import com.android.internal.R;
89import com.android.internal.annotations.GuardedBy;
90import com.android.internal.content.PackageHelper;
91import com.android.internal.notification.SystemNotificationChannels;
92import com.android.internal.util.FastXmlSerializer;
93import com.android.internal.util.ImageUtils;
94import com.android.internal.util.IndentingPrintWriter;
95import com.android.server.IoThread;
96
97import org.xmlpull.v1.XmlPullParser;
98import org.xmlpull.v1.XmlPullParserException;
99import org.xmlpull.v1.XmlSerializer;
100
101import java.io.File;
102import java.io.FileInputStream;
103import java.io.FileNotFoundException;
104import java.io.FileOutputStream;
105import java.io.FilenameFilter;
106import java.io.IOException;
107import java.nio.charset.StandardCharsets;
108import java.security.SecureRandom;
109import java.util.ArrayList;
110import java.util.Collections;
111import java.util.List;
112import java.util.Objects;
113import java.util.Random;
114
115public class PackageInstallerService extends IPackageInstaller.Stub {
116    private static final String TAG = "PackageInstaller";
117    private static final boolean LOGD = false;
118
119    // TODO: remove outstanding sessions when installer package goes away
120    // TODO: notify listeners in other users when package has been installed there
121    // TODO: purge expired sessions periodically in addition to at reboot
122
123    /** XML constants used in {@link #mSessionsFile} */
124    private static final String TAG_SESSIONS = "sessions";
125    private 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    /** Automatically destroy sessions older than this */
153    private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
154    /** Upper bound on number of active sessions for a UID */
155    private static final long MAX_ACTIVE_SESSIONS = 1024;
156    /** Upper bound on number of historical sessions for a UID */
157    private static final long MAX_HISTORICAL_SESSIONS = 1048576;
158
159    private final Context mContext;
160    private final PackageManagerService mPm;
161
162    private AppOpsManager mAppOps;
163
164    private final HandlerThread mInstallThread;
165    private final Handler mInstallHandler;
166
167    private final Callbacks mCallbacks;
168
169    /**
170     * File storing persisted {@link #mSessions} metadata.
171     */
172    private final AtomicFile mSessionsFile;
173
174    /**
175     * Directory storing persisted {@link #mSessions} metadata which is too
176     * heavy to store directly in {@link #mSessionsFile}.
177     */
178    private final File mSessionsDir;
179
180    private final InternalCallback mInternalCallback = new InternalCallback();
181
182    /**
183     * Used for generating session IDs. Since this is created at boot time,
184     * normal random might be predictable.
185     */
186    private final Random mRandom = new SecureRandom();
187
188    /** All sessions allocated */
189    @GuardedBy("mSessions")
190    private final SparseBooleanArray mAllocatedSessions = new SparseBooleanArray();
191
192    @GuardedBy("mSessions")
193    private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
194
195    /** Historical sessions kept around for debugging purposes */
196    @GuardedBy("mSessions")
197    private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>();
198
199    /** Sessions allocated to legacy users */
200    @GuardedBy("mSessions")
201    private final SparseBooleanArray mLegacySessions = new SparseBooleanArray();
202
203    private static final FilenameFilter sStageFilter = new FilenameFilter() {
204        @Override
205        public boolean accept(File dir, String name) {
206            return isStageName(name);
207        }
208    };
209
210    public PackageInstallerService(Context context, PackageManagerService pm) {
211        mContext = context;
212        mPm = pm;
213
214        mInstallThread = new HandlerThread(TAG);
215        mInstallThread.start();
216
217        mInstallHandler = new Handler(mInstallThread.getLooper());
218
219        mCallbacks = new Callbacks(mInstallThread.getLooper());
220
221        mSessionsFile = new AtomicFile(
222                new File(Environment.getDataSystemDirectory(), "install_sessions.xml"));
223        mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions");
224        mSessionsDir.mkdirs();
225    }
226
227    public void systemReady() {
228        mAppOps = mContext.getSystemService(AppOpsManager.class);
229
230        synchronized (mSessions) {
231            readSessionsLocked();
232
233            reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, false /*isInstant*/);
234            reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL, true /*isInstant*/);
235
236            final ArraySet<File> unclaimedIcons = newArraySet(
237                    mSessionsDir.listFiles());
238
239            // Ignore stages and icons claimed by active sessions
240            for (int i = 0; i < mSessions.size(); i++) {
241                final PackageInstallerSession session = mSessions.valueAt(i);
242                unclaimedIcons.remove(buildAppIconFile(session.sessionId));
243            }
244
245            // Clean up orphaned icons
246            for (File icon : unclaimedIcons) {
247                Slog.w(TAG, "Deleting orphan icon " + icon);
248                icon.delete();
249            }
250        }
251    }
252
253    private void reconcileStagesLocked(String volumeUuid, boolean isEphemeral) {
254        final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
255        final ArraySet<File> unclaimedStages = newArraySet(
256                stagingDir.listFiles(sStageFilter));
257
258        // Ignore stages claimed by active sessions
259        for (int i = 0; i < mSessions.size(); i++) {
260            final PackageInstallerSession session = mSessions.valueAt(i);
261            unclaimedStages.remove(session.stageDir);
262        }
263
264        // Clean up orphaned staging directories
265        for (File stage : unclaimedStages) {
266            Slog.w(TAG, "Deleting orphan stage " + stage);
267            synchronized (mPm.mInstallLock) {
268                mPm.removeCodePathLI(stage);
269            }
270        }
271    }
272
273    public void onPrivateVolumeMounted(String volumeUuid) {
274        synchronized (mSessions) {
275            reconcileStagesLocked(volumeUuid, false /*isInstant*/);
276        }
277    }
278
279    public void onSecureContainersAvailable() {
280        synchronized (mSessions) {
281            final ArraySet<String> unclaimed = new ArraySet<>();
282            for (String cid : PackageHelper.getSecureContainerList()) {
283                if (isStageName(cid)) {
284                    unclaimed.add(cid);
285                }
286            }
287
288            // Ignore stages claimed by active sessions
289            for (int i = 0; i < mSessions.size(); i++) {
290                final PackageInstallerSession session = mSessions.valueAt(i);
291                final String cid = session.stageCid;
292
293                if (unclaimed.remove(cid)) {
294                    // Claimed by active session, mount it
295                    PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
296                            Process.SYSTEM_UID);
297                }
298            }
299
300            // Clean up orphaned staging containers
301            for (String cid : unclaimed) {
302                Slog.w(TAG, "Deleting orphan container " + cid);
303                PackageHelper.destroySdDir(cid);
304            }
305        }
306    }
307
308    public static boolean isStageName(String name) {
309        final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
310        final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
311        final boolean isLegacyContainer = name.startsWith("smdl2tmp");
312        return isFile || isContainer || isLegacyContainer;
313    }
314
315    @Deprecated
316    public File allocateStageDirLegacy(String volumeUuid, boolean isEphemeral) throws IOException {
317        synchronized (mSessions) {
318            try {
319                final int sessionId = allocateSessionIdLocked();
320                mLegacySessions.put(sessionId, true);
321                final File stageDir = buildStageDir(volumeUuid, sessionId, isEphemeral);
322                prepareStageDir(stageDir);
323                return stageDir;
324            } catch (IllegalStateException e) {
325                throw new IOException(e);
326            }
327        }
328    }
329
330    @Deprecated
331    public String allocateExternalStageCidLegacy() {
332        synchronized (mSessions) {
333            final int sessionId = allocateSessionIdLocked();
334            mLegacySessions.put(sessionId, true);
335            return "smdl" + sessionId + ".tmp";
336        }
337    }
338
339    private void readSessionsLocked() {
340        if (LOGD) Slog.v(TAG, "readSessionsLocked()");
341
342        mSessions.clear();
343
344        FileInputStream fis = null;
345        try {
346            fis = mSessionsFile.openRead();
347            final XmlPullParser in = Xml.newPullParser();
348            in.setInput(fis, StandardCharsets.UTF_8.name());
349
350            int type;
351            while ((type = in.next()) != END_DOCUMENT) {
352                if (type == START_TAG) {
353                    final String tag = in.getName();
354                    if (TAG_SESSION.equals(tag)) {
355                        final PackageInstallerSession session = readSessionLocked(in);
356                        final long age = System.currentTimeMillis() - session.createdMillis;
357
358                        final boolean valid;
359                        if (age >= MAX_AGE_MILLIS) {
360                            Slog.w(TAG, "Abandoning old session first created at "
361                                    + session.createdMillis);
362                            valid = false;
363                        } else {
364                            valid = true;
365                        }
366
367                        if (valid) {
368                            mSessions.put(session.sessionId, session);
369                        } else {
370                            // Since this is early during boot we don't send
371                            // any observer events about the session, but we
372                            // keep details around for dumpsys.
373                            mHistoricalSessions.put(session.sessionId, session);
374                        }
375                        mAllocatedSessions.put(session.sessionId, true);
376                    }
377                }
378            }
379        } catch (FileNotFoundException e) {
380            // Missing sessions are okay, probably first boot
381        } catch (IOException | XmlPullParserException e) {
382            Slog.wtf(TAG, "Failed reading install sessions", e);
383        } finally {
384            IoUtils.closeQuietly(fis);
385        }
386    }
387
388    private PackageInstallerSession readSessionLocked(XmlPullParser in) throws IOException,
389            XmlPullParserException {
390        final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
391        final int userId = readIntAttribute(in, ATTR_USER_ID);
392        final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
393        final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, mPm.getPackageUid(
394                installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
395        final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
396        final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
397        final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
398        final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
399        final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
400        final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
401
402        final SessionParams params = new SessionParams(
403                SessionParams.MODE_INVALID);
404        params.mode = readIntAttribute(in, ATTR_MODE);
405        params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
406        params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
407        params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
408        params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
409        params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
410        params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
411        params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
412        params.originatingUid =
413                readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
414        params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
415        params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
416        params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
417        params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
418        params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
419
420        final File appIconFile = buildAppIconFile(sessionId);
421        if (appIconFile.exists()) {
422            params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
423            params.appIconLastModified = appIconFile.lastModified();
424        }
425
426        return new PackageInstallerSession(mInternalCallback, mContext, mPm,
427                mInstallThread.getLooper(), sessionId, userId, installerPackageName, installerUid,
428                params, createdMillis, stageDir, stageCid, prepared, sealed);
429    }
430
431    private void writeSessionsLocked() {
432        if (LOGD) Slog.v(TAG, "writeSessionsLocked()");
433
434        FileOutputStream fos = null;
435        try {
436            fos = mSessionsFile.startWrite();
437
438            XmlSerializer out = new FastXmlSerializer();
439            out.setOutput(fos, StandardCharsets.UTF_8.name());
440            out.startDocument(null, true);
441            out.startTag(null, TAG_SESSIONS);
442            final int size = mSessions.size();
443            for (int i = 0; i < size; i++) {
444                final PackageInstallerSession session = mSessions.valueAt(i);
445                writeSessionLocked(out, session);
446            }
447            out.endTag(null, TAG_SESSIONS);
448            out.endDocument();
449
450            mSessionsFile.finishWrite(fos);
451        } catch (IOException e) {
452            if (fos != null) {
453                mSessionsFile.failWrite(fos);
454            }
455        }
456    }
457
458    private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session)
459            throws IOException {
460        final SessionParams params = session.params;
461
462        out.startTag(null, TAG_SESSION);
463
464        writeIntAttribute(out, ATTR_SESSION_ID, session.sessionId);
465        writeIntAttribute(out, ATTR_USER_ID, session.userId);
466        writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
467                session.installerPackageName);
468        writeIntAttribute(out, ATTR_INSTALLER_UID, session.installerUid);
469        writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis);
470        if (session.stageDir != null) {
471            writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
472                    session.stageDir.getAbsolutePath());
473        }
474        if (session.stageCid != null) {
475            writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid);
476        }
477        writeBooleanAttribute(out, ATTR_PREPARED, session.isPrepared());
478        writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());
479
480        writeIntAttribute(out, ATTR_MODE, params.mode);
481        writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
482        writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
483        writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
484        writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
485        writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
486        writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
487        writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
488        writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
489        writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
490        writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
491        writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
492
493        // Persist app icon if changed since last written
494        final File appIconFile = buildAppIconFile(session.sessionId);
495        if (params.appIcon == null && appIconFile.exists()) {
496            appIconFile.delete();
497        } else if (params.appIcon != null
498                && appIconFile.lastModified() != params.appIconLastModified) {
499            if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
500            FileOutputStream os = null;
501            try {
502                os = new FileOutputStream(appIconFile);
503                params.appIcon.compress(CompressFormat.PNG, 90, os);
504            } catch (IOException e) {
505                Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
506            } finally {
507                IoUtils.closeQuietly(os);
508            }
509
510            params.appIconLastModified = appIconFile.lastModified();
511        }
512
513        writeGrantedRuntimePermissions(out, params.grantedRuntimePermissions);
514
515        out.endTag(null, TAG_SESSION);
516    }
517
518    private static void writeGrantedRuntimePermissions(XmlSerializer out,
519            String[] grantedRuntimePermissions) throws IOException {
520        if (grantedRuntimePermissions != null) {
521            for (String permission : grantedRuntimePermissions) {
522                out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
523                writeStringAttribute(out, ATTR_NAME, permission);
524                out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
525            }
526        }
527    }
528
529    private static String[] readGrantedRuntimePermissions(XmlPullParser in)
530            throws IOException, XmlPullParserException {
531        List<String> permissions = null;
532
533        final int outerDepth = in.getDepth();
534        int type;
535        while ((type = in.next()) != XmlPullParser.END_DOCUMENT
536                && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
537            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
538                continue;
539            }
540            if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
541                String permission = readStringAttribute(in, ATTR_NAME);
542                if (permissions == null) {
543                    permissions = new ArrayList<>();
544                }
545                permissions.add(permission);
546            }
547        }
548
549        if (permissions == null) {
550            return null;
551        }
552
553        String[] permissionsArray = new String[permissions.size()];
554        permissions.toArray(permissionsArray);
555        return permissionsArray;
556    }
557
558    private File buildAppIconFile(int sessionId) {
559        return new File(mSessionsDir, "app_icon." + sessionId + ".png");
560    }
561
562    private void writeSessionsAsync() {
563        IoThread.getHandler().post(new Runnable() {
564            @Override
565            public void run() {
566                synchronized (mSessions) {
567                    writeSessionsLocked();
568                }
569            }
570        });
571    }
572
573    @Override
574    public int createSession(SessionParams params, String installerPackageName, int userId) {
575        try {
576            return createSessionInternal(params, installerPackageName, userId);
577        } catch (IOException e) {
578            throw ExceptionUtils.wrap(e);
579        }
580    }
581
582    private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
583            throws IOException {
584        final int callingUid = Binder.getCallingUid();
585        mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");
586
587        if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
588            throw new SecurityException("User restriction prevents installing");
589        }
590
591        if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
592            params.installFlags |= PackageManager.INSTALL_FROM_ADB;
593
594        } else {
595            mAppOps.checkPackage(callingUid, installerPackageName);
596
597            params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
598            params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
599            params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
600        }
601
602        // Only system components can circumvent runtime permissions when installing.
603        if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
604                && mContext.checkCallingOrSelfPermission(Manifest.permission
605                .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
606            throw new SecurityException("You need the "
607                    + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
608                    + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
609        }
610
611        if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
612                || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
613            throw new IllegalArgumentException(
614                    "New installs into ASEC containers no longer supported");
615        }
616
617        // Defensively resize giant app icons
618        if (params.appIcon != null) {
619            final ActivityManager am = (ActivityManager) mContext.getSystemService(
620                    Context.ACTIVITY_SERVICE);
621            final int iconSize = am.getLauncherLargeIconSize();
622            if ((params.appIcon.getWidth() > iconSize * 2)
623                    || (params.appIcon.getHeight() > iconSize * 2)) {
624                params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
625                        true);
626            }
627        }
628
629        switch (params.mode) {
630            case SessionParams.MODE_FULL_INSTALL:
631            case SessionParams.MODE_INHERIT_EXISTING:
632                break;
633            default:
634                throw new IllegalArgumentException("Invalid install mode: " + params.mode);
635        }
636
637        // If caller requested explicit location, sanity check it, otherwise
638        // resolve the best internal or adopted location.
639        if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
640            if (!PackageHelper.fitsOnInternal(mContext, params.sizeBytes)) {
641                throw new IOException("No suitable internal storage available");
642            }
643
644        } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
645            if (!PackageHelper.fitsOnExternal(mContext, params.sizeBytes)) {
646                throw new IOException("No suitable external storage available");
647            }
648
649        } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
650            // For now, installs to adopted media are treated as internal from
651            // an install flag point-of-view.
652            params.setInstallFlagsInternal();
653
654        } else {
655            // For now, installs to adopted media are treated as internal from
656            // an install flag point-of-view.
657            params.setInstallFlagsInternal();
658
659            // Resolve best location for install, based on combination of
660            // requested install flags, delta size, and manifest settings.
661            final long ident = Binder.clearCallingIdentity();
662            try {
663                params.volumeUuid = PackageHelper.resolveInstallVolume(mContext,
664                        params.appPackageName, params.installLocation, params.sizeBytes);
665            } finally {
666                Binder.restoreCallingIdentity(ident);
667            }
668        }
669
670        final int sessionId;
671        final PackageInstallerSession session;
672        synchronized (mSessions) {
673            // Sanity check that installer isn't going crazy
674            final int activeCount = getSessionCount(mSessions, callingUid);
675            if (activeCount >= MAX_ACTIVE_SESSIONS) {
676                throw new IllegalStateException(
677                        "Too many active sessions for UID " + callingUid);
678            }
679            final int historicalCount = getSessionCount(mHistoricalSessions, callingUid);
680            if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
681                throw new IllegalStateException(
682                        "Too many historical sessions for UID " + callingUid);
683            }
684
685            sessionId = allocateSessionIdLocked();
686        }
687
688        final long createdMillis = System.currentTimeMillis();
689        // We're staging to exactly one location
690        File stageDir = null;
691        String stageCid = null;
692        if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
693            final boolean isInstant =
694                    (params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
695            stageDir = buildStageDir(params.volumeUuid, sessionId, isInstant);
696        } else {
697            stageCid = buildExternalStageCid(sessionId);
698        }
699
700        session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
701                mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
702                params, createdMillis, stageDir, stageCid, false, false);
703
704        synchronized (mSessions) {
705            mSessions.put(sessionId, session);
706        }
707
708        mCallbacks.notifySessionCreated(session.sessionId, session.userId);
709        writeSessionsAsync();
710        return sessionId;
711    }
712
713    @Override
714    public void updateSessionAppIcon(int sessionId, Bitmap appIcon) {
715        synchronized (mSessions) {
716            final PackageInstallerSession session = mSessions.get(sessionId);
717            if (session == null || !isCallingUidOwner(session)) {
718                throw new SecurityException("Caller has no access to session " + sessionId);
719            }
720
721            // Defensively resize giant app icons
722            if (appIcon != null) {
723                final ActivityManager am = (ActivityManager) mContext.getSystemService(
724                        Context.ACTIVITY_SERVICE);
725                final int iconSize = am.getLauncherLargeIconSize();
726                if ((appIcon.getWidth() > iconSize * 2)
727                        || (appIcon.getHeight() > iconSize * 2)) {
728                    appIcon = Bitmap.createScaledBitmap(appIcon, iconSize, iconSize, true);
729                }
730            }
731
732            session.params.appIcon = appIcon;
733            session.params.appIconLastModified = -1;
734
735            mInternalCallback.onSessionBadgingChanged(session);
736        }
737    }
738
739    @Override
740    public void updateSessionAppLabel(int sessionId, String appLabel) {
741        synchronized (mSessions) {
742            final PackageInstallerSession session = mSessions.get(sessionId);
743            if (session == null || !isCallingUidOwner(session)) {
744                throw new SecurityException("Caller has no access to session " + sessionId);
745            }
746            session.params.appLabel = appLabel;
747            mInternalCallback.onSessionBadgingChanged(session);
748        }
749    }
750
751    @Override
752    public void abandonSession(int sessionId) {
753        synchronized (mSessions) {
754            final PackageInstallerSession session = mSessions.get(sessionId);
755            if (session == null || !isCallingUidOwner(session)) {
756                throw new SecurityException("Caller has no access to session " + sessionId);
757            }
758            session.abandon();
759        }
760    }
761
762    @Override
763    public IPackageInstallerSession openSession(int sessionId) {
764        try {
765            return openSessionInternal(sessionId);
766        } catch (IOException e) {
767            throw ExceptionUtils.wrap(e);
768        }
769    }
770
771    private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
772        synchronized (mSessions) {
773            final PackageInstallerSession session = mSessions.get(sessionId);
774            if (session == null || !isCallingUidOwner(session)) {
775                throw new SecurityException("Caller has no access to session " + sessionId);
776            }
777            session.open();
778            return session;
779        }
780    }
781
782    private int allocateSessionIdLocked() {
783        int n = 0;
784        int sessionId;
785        do {
786            sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
787            if (!mAllocatedSessions.get(sessionId, false)) {
788                mAllocatedSessions.put(sessionId, true);
789                return sessionId;
790            }
791        } while (n++ < 32);
792
793        throw new IllegalStateException("Failed to allocate session ID");
794    }
795
796    private File buildStagingDir(String volumeUuid, boolean isEphemeral) {
797        return Environment.getDataAppDirectory(volumeUuid);
798    }
799
800    private File buildStageDir(String volumeUuid, int sessionId, boolean isEphemeral) {
801        final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
802        return new File(stagingDir, "vmdl" + sessionId + ".tmp");
803    }
804
805    static void prepareStageDir(File stageDir) throws IOException {
806        if (stageDir.exists()) {
807            throw new IOException("Session dir already exists: " + stageDir);
808        }
809
810        try {
811            Os.mkdir(stageDir.getAbsolutePath(), 0755);
812            Os.chmod(stageDir.getAbsolutePath(), 0755);
813        } catch (ErrnoException e) {
814            // This purposefully throws if directory already exists
815            throw new IOException("Failed to prepare session dir: " + stageDir, e);
816        }
817
818        if (!SELinux.restorecon(stageDir)) {
819            throw new IOException("Failed to restorecon session dir: " + stageDir);
820        }
821    }
822
823    private String buildExternalStageCid(int sessionId) {
824        return "smdl" + sessionId + ".tmp";
825    }
826
827    static void prepareExternalStageCid(String stageCid, long sizeBytes) throws IOException {
828        if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(),
829                Process.SYSTEM_UID, true) == null) {
830            throw new IOException("Failed to create session cid: " + stageCid);
831        }
832    }
833
834    @Override
835    public SessionInfo getSessionInfo(int sessionId) {
836        synchronized (mSessions) {
837            final PackageInstallerSession session = mSessions.get(sessionId);
838            return session != null ? session.generateInfo() : null;
839        }
840    }
841
842    @Override
843    public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
844        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getAllSessions");
845
846        final List<SessionInfo> result = new ArrayList<>();
847        synchronized (mSessions) {
848            for (int i = 0; i < mSessions.size(); i++) {
849                final PackageInstallerSession session = mSessions.valueAt(i);
850                if (session.userId == userId) {
851                    result.add(session.generateInfo());
852                }
853            }
854        }
855        return new ParceledListSlice<>(result);
856    }
857
858    @Override
859    public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) {
860        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getMySessions");
861        mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
862
863        final List<SessionInfo> result = new ArrayList<>();
864        synchronized (mSessions) {
865            for (int i = 0; i < mSessions.size(); i++) {
866                final PackageInstallerSession session = mSessions.valueAt(i);
867                if (Objects.equals(session.installerPackageName, installerPackageName)
868                        && session.userId == userId) {
869                    result.add(session.generateInfo());
870                }
871            }
872        }
873        return new ParceledListSlice<>(result);
874    }
875
876    @Override
877    public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
878                IntentSender statusReceiver, int userId) throws RemoteException {
879        final int callingUid = Binder.getCallingUid();
880        mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
881        if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
882            mAppOps.checkPackage(callingUid, callerPackageName);
883        }
884
885        // Check whether the caller is device owner, in which case we do it silently.
886        DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
887                Context.DEVICE_POLICY_SERVICE);
888        boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
889                callerPackageName);
890
891        final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
892                statusReceiver, versionedPackage.getPackageName(), isDeviceOwner, userId);
893        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
894                    == PackageManager.PERMISSION_GRANTED) {
895            // Sweet, call straight through!
896            mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
897        } else if (isDeviceOwner) {
898            // Allow the DeviceOwner to silently delete packages
899            // Need to clear the calling identity to get DELETE_PACKAGES permission
900            long ident = Binder.clearCallingIdentity();
901            try {
902                mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
903            } finally {
904                Binder.restoreCallingIdentity(ident);
905            }
906        } else {
907            // Take a short detour to confirm with user
908            final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
909            intent.setData(Uri.fromParts("package", versionedPackage.getPackageName(), null));
910            intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
911            adapter.onUserActionRequired(intent);
912        }
913    }
914
915    @Override
916    public void setPermissionsResult(int sessionId, boolean accepted) {
917        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
918
919        synchronized (mSessions) {
920            PackageInstallerSession session = mSessions.get(sessionId);
921            if (session != null) {
922                session.setPermissionsResult(accepted);
923            }
924        }
925    }
926
927    @Override
928    public void registerCallback(IPackageInstallerCallback callback, int userId) {
929        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback");
930        mCallbacks.register(callback, userId);
931    }
932
933    @Override
934    public void unregisterCallback(IPackageInstallerCallback callback) {
935        mCallbacks.unregister(callback);
936    }
937
938    private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
939            int installerUid) {
940        int count = 0;
941        final int size = sessions.size();
942        for (int i = 0; i < size; i++) {
943            final PackageInstallerSession session = sessions.valueAt(i);
944            if (session.installerUid == installerUid) {
945                count++;
946            }
947        }
948        return count;
949    }
950
951    private boolean isCallingUidOwner(PackageInstallerSession session) {
952        final int callingUid = Binder.getCallingUid();
953        if (callingUid == Process.ROOT_UID) {
954            return true;
955        } else {
956            return (session != null) && (callingUid == session.installerUid);
957        }
958    }
959
960    static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
961        private final Context mContext;
962        private final IntentSender mTarget;
963        private final String mPackageName;
964        private final Notification mNotification;
965
966        public PackageDeleteObserverAdapter(Context context, IntentSender target,
967                String packageName, boolean showNotification, int userId) {
968            mContext = context;
969            mTarget = target;
970            mPackageName = packageName;
971            if (showNotification) {
972                mNotification = buildSuccessNotification(mContext,
973                        mContext.getResources().getString(R.string.package_deleted_device_owner),
974                        packageName,
975                        userId);
976            } else {
977                mNotification = null;
978            }
979        }
980
981        @Override
982        public void onUserActionRequired(Intent intent) {
983            final Intent fillIn = new Intent();
984            fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
985            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
986                    PackageInstaller.STATUS_PENDING_USER_ACTION);
987            fillIn.putExtra(Intent.EXTRA_INTENT, intent);
988            try {
989                mTarget.sendIntent(mContext, 0, fillIn, null, null);
990            } catch (SendIntentException ignored) {
991            }
992        }
993
994        @Override
995        public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
996            if (PackageManager.DELETE_SUCCEEDED == returnCode && mNotification != null) {
997                NotificationManager notificationManager = (NotificationManager)
998                        mContext.getSystemService(Context.NOTIFICATION_SERVICE);
999                notificationManager.notify(basePackageName, 0, mNotification);
1000            }
1001            final Intent fillIn = new Intent();
1002            fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
1003            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
1004                    PackageManager.deleteStatusToPublicStatus(returnCode));
1005            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
1006                    PackageManager.deleteStatusToString(returnCode, msg));
1007            fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
1008            try {
1009                mTarget.sendIntent(mContext, 0, fillIn, null, null);
1010            } catch (SendIntentException ignored) {
1011            }
1012        }
1013    }
1014
1015    static class PackageInstallObserverAdapter extends PackageInstallObserver {
1016        private final Context mContext;
1017        private final IntentSender mTarget;
1018        private final int mSessionId;
1019        private final boolean mShowNotification;
1020        private final int mUserId;
1021
1022        public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId,
1023                boolean showNotification, int userId) {
1024            mContext = context;
1025            mTarget = target;
1026            mSessionId = sessionId;
1027            mShowNotification = showNotification;
1028            mUserId = userId;
1029        }
1030
1031        @Override
1032        public void onUserActionRequired(Intent intent) {
1033            final Intent fillIn = new Intent();
1034            fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
1035            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
1036                    PackageInstaller.STATUS_PENDING_USER_ACTION);
1037            fillIn.putExtra(Intent.EXTRA_INTENT, intent);
1038            try {
1039                mTarget.sendIntent(mContext, 0, fillIn, null, null);
1040            } catch (SendIntentException ignored) {
1041            }
1042        }
1043
1044        @Override
1045        public void onPackageInstalled(String basePackageName, int returnCode, String msg,
1046                Bundle extras) {
1047            if (PackageManager.INSTALL_SUCCEEDED == returnCode && mShowNotification) {
1048                boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
1049                Notification notification = buildSuccessNotification(mContext,
1050                        mContext.getResources()
1051                                .getString(update ? R.string.package_updated_device_owner :
1052                                        R.string.package_installed_device_owner),
1053                        basePackageName,
1054                        mUserId);
1055                if (notification != null) {
1056                    NotificationManager notificationManager = (NotificationManager)
1057                            mContext.getSystemService(Context.NOTIFICATION_SERVICE);
1058                    notificationManager.notify(basePackageName, 0, notification);
1059                }
1060            }
1061            final Intent fillIn = new Intent();
1062            fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
1063            fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
1064            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
1065                    PackageManager.installStatusToPublicStatus(returnCode));
1066            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
1067                    PackageManager.installStatusToString(returnCode, msg));
1068            fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
1069            if (extras != null) {
1070                final String existing = extras.getString(
1071                        PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
1072                if (!TextUtils.isEmpty(existing)) {
1073                    fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
1074                }
1075            }
1076            try {
1077                mTarget.sendIntent(mContext, 0, fillIn, null, null);
1078            } catch (SendIntentException ignored) {
1079            }
1080        }
1081    }
1082
1083    /**
1084     * Build a notification for package installation / deletion by device owners that is shown if
1085     * the operation succeeds.
1086     */
1087    private static Notification buildSuccessNotification(Context context, String contentText,
1088            String basePackageName, int userId) {
1089        PackageInfo packageInfo = null;
1090        try {
1091            packageInfo = AppGlobals.getPackageManager().getPackageInfo(
1092                    basePackageName, 0, userId);
1093        } catch (RemoteException ignored) {
1094        }
1095        if (packageInfo == null || packageInfo.applicationInfo == null) {
1096            Slog.w(TAG, "Notification not built for package: " + basePackageName);
1097            return null;
1098        }
1099        PackageManager pm = context.getPackageManager();
1100        Bitmap packageIcon = ImageUtils.buildScaledBitmap(
1101                packageInfo.applicationInfo.loadIcon(pm),
1102                context.getResources().getDimensionPixelSize(
1103                        android.R.dimen.notification_large_icon_width),
1104                context.getResources().getDimensionPixelSize(
1105                        android.R.dimen.notification_large_icon_height));
1106        CharSequence packageLabel = packageInfo.applicationInfo.loadLabel(pm);
1107        return new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN)
1108                .setSmallIcon(R.drawable.ic_check_circle_24px)
1109                .setColor(context.getResources().getColor(
1110                        R.color.system_notification_accent_color))
1111                .setContentTitle(packageLabel)
1112                .setContentText(contentText)
1113                .setStyle(new Notification.BigTextStyle().bigText(contentText))
1114                .setLargeIcon(packageIcon)
1115                .build();
1116    }
1117
1118    public static <E> ArraySet<E> newArraySet(E... elements) {
1119        final ArraySet<E> set = new ArraySet<E>();
1120        if (elements != null) {
1121            set.ensureCapacity(elements.length);
1122            Collections.addAll(set, elements);
1123        }
1124        return set;
1125    }
1126
1127    private static class Callbacks extends Handler {
1128        private static final int MSG_SESSION_CREATED = 1;
1129        private static final int MSG_SESSION_BADGING_CHANGED = 2;
1130        private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
1131        private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
1132        private static final int MSG_SESSION_FINISHED = 5;
1133
1134        private final RemoteCallbackList<IPackageInstallerCallback>
1135                mCallbacks = new RemoteCallbackList<>();
1136
1137        public Callbacks(Looper looper) {
1138            super(looper);
1139        }
1140
1141        public void register(IPackageInstallerCallback callback, int userId) {
1142            mCallbacks.register(callback, new UserHandle(userId));
1143        }
1144
1145        public void unregister(IPackageInstallerCallback callback) {
1146            mCallbacks.unregister(callback);
1147        }
1148
1149        @Override
1150        public void handleMessage(Message msg) {
1151            final int userId = msg.arg2;
1152            final int n = mCallbacks.beginBroadcast();
1153            for (int i = 0; i < n; i++) {
1154                final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
1155                final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
1156                // TODO: dispatch notifications for slave profiles
1157                if (userId == user.getIdentifier()) {
1158                    try {
1159                        invokeCallback(callback, msg);
1160                    } catch (RemoteException ignored) {
1161                    }
1162                }
1163            }
1164            mCallbacks.finishBroadcast();
1165        }
1166
1167        private void invokeCallback(IPackageInstallerCallback callback, Message msg)
1168                throws RemoteException {
1169            final int sessionId = msg.arg1;
1170            switch (msg.what) {
1171                case MSG_SESSION_CREATED:
1172                    callback.onSessionCreated(sessionId);
1173                    break;
1174                case MSG_SESSION_BADGING_CHANGED:
1175                    callback.onSessionBadgingChanged(sessionId);
1176                    break;
1177                case MSG_SESSION_ACTIVE_CHANGED:
1178                    callback.onSessionActiveChanged(sessionId, (boolean) msg.obj);
1179                    break;
1180                case MSG_SESSION_PROGRESS_CHANGED:
1181                    callback.onSessionProgressChanged(sessionId, (float) msg.obj);
1182                    break;
1183                case MSG_SESSION_FINISHED:
1184                    callback.onSessionFinished(sessionId, (boolean) msg.obj);
1185                    break;
1186            }
1187        }
1188
1189        private void notifySessionCreated(int sessionId, int userId) {
1190            obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget();
1191        }
1192
1193        private void notifySessionBadgingChanged(int sessionId, int userId) {
1194            obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget();
1195        }
1196
1197        private void notifySessionActiveChanged(int sessionId, int userId, boolean active) {
1198            obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget();
1199        }
1200
1201        private void notifySessionProgressChanged(int sessionId, int userId, float progress) {
1202            obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget();
1203        }
1204
1205        public void notifySessionFinished(int sessionId, int userId, boolean success) {
1206            obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget();
1207        }
1208    }
1209
1210    void dump(IndentingPrintWriter pw) {
1211        synchronized (mSessions) {
1212            pw.println("Active install sessions:");
1213            pw.increaseIndent();
1214            int N = mSessions.size();
1215            for (int i = 0; i < N; i++) {
1216                final PackageInstallerSession session = mSessions.valueAt(i);
1217                session.dump(pw);
1218                pw.println();
1219            }
1220            pw.println();
1221            pw.decreaseIndent();
1222
1223            pw.println("Historical install sessions:");
1224            pw.increaseIndent();
1225            N = mHistoricalSessions.size();
1226            for (int i = 0; i < N; i++) {
1227                final PackageInstallerSession session = mHistoricalSessions.valueAt(i);
1228                session.dump(pw);
1229                pw.println();
1230            }
1231            pw.println();
1232            pw.decreaseIndent();
1233
1234            pw.println("Legacy install sessions:");
1235            pw.increaseIndent();
1236            pw.println(mLegacySessions.toString());
1237            pw.decreaseIndent();
1238        }
1239    }
1240
1241    class InternalCallback {
1242        public void onSessionBadgingChanged(PackageInstallerSession session) {
1243            mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
1244            writeSessionsAsync();
1245        }
1246
1247        public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
1248            mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active);
1249        }
1250
1251        public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
1252            mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
1253        }
1254
1255        public void onSessionFinished(final PackageInstallerSession session, boolean success) {
1256            mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
1257
1258            mInstallHandler.post(new Runnable() {
1259                @Override
1260                public void run() {
1261                    synchronized (mSessions) {
1262                        mSessions.remove(session.sessionId);
1263                        mHistoricalSessions.put(session.sessionId, session);
1264
1265                        final File appIconFile = buildAppIconFile(session.sessionId);
1266                        if (appIconFile.exists()) {
1267                            appIconFile.delete();
1268                        }
1269
1270                        writeSessionsLocked();
1271                    }
1272                }
1273            });
1274        }
1275
1276        public void onSessionPrepared(PackageInstallerSession session) {
1277            // We prepared the destination to write into; we want to persist
1278            // this, but it's not critical enough to block for.
1279            writeSessionsAsync();
1280        }
1281
1282        public void onSessionSealedBlocking(PackageInstallerSession session) {
1283            // It's very important that we block until we've recorded the
1284            // session as being sealed, since we never want to allow mutation
1285            // after sealing.
1286            synchronized (mSessions) {
1287                writeSessionsLocked();
1288            }
1289        }
1290    }
1291}
1292