PackageInstallerService.java revision 8d05172112436a81bed6e4a0810f8914509d8a4d
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.writeBitmapAttribute;
26import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
27import static com.android.internal.util.XmlUtils.writeIntAttribute;
28import static com.android.internal.util.XmlUtils.writeLongAttribute;
29import static com.android.internal.util.XmlUtils.writeStringAttribute;
30import static com.android.internal.util.XmlUtils.writeUriAttribute;
31import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
32import static org.xmlpull.v1.XmlPullParser.START_TAG;
33
34import android.app.ActivityManager;
35import android.app.AppOpsManager;
36import android.app.PackageDeleteObserver;
37import android.app.PackageInstallObserver;
38import android.content.Context;
39import android.content.Intent;
40import android.content.IntentSender;
41import android.content.IntentSender.SendIntentException;
42import android.content.pm.IPackageInstaller;
43import android.content.pm.IPackageInstallerCallback;
44import android.content.pm.IPackageInstallerSession;
45import android.content.pm.PackageInstaller;
46import android.content.pm.PackageInstaller.SessionInfo;
47import android.content.pm.PackageInstaller.SessionParams;
48import android.content.pm.PackageManager;
49import android.graphics.Bitmap;
50import android.net.Uri;
51import android.os.Binder;
52import android.os.Bundle;
53import android.os.Environment;
54import android.os.FileUtils;
55import android.os.Handler;
56import android.os.HandlerThread;
57import android.os.Looper;
58import android.os.Message;
59import android.os.Process;
60import android.os.RemoteCallbackList;
61import android.os.RemoteException;
62import android.os.SELinux;
63import android.os.UserHandle;
64import android.os.UserManager;
65import android.system.ErrnoException;
66import android.system.Os;
67import android.text.TextUtils;
68import android.text.format.DateUtils;
69import android.util.ArraySet;
70import android.util.AtomicFile;
71import android.util.ExceptionUtils;
72import android.util.Log;
73import android.util.Slog;
74import android.util.SparseArray;
75import android.util.SparseBooleanArray;
76import android.util.Xml;
77
78import com.android.internal.annotations.GuardedBy;
79import com.android.internal.content.PackageHelper;
80import com.android.internal.util.FastXmlSerializer;
81import com.android.internal.util.IndentingPrintWriter;
82import com.android.server.IoThread;
83import com.google.android.collect.Sets;
84
85import libcore.io.IoUtils;
86
87import org.xmlpull.v1.XmlPullParser;
88import org.xmlpull.v1.XmlPullParserException;
89import org.xmlpull.v1.XmlSerializer;
90
91import java.io.File;
92import java.io.FileInputStream;
93import java.io.FileNotFoundException;
94import java.io.FileOutputStream;
95import java.io.FilenameFilter;
96import java.io.IOException;
97import java.security.SecureRandom;
98import java.util.ArrayList;
99import java.util.List;
100import java.util.Objects;
101import java.util.Random;
102
103public class PackageInstallerService extends IPackageInstaller.Stub {
104    private static final String TAG = "PackageInstaller";
105    private static final boolean LOGD = false;
106
107    // TODO: remove outstanding sessions when installer package goes away
108    // TODO: notify listeners in other users when package has been installed there
109    // TODO: purge expired sessions periodically in addition to at reboot
110
111    /** XML constants used in {@link #mSessionsFile} */
112    private static final String TAG_SESSIONS = "sessions";
113    private static final String TAG_SESSION = "session";
114    private static final String ATTR_SESSION_ID = "sessionId";
115    private static final String ATTR_USER_ID = "userId";
116    private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
117    private static final String ATTR_INSTALLER_UID = "installerUid";
118    private static final String ATTR_CREATED_MILLIS = "createdMillis";
119    private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
120    private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
121    private static final String ATTR_PREPARED = "prepared";
122    private static final String ATTR_SEALED = "sealed";
123    private static final String ATTR_MODE = "mode";
124    private static final String ATTR_INSTALL_FLAGS = "installFlags";
125    private static final String ATTR_INSTALL_LOCATION = "installLocation";
126    private static final String ATTR_SIZE_BYTES = "sizeBytes";
127    private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
128    private static final String ATTR_APP_ICON = "appIcon";
129    private static final String ATTR_APP_LABEL = "appLabel";
130    private static final String ATTR_ORIGINATING_URI = "originatingUri";
131    private static final String ATTR_REFERRER_URI = "referrerUri";
132    private static final String ATTR_ABI_OVERRIDE = "abiOverride";
133
134    /** Automatically destroy sessions older than this */
135    private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
136    /** Upper bound on number of active sessions for a UID */
137    private static final long MAX_ACTIVE_SESSIONS = 1024;
138    /** Upper bound on number of historical sessions for a UID */
139    private static final long MAX_HISTORICAL_SESSIONS = 1048576;
140
141    private final Context mContext;
142    private final PackageManagerService mPm;
143    private final AppOpsManager mAppOps;
144
145    private final File mStagingDir;
146    private final HandlerThread mInstallThread;
147    private final Handler mInstallHandler;
148
149    private final Callbacks mCallbacks;
150
151    /**
152     * File storing persisted {@link #mSessions}.
153     */
154    private final AtomicFile mSessionsFile;
155
156    private final InternalCallback mInternalCallback = new InternalCallback();
157
158    /**
159     * Used for generating session IDs. Since this is created at boot time,
160     * normal random might be predictable.
161     */
162    private final Random mRandom = new SecureRandom();
163
164    @GuardedBy("mSessions")
165    private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
166
167    /** Historical sessions kept around for debugging purposes */
168    @GuardedBy("mSessions")
169    private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>();
170
171    /** Sessions allocated to legacy users */
172    @GuardedBy("mSessions")
173    private final SparseBooleanArray mLegacySessions = new SparseBooleanArray();
174
175    private static final FilenameFilter sStageFilter = new FilenameFilter() {
176        @Override
177        public boolean accept(File dir, String name) {
178            return isStageName(name);
179        }
180    };
181
182    public PackageInstallerService(Context context, PackageManagerService pm, File stagingDir) {
183        mContext = context;
184        mPm = pm;
185        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
186
187        mStagingDir = stagingDir;
188
189        mInstallThread = new HandlerThread(TAG);
190        mInstallThread.start();
191
192        mInstallHandler = new Handler(mInstallThread.getLooper());
193
194        mCallbacks = new Callbacks(mInstallThread.getLooper());
195
196        mSessionsFile = new AtomicFile(
197                new File(Environment.getSystemSecureDirectory(), "install_sessions.xml"));
198
199        synchronized (mSessions) {
200            readSessionsLocked();
201
202            final ArraySet<File> unclaimed = Sets.newArraySet(mStagingDir.listFiles(sStageFilter));
203
204            // Ignore stages claimed by active sessions
205            for (int i = 0; i < mSessions.size(); i++) {
206                final PackageInstallerSession session = mSessions.valueAt(i);
207                unclaimed.remove(session.stageDir);
208            }
209
210            // Clean up orphaned staging directories
211            for (File stage : unclaimed) {
212                Slog.w(TAG, "Deleting orphan stage " + stage);
213                if (stage.isDirectory()) {
214                    FileUtils.deleteContents(stage);
215                }
216                stage.delete();
217            }
218        }
219    }
220
221    public void onSecureContainersAvailable() {
222        synchronized (mSessions) {
223            final ArraySet<String> unclaimed = new ArraySet<>();
224            for (String cid : PackageHelper.getSecureContainerList()) {
225                if (isStageName(cid)) {
226                    unclaimed.add(cid);
227                }
228            }
229
230            // Ignore stages claimed by active sessions
231            for (int i = 0; i < mSessions.size(); i++) {
232                final PackageInstallerSession session = mSessions.valueAt(i);
233                final String cid = session.stageCid;
234
235                if (unclaimed.remove(cid)) {
236                    // Claimed by active session, mount it
237                    PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(),
238                            Process.SYSTEM_UID);
239                }
240            }
241
242            // Clean up orphaned staging containers
243            for (String cid : unclaimed) {
244                Slog.w(TAG, "Deleting orphan container " + cid);
245                PackageHelper.destroySdDir(cid);
246            }
247        }
248    }
249
250    public static boolean isStageName(String name) {
251        final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
252        final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
253        final boolean isLegacyContainer = name.startsWith("smdl2tmp");
254        return isFile || isContainer || isLegacyContainer;
255    }
256
257    @Deprecated
258    public File allocateInternalStageDirLegacy() throws IOException {
259        synchronized (mSessions) {
260            try {
261                final int sessionId = allocateSessionIdLocked();
262                mLegacySessions.put(sessionId, true);
263                final File stageDir = buildInternalStageDir(sessionId);
264                prepareInternalStageDir(stageDir);
265                return stageDir;
266            } catch (IllegalStateException e) {
267                throw new IOException(e);
268            }
269        }
270    }
271
272    @Deprecated
273    public String allocateExternalStageCidLegacy() {
274        synchronized (mSessions) {
275            final int sessionId = allocateSessionIdLocked();
276            mLegacySessions.put(sessionId, true);
277            return "smdl" + sessionId + ".tmp";
278        }
279    }
280
281    private void readSessionsLocked() {
282        if (LOGD) Slog.v(TAG, "readSessionsLocked()");
283
284        mSessions.clear();
285
286        FileInputStream fis = null;
287        try {
288            fis = mSessionsFile.openRead();
289            final XmlPullParser in = Xml.newPullParser();
290            in.setInput(fis, null);
291
292            int type;
293            while ((type = in.next()) != END_DOCUMENT) {
294                if (type == START_TAG) {
295                    final String tag = in.getName();
296                    if (TAG_SESSION.equals(tag)) {
297                        final PackageInstallerSession session = readSessionLocked(in);
298                        final long age = System.currentTimeMillis() - session.createdMillis;
299
300                        final boolean valid;
301                        if (age >= MAX_AGE_MILLIS) {
302                            Slog.w(TAG, "Abandoning old session first created at "
303                                    + session.createdMillis);
304                            valid = false;
305                        } else if (session.stageDir != null
306                                && !session.stageDir.exists()) {
307                            Slog.w(TAG, "Abandoning internal session with missing stage "
308                                    + session.stageDir);
309                            valid = false;
310                        } else {
311                            valid = true;
312                        }
313
314                        if (valid) {
315                            mSessions.put(session.sessionId, session);
316                        } else {
317                            // Since this is early during boot we don't send
318                            // any observer events about the session, but we
319                            // keep details around for dumpsys.
320                            mHistoricalSessions.put(session.sessionId, session);
321                        }
322                    }
323                }
324            }
325        } catch (FileNotFoundException e) {
326            // Missing sessions are okay, probably first boot
327        } catch (IOException e) {
328            Slog.wtf(TAG, "Failed reading install sessions", e);
329        } catch (XmlPullParserException e) {
330            Slog.wtf(TAG, "Failed reading install sessions", e);
331        } finally {
332            IoUtils.closeQuietly(fis);
333        }
334    }
335
336    private PackageInstallerSession readSessionLocked(XmlPullParser in) throws IOException {
337        final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
338        final int userId = readIntAttribute(in, ATTR_USER_ID);
339        final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
340        final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID,
341                mPm.getPackageUid(installerPackageName, userId));
342        final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
343        final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
344        final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
345        final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
346        final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
347        final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
348
349        final SessionParams params = new SessionParams(
350                SessionParams.MODE_INVALID);
351        params.mode = readIntAttribute(in, ATTR_MODE);
352        params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
353        params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
354        params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
355        params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
356        params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
357        params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
358        params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
359        params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
360        params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
361
362        return new PackageInstallerSession(mInternalCallback, mContext, mPm,
363                mInstallThread.getLooper(), sessionId, userId, installerPackageName, installerUid,
364                params, createdMillis, stageDir, stageCid, prepared, sealed);
365    }
366
367    private void writeSessionsLocked() {
368        if (LOGD) Slog.v(TAG, "writeSessionsLocked()");
369
370        FileOutputStream fos = null;
371        try {
372            fos = mSessionsFile.startWrite();
373
374            XmlSerializer out = new FastXmlSerializer();
375            out.setOutput(fos, "utf-8");
376            out.startDocument(null, true);
377            out.startTag(null, TAG_SESSIONS);
378            final int size = mSessions.size();
379            for (int i = 0; i < size; i++) {
380                final PackageInstallerSession session = mSessions.valueAt(i);
381                writeSessionLocked(out, session);
382            }
383            out.endTag(null, TAG_SESSIONS);
384            out.endDocument();
385
386            mSessionsFile.finishWrite(fos);
387        } catch (IOException e) {
388            if (fos != null) {
389                mSessionsFile.failWrite(fos);
390            }
391        }
392    }
393
394    private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session)
395            throws IOException {
396        final SessionParams params = session.params;
397
398        out.startTag(null, TAG_SESSION);
399
400        writeIntAttribute(out, ATTR_SESSION_ID, session.sessionId);
401        writeIntAttribute(out, ATTR_USER_ID, session.userId);
402        writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
403                session.installerPackageName);
404        writeIntAttribute(out, ATTR_INSTALLER_UID, session.installerUid);
405        writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis);
406        if (session.stageDir != null) {
407            writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
408                    session.stageDir.getAbsolutePath());
409        }
410        if (session.stageCid != null) {
411            writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid);
412        }
413        writeBooleanAttribute(out, ATTR_PREPARED, session.isPrepared());
414        writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());
415
416        writeIntAttribute(out, ATTR_MODE, params.mode);
417        writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
418        writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
419        writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
420        writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
421        writeBitmapAttribute(out, ATTR_APP_ICON, params.appIcon);
422        writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
423        writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
424        writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
425        writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
426
427        out.endTag(null, TAG_SESSION);
428    }
429
430    private void writeSessionsAsync() {
431        IoThread.getHandler().post(new Runnable() {
432            @Override
433            public void run() {
434                synchronized (mSessions) {
435                    writeSessionsLocked();
436                }
437            }
438        });
439    }
440
441    @Override
442    public int createSession(SessionParams params, String installerPackageName, int userId) {
443        try {
444            return createSessionInternal(params, installerPackageName, userId);
445        } catch (IOException e) {
446            throw ExceptionUtils.wrap(e);
447        }
448    }
449
450    private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
451            throws IOException {
452        final int callingUid = Binder.getCallingUid();
453        mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");
454
455        if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
456            throw new SecurityException("User restriction prevents installing");
457        }
458
459        if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
460            params.installFlags |= PackageManager.INSTALL_FROM_ADB;
461
462        } else {
463            mAppOps.checkPackage(callingUid, installerPackageName);
464
465            params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
466            params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
467            params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
468        }
469
470        // Defensively resize giant app icons
471        if (params.appIcon != null) {
472            final ActivityManager am = (ActivityManager) mContext.getSystemService(
473                    Context.ACTIVITY_SERVICE);
474            final int iconSize = am.getLauncherLargeIconSize();
475            if ((params.appIcon.getWidth() > iconSize * 2)
476                    || (params.appIcon.getHeight() > iconSize * 2)) {
477                params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
478                        true);
479            }
480        }
481
482        if (params.mode == SessionParams.MODE_FULL_INSTALL
483                || params.mode == SessionParams.MODE_INHERIT_EXISTING) {
484            // Resolve best location for install, based on combination of
485            // requested install flags, delta size, and manifest settings.
486            final long ident = Binder.clearCallingIdentity();
487            try {
488                final int resolved = PackageHelper.resolveInstallLocation(mContext,
489                        params.appPackageName, params.installLocation, params.sizeBytes,
490                        params.installFlags);
491
492                if (resolved == PackageHelper.RECOMMEND_INSTALL_INTERNAL) {
493                    params.setInstallFlagsInternal();
494                } else if (resolved == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
495                    params.setInstallFlagsExternal();
496                } else {
497                    throw new IOException("No storage with enough free space; res=" + resolved);
498                }
499            } finally {
500                Binder.restoreCallingIdentity(ident);
501            }
502        } else {
503            throw new IllegalArgumentException("Invalid install mode: " + params.mode);
504        }
505
506        final int sessionId;
507        final PackageInstallerSession session;
508        synchronized (mSessions) {
509            // Sanity check that installer isn't going crazy
510            final int activeCount = getSessionCount(mSessions, callingUid);
511            if (activeCount >= MAX_ACTIVE_SESSIONS) {
512                throw new IllegalStateException(
513                        "Too many active sessions for UID " + callingUid);
514            }
515            final int historicalCount = getSessionCount(mHistoricalSessions, callingUid);
516            if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
517                throw new IllegalStateException(
518                        "Too many historical sessions for UID " + callingUid);
519            }
520
521            final long createdMillis = System.currentTimeMillis();
522            sessionId = allocateSessionIdLocked();
523
524            // We're staging to exactly one location
525            File stageDir = null;
526            String stageCid = null;
527            if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
528                stageDir = buildInternalStageDir(sessionId);
529            } else {
530                stageCid = buildExternalStageCid(sessionId);
531            }
532
533            session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
534                    mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
535                    params, createdMillis, stageDir, stageCid, false, false);
536            mSessions.put(sessionId, session);
537        }
538
539        mCallbacks.notifySessionCreated(session.sessionId, session.userId);
540        writeSessionsAsync();
541        return sessionId;
542    }
543
544    @Override
545    public void updateSessionAppIcon(int sessionId, Bitmap appIcon) {
546        synchronized (mSessions) {
547            final PackageInstallerSession session = mSessions.get(sessionId);
548            if (session == null || !isCallingUidOwner(session)) {
549                throw new SecurityException("Caller has no access to session " + sessionId);
550            }
551            session.params.appIcon = appIcon;
552            mInternalCallback.onSessionBadgingChanged(session);
553        }
554    }
555
556    @Override
557    public void updateSessionAppLabel(int sessionId, String appLabel) {
558        synchronized (mSessions) {
559            final PackageInstallerSession session = mSessions.get(sessionId);
560            if (session == null || !isCallingUidOwner(session)) {
561                throw new SecurityException("Caller has no access to session " + sessionId);
562            }
563            session.params.appLabel = appLabel;
564            mInternalCallback.onSessionBadgingChanged(session);
565        }
566    }
567
568    @Override
569    public void abandonSession(int sessionId) {
570        synchronized (mSessions) {
571            final PackageInstallerSession session = mSessions.get(sessionId);
572            if (session == null || !isCallingUidOwner(session)) {
573                throw new SecurityException("Caller has no access to session " + sessionId);
574            }
575            session.abandon();
576        }
577    }
578
579    @Override
580    public IPackageInstallerSession openSession(int sessionId) {
581        try {
582            return openSessionInternal(sessionId);
583        } catch (IOException e) {
584            throw ExceptionUtils.wrap(e);
585        }
586    }
587
588    private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
589        synchronized (mSessions) {
590            final PackageInstallerSession session = mSessions.get(sessionId);
591            if (session == null || !isCallingUidOwner(session)) {
592                throw new SecurityException("Caller has no access to session " + sessionId);
593            }
594            session.open();
595            return session;
596        }
597    }
598
599    private int allocateSessionIdLocked() {
600        int n = 0;
601        int sessionId;
602        do {
603            sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
604            if (mSessions.get(sessionId) == null && mHistoricalSessions.get(sessionId) == null
605                    && !mLegacySessions.get(sessionId, false)) {
606                return sessionId;
607            }
608        } while (n++ < 32);
609
610        throw new IllegalStateException("Failed to allocate session ID");
611    }
612
613    private File buildInternalStageDir(int sessionId) {
614        return new File(mStagingDir, "vmdl" + sessionId + ".tmp");
615    }
616
617    static void prepareInternalStageDir(File stageDir) throws IOException {
618        if (stageDir.exists()) {
619            throw new IOException("Session dir already exists: " + stageDir);
620        }
621
622        try {
623            Os.mkdir(stageDir.getAbsolutePath(), 0755);
624            Os.chmod(stageDir.getAbsolutePath(), 0755);
625        } catch (ErrnoException e) {
626            // This purposefully throws if directory already exists
627            throw new IOException("Failed to prepare session dir: " + stageDir, e);
628        }
629
630        if (!SELinux.restorecon(stageDir)) {
631            throw new IOException("Failed to restorecon session dir: " + stageDir);
632        }
633    }
634
635    private String buildExternalStageCid(int sessionId) {
636        return "smdl" + sessionId + ".tmp";
637    }
638
639    static void prepareExternalStageCid(String stageCid, long sizeBytes) throws IOException {
640        if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(),
641                Process.SYSTEM_UID, true) == null) {
642            throw new IOException("Failed to create session cid: " + stageCid);
643        }
644    }
645
646    @Override
647    public SessionInfo getSessionInfo(int sessionId) {
648        synchronized (mSessions) {
649            final PackageInstallerSession session = mSessions.get(sessionId);
650            return session != null ? session.generateInfo() : null;
651        }
652    }
653
654    @Override
655    public List<SessionInfo> getAllSessions(int userId) {
656        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getAllSessions");
657
658        final List<SessionInfo> result = new ArrayList<>();
659        synchronized (mSessions) {
660            for (int i = 0; i < mSessions.size(); i++) {
661                final PackageInstallerSession session = mSessions.valueAt(i);
662                if (session.userId == userId) {
663                    result.add(session.generateInfo());
664                }
665            }
666        }
667        return result;
668    }
669
670    @Override
671    public List<SessionInfo> getMySessions(String installerPackageName, int userId) {
672        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getMySessions");
673        mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
674
675        final List<SessionInfo> result = new ArrayList<>();
676        synchronized (mSessions) {
677            for (int i = 0; i < mSessions.size(); i++) {
678                final PackageInstallerSession session = mSessions.valueAt(i);
679                if (Objects.equals(session.installerPackageName, installerPackageName)
680                        && session.userId == userId) {
681                    result.add(session.generateInfo());
682                }
683            }
684        }
685        return result;
686    }
687
688    @Override
689    public void uninstall(String packageName, int flags, IntentSender statusReceiver, int userId) {
690        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, true, "uninstall");
691
692        final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
693                statusReceiver, packageName);
694        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
695                == PackageManager.PERMISSION_GRANTED) {
696            // Sweet, call straight through!
697            mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
698
699        } else {
700            // Take a short detour to confirm with user
701            final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
702            intent.setData(Uri.fromParts("package", packageName, null));
703            intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
704            adapter.onUserActionRequired(intent);
705        }
706    }
707
708    @Override
709    public void setPermissionsResult(int sessionId, boolean accepted) {
710        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
711
712        synchronized (mSessions) {
713            mSessions.get(sessionId).setPermissionsResult(accepted);
714        }
715    }
716
717    @Override
718    public void registerCallback(IPackageInstallerCallback callback, int userId) {
719        mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback");
720        mCallbacks.register(callback, userId);
721    }
722
723    @Override
724    public void unregisterCallback(IPackageInstallerCallback callback) {
725        mCallbacks.unregister(callback);
726    }
727
728    private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
729            int installerUid) {
730        int count = 0;
731        final int size = sessions.size();
732        for (int i = 0; i < size; i++) {
733            final PackageInstallerSession session = sessions.valueAt(i);
734            if (session.installerUid == installerUid) {
735                count++;
736            }
737        }
738        return count;
739    }
740
741    private boolean isCallingUidOwner(PackageInstallerSession session) {
742        final int callingUid = Binder.getCallingUid();
743        if (callingUid == Process.ROOT_UID) {
744            return true;
745        } else {
746            return (session != null) && (callingUid == session.installerUid);
747        }
748    }
749
750    static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
751        private final Context mContext;
752        private final IntentSender mTarget;
753        private final String mPackageName;
754
755        public PackageDeleteObserverAdapter(Context context, IntentSender target,
756                String packageName) {
757            mContext = context;
758            mTarget = target;
759            mPackageName = packageName;
760        }
761
762        @Override
763        public void onUserActionRequired(Intent intent) {
764            final Intent fillIn = new Intent();
765            fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
766            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
767                    PackageInstaller.STATUS_PENDING_USER_ACTION);
768            fillIn.putExtra(Intent.EXTRA_INTENT, intent);
769            try {
770                mTarget.sendIntent(mContext, 0, fillIn, null, null);
771            } catch (SendIntentException ignored) {
772            }
773        }
774
775        @Override
776        public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
777            final Intent fillIn = new Intent();
778            fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
779            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
780                    PackageManager.deleteStatusToPublicStatus(returnCode));
781            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
782                    PackageManager.deleteStatusToString(returnCode, msg));
783            fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
784            try {
785                mTarget.sendIntent(mContext, 0, fillIn, null, null);
786            } catch (SendIntentException ignored) {
787            }
788        }
789    }
790
791    static class PackageInstallObserverAdapter extends PackageInstallObserver {
792        private final Context mContext;
793        private final IntentSender mTarget;
794        private final int mSessionId;
795
796        public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId) {
797            mContext = context;
798            mTarget = target;
799            mSessionId = sessionId;
800        }
801
802        @Override
803        public void onUserActionRequired(Intent intent) {
804            final Intent fillIn = new Intent();
805            fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
806            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
807                    PackageInstaller.STATUS_PENDING_USER_ACTION);
808            fillIn.putExtra(Intent.EXTRA_INTENT, intent);
809            try {
810                mTarget.sendIntent(mContext, 0, fillIn, null, null);
811            } catch (SendIntentException ignored) {
812            }
813        }
814
815        @Override
816        public void onPackageInstalled(String basePackageName, int returnCode, String msg,
817                Bundle extras) {
818            final Intent fillIn = new Intent();
819            fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
820            fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
821                    PackageManager.installStatusToPublicStatus(returnCode));
822            fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
823                    PackageManager.installStatusToString(returnCode, msg));
824            fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
825            if (extras != null) {
826                final String existing = extras.getString(
827                        PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
828                if (!TextUtils.isEmpty(existing)) {
829                    fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
830                }
831            }
832            try {
833                mTarget.sendIntent(mContext, 0, fillIn, null, null);
834            } catch (SendIntentException ignored) {
835            }
836        }
837    }
838
839    private static class Callbacks extends Handler {
840        private static final int MSG_SESSION_CREATED = 1;
841        private static final int MSG_SESSION_BADGING_CHANGED = 2;
842        private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
843        private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
844        private static final int MSG_SESSION_FINISHED = 5;
845
846        private final RemoteCallbackList<IPackageInstallerCallback>
847                mCallbacks = new RemoteCallbackList<>();
848
849        public Callbacks(Looper looper) {
850            super(looper);
851        }
852
853        public void register(IPackageInstallerCallback callback, int userId) {
854            mCallbacks.register(callback, new UserHandle(userId));
855        }
856
857        public void unregister(IPackageInstallerCallback callback) {
858            mCallbacks.unregister(callback);
859        }
860
861        @Override
862        public void handleMessage(Message msg) {
863            final int userId = msg.arg2;
864            final int n = mCallbacks.beginBroadcast();
865            for (int i = 0; i < n; i++) {
866                final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
867                final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
868                // TODO: dispatch notifications for slave profiles
869                if (userId == user.getIdentifier()) {
870                    try {
871                        invokeCallback(callback, msg);
872                    } catch (RemoteException ignored) {
873                    }
874                }
875            }
876            mCallbacks.finishBroadcast();
877        }
878
879        private void invokeCallback(IPackageInstallerCallback callback, Message msg)
880                throws RemoteException {
881            final int sessionId = msg.arg1;
882            switch (msg.what) {
883                case MSG_SESSION_CREATED:
884                    callback.onSessionCreated(sessionId);
885                    break;
886                case MSG_SESSION_BADGING_CHANGED:
887                    callback.onSessionBadgingChanged(sessionId);
888                    break;
889                case MSG_SESSION_ACTIVE_CHANGED:
890                    callback.onSessionActiveChanged(sessionId, (boolean) msg.obj);
891                    break;
892                case MSG_SESSION_PROGRESS_CHANGED:
893                    callback.onSessionProgressChanged(sessionId, (float) msg.obj);
894                    break;
895                case MSG_SESSION_FINISHED:
896                    callback.onSessionFinished(sessionId, (boolean) msg.obj);
897                    break;
898            }
899        }
900
901        private void notifySessionCreated(int sessionId, int userId) {
902            obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget();
903        }
904
905        private void notifySessionBadgingChanged(int sessionId, int userId) {
906            obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget();
907        }
908
909        private void notifySessionActiveChanged(int sessionId, int userId, boolean active) {
910            obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget();
911        }
912
913        private void notifySessionProgressChanged(int sessionId, int userId, float progress) {
914            obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget();
915        }
916
917        public void notifySessionFinished(int sessionId, int userId, boolean success) {
918            obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget();
919        }
920    }
921
922    void dump(IndentingPrintWriter pw) {
923        synchronized (mSessions) {
924            pw.println("Active install sessions:");
925            pw.increaseIndent();
926            int N = mSessions.size();
927            for (int i = 0; i < N; i++) {
928                final PackageInstallerSession session = mSessions.valueAt(i);
929                session.dump(pw);
930                pw.println();
931            }
932            pw.println();
933            pw.decreaseIndent();
934
935            pw.println("Historical install sessions:");
936            pw.increaseIndent();
937            N = mHistoricalSessions.size();
938            for (int i = 0; i < N; i++) {
939                final PackageInstallerSession session = mHistoricalSessions.valueAt(i);
940                session.dump(pw);
941                pw.println();
942            }
943            pw.println();
944            pw.decreaseIndent();
945
946            pw.println("Legacy install sessions:");
947            pw.increaseIndent();
948            pw.println(mLegacySessions.toString());
949            pw.decreaseIndent();
950        }
951    }
952
953    class InternalCallback {
954        public void onSessionBadgingChanged(PackageInstallerSession session) {
955            mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
956            writeSessionsAsync();
957        }
958
959        public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
960            mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active);
961        }
962
963        public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
964            mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
965        }
966
967        public void onSessionFinished(final PackageInstallerSession session, boolean success) {
968            mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
969
970            mInstallHandler.post(new Runnable() {
971                @Override
972                public void run() {
973                    synchronized (mSessions) {
974                        mSessions.remove(session.sessionId);
975                        mHistoricalSessions.put(session.sessionId, session);
976                        writeSessionsLocked();
977                    }
978                }
979            });
980        }
981
982        public void onSessionPrepared(PackageInstallerSession session) {
983            // We prepared the destination to write into; we want to persist
984            // this, but it's not critical enough to block for.
985            writeSessionsAsync();
986        }
987
988        public void onSessionSealedBlocking(PackageInstallerSession session) {
989            // It's very important that we block until we've recorded the
990            // session as being sealed, since we never want to allow mutation
991            // after sealing.
992            synchronized (mSessions) {
993                writeSessionsLocked();
994            }
995        }
996    }
997}
998