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