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