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