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