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