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