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