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