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