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