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 android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO; 20 import static android.os.Process.INVALID_UID; 21 22 import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME; 23 24 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 25 import static org.xmlpull.v1.XmlPullParser.START_TAG; 26 27 import android.Manifest; 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.app.ActivityManager; 31 import android.app.AppGlobals; 32 import android.app.AppOpsManager; 33 import android.app.BroadcastOptions; 34 import android.app.Notification; 35 import android.app.NotificationManager; 36 import android.app.PackageDeleteObserver; 37 import android.app.admin.DevicePolicyEventLogger; 38 import android.app.admin.DevicePolicyManager; 39 import android.app.admin.DevicePolicyManagerInternal; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.content.IntentSender; 43 import android.content.IntentSender.SendIntentException; 44 import android.content.pm.ApplicationInfo; 45 import android.content.pm.IPackageInstaller; 46 import android.content.pm.IPackageInstallerCallback; 47 import android.content.pm.IPackageInstallerSession; 48 import android.content.pm.PackageInfo; 49 import android.content.pm.PackageInstaller; 50 import android.content.pm.PackageInstaller.InstallConstraints; 51 import android.content.pm.PackageInstaller.InstallConstraintsResult; 52 import android.content.pm.PackageInstaller.SessionInfo; 53 import android.content.pm.PackageInstaller.SessionParams; 54 import android.content.pm.PackageItemInfo; 55 import android.content.pm.PackageManager; 56 import android.content.pm.ParceledListSlice; 57 import android.content.pm.VersionedPackage; 58 import android.graphics.Bitmap; 59 import android.net.Uri; 60 import android.os.Binder; 61 import android.os.Build; 62 import android.os.Bundle; 63 import android.os.Environment; 64 import android.os.Handler; 65 import android.os.HandlerThread; 66 import android.os.Looper; 67 import android.os.Message; 68 import android.os.Process; 69 import android.os.RemoteCallback; 70 import android.os.RemoteCallbackList; 71 import android.os.RemoteException; 72 import android.os.SELinux; 73 import android.os.UserHandle; 74 import android.os.UserManager; 75 import android.os.storage.StorageManager; 76 import android.stats.devicepolicy.DevicePolicyEnums; 77 import android.system.ErrnoException; 78 import android.system.Os; 79 import android.text.TextUtils; 80 import android.text.format.DateUtils; 81 import android.util.ArraySet; 82 import android.util.AtomicFile; 83 import android.util.ExceptionUtils; 84 import android.util.Log; 85 import android.util.Slog; 86 import android.util.SparseArray; 87 import android.util.SparseBooleanArray; 88 import android.util.SparseIntArray; 89 import android.util.Xml; 90 91 import com.android.internal.R; 92 import com.android.internal.annotations.GuardedBy; 93 import com.android.internal.annotations.VisibleForTesting; 94 import com.android.internal.content.InstallLocationUtils; 95 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 96 import com.android.internal.notification.SystemNotificationChannels; 97 import com.android.internal.util.ImageUtils; 98 import com.android.internal.util.IndentingPrintWriter; 99 import com.android.modules.utils.TypedXmlPullParser; 100 import com.android.modules.utils.TypedXmlSerializer; 101 import com.android.server.IoThread; 102 import com.android.server.LocalServices; 103 import com.android.server.SystemConfig; 104 import com.android.server.SystemService; 105 import com.android.server.SystemServiceManager; 106 import com.android.server.pm.parsing.PackageParser2; 107 import com.android.server.pm.pkg.PackageStateInternal; 108 import com.android.server.pm.utils.RequestThrottle; 109 110 import libcore.io.IoUtils; 111 112 import org.xmlpull.v1.XmlPullParserException; 113 114 import java.io.CharArrayWriter; 115 import java.io.File; 116 import java.io.FileInputStream; 117 import java.io.FileNotFoundException; 118 import java.io.FileOutputStream; 119 import java.io.FilenameFilter; 120 import java.io.IOException; 121 import java.security.SecureRandom; 122 import java.util.ArrayList; 123 import java.util.Collections; 124 import java.util.Comparator; 125 import java.util.List; 126 import java.util.Map; 127 import java.util.Objects; 128 import java.util.Random; 129 import java.util.Set; 130 import java.util.TreeMap; 131 import java.util.TreeSet; 132 import java.util.concurrent.CompletableFuture; 133 import java.util.function.IntPredicate; 134 import java.util.function.Supplier; 135 136 /** The service responsible for installing packages. */ 137 public class PackageInstallerService extends IPackageInstaller.Stub implements 138 PackageSessionProvider { 139 private static final String TAG = "PackageInstaller"; 140 private static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG); 141 142 private static final boolean DEBUG = Build.IS_DEBUGGABLE; 143 144 // TODO: remove outstanding sessions when installer package goes away 145 // TODO: notify listeners in other users when package has been installed there 146 // TODO: purge expired sessions periodically in addition to at reboot 147 148 /** XML constants used in {@link #mSessionsFile} */ 149 private static final String TAG_SESSIONS = "sessions"; 150 151 /** Automatically destroy sessions older than this */ 152 private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS; 153 /** Automatically destroy staged sessions that have not changed state in this time */ 154 private static final long MAX_TIME_SINCE_UPDATE_MILLIS = 21 * DateUtils.DAY_IN_MILLIS; 155 /** Upper bound on number of active sessions for a UID that has INSTALL_PACKAGES */ 156 private static final long MAX_ACTIVE_SESSIONS_WITH_PERMISSION = 1024; 157 /** Upper bound on number of active sessions for a UID without INSTALL_PACKAGES */ 158 private static final long MAX_ACTIVE_SESSIONS_NO_PERMISSION = 50; 159 /** Upper bound on number of historical sessions for a UID */ 160 private static final long MAX_HISTORICAL_SESSIONS = 1048576; 161 /** Destroy sessions older than this on storage free request */ 162 private static final long MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS = 8 * DateUtils.HOUR_IN_MILLIS; 163 /** Maximum time to wait for install constraints to be satisfied */ 164 private static final long MAX_INSTALL_CONSTRAINTS_TIMEOUT_MILLIS = DateUtils.WEEK_IN_MILLIS; 165 166 /** Threshold of historical sessions size */ 167 private static final int HISTORICAL_SESSIONS_THRESHOLD = 500; 168 /** Size of historical sessions to be cleared when reaching threshold */ 169 private static final int HISTORICAL_CLEAR_SIZE = 400; 170 171 /** 172 * Allow verification-skipping if it's a development app installed through ADB with 173 * disable verification flag specified. 174 */ 175 private static final int ADB_DEV_MODE = PackageManager.INSTALL_FROM_ADB 176 | PackageManager.INSTALL_ALLOW_TEST; 177 178 /** 179 * Set of app op permissions that the installer of a session is allowed to change through 180 * {@link PackageInstaller.SessionParams#setPermissionState(String, int)}. 181 */ 182 public static final Set<String> INSTALLER_CHANGEABLE_APP_OP_PERMISSIONS = Set.of( 183 Manifest.permission.USE_FULL_SCREEN_INTENT 184 ); 185 186 private final Context mContext; 187 private final PackageManagerService mPm; 188 private final ApexManager mApexManager; 189 private final StagingManager mStagingManager; 190 191 private AppOpsManager mAppOps; 192 193 private final HandlerThread mInstallThread; 194 private final Handler mInstallHandler; 195 196 private final Callbacks mCallbacks; 197 198 private volatile boolean mOkToSendBroadcasts = false; 199 private volatile boolean mBypassNextStagedInstallerCheck = false; 200 private volatile boolean mBypassNextAllowedApexUpdateCheck = false; 201 private volatile int mDisableVerificationForUid = INVALID_UID; 202 203 /** 204 * File storing persisted {@link #mSessions} metadata. 205 */ 206 private final AtomicFile mSessionsFile; 207 208 /** 209 * Directory storing persisted {@link #mSessions} metadata which is too 210 * heavy to store directly in {@link #mSessionsFile}. 211 */ 212 private final File mSessionsDir; 213 214 private final InternalCallback mInternalCallback = new InternalCallback(); 215 private final PackageSessionVerifier mSessionVerifier; 216 private final GentleUpdateHelper mGentleUpdateHelper; 217 218 /** 219 * Used for generating session IDs. Since this is created at boot time, 220 * normal random might be predictable. 221 */ 222 private final Random mRandom = new SecureRandom(); 223 224 /** All sessions allocated */ 225 @GuardedBy("mSessions") 226 private final SparseBooleanArray mAllocatedSessions = new SparseBooleanArray(); 227 228 @GuardedBy("mSessions") 229 private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>(); 230 231 /** Historical sessions kept around for debugging purposes */ 232 @GuardedBy("mSessions") 233 private final List<String> mHistoricalSessions = new ArrayList<>(); 234 235 @GuardedBy("mSessions") 236 private final SparseIntArray mHistoricalSessionsByInstaller = new SparseIntArray(); 237 238 /** Sessions allocated to legacy users */ 239 @GuardedBy("mSessions") 240 private final SparseBooleanArray mLegacySessions = new SparseBooleanArray(); 241 242 /** Policy for allowing a silent update. */ 243 private final SilentUpdatePolicy mSilentUpdatePolicy = new SilentUpdatePolicy(); 244 245 private static final FilenameFilter sStageFilter = new FilenameFilter() { 246 @Override 247 public boolean accept(File dir, String name) { 248 return isStageName(name); 249 } 250 }; 251 252 private static final class Lifecycle extends SystemService { 253 private final PackageInstallerService mPackageInstallerService; 254 Lifecycle(Context context, PackageInstallerService service)255 Lifecycle(Context context, PackageInstallerService service) { 256 super(context); 257 mPackageInstallerService = service; 258 } 259 260 @Override onStart()261 public void onStart() { 262 // no-op 263 } 264 265 @Override onBootPhase(int phase)266 public void onBootPhase(int phase) { 267 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { 268 mPackageInstallerService.onBroadcastReady(); 269 } 270 } 271 } 272 273 @NonNull 274 private final RequestThrottle mSettingsWriteRequest = new RequestThrottle(IoThread.getHandler(), 275 () -> { 276 synchronized (mSessions) { 277 return writeSessionsLocked(); 278 } 279 }); 280 PackageInstallerService(Context context, PackageManagerService pm, Supplier<PackageParser2> apexParserSupplier)281 public PackageInstallerService(Context context, PackageManagerService pm, 282 Supplier<PackageParser2> apexParserSupplier) { 283 mContext = context; 284 mPm = pm; 285 286 mInstallThread = new HandlerThread(TAG); 287 mInstallThread.start(); 288 289 mInstallHandler = new Handler(mInstallThread.getLooper()); 290 291 mCallbacks = new Callbacks(mInstallThread.getLooper()); 292 293 mSessionsFile = new AtomicFile( 294 new File(Environment.getDataSystemDirectory(), "install_sessions.xml"), 295 "package-session"); 296 mSessionsDir = new File(Environment.getDataSystemDirectory(), "install_sessions"); 297 mSessionsDir.mkdirs(); 298 299 mApexManager = ApexManager.getInstance(); 300 mStagingManager = new StagingManager(context); 301 mSessionVerifier = new PackageSessionVerifier(context, mPm, mApexManager, 302 apexParserSupplier, mInstallThread.getLooper()); 303 mGentleUpdateHelper = new GentleUpdateHelper( 304 context, mInstallThread.getLooper(), new AppStateHelper(context)); 305 306 LocalServices.getService(SystemServiceManager.class).startService( 307 new Lifecycle(context, this)); 308 } 309 getStagingManager()310 StagingManager getStagingManager() { 311 return mStagingManager; 312 } 313 okToSendBroadcasts()314 boolean okToSendBroadcasts() { 315 return mOkToSendBroadcasts; 316 } 317 systemReady()318 public void systemReady() { 319 mAppOps = mContext.getSystemService(AppOpsManager.class); 320 mStagingManager.systemReady(); 321 mGentleUpdateHelper.systemReady(); 322 323 synchronized (mSessions) { 324 readSessionsLocked(); 325 expireSessionsLocked(); 326 327 reconcileStagesLocked(StorageManager.UUID_PRIVATE_INTERNAL); 328 329 final ArraySet<File> unclaimedIcons = newArraySet( 330 mSessionsDir.listFiles()); 331 332 // Ignore stages and icons claimed by active sessions 333 for (int i = 0; i < mSessions.size(); i++) { 334 final PackageInstallerSession session = mSessions.valueAt(i); 335 unclaimedIcons.remove(buildAppIconFile(session.sessionId)); 336 } 337 338 // Clean up orphaned icons 339 for (File icon : unclaimedIcons) { 340 Slog.w(TAG, "Deleting orphan icon " + icon); 341 icon.delete(); 342 } 343 344 // Invalid sessions might have been marked while parsing. Re-write the database with 345 // the updated information. 346 mSettingsWriteRequest.runNow(); 347 348 } 349 } 350 onBroadcastReady()351 private void onBroadcastReady() { 352 // Broadcasts are not sent while we restore sessions on boot, since no processes would be 353 // ready to listen to them. From now on, it is safe to send broadcasts which otherwise will 354 // be rejected by ActivityManagerService if its systemReady() is not completed. 355 mOkToSendBroadcasts = true; 356 } 357 restoreAndApplyStagedSessionIfNeeded()358 void restoreAndApplyStagedSessionIfNeeded() { 359 List<StagingManager.StagedSession> stagedSessionsToRestore = new ArrayList<>(); 360 synchronized (mSessions) { 361 for (int i = 0; i < mSessions.size(); i++) { 362 final PackageInstallerSession session = mSessions.valueAt(i); 363 if (!session.isStaged()) { 364 continue; 365 } 366 StagingManager.StagedSession stagedSession = session.mStagedSession; 367 if (!stagedSession.isInTerminalState() && stagedSession.hasParentSessionId() 368 && getSession(stagedSession.getParentSessionId()) == null) { 369 stagedSession.setSessionFailed(PackageManager.INSTALL_ACTIVATION_FAILED, 370 "An orphan staged session " + stagedSession.sessionId() + " is found, " 371 + "parent " + stagedSession.getParentSessionId() + " is missing"); 372 continue; 373 } 374 if (!stagedSession.hasParentSessionId() && stagedSession.isCommitted() 375 && !stagedSession.isInTerminalState()) { 376 // StagingManager.restoreSessions expects a list of committed, non-finalized 377 // parent staged sessions. 378 stagedSessionsToRestore.add(stagedSession); 379 } 380 } 381 } 382 // Don't hold mSessions lock when calling restoreSessions, since it might trigger an APK 383 // atomic install which needs to query sessions, which requires lock on mSessions. 384 // Note: restoreSessions mutates content of stagedSessionsToRestore. 385 mStagingManager.restoreSessions(stagedSessionsToRestore, mPm.isDeviceUpgrading()); 386 } 387 388 @GuardedBy("mSessions") reconcileStagesLocked(String volumeUuid)389 private void reconcileStagesLocked(String volumeUuid) { 390 final ArraySet<File> unclaimedStages = getStagingDirsOnVolume(volumeUuid); 391 // Ignore stages claimed by active sessions 392 for (int i = 0; i < mSessions.size(); i++) { 393 final PackageInstallerSession session = mSessions.valueAt(i); 394 unclaimedStages.remove(session.stageDir); 395 } 396 removeStagingDirs(unclaimedStages); 397 } 398 getStagingDirsOnVolume(String volumeUuid)399 private ArraySet<File> getStagingDirsOnVolume(String volumeUuid) { 400 final File stagingDir = getTmpSessionDir(volumeUuid); 401 final ArraySet<File> stagingDirs = newArraySet(stagingDir.listFiles(sStageFilter)); 402 403 // We also need to clean up orphaned staging directory for staged sessions 404 final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid); 405 stagingDirs.addAll(newArraySet(stagedSessionStagingDir.listFiles())); 406 return stagingDirs; 407 } 408 removeStagingDirs(ArraySet<File> stagingDirsToRemove)409 private void removeStagingDirs(ArraySet<File> stagingDirsToRemove) { 410 final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm); 411 // Clean up orphaned staging directories 412 for (File stage : stagingDirsToRemove) { 413 Slog.w(TAG, "Deleting orphan stage " + stage); 414 removePackageHelper.removeCodePath(stage); 415 } 416 } 417 onPrivateVolumeMounted(String volumeUuid)418 public void onPrivateVolumeMounted(String volumeUuid) { 419 synchronized (mSessions) { 420 reconcileStagesLocked(volumeUuid); 421 } 422 } 423 424 /** 425 * Called to free up some storage space from obsolete installation files 426 */ freeStageDirs(String volumeUuid)427 public void freeStageDirs(String volumeUuid) { 428 final ArraySet<File> unclaimedStagingDirsOnVolume = getStagingDirsOnVolume(volumeUuid); 429 final long currentTimeMillis = System.currentTimeMillis(); 430 synchronized (mSessions) { 431 for (int i = 0; i < mSessions.size(); i++) { 432 final PackageInstallerSession session = mSessions.valueAt(i); 433 if (!unclaimedStagingDirsOnVolume.contains(session.stageDir)) { 434 // Only handles sessions stored on the target volume 435 continue; 436 } 437 final long age = currentTimeMillis - session.createdMillis; 438 if (age >= MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS) { 439 // Aggressively close old sessions because we are running low on storage 440 // Their staging dirs will be removed too 441 PackageInstallerSession root = !session.hasParentSessionId() 442 ? session : mSessions.get(session.getParentSessionId()); 443 if (root == null) { 444 Slog.e(TAG, "freeStageDirs: found an orphaned session: " 445 + session.sessionId + " parent=" + session.getParentSessionId()); 446 } else if (!root.isDestroyed()) { 447 root.abandon(); 448 } 449 } else { 450 // Session is new enough, so it deserves to be kept even on low storage 451 unclaimedStagingDirsOnVolume.remove(session.stageDir); 452 } 453 } 454 } 455 removeStagingDirs(unclaimedStagingDirsOnVolume); 456 } 457 458 @Deprecated allocateStageDirLegacy(String volumeUuid, boolean isEphemeral)459 public File allocateStageDirLegacy(String volumeUuid, boolean isEphemeral) throws IOException { 460 synchronized (mSessions) { 461 try { 462 final int sessionId = allocateSessionIdLocked(); 463 mLegacySessions.put(sessionId, true); 464 final File sessionStageDir = buildTmpSessionDir(sessionId, volumeUuid); 465 prepareStageDir(sessionStageDir); 466 return sessionStageDir; 467 } catch (IllegalStateException e) { 468 throw new IOException(e); 469 } 470 } 471 } 472 473 @Deprecated allocateExternalStageCidLegacy()474 public String allocateExternalStageCidLegacy() { 475 synchronized (mSessions) { 476 final int sessionId = allocateSessionIdLocked(); 477 mLegacySessions.put(sessionId, true); 478 return "smdl" + sessionId + ".tmp"; 479 } 480 } 481 482 @GuardedBy("mSessions") readSessionsLocked()483 private void readSessionsLocked() { 484 if (LOGD) Slog.v(TAG, "readSessionsLocked()"); 485 486 mSessions.clear(); 487 488 FileInputStream fis = null; 489 try { 490 fis = mSessionsFile.openRead(); 491 final TypedXmlPullParser in = Xml.resolvePullParser(fis); 492 493 int type; 494 while ((type = in.next()) != END_DOCUMENT) { 495 if (type == START_TAG) { 496 final String tag = in.getName(); 497 if (PackageInstallerSession.TAG_SESSION.equals(tag)) { 498 final PackageInstallerSession session; 499 try { 500 session = PackageInstallerSession.readFromXml(in, mInternalCallback, 501 mContext, mPm, mInstallThread.getLooper(), mStagingManager, 502 mSessionsDir, this, mSilentUpdatePolicy); 503 } catch (Exception e) { 504 Slog.e(TAG, "Could not read session", e); 505 continue; 506 } 507 mSessions.put(session.sessionId, session); 508 mAllocatedSessions.put(session.sessionId, true); 509 } 510 } 511 } 512 } catch (FileNotFoundException e) { 513 // Missing sessions are okay, probably first boot 514 } catch (IOException | XmlPullParserException | ArrayIndexOutOfBoundsException e) { 515 Slog.wtf(TAG, "Failed reading install sessions", e); 516 } finally { 517 IoUtils.closeQuietly(fis); 518 } 519 // After reboot housekeeping. 520 for (int i = 0; i < mSessions.size(); ++i) { 521 PackageInstallerSession session = mSessions.valueAt(i); 522 session.onAfterSessionRead(mSessions); 523 } 524 } 525 526 @GuardedBy("mSessions") expireSessionsLocked()527 private void expireSessionsLocked() { 528 SparseArray<PackageInstallerSession> tmp = mSessions.clone(); 529 final int n = tmp.size(); 530 for (int i = 0; i < n; ++i) { 531 PackageInstallerSession session = tmp.valueAt(i); 532 if (session.hasParentSessionId()) { 533 // Child sessions will be expired when handling parent sessions 534 continue; 535 } 536 final long age = System.currentTimeMillis() - session.createdMillis; 537 final long timeSinceUpdate = System.currentTimeMillis() - session.getUpdatedMillis(); 538 final boolean valid; 539 if (session.isStaged()) { 540 valid = !session.isStagedAndInTerminalState() 541 || timeSinceUpdate < MAX_TIME_SINCE_UPDATE_MILLIS; 542 } else if (age >= MAX_AGE_MILLIS) { 543 Slog.w(TAG, "Abandoning old session created at " 544 + session.createdMillis); 545 valid = false; 546 } else { 547 valid = true; 548 } 549 if (!valid) { 550 Slog.w(TAG, "Remove old session: " + session.sessionId); 551 // Remove expired sessions as well as child sessions if any 552 removeActiveSession(session); 553 } 554 } 555 } 556 557 /** 558 * Moves a session (including the child sessions) from mSessions to mHistoricalSessions. 559 * This should only be called on a root session. 560 */ 561 @GuardedBy("mSessions") 562 private void removeActiveSession(PackageInstallerSession session) { 563 mSessions.remove(session.sessionId); 564 addHistoricalSessionLocked(session); 565 for (PackageInstallerSession child : session.getChildSessions()) { 566 mSessions.remove(child.sessionId); 567 addHistoricalSessionLocked(child); 568 } 569 } 570 571 @GuardedBy("mSessions") 572 private void addHistoricalSessionLocked(PackageInstallerSession session) { 573 CharArrayWriter writer = new CharArrayWriter(); 574 IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 575 session.dump(pw); 576 if (mHistoricalSessions.size() > HISTORICAL_SESSIONS_THRESHOLD) { 577 Slog.d(TAG, "Historical sessions size reaches threshold, clear the oldest"); 578 mHistoricalSessions.subList(0, HISTORICAL_CLEAR_SIZE).clear(); 579 } 580 mHistoricalSessions.add(writer.toString()); 581 582 int installerUid = session.getInstallerUid(); 583 // Increment the number of sessions by this installerUid. 584 mHistoricalSessionsByInstaller.put(installerUid, 585 mHistoricalSessionsByInstaller.get(installerUid) + 1); 586 } 587 588 @GuardedBy("mSessions") 589 private boolean writeSessionsLocked() { 590 if (LOGD) Slog.v(TAG, "writeSessionsLocked()"); 591 592 FileOutputStream fos = null; 593 try { 594 fos = mSessionsFile.startWrite(); 595 596 final TypedXmlSerializer out = Xml.resolveSerializer(fos); 597 out.startDocument(null, true); 598 out.startTag(null, TAG_SESSIONS); 599 final int size = mSessions.size(); 600 for (int i = 0; i < size; i++) { 601 final PackageInstallerSession session = mSessions.valueAt(i); 602 session.write(out, mSessionsDir); 603 } 604 out.endTag(null, TAG_SESSIONS); 605 out.endDocument(); 606 607 mSessionsFile.finishWrite(fos); 608 return true; 609 } catch (IOException e) { 610 if (fos != null) { 611 mSessionsFile.failWrite(fos); 612 } 613 } 614 615 return false; 616 } 617 618 private File buildAppIconFile(int sessionId) { 619 return new File(mSessionsDir, "app_icon." + sessionId + ".png"); 620 } 621 622 @Override 623 public int createSession(SessionParams params, String installerPackageName, 624 String callingAttributionTag, int userId) { 625 try { 626 return createSessionInternal(params, installerPackageName, callingAttributionTag, 627 userId); 628 } catch (IOException e) { 629 throw ExceptionUtils.wrap(e); 630 } 631 } 632 633 private int createSessionInternal(SessionParams params, String installerPackageName, 634 String installerAttributionTag, int userId) 635 throws IOException { 636 final int callingUid = Binder.getCallingUid(); 637 final Computer snapshot = mPm.snapshotComputer(); 638 snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "createSession"); 639 640 if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { 641 throw new SecurityException("User restriction prevents installing"); 642 } 643 644 if (params.dataLoaderParams != null 645 && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2) 646 != PackageManager.PERMISSION_GRANTED) { 647 throw new SecurityException("You need the " 648 + "com.android.permission.USE_INSTALLER_V2 permission " 649 + "to use a data loader"); 650 } 651 652 // INSTALL_REASON_ROLLBACK allows an app to be rolled back without requiring the ROLLBACK 653 // capability; ensure if this is set as the install reason the app has one of the necessary 654 // signature permissions to perform the rollback. 655 if (params.installReason == PackageManager.INSTALL_REASON_ROLLBACK) { 656 if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_ROLLBACKS) 657 != PackageManager.PERMISSION_GRANTED && 658 mContext.checkCallingOrSelfPermission(Manifest.permission.TEST_MANAGE_ROLLBACKS) 659 != PackageManager.PERMISSION_GRANTED) { 660 throw new SecurityException( 661 "INSTALL_REASON_ROLLBACK requires the MANAGE_ROLLBACKS permission or the " 662 + "TEST_MANAGE_ROLLBACKS permission"); 663 } 664 } 665 666 // App package name and label length is restricted so that really long strings aren't 667 // written to disk. 668 if (params.appPackageName != null 669 && params.appPackageName.length() > SessionParams.MAX_PACKAGE_NAME_LENGTH) { 670 params.appPackageName = null; 671 } 672 673 params.appLabel = TextUtils.trimToSize(params.appLabel, 674 PackageItemInfo.MAX_SAFE_LABEL_LENGTH); 675 676 String requestedInstallerPackageName = (params.installerPackageName != null 677 && params.installerPackageName.length() < SessionParams.MAX_PACKAGE_NAME_LENGTH) 678 ? params.installerPackageName : installerPackageName; 679 680 if (PackageManagerServiceUtils.isRootOrShell(callingUid) 681 || PackageInstallerSession.isSystemDataLoaderInstallation(params) 682 || PackageManagerServiceUtils.isAdoptedShell(callingUid, mContext)) { 683 params.installFlags |= PackageManager.INSTALL_FROM_ADB; 684 // adb installs can override the installingPackageName, but not the 685 // initiatingPackageName 686 installerPackageName = SHELL_PACKAGE_NAME; 687 } else { 688 if (callingUid != Process.SYSTEM_UID) { 689 // The supplied installerPackageName must always belong to the calling app. 690 mAppOps.checkPackage(callingUid, installerPackageName); 691 } 692 // Only apps with INSTALL_PACKAGES are allowed to set an installer that is not the 693 // caller. 694 if (!TextUtils.equals(requestedInstallerPackageName, installerPackageName)) { 695 if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) 696 != PackageManager.PERMISSION_GRANTED) { 697 mAppOps.checkPackage(callingUid, requestedInstallerPackageName); 698 } 699 } 700 701 params.installFlags &= ~PackageManager.INSTALL_FROM_ADB; 702 params.installFlags &= ~PackageManager.INSTALL_ALL_USERS; 703 params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; 704 if ((params.installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0 705 && !mPm.isCallerVerifier(snapshot, callingUid)) { 706 params.installFlags &= ~PackageManager.INSTALL_VIRTUAL_PRELOAD; 707 } 708 if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_TEST_ONLY_PACKAGE) 709 != PackageManager.PERMISSION_GRANTED) { 710 params.installFlags &= ~PackageManager.INSTALL_ALLOW_TEST; 711 } 712 } 713 714 String originatingPackageName = null; 715 if (params.originatingUid != SessionParams.UID_UNKNOWN 716 && params.originatingUid != callingUid) { 717 String[] packages = snapshot.getPackagesForUid(params.originatingUid); 718 if (packages != null && packages.length > 0) { 719 // Choose an arbitrary representative package in the case of a shared UID. 720 originatingPackageName = packages[0]; 721 } 722 } 723 724 if (Build.IS_DEBUGGABLE || PackageManagerServiceUtils.isSystemOrRoot(callingUid)) { 725 params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE; 726 } else { 727 params.installFlags &= ~PackageManager.INSTALL_ALLOW_DOWNGRADE; 728 } 729 730 if (mDisableVerificationForUid != INVALID_UID) { 731 if (callingUid == mDisableVerificationForUid) { 732 params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION; 733 } else { 734 // Clear the flag if current calling uid doesn't match the requested uid. 735 params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION; 736 } 737 // Reset the field as this is a one-off request. 738 mDisableVerificationForUid = INVALID_UID; 739 } else if ((params.installFlags & ADB_DEV_MODE) != ADB_DEV_MODE) { 740 // Only tools under specific conditions (test app installed through ADB, and 741 // verification disabled flag specified) can disable verification. 742 params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION; 743 } 744 745 boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0; 746 if (isApex) { 747 if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGE_UPDATES) 748 == PackageManager.PERMISSION_DENIED 749 && mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) 750 == PackageManager.PERMISSION_DENIED) { 751 throw new SecurityException("Not allowed to perform APEX updates"); 752 } 753 } else if (params.isStaged) { 754 mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, TAG); 755 } 756 757 if (isApex) { 758 if (!mApexManager.isApexSupported()) { 759 throw new IllegalArgumentException( 760 "This device doesn't support the installation of APEX files"); 761 } 762 if (params.isMultiPackage) { 763 throw new IllegalArgumentException("A multi-session can't be set as APEX."); 764 } 765 if (PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid) 766 || mBypassNextAllowedApexUpdateCheck) { 767 params.installFlags |= PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK; 768 } else { 769 // Only specific APEX updates (installed through ADB, or for CTS tests) can disable 770 // allowed APEX update check. 771 params.installFlags &= ~PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK; 772 } 773 } 774 775 if ((params.installFlags & PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK) != 0 776 && !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid) 777 && !Build.IS_DEBUGGABLE) { 778 // If the bypass flag is set, but not running as system root or shell then remove 779 // the flag 780 params.installFlags &= ~PackageManager.INSTALL_BYPASS_LOW_TARGET_SDK_BLOCK; 781 } 782 783 if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0 784 && !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid) 785 && (snapshot.getFlagsForUid(callingUid) & ApplicationInfo.FLAG_SYSTEM) 786 == 0) { 787 throw new SecurityException( 788 "Only system apps could use the PackageManager.INSTALL_INSTANT_APP flag."); 789 } 790 791 if (params.isStaged && !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)) { 792 if (!mBypassNextStagedInstallerCheck 793 && !isStagedInstallerAllowed(requestedInstallerPackageName)) { 794 throw new SecurityException("Installer not allowed to commit staged install"); 795 } 796 } 797 if (isApex && !PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)) { 798 if (!mBypassNextStagedInstallerCheck 799 && !isStagedInstallerAllowed(requestedInstallerPackageName)) { 800 throw new SecurityException( 801 "Installer not allowed to commit non-staged APEX install"); 802 } 803 } 804 805 mBypassNextStagedInstallerCheck = false; 806 mBypassNextAllowedApexUpdateCheck = false; 807 808 if (!params.isMultiPackage) { 809 var hasInstallGrantRuntimePermissions = mContext.checkCallingOrSelfPermission( 810 Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) 811 == PackageManager.PERMISSION_GRANTED; 812 813 // Only system components can circumvent runtime permissions when installing. 814 if ((params.installFlags & PackageManager.INSTALL_GRANT_ALL_REQUESTED_PERMISSIONS) != 0 815 && !hasInstallGrantRuntimePermissions) { 816 throw new SecurityException("You need the " 817 + Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS 818 + " permission to use the" 819 + " PackageManager.INSTALL_GRANT_ALL_REQUESTED_PERMISSIONS flag"); 820 } 821 822 var permissionStates = params.getPermissionStates(); 823 if (!permissionStates.isEmpty()) { 824 if (!hasInstallGrantRuntimePermissions) { 825 for (int index = 0; index < permissionStates.size(); index++) { 826 var permissionName = permissionStates.keyAt(index); 827 if (!INSTALLER_CHANGEABLE_APP_OP_PERMISSIONS.contains(permissionName)) { 828 throw new SecurityException("You need the " 829 + Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS 830 + " permission to grant runtime permissions for a session"); 831 } 832 } 833 } 834 } 835 836 // Defensively resize giant app icons 837 if (params.appIcon != null) { 838 final ActivityManager am = (ActivityManager) mContext.getSystemService( 839 Context.ACTIVITY_SERVICE); 840 final int iconSize = am.getLauncherLargeIconSize(); 841 if ((params.appIcon.getWidth() > iconSize * 2) 842 || (params.appIcon.getHeight() > iconSize * 2)) { 843 params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize, 844 true); 845 } 846 } 847 848 switch (params.mode) { 849 case SessionParams.MODE_FULL_INSTALL: 850 case SessionParams.MODE_INHERIT_EXISTING: 851 break; 852 default: 853 throw new IllegalArgumentException("Invalid install mode: " + params.mode); 854 } 855 856 // If caller requested explicit location, validity check it, otherwise 857 // resolve the best internal or adopted location. 858 if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) { 859 if (!InstallLocationUtils.fitsOnInternal(mContext, params)) { 860 throw new IOException("No suitable internal storage available"); 861 } 862 } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) { 863 // For now, installs to adopted media are treated as internal from 864 // an install flag point-of-view. 865 params.installFlags |= PackageManager.INSTALL_INTERNAL; 866 } else { 867 params.installFlags |= PackageManager.INSTALL_INTERNAL; 868 869 // Resolve best location for install, based on combination of 870 // requested install flags, delta size, and manifest settings. 871 final long ident = Binder.clearCallingIdentity(); 872 try { 873 params.volumeUuid = InstallLocationUtils.resolveInstallVolume(mContext, params); 874 } finally { 875 Binder.restoreCallingIdentity(ident); 876 } 877 } 878 } 879 880 final int sessionId; 881 final PackageInstallerSession session; 882 synchronized (mSessions) { 883 // Check that the installer does not have too many active sessions. 884 final int activeCount = getSessionCount(mSessions, callingUid); 885 if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) 886 == PackageManager.PERMISSION_GRANTED) { 887 if (activeCount >= MAX_ACTIVE_SESSIONS_WITH_PERMISSION) { 888 throw new IllegalStateException( 889 "Too many active sessions for UID " + callingUid); 890 } 891 } else if (activeCount >= MAX_ACTIVE_SESSIONS_NO_PERMISSION) { 892 throw new IllegalStateException( 893 "Too many active sessions for UID " + callingUid); 894 } 895 final int historicalCount = mHistoricalSessionsByInstaller.get(callingUid); 896 if (historicalCount >= MAX_HISTORICAL_SESSIONS) { 897 throw new IllegalStateException( 898 "Too many historical sessions for UID " + callingUid); 899 } 900 901 sessionId = allocateSessionIdLocked(); 902 } 903 904 final long createdMillis = System.currentTimeMillis(); 905 // We're staging to exactly one location 906 File stageDir = null; 907 String stageCid = null; 908 if (!params.isMultiPackage) { 909 if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) { 910 stageDir = buildSessionDir(sessionId, params); 911 } else { 912 stageCid = buildExternalStageCid(sessionId); 913 } 914 } 915 916 // reset the force queryable param if it's not called by an approved caller. 917 if (params.forceQueryableOverride) { 918 if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) { 919 params.forceQueryableOverride = false; 920 } 921 } 922 int requestedInstallerPackageUid = INVALID_UID; 923 if (requestedInstallerPackageName != null) { 924 requestedInstallerPackageUid = snapshot.getPackageUid(requestedInstallerPackageName, 925 0 /* flags */, userId); 926 } 927 if (requestedInstallerPackageUid == INVALID_UID) { 928 // Requested installer package is invalid, reset it 929 requestedInstallerPackageName = null; 930 } 931 932 final var dpmi = LocalServices.getService(DevicePolicyManagerInternal.class); 933 if (dpmi != null && dpmi.isUserOrganizationManaged(userId)) { 934 params.installFlags |= PackageManager.INSTALL_FROM_MANAGED_USER_OR_PROFILE; 935 } 936 937 if (isApex || mContext.checkCallingOrSelfPermission( 938 Manifest.permission.ENFORCE_UPDATE_OWNERSHIP) == PackageManager.PERMISSION_DENIED) { 939 params.installFlags &= ~PackageManager.INSTALL_REQUEST_UPDATE_OWNERSHIP; 940 } 941 942 InstallSource installSource = InstallSource.create(installerPackageName, 943 originatingPackageName, requestedInstallerPackageName, requestedInstallerPackageUid, 944 requestedInstallerPackageName, installerAttributionTag, params.packageSource); 945 session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this, 946 mSilentUpdatePolicy, mInstallThread.getLooper(), mStagingManager, sessionId, 947 userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid, 948 null, null, false, false, false, false, null, SessionInfo.INVALID_ID, 949 false, false, false, PackageManager.INSTALL_UNKNOWN, ""); 950 951 synchronized (mSessions) { 952 mSessions.put(sessionId, session); 953 } 954 mPm.addInstallerPackageName(session.getInstallSource()); 955 956 mCallbacks.notifySessionCreated(session.sessionId, session.userId); 957 958 mSettingsWriteRequest.schedule(); 959 if (LOGD) { 960 Slog.d(TAG, "Created session id=" + sessionId + " staged=" + params.isStaged); 961 } 962 return sessionId; 963 } 964 965 private boolean isStagedInstallerAllowed(String installerName) { 966 return SystemConfig.getInstance().getWhitelistedStagedInstallers().contains(installerName); 967 } 968 969 @Override 970 public void updateSessionAppIcon(int sessionId, Bitmap appIcon) { 971 synchronized (mSessions) { 972 final PackageInstallerSession session = mSessions.get(sessionId); 973 if (session == null || !isCallingUidOwner(session)) { 974 throw new SecurityException("Caller has no access to session " + sessionId); 975 } 976 977 // Defensively resize giant app icons 978 if (appIcon != null) { 979 final ActivityManager am = (ActivityManager) mContext.getSystemService( 980 Context.ACTIVITY_SERVICE); 981 final int iconSize = am.getLauncherLargeIconSize(); 982 if ((appIcon.getWidth() > iconSize * 2) 983 || (appIcon.getHeight() > iconSize * 2)) { 984 appIcon = Bitmap.createScaledBitmap(appIcon, iconSize, iconSize, true); 985 } 986 } 987 988 session.params.appIcon = appIcon; 989 session.params.appIconLastModified = -1; 990 991 mInternalCallback.onSessionBadgingChanged(session); 992 } 993 } 994 995 @Override 996 public void updateSessionAppLabel(int sessionId, String appLabel) { 997 synchronized (mSessions) { 998 final PackageInstallerSession session = mSessions.get(sessionId); 999 if (session == null || !isCallingUidOwner(session)) { 1000 throw new SecurityException("Caller has no access to session " + sessionId); 1001 } 1002 if (!appLabel.equals(session.params.appLabel)) { 1003 session.params.appLabel = appLabel; 1004 mInternalCallback.onSessionBadgingChanged(session); 1005 } 1006 } 1007 } 1008 1009 @Override 1010 public void abandonSession(int sessionId) { 1011 synchronized (mSessions) { 1012 final PackageInstallerSession session = mSessions.get(sessionId); 1013 if (session == null || !isCallingUidOwner(session)) { 1014 throw new SecurityException("Caller has no access to session " + sessionId); 1015 } 1016 session.abandon(); 1017 } 1018 } 1019 1020 @Override 1021 public IPackageInstallerSession openSession(int sessionId) { 1022 try { 1023 return openSessionInternal(sessionId); 1024 } catch (IOException e) { 1025 throw ExceptionUtils.wrap(e); 1026 } 1027 } 1028 1029 private boolean checkOpenSessionAccess(final PackageInstallerSession session) { 1030 if (session == null) { 1031 return false; 1032 } 1033 if (isCallingUidOwner(session)) { 1034 return true; 1035 } 1036 // Package verifiers have access to openSession for sealed sessions. 1037 if (session.isSealed() && mContext.checkCallingOrSelfPermission( 1038 android.Manifest.permission.PACKAGE_VERIFICATION_AGENT) 1039 == PackageManager.PERMISSION_GRANTED) { 1040 return true; 1041 } 1042 return false; 1043 } 1044 1045 private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException { 1046 synchronized (mSessions) { 1047 final PackageInstallerSession session = mSessions.get(sessionId); 1048 if (!checkOpenSessionAccess(session)) { 1049 throw new SecurityException("Caller has no access to session " + sessionId); 1050 } 1051 session.open(); 1052 return session; 1053 } 1054 } 1055 1056 @GuardedBy("mSessions") 1057 private int allocateSessionIdLocked() { 1058 int n = 0; 1059 int sessionId; 1060 do { 1061 sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1; 1062 if (!mAllocatedSessions.get(sessionId, false)) { 1063 mAllocatedSessions.put(sessionId, true); 1064 return sessionId; 1065 } 1066 } while (n++ < 32); 1067 1068 throw new IllegalStateException("Failed to allocate session ID"); 1069 } 1070 1071 static boolean isStageName(String name) { 1072 final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp"); 1073 final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp"); 1074 final boolean isLegacyContainer = name.startsWith("smdl2tmp"); 1075 return isFile || isContainer || isLegacyContainer; 1076 } 1077 1078 static int tryParseSessionId(@NonNull String tmpSessionDir) 1079 throws IllegalArgumentException { 1080 if (!tmpSessionDir.startsWith("vmdl") || !tmpSessionDir.endsWith(".tmp")) { 1081 throw new IllegalArgumentException("Not a temporary session directory"); 1082 } 1083 String sessionId = tmpSessionDir.substring("vmdl".length(), 1084 tmpSessionDir.length() - ".tmp".length()); 1085 return Integer.parseInt(sessionId); 1086 } 1087 1088 private File getTmpSessionDir(String volumeUuid) { 1089 return Environment.getDataAppDirectory(volumeUuid); 1090 } 1091 1092 private File buildTmpSessionDir(int sessionId, String volumeUuid) { 1093 final File sessionStagingDir = getTmpSessionDir(volumeUuid); 1094 return new File(sessionStagingDir, "vmdl" + sessionId + ".tmp"); 1095 } 1096 1097 private File buildSessionDir(int sessionId, SessionParams params) { 1098 if (params.isStaged || (params.installFlags & PackageManager.INSTALL_APEX) != 0) { 1099 final File sessionStagingDir = Environment.getDataStagingDirectory(params.volumeUuid); 1100 return new File(sessionStagingDir, "session_" + sessionId); 1101 } 1102 final File result = buildTmpSessionDir(sessionId, params.volumeUuid); 1103 if (DEBUG && !Objects.equals(tryParseSessionId(result.getName()), sessionId)) { 1104 throw new RuntimeException( 1105 "session folder format is off: " + result.getName() + " (" + sessionId + ")"); 1106 } 1107 return result; 1108 } 1109 1110 static void prepareStageDir(File stageDir) throws IOException { 1111 if (stageDir.exists()) { 1112 throw new IOException("Session dir already exists: " + stageDir); 1113 } 1114 1115 try { 1116 Os.mkdir(stageDir.getAbsolutePath(), 0775); 1117 Os.chmod(stageDir.getAbsolutePath(), 0775); 1118 } catch (ErrnoException e) { 1119 // This purposefully throws if directory already exists 1120 throw new IOException("Failed to prepare session dir: " + stageDir, e); 1121 } 1122 1123 if (!SELinux.restorecon(stageDir)) { 1124 String path = stageDir.getCanonicalPath(); 1125 String ctx = SELinux.fileSelabelLookup(path); 1126 boolean success = SELinux.setFileContext(path, ctx); 1127 Slog.e(TAG, 1128 "Failed to SELinux.restorecon session dir, path: [" + path + "], ctx: [" + ctx 1129 + "]. Retrying via SELinux.fileSelabelLookup/SELinux.setFileContext: " 1130 + (success ? "SUCCESS" : "FAILURE")); 1131 if (!success) { 1132 throw new IOException("Failed to restorecon session dir: " + stageDir); 1133 } 1134 } 1135 } 1136 1137 private String buildExternalStageCid(int sessionId) { 1138 return "smdl" + sessionId + ".tmp"; 1139 } 1140 1141 private boolean shouldFilterSession(@NonNull Computer snapshot, int uid, SessionInfo info) { 1142 if (info == null) { 1143 return false; 1144 } 1145 return uid != info.getInstallerUid() 1146 && !snapshot.canQueryPackage(uid, info.getAppPackageName()); 1147 } 1148 1149 @Override 1150 public SessionInfo getSessionInfo(int sessionId) { 1151 final int callingUid = Binder.getCallingUid(); 1152 final SessionInfo result; 1153 synchronized (mSessions) { 1154 final PackageInstallerSession session = mSessions.get(sessionId); 1155 result = (session != null && !(session.isStaged() && session.isDestroyed())) 1156 ? session.generateInfoForCaller(true /* includeIcon */, callingUid) 1157 : null; 1158 } 1159 return shouldFilterSession(mPm.snapshotComputer(), callingUid, result) ? null : result; 1160 } 1161 1162 @Override 1163 public ParceledListSlice<SessionInfo> getStagedSessions() { 1164 final int callingUid = Binder.getCallingUid(); 1165 final List<SessionInfo> result = new ArrayList<>(); 1166 synchronized (mSessions) { 1167 for (int i = 0; i < mSessions.size(); i++) { 1168 final PackageInstallerSession session = mSessions.valueAt(i); 1169 if (session.isStaged() && !session.isDestroyed()) { 1170 result.add(session.generateInfoForCaller(false /* includeIcon */, callingUid)); 1171 } 1172 } 1173 } 1174 final Computer snapshot = mPm.snapshotComputer(); 1175 result.removeIf(info -> shouldFilterSession(snapshot, callingUid, info)); 1176 return new ParceledListSlice<>(result); 1177 } 1178 1179 @Override 1180 public ParceledListSlice<SessionInfo> getAllSessions(int userId) { 1181 final int callingUid = Binder.getCallingUid(); 1182 final Computer snapshot = mPm.snapshotComputer(); 1183 snapshot.enforceCrossUserPermission(callingUid, userId, true, false, "getAllSessions"); 1184 1185 final List<SessionInfo> result = new ArrayList<>(); 1186 synchronized (mSessions) { 1187 for (int i = 0; i < mSessions.size(); i++) { 1188 final PackageInstallerSession session = mSessions.valueAt(i); 1189 if (session.userId == userId && !session.hasParentSessionId() 1190 && !(session.isStaged() && session.isDestroyed())) { 1191 result.add(session.generateInfoForCaller(false /* includeIcon */, callingUid)); 1192 } 1193 } 1194 } 1195 result.removeIf(info -> shouldFilterSession(snapshot, callingUid, info)); 1196 return new ParceledListSlice<>(result); 1197 } 1198 1199 @Override 1200 public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) { 1201 final Computer snapshot = mPm.snapshotComputer(); 1202 final int callingUid = Binder.getCallingUid(); 1203 snapshot.enforceCrossUserPermission(callingUid, userId, true, false, "getMySessions"); 1204 mAppOps.checkPackage(callingUid, installerPackageName); 1205 1206 final List<SessionInfo> result = new ArrayList<>(); 1207 synchronized (mSessions) { 1208 for (int i = 0; i < mSessions.size(); i++) { 1209 final PackageInstallerSession session = mSessions.valueAt(i); 1210 1211 SessionInfo info = 1212 session.generateInfoForCaller(false /*withIcon*/, Process.SYSTEM_UID); 1213 if (Objects.equals(info.getInstallerPackageName(), installerPackageName) 1214 && session.userId == userId && !session.hasParentSessionId() 1215 && isCallingUidOwner(session)) { 1216 result.add(info); 1217 } 1218 } 1219 } 1220 return new ParceledListSlice<>(result); 1221 } 1222 1223 @Override 1224 public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags, 1225 IntentSender statusReceiver, int userId) { 1226 final Computer snapshot = mPm.snapshotComputer(); 1227 final int callingUid = Binder.getCallingUid(); 1228 snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall"); 1229 if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) { 1230 mAppOps.checkPackage(callingUid, callerPackageName); 1231 } 1232 1233 // Check whether the caller is device owner or affiliated profile owner, in which case we do 1234 // it silently. 1235 DevicePolicyManagerInternal dpmi = 1236 LocalServices.getService(DevicePolicyManagerInternal.class); 1237 final boolean canSilentlyInstallPackage = 1238 dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid); 1239 1240 final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, 1241 statusReceiver, versionedPackage.getPackageName(), 1242 canSilentlyInstallPackage, userId); 1243 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES) 1244 == PackageManager.PERMISSION_GRANTED) { 1245 // Sweet, call straight through! 1246 mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags); 1247 } else if (canSilentlyInstallPackage) { 1248 // Allow the device owner and affiliated profile owner to silently delete packages 1249 // Need to clear the calling identity to get DELETE_PACKAGES permission 1250 final long ident = Binder.clearCallingIdentity(); 1251 try { 1252 mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags); 1253 } finally { 1254 Binder.restoreCallingIdentity(ident); 1255 } 1256 DevicePolicyEventLogger 1257 .createEvent(DevicePolicyEnums.UNINSTALL_PACKAGE) 1258 .setAdmin(callerPackageName) 1259 .write(); 1260 } else { 1261 ApplicationInfo appInfo = snapshot.getApplicationInfo(callerPackageName, 0, userId); 1262 if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P) { 1263 mContext.enforceCallingOrSelfPermission(Manifest.permission.REQUEST_DELETE_PACKAGES, 1264 null); 1265 } 1266 1267 // Take a short detour to confirm with user 1268 final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE); 1269 intent.setData(Uri.fromParts("package", versionedPackage.getPackageName(), null)); 1270 intent.putExtra(PackageInstaller.EXTRA_CALLBACK, 1271 new PackageManager.UninstallCompleteCallback(adapter.getBinder().asBinder())); 1272 adapter.onUserActionRequired(intent); 1273 } 1274 } 1275 1276 @Override 1277 public void uninstallExistingPackage(VersionedPackage versionedPackage, 1278 String callerPackageName, IntentSender statusReceiver, int userId) { 1279 final int callingUid = Binder.getCallingUid(); 1280 mContext.enforceCallingOrSelfPermission(Manifest.permission.DELETE_PACKAGES, null); 1281 final Computer snapshot = mPm.snapshotComputer(); 1282 snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall"); 1283 if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) { 1284 mAppOps.checkPackage(callingUid, callerPackageName); 1285 } 1286 1287 final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, 1288 statusReceiver, versionedPackage.getPackageName(), false, userId); 1289 mPm.deleteExistingPackageAsUser(versionedPackage, adapter.getBinder(), userId); 1290 } 1291 1292 @Override 1293 public void installExistingPackage(String packageName, int installFlags, int installReason, 1294 IntentSender statusReceiver, int userId, List<String> allowListedPermissions) { 1295 final InstallPackageHelper installPackageHelper = new InstallPackageHelper(mPm); 1296 1297 var result = installPackageHelper.installExistingPackageAsUser(packageName, userId, 1298 installFlags, installReason, allowListedPermissions, statusReceiver); 1299 1300 int returnCode = result.first; 1301 IntentSender onCompleteSender = result.second; 1302 if (onCompleteSender != null) { 1303 InstallPackageHelper.onInstallComplete(returnCode, mContext, onCompleteSender); 1304 } 1305 } 1306 1307 @Override 1308 public void setPermissionsResult(int sessionId, boolean accepted) { 1309 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG); 1310 1311 synchronized (mSessions) { 1312 PackageInstallerSession session = mSessions.get(sessionId); 1313 if (session != null) { 1314 session.setPermissionsResult(accepted); 1315 } 1316 } 1317 } 1318 1319 private boolean isValidForInstallConstraints(PackageStateInternal ps, 1320 String installerPackageName) { 1321 return TextUtils.equals(ps.getInstallSource().mInstallerPackageName, installerPackageName) 1322 || TextUtils.equals(ps.getInstallSource().mUpdateOwnerPackageName, 1323 installerPackageName); 1324 } 1325 1326 private CompletableFuture<InstallConstraintsResult> checkInstallConstraintsInternal( 1327 String installerPackageName, List<String> packageNames, 1328 InstallConstraints constraints, long timeoutMillis) { 1329 Objects.requireNonNull(packageNames); 1330 Objects.requireNonNull(constraints); 1331 1332 final var snapshot = mPm.snapshotComputer(); 1333 final int callingUid = Binder.getCallingUid(); 1334 final var callingPackageName = snapshot.getNameForUid(callingUid); 1335 if (!TextUtils.equals(callingPackageName, installerPackageName)) { 1336 throw new SecurityException("The installerPackageName set by the caller doesn't match " 1337 + "the caller's own package name."); 1338 } 1339 if (!PackageManagerServiceUtils.isSystemOrRootOrShell(callingUid)) { 1340 for (var packageName : packageNames) { 1341 var ps = snapshot.getPackageStateInternal(packageName); 1342 if (ps == null || !isValidForInstallConstraints(ps, installerPackageName)) { 1343 throw new SecurityException("Caller has no access to package " + packageName); 1344 } 1345 } 1346 } 1347 1348 return mGentleUpdateHelper.checkInstallConstraints( 1349 packageNames, constraints, timeoutMillis); 1350 } 1351 1352 @Override 1353 public void checkInstallConstraints(String installerPackageName, List<String> packageNames, 1354 InstallConstraints constraints, RemoteCallback callback) { 1355 Objects.requireNonNull(callback); 1356 var future = checkInstallConstraintsInternal( 1357 installerPackageName, packageNames, constraints, /*timeoutMillis=*/0); 1358 future.thenAccept(result -> { 1359 var b = new Bundle(); 1360 b.putParcelable("result", result); 1361 callback.sendResult(b); 1362 }); 1363 } 1364 1365 @Override 1366 public void waitForInstallConstraints(String installerPackageName, List<String> packageNames, 1367 InstallConstraints constraints, IntentSender callback, long timeoutMillis) { 1368 Objects.requireNonNull(callback); 1369 if (timeoutMillis < 0 || timeoutMillis > MAX_INSTALL_CONSTRAINTS_TIMEOUT_MILLIS) { 1370 throw new IllegalArgumentException("Invalid timeoutMillis=" + timeoutMillis); 1371 } 1372 var future = checkInstallConstraintsInternal( 1373 installerPackageName, packageNames, constraints, timeoutMillis); 1374 future.thenAccept(result -> { 1375 final var intent = new Intent(); 1376 intent.putExtra(Intent.EXTRA_PACKAGES, packageNames.toArray(new String[0])); 1377 intent.putExtra(PackageInstaller.EXTRA_INSTALL_CONSTRAINTS, constraints); 1378 intent.putExtra(PackageInstaller.EXTRA_INSTALL_CONSTRAINTS_RESULT, result); 1379 try { 1380 final BroadcastOptions options = BroadcastOptions.makeBasic(); 1381 options.setPendingIntentBackgroundActivityLaunchAllowed(false); 1382 callback.sendIntent(mContext, 0, intent, null /* onFinished*/, 1383 null /* handler */, null /* requiredPermission */, options.toBundle()); 1384 } catch (SendIntentException ignore) { 1385 } 1386 }); 1387 } 1388 1389 @Override 1390 public void registerCallback(IPackageInstallerCallback callback, int userId) { 1391 final Computer snapshot = mPm.snapshotComputer(); 1392 snapshot.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, 1393 "registerCallback"); 1394 registerCallback(callback, eventUserId -> userId == eventUserId); 1395 } 1396 1397 /** 1398 * Assume permissions already checked and caller's identity cleared 1399 */ 1400 public void registerCallback(IPackageInstallerCallback callback, IntPredicate userCheck) { 1401 mCallbacks.register(callback, new BroadcastCookie(Binder.getCallingUid(), userCheck)); 1402 } 1403 1404 @Override 1405 public void unregisterCallback(IPackageInstallerCallback callback) { 1406 mCallbacks.unregister(callback); 1407 } 1408 1409 @Override 1410 public PackageInstallerSession getSession(int sessionId) { 1411 synchronized (mSessions) { 1412 return mSessions.get(sessionId); 1413 } 1414 } 1415 1416 @Override 1417 public PackageSessionVerifier getSessionVerifier() { 1418 return mSessionVerifier; 1419 } 1420 1421 @Override 1422 public GentleUpdateHelper getGentleUpdateHelper() { 1423 return mGentleUpdateHelper; 1424 } 1425 1426 @Override 1427 public void bypassNextStagedInstallerCheck(boolean value) { 1428 if (!PackageManagerServiceUtils.isSystemOrRootOrShell(Binder.getCallingUid())) { 1429 throw new SecurityException("Caller not allowed to bypass staged installer check"); 1430 } 1431 mBypassNextStagedInstallerCheck = value; 1432 } 1433 1434 @Override 1435 public void bypassNextAllowedApexUpdateCheck(boolean value) { 1436 if (!PackageManagerServiceUtils.isSystemOrRootOrShell(Binder.getCallingUid())) { 1437 throw new SecurityException("Caller not allowed to bypass allowed apex update check"); 1438 } 1439 mBypassNextAllowedApexUpdateCheck = value; 1440 } 1441 1442 @Override 1443 public void disableVerificationForUid(int uid) { 1444 if (!PackageManagerServiceUtils.isSystemOrRootOrShell(Binder.getCallingUid())) { 1445 throw new SecurityException("Operation not allowed for caller"); 1446 } 1447 mDisableVerificationForUid = uid; 1448 } 1449 1450 /** 1451 * Set an installer to allow for the unlimited silent updates. 1452 */ 1453 @Override 1454 public void setAllowUnlimitedSilentUpdates(@Nullable String installerPackageName) { 1455 if (!PackageManagerServiceUtils.isSystemOrRootOrShell(Binder.getCallingUid())) { 1456 throw new SecurityException("Caller not allowed to unlimite silent updates"); 1457 } 1458 mSilentUpdatePolicy.setAllowUnlimitedSilentUpdates(installerPackageName); 1459 } 1460 1461 /** 1462 * Set the silent updates throttle time in seconds. 1463 */ 1464 @Override 1465 public void setSilentUpdatesThrottleTime(long throttleTimeInSeconds) { 1466 if (!PackageManagerServiceUtils.isSystemOrRootOrShell(Binder.getCallingUid())) { 1467 throw new SecurityException("Caller not allowed to set silent updates throttle time"); 1468 } 1469 mSilentUpdatePolicy.setSilentUpdatesThrottleTime(throttleTimeInSeconds); 1470 } 1471 1472 private static int getSessionCount(SparseArray<PackageInstallerSession> sessions, 1473 int installerUid) { 1474 int count = 0; 1475 final int size = sessions.size(); 1476 for (int i = 0; i < size; i++) { 1477 final PackageInstallerSession session = sessions.valueAt(i); 1478 if (session.getInstallerUid() == installerUid) { 1479 count++; 1480 } 1481 } 1482 return count; 1483 } 1484 1485 private boolean isCallingUidOwner(PackageInstallerSession session) { 1486 final int callingUid = Binder.getCallingUid(); 1487 if (callingUid == Process.ROOT_UID) { 1488 return true; 1489 } else { 1490 return (session != null) && (callingUid == session.getInstallerUid()); 1491 } 1492 } 1493 1494 private boolean shouldFilterSession(@NonNull Computer snapshot, int uid, int sessionId) { 1495 final PackageInstallerSession session = getSession(sessionId); 1496 if (session == null) { 1497 return false; 1498 } 1499 return uid != session.getInstallerUid() 1500 && !snapshot.canQueryPackage(uid, session.getPackageName()); 1501 } 1502 1503 static class PackageDeleteObserverAdapter extends PackageDeleteObserver { 1504 private final Context mContext; 1505 private final IntentSender mTarget; 1506 private final String mPackageName; 1507 private final Notification mNotification; 1508 1509 public PackageDeleteObserverAdapter(Context context, IntentSender target, 1510 String packageName, boolean showNotification, int userId) { 1511 mContext = context; 1512 mTarget = target; 1513 mPackageName = packageName; 1514 if (showNotification) { 1515 mNotification = buildSuccessNotification(mContext, 1516 getDeviceOwnerDeletedPackageMsg(), 1517 packageName, 1518 userId); 1519 } else { 1520 mNotification = null; 1521 } 1522 } 1523 1524 private String getDeviceOwnerDeletedPackageMsg() { 1525 final long ident = Binder.clearCallingIdentity(); 1526 try { 1527 DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); 1528 return dpm.getResources().getString(PACKAGE_DELETED_BY_DO, 1529 () -> mContext.getString(R.string.package_deleted_device_owner)); 1530 } finally { 1531 Binder.restoreCallingIdentity(ident); 1532 } 1533 } 1534 1535 @Override 1536 public void onUserActionRequired(Intent intent) { 1537 if (mTarget == null) { 1538 return; 1539 } 1540 final Intent fillIn = new Intent(); 1541 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName); 1542 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, 1543 PackageInstaller.STATUS_PENDING_USER_ACTION); 1544 fillIn.putExtra(Intent.EXTRA_INTENT, intent); 1545 try { 1546 final BroadcastOptions options = BroadcastOptions.makeBasic(); 1547 options.setPendingIntentBackgroundActivityLaunchAllowed(false); 1548 mTarget.sendIntent(mContext, 0, fillIn, null /* onFinished*/, 1549 null /* handler */, null /* requiredPermission */, options.toBundle()); 1550 } catch (SendIntentException ignored) { 1551 } 1552 } 1553 1554 @Override 1555 public void onPackageDeleted(String basePackageName, int returnCode, String msg) { 1556 if (PackageManager.DELETE_SUCCEEDED == returnCode && mNotification != null) { 1557 NotificationManager notificationManager = (NotificationManager) 1558 mContext.getSystemService(Context.NOTIFICATION_SERVICE); 1559 notificationManager.notify(basePackageName, 1560 SystemMessage.NOTE_PACKAGE_STATE, 1561 mNotification); 1562 } 1563 if (mTarget == null) { 1564 return; 1565 } 1566 final Intent fillIn = new Intent(); 1567 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName); 1568 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, 1569 PackageManager.deleteStatusToPublicStatus(returnCode)); 1570 fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, 1571 PackageManager.deleteStatusToString(returnCode, msg)); 1572 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode); 1573 try { 1574 final BroadcastOptions options = BroadcastOptions.makeBasic(); 1575 options.setPendingIntentBackgroundActivityLaunchAllowed(false); 1576 mTarget.sendIntent(mContext, 0, fillIn, null /* onFinished*/, 1577 null /* handler */, null /* requiredPermission */, options.toBundle()); 1578 } catch (SendIntentException ignored) { 1579 } 1580 } 1581 } 1582 1583 /** 1584 * Build a notification for package installation / deletion by device owners that is shown if 1585 * the operation succeeds. 1586 */ 1587 static Notification buildSuccessNotification(Context context, String contentText, 1588 String basePackageName, int userId) { 1589 PackageInfo packageInfo = null; 1590 try { 1591 packageInfo = AppGlobals.getPackageManager().getPackageInfo( 1592 basePackageName, PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES, userId); 1593 } catch (RemoteException ignored) { 1594 } 1595 if (packageInfo == null || packageInfo.applicationInfo == null) { 1596 Slog.w(TAG, "Notification not built for package: " + basePackageName); 1597 return null; 1598 } 1599 PackageManager pm = context.getPackageManager(); 1600 Bitmap packageIcon = ImageUtils.buildScaledBitmap( 1601 packageInfo.applicationInfo.loadIcon(pm), 1602 context.getResources().getDimensionPixelSize( 1603 android.R.dimen.notification_large_icon_width), 1604 context.getResources().getDimensionPixelSize( 1605 android.R.dimen.notification_large_icon_height)); 1606 CharSequence packageLabel = packageInfo.applicationInfo.loadLabel(pm); 1607 return new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN) 1608 .setSmallIcon(R.drawable.ic_check_circle_24px) 1609 .setColor(context.getResources().getColor( 1610 R.color.system_notification_accent_color)) 1611 .setContentTitle(packageLabel) 1612 .setContentText(contentText) 1613 .setStyle(new Notification.BigTextStyle().bigText(contentText)) 1614 .setLargeIcon(packageIcon) 1615 .build(); 1616 } 1617 1618 public static <E> ArraySet<E> newArraySet(E... elements) { 1619 final ArraySet<E> set = new ArraySet<E>(); 1620 if (elements != null) { 1621 set.ensureCapacity(elements.length); 1622 Collections.addAll(set, elements); 1623 } 1624 return set; 1625 } 1626 1627 private static final class BroadcastCookie { 1628 public final int callingUid; 1629 public final IntPredicate userCheck; 1630 1631 BroadcastCookie(int callingUid, IntPredicate userCheck) { 1632 this.callingUid = callingUid; 1633 this.userCheck = userCheck; 1634 } 1635 } 1636 1637 private class Callbacks extends Handler { 1638 private static final int MSG_SESSION_CREATED = 1; 1639 private static final int MSG_SESSION_BADGING_CHANGED = 2; 1640 private static final int MSG_SESSION_ACTIVE_CHANGED = 3; 1641 private static final int MSG_SESSION_PROGRESS_CHANGED = 4; 1642 private static final int MSG_SESSION_FINISHED = 5; 1643 1644 private final RemoteCallbackList<IPackageInstallerCallback> 1645 mCallbacks = new RemoteCallbackList<>(); 1646 1647 public Callbacks(Looper looper) { 1648 super(looper); 1649 } 1650 1651 public void register(IPackageInstallerCallback callback, BroadcastCookie cookie) { 1652 mCallbacks.register(callback, cookie); 1653 } 1654 1655 public void unregister(IPackageInstallerCallback callback) { 1656 mCallbacks.unregister(callback); 1657 } 1658 1659 @Override 1660 public void handleMessage(Message msg) { 1661 final int sessionId = msg.arg1; 1662 final int userId = msg.arg2; 1663 final int n = mCallbacks.beginBroadcast(); 1664 final Computer snapshot = mPm.snapshotComputer(); 1665 for (int i = 0; i < n; i++) { 1666 final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i); 1667 final BroadcastCookie cookie = (BroadcastCookie) mCallbacks.getBroadcastCookie(i); 1668 if (cookie.userCheck.test(userId) 1669 && !shouldFilterSession(snapshot, cookie.callingUid, sessionId)) { 1670 try { 1671 invokeCallback(callback, msg); 1672 } catch (RemoteException ignored) { 1673 } 1674 } 1675 } 1676 mCallbacks.finishBroadcast(); 1677 } 1678 1679 private void invokeCallback(IPackageInstallerCallback callback, Message msg) 1680 throws RemoteException { 1681 final int sessionId = msg.arg1; 1682 switch (msg.what) { 1683 case MSG_SESSION_CREATED: 1684 callback.onSessionCreated(sessionId); 1685 break; 1686 case MSG_SESSION_BADGING_CHANGED: 1687 callback.onSessionBadgingChanged(sessionId); 1688 break; 1689 case MSG_SESSION_ACTIVE_CHANGED: 1690 callback.onSessionActiveChanged(sessionId, (boolean) msg.obj); 1691 break; 1692 case MSG_SESSION_PROGRESS_CHANGED: 1693 callback.onSessionProgressChanged(sessionId, (float) msg.obj); 1694 break; 1695 case MSG_SESSION_FINISHED: 1696 callback.onSessionFinished(sessionId, (boolean) msg.obj); 1697 break; 1698 } 1699 } 1700 1701 private void notifySessionCreated(int sessionId, int userId) { 1702 obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget(); 1703 } 1704 1705 private void notifySessionBadgingChanged(int sessionId, int userId) { 1706 obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget(); 1707 } 1708 1709 private void notifySessionActiveChanged(int sessionId, int userId, boolean active) { 1710 obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget(); 1711 } 1712 1713 private void notifySessionProgressChanged(int sessionId, int userId, float progress) { 1714 obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget(); 1715 } 1716 1717 public void notifySessionFinished(int sessionId, int userId, boolean success) { 1718 obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget(); 1719 } 1720 } 1721 1722 static class ParentChildSessionMap { 1723 private TreeMap<PackageInstallerSession, TreeSet<PackageInstallerSession>> mSessionMap; 1724 1725 private final Comparator<PackageInstallerSession> mSessionCreationComparator = 1726 Comparator.comparingLong( 1727 (PackageInstallerSession sess) -> sess != null ? sess.createdMillis : -1) 1728 .thenComparingInt(sess -> sess != null ? sess.sessionId : -1); 1729 1730 ParentChildSessionMap() { 1731 mSessionMap = new TreeMap<>(mSessionCreationComparator); 1732 } 1733 1734 boolean containsSession() { 1735 return !(mSessionMap.isEmpty()); 1736 } 1737 1738 private void addParentSession(PackageInstallerSession session) { 1739 if (!mSessionMap.containsKey(session)) { 1740 mSessionMap.put(session, new TreeSet<>(mSessionCreationComparator)); 1741 } 1742 } 1743 1744 private void addChildSession(PackageInstallerSession session, 1745 PackageInstallerSession parentSession) { 1746 addParentSession(parentSession); 1747 mSessionMap.get(parentSession).add(session); 1748 } 1749 1750 void addSession(PackageInstallerSession session, 1751 PackageInstallerSession parentSession) { 1752 if (session.hasParentSessionId()) { 1753 addChildSession(session, parentSession); 1754 } else { 1755 addParentSession(session); 1756 } 1757 } 1758 1759 void dump(String tag, IndentingPrintWriter pw) { 1760 pw.println(tag + " install sessions:"); 1761 pw.increaseIndent(); 1762 1763 for (Map.Entry<PackageInstallerSession, TreeSet<PackageInstallerSession>> entry 1764 : mSessionMap.entrySet()) { 1765 PackageInstallerSession parentSession = entry.getKey(); 1766 if (parentSession != null) { 1767 pw.print(tag + " "); 1768 parentSession.dump(pw); 1769 pw.println(); 1770 pw.increaseIndent(); 1771 } 1772 1773 for (PackageInstallerSession childSession : entry.getValue()) { 1774 pw.print(tag + " Child "); 1775 childSession.dump(pw); 1776 pw.println(); 1777 } 1778 1779 pw.decreaseIndent(); 1780 } 1781 1782 pw.println(); 1783 pw.decreaseIndent(); 1784 } 1785 } 1786 1787 void dump(IndentingPrintWriter pw) { 1788 synchronized (mSessions) { 1789 ParentChildSessionMap activeSessionMap = new ParentChildSessionMap(); 1790 ParentChildSessionMap orphanedChildSessionMap = new ParentChildSessionMap(); 1791 ParentChildSessionMap finalizedSessionMap = new ParentChildSessionMap(); 1792 1793 int N = mSessions.size(); 1794 for (int i = 0; i < N; i++) { 1795 final PackageInstallerSession session = mSessions.valueAt(i); 1796 1797 final PackageInstallerSession rootSession = session.hasParentSessionId() 1798 ? getSession(session.getParentSessionId()) 1799 : session; 1800 // Do not print orphaned child sessions as active install sessions 1801 if (rootSession == null) { 1802 orphanedChildSessionMap.addSession(session, rootSession); 1803 continue; 1804 } 1805 1806 // Do not print finalized staged session as active install sessions 1807 if (rootSession.isStagedAndInTerminalState()) { 1808 finalizedSessionMap.addSession(session, rootSession); 1809 continue; 1810 } 1811 1812 activeSessionMap.addSession(session, rootSession); 1813 } 1814 1815 activeSessionMap.dump("Active", pw); 1816 1817 if (orphanedChildSessionMap.containsSession()) { 1818 // Presence of orphaned sessions indicate leak in cleanup for multi-package and 1819 // should be cleaned up. 1820 orphanedChildSessionMap.dump("Orphaned", pw); 1821 } 1822 1823 finalizedSessionMap.dump("Finalized", pw); 1824 1825 pw.println("Historical install sessions:"); 1826 pw.increaseIndent(); 1827 N = mHistoricalSessions.size(); 1828 for (int i = 0; i < N; i++) { 1829 pw.print(mHistoricalSessions.get(i)); 1830 pw.println(); 1831 } 1832 pw.println(); 1833 pw.decreaseIndent(); 1834 1835 pw.println("Legacy install sessions:"); 1836 pw.increaseIndent(); 1837 pw.println(mLegacySessions.toString()); 1838 pw.println(); 1839 pw.decreaseIndent(); 1840 } 1841 mSilentUpdatePolicy.dump(pw); 1842 mGentleUpdateHelper.dump(pw); 1843 } 1844 1845 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 1846 public class InternalCallback { 1847 public void onSessionBadgingChanged(PackageInstallerSession session) { 1848 mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId); 1849 mSettingsWriteRequest.schedule(); 1850 } 1851 1852 public void onSessionActiveChanged(PackageInstallerSession session, boolean active) { 1853 mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, 1854 active); 1855 } 1856 1857 public void onSessionProgressChanged(PackageInstallerSession session, float progress) { 1858 mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, 1859 progress); 1860 } 1861 1862 public void onSessionChanged(PackageInstallerSession session) { 1863 session.markUpdated(); 1864 mSettingsWriteRequest.schedule(); 1865 // TODO(b/210359798): Remove the session.isStaged() check. Some apps assume this 1866 // broadcast is sent by only staged sessions and call isStagedSessionApplied() without 1867 // checking if it is a staged session or not and cause exception. 1868 if (mOkToSendBroadcasts && !session.isDestroyed() && session.isStaged()) { 1869 // we don't scrub the data here as this is sent only to the installer several 1870 // privileged system packages 1871 sendSessionUpdatedBroadcast( 1872 session.generateInfoForCaller(false/*icon*/, Process.SYSTEM_UID), 1873 session.userId); 1874 } 1875 } 1876 1877 public void onSessionFinished(final PackageInstallerSession session, boolean success) { 1878 mCallbacks.notifySessionFinished(session.sessionId, session.userId, success); 1879 1880 mInstallHandler.post(new Runnable() { 1881 @Override 1882 public void run() { 1883 if (session.isStaged() && !success) { 1884 mStagingManager.abortSession(session.mStagedSession); 1885 } 1886 synchronized (mSessions) { 1887 // Child sessions will be removed along with its parent as a whole 1888 if (!session.hasParentSessionId()) { 1889 // Retain policy: 1890 // 1. Don't keep non-staged sessions 1891 // 2. Don't keep explicitly abandoned sessions 1892 // 3. Don't keep sessions that fail validation (isCommitted() is false) 1893 boolean shouldRemove = !session.isStaged() || session.isDestroyed() 1894 || !session.isCommitted(); 1895 if (shouldRemove) { 1896 removeActiveSession(session); 1897 } 1898 } 1899 1900 final File appIconFile = buildAppIconFile(session.sessionId); 1901 if (appIconFile.exists()) { 1902 appIconFile.delete(); 1903 } 1904 1905 mSettingsWriteRequest.runNow(); 1906 } 1907 } 1908 }); 1909 } 1910 1911 public void onSessionPrepared(PackageInstallerSession session) { 1912 // We prepared the destination to write into; we want to persist 1913 // this, but it's not critical enough to block for. 1914 mSettingsWriteRequest.schedule(); 1915 } 1916 1917 public void onSessionSealedBlocking(PackageInstallerSession session) { 1918 // It's very important that we block until we've recorded the 1919 // session as being sealed, since we never want to allow mutation 1920 // after sealing. 1921 mSettingsWriteRequest.runNow(); 1922 } 1923 } 1924 1925 /** 1926 * Send a {@code PackageInstaller.ACTION_SESSION_UPDATED} broadcast intent, containing 1927 * the {@code sessionInfo} in the extra field {@code PackageInstaller.EXTRA_SESSION}. 1928 */ 1929 private void sendSessionUpdatedBroadcast(PackageInstaller.SessionInfo sessionInfo, 1930 int userId) { 1931 if (TextUtils.isEmpty(sessionInfo.installerPackageName)) { 1932 return; 1933 } 1934 Intent sessionUpdatedIntent = new Intent(PackageInstaller.ACTION_SESSION_UPDATED) 1935 .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo) 1936 .setPackage(sessionInfo.installerPackageName); 1937 mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId)); 1938 } 1939 1940 /** 1941 * Abandon unfinished sessions if the installer package has been uninstalled. 1942 * @param installerAppId the app ID of the installer package that has been uninstalled. 1943 * @param userId the user that has the installer package uninstalled. 1944 */ 1945 void onInstallerPackageDeleted(int installerAppId, int userId) { 1946 synchronized (mSessions) { 1947 for (int i = 0; i < mSessions.size(); i++) { 1948 final PackageInstallerSession session = mSessions.valueAt(i); 1949 if (!matchesInstaller(session, installerAppId, userId)) { 1950 continue; 1951 } 1952 // Find parent session and only abandon parent session if installer matches 1953 PackageInstallerSession root = !session.hasParentSessionId() 1954 ? session : mSessions.get(session.getParentSessionId()); 1955 if (root != null && matchesInstaller(root, installerAppId, userId) 1956 && !root.isDestroyed()) { 1957 root.abandon(); 1958 } 1959 } 1960 } 1961 } 1962 1963 private boolean matchesInstaller(PackageInstallerSession session, int installerAppId, 1964 int userId) { 1965 final int installerUid = session.getInstallerUid(); 1966 if (installerAppId == UserHandle.USER_ALL) { 1967 return UserHandle.getAppId(installerUid) == installerAppId; 1968 } else { 1969 return UserHandle.getUid(userId, installerAppId) == installerUid; 1970 } 1971 } 1972 } 1973