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