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