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