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