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