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