OSDN Git Service

Only allow INSTALL_ALLOW_TEST from shell or root
[android-x86/frameworks-base.git] / services / core / java / com / android / server / pm / PackageInstallerService.java
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.DevicePolicyManager;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentSender;
34 import android.content.IntentSender.SendIntentException;
35 import android.content.pm.IPackageInstaller;
36 import android.content.pm.IPackageInstallerCallback;
37 import android.content.pm.IPackageInstallerSession;
38 import android.content.pm.PackageInfo;
39 import android.content.pm.PackageInstaller;
40 import android.content.pm.PackageInstaller.SessionInfo;
41 import android.content.pm.PackageInstaller.SessionParams;
42 import android.content.pm.PackageManager;
43 import android.content.pm.ParceledListSlice;
44 import android.content.pm.VersionedPackage;
45 import android.graphics.Bitmap;
46 import android.net.Uri;
47 import android.os.Binder;
48 import android.os.Bundle;
49 import android.os.Environment;
50 import android.os.Handler;
51 import android.os.HandlerThread;
52 import android.os.Looper;
53 import android.os.Message;
54 import android.os.Process;
55 import android.os.RemoteCallbackList;
56 import android.os.RemoteException;
57 import android.os.SELinux;
58 import android.os.UserHandle;
59 import android.os.UserManager;
60 import android.os.storage.StorageManager;
61 import android.system.ErrnoException;
62 import android.system.Os;
63 import android.text.TextUtils;
64 import android.text.format.DateUtils;
65 import android.util.ArraySet;
66 import android.util.AtomicFile;
67 import android.util.ExceptionUtils;
68 import android.util.Slog;
69 import android.util.SparseArray;
70 import android.util.SparseBooleanArray;
71 import android.util.SparseIntArray;
72 import android.util.Xml;
73
74 import com.android.internal.R;
75 import com.android.internal.annotations.GuardedBy;
76 import com.android.internal.content.PackageHelper;
77 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
78 import com.android.internal.notification.SystemNotificationChannels;
79 import com.android.internal.util.FastXmlSerializer;
80 import com.android.internal.util.ImageUtils;
81 import com.android.internal.util.IndentingPrintWriter;
82 import com.android.server.IoThread;
83
84 import libcore.io.IoUtils;
85
86 import org.xmlpull.v1.XmlPullParser;
87 import org.xmlpull.v1.XmlPullParserException;
88 import org.xmlpull.v1.XmlSerializer;
89
90 import java.io.CharArrayWriter;
91 import java.io.File;
92 import java.io.FileInputStream;
93 import java.io.FileNotFoundException;
94 import java.io.FileOutputStream;
95 import java.io.FilenameFilter;
96 import java.io.IOException;
97 import java.nio.charset.StandardCharsets;
98 import java.security.SecureRandom;
99 import java.util.ArrayList;
100 import java.util.Collections;
101 import java.util.List;
102 import java.util.Objects;
103 import java.util.Random;
104
105 public 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_ALLOW_TEST;
444             params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
445             if ((params.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0
446                     && !mPm.isCallerVerifier(callingUid)) {
447                 params.installFlags &= ~PackageManager.INSTALL_VIRTUAL_PRELOAD;
448             }
449         }
450
451         // Only system components can circumvent runtime permissions when installing.
452         if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
453                 && mContext.checkCallingOrSelfPermission(Manifest.permission
454                 .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
455             throw new SecurityException("You need the "
456                     + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
457                     + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
458         }
459
460         if ((params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0
461                 || (params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
462             throw new IllegalArgumentException(
463                     "New installs into ASEC containers no longer supported");
464         }
465
466         // Defensively resize giant app icons
467         if (params.appIcon != null) {
468             final ActivityManager am = (ActivityManager) mContext.getSystemService(
469                     Context.ACTIVITY_SERVICE);
470             final int iconSize = am.getLauncherLargeIconSize();
471             if ((params.appIcon.getWidth() > iconSize * 2)
472                     || (params.appIcon.getHeight() > iconSize * 2)) {
473                 params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
474                         true);
475             }
476         }
477
478         switch (params.mode) {
479             case SessionParams.MODE_FULL_INSTALL:
480             case SessionParams.MODE_INHERIT_EXISTING:
481                 break;
482             default:
483                 throw new IllegalArgumentException("Invalid install mode: " + params.mode);
484         }
485
486         // If caller requested explicit location, sanity check it, otherwise
487         // resolve the best internal or adopted location.
488         if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
489             if (!PackageHelper.fitsOnInternal(mContext, params)) {
490                 throw new IOException("No suitable internal storage available");
491             }
492
493         } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
494             if (!PackageHelper.fitsOnExternal(mContext, params)) {
495                 throw new IOException("No suitable external storage available");
496             }
497
498         } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
499             // For now, installs to adopted media are treated as internal from
500             // an install flag point-of-view.
501             params.setInstallFlagsInternal();
502
503         } else {
504             // For now, installs to adopted media are treated as internal from
505             // an install flag point-of-view.
506             params.setInstallFlagsInternal();
507
508             // Resolve best location for install, based on combination of
509             // requested install flags, delta size, and manifest settings.
510             final long ident = Binder.clearCallingIdentity();
511             try {
512                 params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, params);
513             } finally {
514                 Binder.restoreCallingIdentity(ident);
515             }
516         }
517
518         final int sessionId;
519         final PackageInstallerSession session;
520         synchronized (mSessions) {
521             // Sanity check that installer isn't going crazy
522             final int activeCount = getSessionCount(mSessions, callingUid);
523             if (activeCount >= MAX_ACTIVE_SESSIONS) {
524                 throw new IllegalStateException(
525                         "Too many active sessions for UID " + callingUid);
526             }
527             final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid);
528             if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
529                 throw new IllegalStateException(
530                         "Too many historical sessions for UID " + callingUid);
531             }
532
533             sessionId = allocateSessionIdLocked();
534         }
535
536         final long createdMillis = System.currentTimeMillis();
537         // We're staging to exactly one location
538         File stageDir = null;
539         String stageCid = null;
540         if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
541             final boolean isInstant =
542                     (params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
543             stageDir = buildStageDir(params.volumeUuid, sessionId, isInstant);
544         } else {
545             stageCid = buildExternalStageCid(sessionId);
546         }
547
548         session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
549                 mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
550                 params, createdMillis, stageDir, stageCid, false, false);
551
552         synchronized (mSessions) {
553             mSessions.put(sessionId, session);
554         }
555
556         mCallbacks.notifySessionCreated(session.sessionId, session.userId);
557         writeSessionsAsync();
558         return sessionId;
559     }
560
561     @Override
562     public void updateSessionAppIcon(int sessionId, Bitmap appIcon) {
563         synchronized (mSessions) {
564             final PackageInstallerSession session = mSessions.get(sessionId);
565             if (session == null || !isCallingUidOwner(session)) {
566                 throw new SecurityException("Caller has no access to session " + sessionId);
567             }
568
569             // Defensively resize giant app icons
570             if (appIcon != null) {
571                 final ActivityManager am = (ActivityManager) mContext.getSystemService(
572                         Context.ACTIVITY_SERVICE);
573                 final int iconSize = am.getLauncherLargeIconSize();
574                 if ((appIcon.getWidth() > iconSize * 2)
575                         || (appIcon.getHeight() > iconSize * 2)) {
576                     appIcon = Bitmap.createScaledBitmap(appIcon, iconSize, iconSize, true);
577                 }
578             }
579
580             session.params.appIcon = appIcon;
581             session.params.appIconLastModified = -1;
582
583             mInternalCallback.onSessionBadgingChanged(session);
584         }
585     }
586
587     @Override
588     public void updateSessionAppLabel(int sessionId, String appLabel) {
589         synchronized (mSessions) {
590             final PackageInstallerSession session = mSessions.get(sessionId);
591             if (session == null || !isCallingUidOwner(session)) {
592                 throw new SecurityException("Caller has no access to session " + sessionId);
593             }
594             session.params.appLabel = appLabel;
595             mInternalCallback.onSessionBadgingChanged(session);
596         }
597     }
598
599     @Override
600     public void abandonSession(int sessionId) {
601         synchronized (mSessions) {
602             final PackageInstallerSession session = mSessions.get(sessionId);
603             if (session == null || !isCallingUidOwner(session)) {
604                 throw new SecurityException("Caller has no access to session " + sessionId);
605             }
606             session.abandon();
607         }
608     }
609
610     @Override
611     public IPackageInstallerSession openSession(int sessionId) {
612         try {
613             return openSessionInternal(sessionId);
614         } catch (IOException e) {
615             throw ExceptionUtils.wrap(e);
616         }
617     }
618
619     private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
620         synchronized (mSessions) {
621             final PackageInstallerSession session = mSessions.get(sessionId);
622             if (session == null || !isCallingUidOwner(session)) {
623                 throw new SecurityException("Caller has no access to session " + sessionId);
624             }
625             session.open();
626             return session;
627         }
628     }
629
630     private int allocateSessionIdLocked() {
631         int n = 0;
632         int sessionId;
633         do {
634             sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
635             if (!mAllocatedSessions.get(sessionId, false)) {
636                 mAllocatedSessions.put(sessionId, true);
637                 return sessionId;
638             }
639         } while (n++ < 32);
640
641         throw new IllegalStateException("Failed to allocate session ID");
642     }
643
644     private File buildStagingDir(String volumeUuid, boolean isEphemeral) {
645         return Environment.getDataAppDirectory(volumeUuid);
646     }
647
648     private File buildStageDir(String volumeUuid, int sessionId, boolean isEphemeral) {
649         final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
650         return new File(stagingDir, "vmdl" + sessionId + ".tmp");
651     }
652
653     static void prepareStageDir(File stageDir) throws IOException {
654         if (stageDir.exists()) {
655             throw new IOException("Session dir already exists: " + stageDir);
656         }
657
658         try {
659             Os.mkdir(stageDir.getAbsolutePath(), 0755);
660             Os.chmod(stageDir.getAbsolutePath(), 0755);
661         } catch (ErrnoException e) {
662             // This purposefully throws if directory already exists
663             throw new IOException("Failed to prepare session dir: " + stageDir, e);
664         }
665
666         if (!SELinux.restorecon(stageDir)) {
667             throw new IOException("Failed to restorecon session dir: " + stageDir);
668         }
669     }
670
671     private String buildExternalStageCid(int sessionId) {
672         return "smdl" + sessionId + ".tmp";
673     }
674
675     static void prepareExternalStageCid(String stageCid, long sizeBytes) throws IOException {
676         if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(),
677                 Process.SYSTEM_UID, true) == null) {
678             throw new IOException("Failed to create session cid: " + stageCid);
679         }
680     }
681
682     @Override
683     public SessionInfo getSessionInfo(int sessionId) {
684         synchronized (mSessions) {
685             final PackageInstallerSession session = mSessions.get(sessionId);
686             return session != null ? session.generateInfo() : null;
687         }
688     }
689
690     @Override
691     public ParceledListSlice<SessionInfo> getAllSessions(int userId) {
692         mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getAllSessions");
693
694         final List<SessionInfo> result = new ArrayList<>();
695         synchronized (mSessions) {
696             for (int i = 0; i < mSessions.size(); i++) {
697                 final PackageInstallerSession session = mSessions.valueAt(i);
698                 if (session.userId == userId) {
699                     result.add(session.generateInfo(false));
700                 }
701             }
702         }
703         return new ParceledListSlice<>(result);
704     }
705
706     @Override
707     public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) {
708         mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getMySessions");
709         mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
710
711         final List<SessionInfo> result = new ArrayList<>();
712         synchronized (mSessions) {
713             for (int i = 0; i < mSessions.size(); i++) {
714                 final PackageInstallerSession session = mSessions.valueAt(i);
715
716                 SessionInfo info = session.generateInfo(false);
717                 if (Objects.equals(info.getInstallerPackageName(), installerPackageName)
718                         && session.userId == userId) {
719                     result.add(info);
720                 }
721             }
722         }
723         return new ParceledListSlice<>(result);
724     }
725
726     @Override
727     public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
728                 IntentSender statusReceiver, int userId) throws RemoteException {
729         final int callingUid = Binder.getCallingUid();
730         mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
731         if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
732             mAppOps.checkPackage(callingUid, callerPackageName);
733         }
734
735         // Check whether the caller is device owner, in which case we do it silently.
736         DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
737                 Context.DEVICE_POLICY_SERVICE);
738         boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
739                 callerPackageName);
740
741         final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
742                 statusReceiver, versionedPackage.getPackageName(), isDeviceOwner, userId);
743         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
744                     == PackageManager.PERMISSION_GRANTED) {
745             // Sweet, call straight through!
746             mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
747         } else if (isDeviceOwner) {
748             // Allow the DeviceOwner to silently delete packages
749             // Need to clear the calling identity to get DELETE_PACKAGES permission
750             long ident = Binder.clearCallingIdentity();
751             try {
752                 mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
753             } finally {
754                 Binder.restoreCallingIdentity(ident);
755             }
756         } else {
757             // Take a short detour to confirm with user
758             final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
759             intent.setData(Uri.fromParts("package", versionedPackage.getPackageName(), null));
760             intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
761             adapter.onUserActionRequired(intent);
762         }
763     }
764
765     @Override
766     public void setPermissionsResult(int sessionId, boolean accepted) {
767         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
768
769         synchronized (mSessions) {
770             PackageInstallerSession session = mSessions.get(sessionId);
771             if (session != null) {
772                 session.setPermissionsResult(accepted);
773             }
774         }
775     }
776
777     @Override
778     public void registerCallback(IPackageInstallerCallback callback, int userId) {
779         mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback");
780         mCallbacks.register(callback, userId);
781     }
782
783     @Override
784     public void unregisterCallback(IPackageInstallerCallback callback) {
785         mCallbacks.unregister(callback);
786     }
787
788     private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
789             int installerUid) {
790         int count = 0;
791         final int size = sessions.size();
792         for (int i = 0; i < size; i++) {
793             final PackageInstallerSession session = sessions.valueAt(i);
794             if (session.getInstallerUid() == installerUid) {
795                 count++;
796             }
797         }
798         return count;
799     }
800
801     private boolean isCallingUidOwner(PackageInstallerSession session) {
802         final int callingUid = Binder.getCallingUid();
803         if (callingUid == Process.ROOT_UID) {
804             return true;
805         } else {
806             return (session != null) && (callingUid == session.getInstallerUid());
807         }
808     }
809
810     static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
811         private final Context mContext;
812         private final IntentSender mTarget;
813         private final String mPackageName;
814         private final Notification mNotification;
815
816         public PackageDeleteObserverAdapter(Context context, IntentSender target,
817                 String packageName, boolean showNotification, int userId) {
818             mContext = context;
819             mTarget = target;
820             mPackageName = packageName;
821             if (showNotification) {
822                 mNotification = buildSuccessNotification(mContext,
823                         mContext.getResources().getString(R.string.package_deleted_device_owner),
824                         packageName,
825                         userId);
826             } else {
827                 mNotification = null;
828             }
829         }
830
831         @Override
832         public void onUserActionRequired(Intent intent) {
833             final Intent fillIn = new Intent();
834             fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
835             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
836                     PackageInstaller.STATUS_PENDING_USER_ACTION);
837             fillIn.putExtra(Intent.EXTRA_INTENT, intent);
838             try {
839                 mTarget.sendIntent(mContext, 0, fillIn, null, null);
840             } catch (SendIntentException ignored) {
841             }
842         }
843
844         @Override
845         public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
846             if (PackageManager.DELETE_SUCCEEDED == returnCode && mNotification != null) {
847                 NotificationManager notificationManager = (NotificationManager)
848                         mContext.getSystemService(Context.NOTIFICATION_SERVICE);
849                 notificationManager.notify(basePackageName,
850                         SystemMessage.NOTE_PACKAGE_STATE,
851                         mNotification);
852             }
853             final Intent fillIn = new Intent();
854             fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName);
855             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
856                     PackageManager.deleteStatusToPublicStatus(returnCode));
857             fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
858                     PackageManager.deleteStatusToString(returnCode, msg));
859             fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
860             try {
861                 mTarget.sendIntent(mContext, 0, fillIn, null, null);
862             } catch (SendIntentException ignored) {
863             }
864         }
865     }
866
867     static class PackageInstallObserverAdapter extends PackageInstallObserver {
868         private final Context mContext;
869         private final IntentSender mTarget;
870         private final int mSessionId;
871         private final boolean mShowNotification;
872         private final int mUserId;
873
874         public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId,
875                 boolean showNotification, int userId) {
876             mContext = context;
877             mTarget = target;
878             mSessionId = sessionId;
879             mShowNotification = showNotification;
880             mUserId = userId;
881         }
882
883         @Override
884         public void onUserActionRequired(Intent intent) {
885             final Intent fillIn = new Intent();
886             fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
887             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
888                     PackageInstaller.STATUS_PENDING_USER_ACTION);
889             fillIn.putExtra(Intent.EXTRA_INTENT, intent);
890             try {
891                 mTarget.sendIntent(mContext, 0, fillIn, null, null);
892             } catch (SendIntentException ignored) {
893             }
894         }
895
896         @Override
897         public void onPackageInstalled(String basePackageName, int returnCode, String msg,
898                 Bundle extras) {
899             if (PackageManager.INSTALL_SUCCEEDED == returnCode && mShowNotification) {
900                 boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
901                 Notification notification = buildSuccessNotification(mContext,
902                         mContext.getResources()
903                                 .getString(update ? R.string.package_updated_device_owner :
904                                         R.string.package_installed_device_owner),
905                         basePackageName,
906                         mUserId);
907                 if (notification != null) {
908                     NotificationManager notificationManager = (NotificationManager)
909                             mContext.getSystemService(Context.NOTIFICATION_SERVICE);
910                     notificationManager.notify(basePackageName,
911                             SystemMessage.NOTE_PACKAGE_STATE,
912                             notification);
913                 }
914             }
915             final Intent fillIn = new Intent();
916             fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, basePackageName);
917             fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
918             fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
919                     PackageManager.installStatusToPublicStatus(returnCode));
920             fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
921                     PackageManager.installStatusToString(returnCode, msg));
922             fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
923             if (extras != null) {
924                 final String existing = extras.getString(
925                         PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
926                 if (!TextUtils.isEmpty(existing)) {
927                     fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing);
928                 }
929             }
930             try {
931                 mTarget.sendIntent(mContext, 0, fillIn, null, null);
932             } catch (SendIntentException ignored) {
933             }
934         }
935     }
936
937     /**
938      * Build a notification for package installation / deletion by device owners that is shown if
939      * the operation succeeds.
940      */
941     private static Notification buildSuccessNotification(Context context, String contentText,
942             String basePackageName, int userId) {
943         PackageInfo packageInfo = null;
944         try {
945             packageInfo = AppGlobals.getPackageManager().getPackageInfo(
946                     basePackageName, PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
947         } catch (RemoteException ignored) {
948         }
949         if (packageInfo == null || packageInfo.applicationInfo == null) {
950             Slog.w(TAG, "Notification not built for package: " + basePackageName);
951             return null;
952         }
953         PackageManager pm = context.getPackageManager();
954         Bitmap packageIcon = ImageUtils.buildScaledBitmap(
955                 packageInfo.applicationInfo.loadIcon(pm),
956                 context.getResources().getDimensionPixelSize(
957                         android.R.dimen.notification_large_icon_width),
958                 context.getResources().getDimensionPixelSize(
959                         android.R.dimen.notification_large_icon_height));
960         CharSequence packageLabel = packageInfo.applicationInfo.loadLabel(pm);
961         return new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN)
962                 .setSmallIcon(R.drawable.ic_check_circle_24px)
963                 .setColor(context.getResources().getColor(
964                         R.color.system_notification_accent_color))
965                 .setContentTitle(packageLabel)
966                 .setContentText(contentText)
967                 .setStyle(new Notification.BigTextStyle().bigText(contentText))
968                 .setLargeIcon(packageIcon)
969                 .build();
970     }
971
972     public static <E> ArraySet<E> newArraySet(E... elements) {
973         final ArraySet<E> set = new ArraySet<E>();
974         if (elements != null) {
975             set.ensureCapacity(elements.length);
976             Collections.addAll(set, elements);
977         }
978         return set;
979     }
980
981     private static class Callbacks extends Handler {
982         private static final int MSG_SESSION_CREATED = 1;
983         private static final int MSG_SESSION_BADGING_CHANGED = 2;
984         private static final int MSG_SESSION_ACTIVE_CHANGED = 3;
985         private static final int MSG_SESSION_PROGRESS_CHANGED = 4;
986         private static final int MSG_SESSION_FINISHED = 5;
987
988         private final RemoteCallbackList<IPackageInstallerCallback>
989                 mCallbacks = new RemoteCallbackList<>();
990
991         public Callbacks(Looper looper) {
992             super(looper);
993         }
994
995         public void register(IPackageInstallerCallback callback, int userId) {
996             mCallbacks.register(callback, new UserHandle(userId));
997         }
998
999         public void unregister(IPackageInstallerCallback callback) {
1000             mCallbacks.unregister(callback);
1001         }
1002
1003         @Override
1004         public void handleMessage(Message msg) {
1005             final int userId = msg.arg2;
1006             final int n = mCallbacks.beginBroadcast();
1007             for (int i = 0; i < n; i++) {
1008                 final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i);
1009                 final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i);
1010                 // TODO: dispatch notifications for slave profiles
1011                 if (userId == user.getIdentifier()) {
1012                     try {
1013                         invokeCallback(callback, msg);
1014                     } catch (RemoteException ignored) {
1015                     }
1016                 }
1017             }
1018             mCallbacks.finishBroadcast();
1019         }
1020
1021         private void invokeCallback(IPackageInstallerCallback callback, Message msg)
1022                 throws RemoteException {
1023             final int sessionId = msg.arg1;
1024             switch (msg.what) {
1025                 case MSG_SESSION_CREATED:
1026                     callback.onSessionCreated(sessionId);
1027                     break;
1028                 case MSG_SESSION_BADGING_CHANGED:
1029                     callback.onSessionBadgingChanged(sessionId);
1030                     break;
1031                 case MSG_SESSION_ACTIVE_CHANGED:
1032                     callback.onSessionActiveChanged(sessionId, (boolean) msg.obj);
1033                     break;
1034                 case MSG_SESSION_PROGRESS_CHANGED:
1035                     callback.onSessionProgressChanged(sessionId, (float) msg.obj);
1036                     break;
1037                 case MSG_SESSION_FINISHED:
1038                     callback.onSessionFinished(sessionId, (boolean) msg.obj);
1039                     break;
1040             }
1041         }
1042
1043         private void notifySessionCreated(int sessionId, int userId) {
1044             obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget();
1045         }
1046
1047         private void notifySessionBadgingChanged(int sessionId, int userId) {
1048             obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget();
1049         }
1050
1051         private void notifySessionActiveChanged(int sessionId, int userId, boolean active) {
1052             obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget();
1053         }
1054
1055         private void notifySessionProgressChanged(int sessionId, int userId, float progress) {
1056             obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget();
1057         }
1058
1059         public void notifySessionFinished(int sessionId, int userId, boolean success) {
1060             obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget();
1061         }
1062     }
1063
1064     void dump(IndentingPrintWriter pw) {
1065         synchronized (mSessions) {
1066             pw.println("Active install sessions:");
1067             pw.increaseIndent();
1068             int N = mSessions.size();
1069             for (int i = 0; i < N; i++) {
1070                 final PackageInstallerSession session = mSessions.valueAt(i);
1071                 session.dump(pw);
1072                 pw.println();
1073             }
1074             pw.println();
1075             pw.decreaseIndent();
1076
1077             pw.println("Historical install sessions:");
1078             pw.increaseIndent();
1079             N = mHistoricalSessions.size();
1080             for (int i = 0; i < N; i++) {
1081                 pw.print(mHistoricalSessions.get(i));
1082                 pw.println();
1083             }
1084             pw.println();
1085             pw.decreaseIndent();
1086
1087             pw.println("Legacy install sessions:");
1088             pw.increaseIndent();
1089             pw.println(mLegacySessions.toString());
1090             pw.decreaseIndent();
1091         }
1092     }
1093
1094     class InternalCallback {
1095         public void onSessionBadgingChanged(PackageInstallerSession session) {
1096             mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId);
1097             writeSessionsAsync();
1098         }
1099
1100         public void onSessionActiveChanged(PackageInstallerSession session, boolean active) {
1101             mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active);
1102         }
1103
1104         public void onSessionProgressChanged(PackageInstallerSession session, float progress) {
1105             mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress);
1106         }
1107
1108         public void onSessionFinished(final PackageInstallerSession session, boolean success) {
1109             mCallbacks.notifySessionFinished(session.sessionId, session.userId, success);
1110
1111             mInstallHandler.post(new Runnable() {
1112                 @Override
1113                 public void run() {
1114                     synchronized (mSessions) {
1115                         mSessions.remove(session.sessionId);
1116                         addHistoricalSessionLocked(session);
1117
1118                         final File appIconFile = buildAppIconFile(session.sessionId);
1119                         if (appIconFile.exists()) {
1120                             appIconFile.delete();
1121                         }
1122
1123                         writeSessionsLocked();
1124                     }
1125                 }
1126             });
1127         }
1128
1129         public void onSessionPrepared(PackageInstallerSession session) {
1130             // We prepared the destination to write into; we want to persist
1131             // this, but it's not critical enough to block for.
1132             writeSessionsAsync();
1133         }
1134
1135         public void onSessionSealedBlocking(PackageInstallerSession session) {
1136             // It's very important that we block until we've recorded the
1137             // session as being sealed, since we never want to allow mutation
1138             // after sealing.
1139             synchronized (mSessions) {
1140                 writeSessionsLocked();
1141             }
1142         }
1143     }
1144 }