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