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