PackageInstallerService.java revision 1cb2d0d4bba387665128c62c342e59103ea4be26
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 android.content.pm.PackageManager.INSTALL_ALL_USERS; 20import static android.content.pm.PackageManager.INSTALL_FROM_ADB; 21import static android.content.pm.PackageManager.INSTALL_REPLACE_EXISTING; 22import static com.android.internal.util.XmlUtils.readBitmapAttribute; 23import static com.android.internal.util.XmlUtils.readBooleanAttribute; 24import static com.android.internal.util.XmlUtils.readIntAttribute; 25import static com.android.internal.util.XmlUtils.readLongAttribute; 26import static com.android.internal.util.XmlUtils.readStringAttribute; 27import static com.android.internal.util.XmlUtils.readUriAttribute; 28import static com.android.internal.util.XmlUtils.writeBitmapAttribute; 29import static com.android.internal.util.XmlUtils.writeBooleanAttribute; 30import static com.android.internal.util.XmlUtils.writeIntAttribute; 31import static com.android.internal.util.XmlUtils.writeLongAttribute; 32import static com.android.internal.util.XmlUtils.writeStringAttribute; 33import static com.android.internal.util.XmlUtils.writeUriAttribute; 34import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 35import static org.xmlpull.v1.XmlPullParser.START_TAG; 36 37import android.app.ActivityManager; 38import android.app.AppOpsManager; 39import android.content.Context; 40import android.content.pm.IPackageDeleteObserver; 41import android.content.pm.IPackageInstaller; 42import android.content.pm.IPackageInstallerCallback; 43import android.content.pm.IPackageInstallerSession; 44import android.content.pm.InstallSessionInfo; 45import android.content.pm.InstallSessionParams; 46import android.content.pm.PackageManager; 47import android.graphics.Bitmap; 48import android.os.Binder; 49import android.os.Environment; 50import android.os.FileUtils; 51import android.os.Handler; 52import android.os.HandlerThread; 53import android.os.Looper; 54import android.os.Message; 55import android.os.Process; 56import android.os.RemoteCallbackList; 57import android.os.RemoteException; 58import android.os.SELinux; 59import android.os.UserHandle; 60import android.os.UserManager; 61import android.system.ErrnoException; 62import android.system.Os; 63import android.text.format.DateUtils; 64import android.util.ArraySet; 65import android.util.AtomicFile; 66import android.util.ExceptionUtils; 67import android.util.Log; 68import android.util.Slog; 69import android.util.SparseArray; 70import android.util.Xml; 71 72import com.android.internal.annotations.GuardedBy; 73import com.android.internal.util.FastXmlSerializer; 74import com.android.internal.util.IndentingPrintWriter; 75import com.android.server.IoThread; 76import com.android.server.pm.PackageInstallerSession.Snapshot; 77import com.google.android.collect.Sets; 78 79import libcore.io.IoUtils; 80 81import org.xmlpull.v1.XmlPullParser; 82import org.xmlpull.v1.XmlPullParserException; 83import org.xmlpull.v1.XmlSerializer; 84 85import java.io.File; 86import java.io.FileInputStream; 87import java.io.FileNotFoundException; 88import java.io.FileOutputStream; 89import java.io.FilenameFilter; 90import java.io.IOException; 91import java.security.SecureRandom; 92import java.util.ArrayList; 93import java.util.List; 94import java.util.Objects; 95import java.util.Random; 96 97public class PackageInstallerService extends IPackageInstaller.Stub { 98 private static final String TAG = "PackageInstaller"; 99 private static final boolean LOGD = true; 100 101 // TODO: remove outstanding sessions when installer package goes away 102 // TODO: notify listeners in other users when package has been installed there 103 104 /** XML constants used in {@link #mSessionsFile} */ 105 private static final String TAG_SESSIONS = "sessions"; 106 private static final String TAG_SESSION = "session"; 107 private static final String ATTR_SESSION_ID = "sessionId"; 108 private static final String ATTR_USER_ID = "userId"; 109 private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName"; 110 private static final String ATTR_CREATED_MILLIS = "createdMillis"; 111 private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir"; 112 private static final String ATTR_SEALED = "sealed"; 113 private static final String ATTR_MODE = "mode"; 114 private static final String ATTR_INSTALL_FLAGS = "installFlags"; 115 private static final String ATTR_INSTALL_LOCATION = "installLocation"; 116 private static final String ATTR_SIZE_BYTES = "sizeBytes"; 117 private static final String ATTR_APP_PACKAGE_NAME = "appPackageName"; 118 private static final String ATTR_APP_ICON = "appIcon"; 119 private static final String ATTR_APP_LABEL = "appLabel"; 120 private static final String ATTR_ORIGINATING_URI = "originatingUri"; 121 private static final String ATTR_REFERRER_URI = "referrerUri"; 122 private static final String ATTR_ABI_OVERRIDE = "abiOverride"; 123 124 private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS; 125 private static final long MAX_ACTIVE_SESSIONS = 1024; 126 127 private final Context mContext; 128 private final PackageManagerService mPm; 129 private final AppOpsManager mAppOps; 130 131 private final File mStagingDir; 132 private final HandlerThread mInstallThread; 133 134 private final Callbacks mCallbacks; 135 136 /** 137 * File storing persisted {@link #mSessions}. 138 */ 139 private final AtomicFile mSessionsFile; 140 141 private final InternalCallback mInternalCallback = new InternalCallback(); 142 143 /** 144 * Used for generating session IDs. Since this is created at boot time, 145 * normal random might be predictable. 146 */ 147 private final Random mRandom = new SecureRandom(); 148 149 @GuardedBy("mSessions") 150 private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>(); 151 152 /** Historical sessions kept around for debugging purposes */ 153 @GuardedBy("mSessions") 154 private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>(); 155 156 private static final FilenameFilter sStageFilter = new FilenameFilter() { 157 @Override 158 public boolean accept(File dir, String name) { 159 return name.startsWith("vmdl") && name.endsWith(".tmp"); 160 } 161 }; 162 163 public PackageInstallerService(Context context, PackageManagerService pm, File stagingDir) { 164 mContext = context; 165 mPm = pm; 166 mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 167 168 mStagingDir = stagingDir; 169 170 mInstallThread = new HandlerThread(TAG); 171 mInstallThread.start(); 172 173 mCallbacks = new Callbacks(mInstallThread.getLooper()); 174 175 mSessionsFile = new AtomicFile( 176 new File(Environment.getSystemSecureDirectory(), "install_sessions.xml")); 177 178 synchronized (mSessions) { 179 readSessionsLocked(); 180 181 // Clean up orphaned staging directories 182 final ArraySet<File> stages = Sets.newArraySet(mStagingDir.listFiles(sStageFilter)); 183 for (int i = 0; i < mSessions.size(); i++) { 184 final PackageInstallerSession session = mSessions.valueAt(i); 185 stages.remove(session.sessionStageDir); 186 } 187 for (File stage : stages) { 188 Slog.w(TAG, "Deleting orphan stage " + stage); 189 if (stage.isDirectory()) { 190 FileUtils.deleteContents(stage); 191 } 192 stage.delete(); 193 } 194 } 195 } 196 197 @Deprecated 198 public File allocateSessionDir() throws IOException { 199 synchronized (mSessions) { 200 try { 201 final int sessionId = allocateSessionIdLocked(); 202 return prepareSessionStageDir(sessionId); 203 } catch (IllegalStateException e) { 204 throw new IOException(e); 205 } 206 } 207 } 208 209 private void readSessionsLocked() { 210 if (LOGD) Slog.v(TAG, "readSessionsLocked()"); 211 212 mSessions.clear(); 213 214 FileInputStream fis = null; 215 try { 216 fis = mSessionsFile.openRead(); 217 final XmlPullParser in = Xml.newPullParser(); 218 in.setInput(fis, null); 219 220 int type; 221 while ((type = in.next()) != END_DOCUMENT) { 222 if (type == START_TAG) { 223 final String tag = in.getName(); 224 if (TAG_SESSION.equals(tag)) { 225 final PackageInstallerSession session = readSessionLocked(in); 226 final long age = System.currentTimeMillis() - session.createdMillis; 227 228 final boolean valid; 229 if (age >= MAX_AGE_MILLIS) { 230 Slog.w(TAG, "Abandoning old session first created at " 231 + session.createdMillis); 232 valid = false; 233 } else if (!session.sessionStageDir.exists()) { 234 Slog.w(TAG, "Abandoning session with missing stage " 235 + session.sessionStageDir); 236 valid = false; 237 } else { 238 valid = true; 239 } 240 241 if (valid) { 242 mSessions.put(session.sessionId, session); 243 } else { 244 // Since this is early during boot we don't send 245 // any observer events about the session, but we 246 // keep details around for dumpsys. 247 mHistoricalSessions.put(session.sessionId, session); 248 } 249 } 250 } 251 } 252 } catch (FileNotFoundException e) { 253 // Missing sessions are okay, probably first boot 254 } catch (IOException e) { 255 Log.wtf(TAG, "Failed reading install sessions", e); 256 } catch (XmlPullParserException e) { 257 Log.wtf(TAG, "Failed reading install sessions", e); 258 } finally { 259 IoUtils.closeQuietly(fis); 260 } 261 } 262 263 private PackageInstallerSession readSessionLocked(XmlPullParser in) throws IOException { 264 final int sessionId = readIntAttribute(in, ATTR_SESSION_ID); 265 final int userId = readIntAttribute(in, ATTR_USER_ID); 266 final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME); 267 final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS); 268 final File sessionStageDir = new File(readStringAttribute(in, ATTR_SESSION_STAGE_DIR)); 269 final boolean sealed = readBooleanAttribute(in, ATTR_SEALED); 270 271 final InstallSessionParams params = new InstallSessionParams( 272 InstallSessionParams.MODE_INVALID); 273 params.mode = readIntAttribute(in, ATTR_MODE); 274 params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS); 275 params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION); 276 params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES); 277 params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME); 278 params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON); 279 params.appLabel = readStringAttribute(in, ATTR_APP_LABEL); 280 params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI); 281 params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI); 282 params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE); 283 284 return new PackageInstallerSession(mInternalCallback, mPm, mInstallThread.getLooper(), 285 sessionId, userId, installerPackageName, params, createdMillis, sessionStageDir, 286 sealed); 287 } 288 289 private void writeSessionsLocked() { 290 if (LOGD) Slog.v(TAG, "writeSessionsLocked()"); 291 292 FileOutputStream fos = null; 293 try { 294 fos = mSessionsFile.startWrite(); 295 296 XmlSerializer out = new FastXmlSerializer(); 297 out.setOutput(fos, "utf-8"); 298 out.startDocument(null, true); 299 out.startTag(null, TAG_SESSIONS); 300 final int size = mSessions.size(); 301 for (int i = 0; i < size; i++) { 302 final PackageInstallerSession session = mSessions.valueAt(i); 303 writeSessionLocked(out, session); 304 } 305 out.endTag(null, TAG_SESSIONS); 306 out.endDocument(); 307 308 mSessionsFile.finishWrite(fos); 309 } catch (IOException e) { 310 if (fos != null) { 311 mSessionsFile.failWrite(fos); 312 } 313 } 314 } 315 316 private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session) 317 throws IOException { 318 final InstallSessionParams params = session.params; 319 final Snapshot snapshot = session.snapshot(); 320 321 out.startTag(null, TAG_SESSION); 322 323 writeIntAttribute(out, ATTR_SESSION_ID, session.sessionId); 324 writeIntAttribute(out, ATTR_USER_ID, session.userId); 325 writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME, 326 session.installerPackageName); 327 writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis); 328 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR, 329 session.sessionStageDir.getAbsolutePath()); 330 writeBooleanAttribute(out, ATTR_SEALED, snapshot.sealed); 331 332 writeIntAttribute(out, ATTR_MODE, params.mode); 333 writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags); 334 writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation); 335 writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes); 336 writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName); 337 writeBitmapAttribute(out, ATTR_APP_ICON, params.appIcon); 338 writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel); 339 writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri); 340 writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri); 341 writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride); 342 343 out.endTag(null, TAG_SESSION); 344 } 345 346 private void writeSessionsAsync() { 347 IoThread.getHandler().post(new Runnable() { 348 @Override 349 public void run() { 350 synchronized (mSessions) { 351 writeSessionsLocked(); 352 } 353 } 354 }); 355 } 356 357 @Override 358 public int createSession(InstallSessionParams params, String installerPackageName, int userId) { 359 final int callingUid = Binder.getCallingUid(); 360 mPm.enforceCrossUserPermission(callingUid, userId, true, "createSession"); 361 362 if (mPm.isUserRestricted(UserHandle.getUserId(callingUid), 363 UserManager.DISALLOW_INSTALL_APPS)) { 364 throw new SecurityException("User restriction prevents installing"); 365 } 366 367 if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) { 368 installerPackageName = "com.android.shell"; 369 370 params.installFlags |= INSTALL_FROM_ADB; 371 372 } else { 373 mAppOps.checkPackage(callingUid, installerPackageName); 374 375 params.installFlags &= ~INSTALL_FROM_ADB; 376 params.installFlags &= ~INSTALL_ALL_USERS; 377 params.installFlags |= INSTALL_REPLACE_EXISTING; 378 } 379 380 switch (params.mode) { 381 case InstallSessionParams.MODE_FULL_INSTALL: 382 case InstallSessionParams.MODE_INHERIT_EXISTING: 383 break; 384 default: 385 throw new IllegalArgumentException("Params must have valid mode set"); 386 } 387 388 // Defensively resize giant app icons 389 if (params.appIcon != null) { 390 final ActivityManager am = (ActivityManager) mContext.getSystemService( 391 Context.ACTIVITY_SERVICE); 392 final int iconSize = am.getLauncherLargeIconSize(); 393 if ((params.appIcon.getWidth() > iconSize * 2) 394 || (params.appIcon.getHeight() > iconSize * 2)) { 395 params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize, 396 true); 397 } 398 } 399 400 // Sanity check that install could fit 401 if (params.sizeBytes > 0) { 402 try { 403 mPm.freeStorage(params.sizeBytes); 404 } catch (IOException e) { 405 throw ExceptionUtils.wrap(e); 406 } 407 } 408 409 final int sessionId; 410 final PackageInstallerSession session; 411 synchronized (mSessions) { 412 // Sanity check that installer isn't going crazy 413 final int activeCount = getSessionCountLocked(callingUid); 414 if (activeCount >= MAX_ACTIVE_SESSIONS) { 415 throw new IllegalStateException("Too many active sessions for UID " + callingUid); 416 } 417 418 sessionId = allocateSessionIdLocked(); 419 420 final long createdMillis = System.currentTimeMillis(); 421 final File sessionStageDir = prepareSessionStageDir(sessionId); 422 423 session = new PackageInstallerSession(mInternalCallback, mPm, 424 mInstallThread.getLooper(), sessionId, userId, installerPackageName, params, 425 createdMillis, sessionStageDir, false); 426 mSessions.put(sessionId, session); 427 } 428 429 mCallbacks.notifySessionCreated(session.sessionId, session.userId); 430 writeSessionsAsync(); 431 return sessionId; 432 } 433 434 @Override 435 public IPackageInstallerSession openSession(int sessionId) { 436 synchronized (mSessions) { 437 final PackageInstallerSession session = mSessions.get(sessionId); 438 if (session == null) { 439 throw new IllegalStateException("Missing session " + sessionId); 440 } 441 if (!isCallingUidOwner(session)) { 442 throw new SecurityException("Caller has no access to session " + sessionId); 443 } 444 if (session.openCount.getAndIncrement() == 0) { 445 mCallbacks.notifySessionOpened(sessionId, session.userId); 446 } 447 return session; 448 } 449 } 450 451 private int allocateSessionIdLocked() { 452 int n = 0; 453 int sessionId; 454 do { 455 sessionId = mRandom.nextInt(Integer.MAX_VALUE); 456 if (mSessions.get(sessionId) == null) { 457 return sessionId; 458 } 459 } while (n++ < 32); 460 461 throw new IllegalStateException("Failed to allocate session ID"); 462 } 463 464 private File prepareSessionStageDir(int sessionId) { 465 final File file = new File(mStagingDir, "vmdl" + sessionId + ".tmp"); 466 467 if (file.exists()) { 468 throw new IllegalStateException("Session dir already exists: " + file); 469 } 470 471 try { 472 Os.mkdir(file.getAbsolutePath(), 0755); 473 Os.chmod(file.getAbsolutePath(), 0755); 474 } catch (ErrnoException e) { 475 // This purposefully throws if directory already exists 476 throw new IllegalStateException("Failed to prepare session dir", e); 477 } 478 479 if (!SELinux.restorecon(file)) { 480 throw new IllegalStateException("Failed to restorecon session dir"); 481 } 482 483 return file; 484 } 485 486 @Override 487 public InstallSessionInfo getSessionInfo(int sessionId) { 488 synchronized (mSessions) { 489 final PackageInstallerSession session = mSessions.get(sessionId); 490 if (!isCallingUidOwner(session)) { 491 enforceCallerCanReadSessions(); 492 } 493 return session != null ? session.generateInfo() : null; 494 } 495 } 496 497 @Override 498 public List<InstallSessionInfo> getAllSessions(int userId) { 499 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getAllSessions"); 500 enforceCallerCanReadSessions(); 501 502 final List<InstallSessionInfo> result = new ArrayList<>(); 503 synchronized (mSessions) { 504 for (int i = 0; i < mSessions.size(); i++) { 505 final PackageInstallerSession session = mSessions.valueAt(i); 506 if (session.userId == userId) { 507 result.add(session.generateInfo()); 508 } 509 } 510 } 511 return result; 512 } 513 514 @Override 515 public List<InstallSessionInfo> getMySessions(String installerPackageName, int userId) { 516 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getMySessions"); 517 mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName); 518 519 final List<InstallSessionInfo> result = new ArrayList<>(); 520 synchronized (mSessions) { 521 for (int i = 0; i < mSessions.size(); i++) { 522 final PackageInstallerSession session = mSessions.valueAt(i); 523 if (Objects.equals(session.installerPackageName, installerPackageName) 524 && session.userId == userId) { 525 result.add(session.generateInfo()); 526 } 527 } 528 } 529 return result; 530 } 531 532 @Override 533 public void uninstall(String packageName, int flags, IPackageDeleteObserver observer, 534 int userId) { 535 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstall"); 536 537 // TODO: enforce installer of record or permission 538 mPm.deletePackageAsUser(packageName, observer, userId, flags); 539 } 540 541 @Override 542 public void uninstallSplit(String basePackageName, String overlayName, int flags, 543 IPackageDeleteObserver observer, int userId) { 544 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstallSplit"); 545 546 // TODO: flesh out once PM has split support 547 throw new UnsupportedOperationException(); 548 } 549 550 @Override 551 public void registerCallback(IPackageInstallerCallback callback, int userId) { 552 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "registerCallback"); 553 enforceCallerCanReadSessions(); 554 555 mCallbacks.register(callback, userId); 556 } 557 558 @Override 559 public void unregisterCallback(IPackageInstallerCallback callback) { 560 mCallbacks.unregister(callback); 561 } 562 563 private int getSessionCountLocked(int installerUid) { 564 int count = 0; 565 final int size = mSessions.size(); 566 for (int i = 0; i < size; i++) { 567 final PackageInstallerSession session = mSessions.valueAt(i); 568 if (session.installerUid == installerUid) { 569 count++; 570 } 571 } 572 return count; 573 } 574 575 private boolean isCallingUidOwner(PackageInstallerSession session) { 576 final int callingUid = Binder.getCallingUid(); 577 if (callingUid == Process.ROOT_UID) { 578 return true; 579 } else { 580 return (session != null) && (callingUid == session.installerUid); 581 } 582 } 583 584 /** 585 * We allow those with permission, or the current home app. 586 */ 587 private void enforceCallerCanReadSessions() { 588 final boolean hasPermission = (mContext.checkCallingOrSelfPermission( 589 android.Manifest.permission.READ_INSTALL_SESSIONS) 590 == PackageManager.PERMISSION_GRANTED); 591 final boolean isHomeApp = mPm.checkCallerIsHomeApp(); 592 if (hasPermission || isHomeApp) { 593 return; 594 } else { 595 throw new SecurityException("Caller must be current home app to read install sessions"); 596 } 597 } 598 599 private static class Callbacks extends Handler { 600 private static final int MSG_SESSION_CREATED = 1; 601 private static final int MSG_SESSION_OPENED = 2; 602 private static final int MSG_SESSION_PROGRESS_CHANGED = 3; 603 private static final int MSG_SESSION_CLOSED = 4; 604 private static final int MSG_SESSION_FINISHED = 5; 605 606 private final RemoteCallbackList<IPackageInstallerCallback> 607 mCallbacks = new RemoteCallbackList<>(); 608 609 public Callbacks(Looper looper) { 610 super(looper); 611 } 612 613 public void register(IPackageInstallerCallback callback, int userId) { 614 mCallbacks.register(callback, new UserHandle(userId)); 615 } 616 617 public void unregister(IPackageInstallerCallback callback) { 618 mCallbacks.unregister(callback); 619 } 620 621 @Override 622 public void handleMessage(Message msg) { 623 final int userId = msg.arg2; 624 final int n = mCallbacks.beginBroadcast(); 625 for (int i = 0; i < n; i++) { 626 final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i); 627 final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i); 628 // TODO: dispatch notifications for slave profiles 629 if (userId == user.getIdentifier()) { 630 try { 631 invokeCallback(callback, msg); 632 } catch (RemoteException ignored) { 633 } 634 } 635 } 636 mCallbacks.finishBroadcast(); 637 } 638 639 private void invokeCallback(IPackageInstallerCallback callback, Message msg) 640 throws RemoteException { 641 final int sessionId = msg.arg1; 642 switch (msg.what) { 643 case MSG_SESSION_CREATED: 644 callback.onSessionCreated(sessionId); 645 break; 646 case MSG_SESSION_OPENED: 647 callback.onSessionOpened(sessionId); 648 break; 649 case MSG_SESSION_PROGRESS_CHANGED: 650 callback.onSessionProgressChanged(sessionId, (float) msg.obj); 651 break; 652 case MSG_SESSION_CLOSED: 653 callback.onSessionClosed(sessionId); 654 break; 655 case MSG_SESSION_FINISHED: 656 callback.onSessionFinished(sessionId, (boolean) msg.obj); 657 break; 658 } 659 } 660 661 private void notifySessionCreated(int sessionId, int userId) { 662 obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget(); 663 } 664 665 private void notifySessionOpened(int sessionId, int userId) { 666 obtainMessage(MSG_SESSION_OPENED, sessionId, userId).sendToTarget(); 667 } 668 669 private void notifySessionProgressChanged(int sessionId, int userId, float progress) { 670 obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget(); 671 } 672 673 private void notifySessionClosed(int sessionId, int userId) { 674 obtainMessage(MSG_SESSION_CLOSED, sessionId, userId).sendToTarget(); 675 } 676 677 public void notifySessionFinished(int sessionId, int userId, boolean success) { 678 obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget(); 679 } 680 } 681 682 void dump(IndentingPrintWriter pw) { 683 synchronized (mSessions) { 684 pw.println("Active install sessions:"); 685 pw.increaseIndent(); 686 int N = mSessions.size(); 687 for (int i = 0; i < N; i++) { 688 final PackageInstallerSession session = mSessions.valueAt(i); 689 session.dump(pw); 690 pw.println(); 691 } 692 pw.println(); 693 pw.decreaseIndent(); 694 695 pw.println("Historical install sessions:"); 696 pw.increaseIndent(); 697 N = mHistoricalSessions.size(); 698 for (int i = 0; i < N; i++) { 699 final PackageInstallerSession session = mHistoricalSessions.valueAt(i); 700 session.dump(pw); 701 pw.println(); 702 } 703 pw.println(); 704 pw.decreaseIndent(); 705 } 706 } 707 708 class InternalCallback { 709 public void onSessionProgressChanged(PackageInstallerSession session, float progress) { 710 mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress); 711 } 712 713 public void onSessionClosed(PackageInstallerSession session) { 714 mCallbacks.notifySessionClosed(session.sessionId, session.userId); 715 } 716 717 public void onSessionFinished(PackageInstallerSession session, boolean success) { 718 mCallbacks.notifySessionFinished(session.sessionId, session.userId, success); 719 synchronized (mSessions) { 720 mSessions.remove(session.sessionId); 721 mHistoricalSessions.put(session.sessionId, session); 722 } 723 writeSessionsAsync(); 724 } 725 } 726} 727