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