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.app.ActivityManager; 34 import android.app.AppOpsManager; 35 import android.app.PackageDeleteObserver; 36 import android.app.PackageInstallObserver; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.IntentSender; 40 import android.content.IntentSender.SendIntentException; 41 import android.content.pm.IPackageInstaller; 42 import android.content.pm.IPackageInstallerCallback; 43 import android.content.pm.IPackageInstallerSession; 44 import android.content.pm.PackageInstaller; 45 import android.content.pm.PackageInstaller.SessionInfo; 46 import android.content.pm.PackageInstaller.SessionParams; 47 import android.content.pm.PackageManager; 48 import android.content.pm.ParceledListSlice; 49 import android.graphics.Bitmap; 50 import android.graphics.Bitmap.CompressFormat; 51 import android.graphics.BitmapFactory; 52 import android.net.Uri; 53 import android.os.Binder; 54 import android.os.Bundle; 55 import android.os.Environment; 56 import android.os.FileUtils; 57 import android.os.Handler; 58 import android.os.HandlerThread; 59 import android.os.Looper; 60 import android.os.Message; 61 import android.os.Process; 62 import android.os.RemoteCallbackList; 63 import android.os.RemoteException; 64 import android.os.SELinux; 65 import android.os.UserHandle; 66 import android.os.UserManager; 67 import android.system.ErrnoException; 68 import android.system.Os; 69 import android.text.TextUtils; 70 import android.text.format.DateUtils; 71 import android.util.ArraySet; 72 import android.util.AtomicFile; 73 import android.util.ExceptionUtils; 74 import android.util.Slog; 75 import android.util.SparseArray; 76 import android.util.SparseBooleanArray; 77 import android.util.Xml; 78 79 import com.android.internal.annotations.GuardedBy; 80 import com.android.internal.content.PackageHelper; 81 import com.android.internal.util.FastXmlSerializer; 82 import com.android.internal.util.IndentingPrintWriter; 83 import com.android.server.IoThread; 84 import com.google.android.collect.Sets; 85 86 import libcore.io.IoUtils; 87 88 import org.xmlpull.v1.XmlPullParser; 89 import org.xmlpull.v1.XmlPullParserException; 90 import org.xmlpull.v1.XmlSerializer; 91 92 import java.io.File; 93 import java.io.FileInputStream; 94 import java.io.FileNotFoundException; 95 import java.io.FileOutputStream; 96 import java.io.FilenameFilter; 97 import java.io.IOException; 98 import java.security.SecureRandom; 99 import java.util.ArrayList; 100 import java.util.List; 101 import java.util.Objects; 102 import java.util.Random; 103 104 public class PackageInstallerService extends IPackageInstaller.Stub { 105 private static final String TAG = "PackageInstaller"; 106 private static final boolean LOGD = false; 107 108 // TODO: remove outstanding sessions when installer package goes away 109 // TODO: notify listeners in other users when package has been installed there 110 // TODO: purge expired sessions periodically in addition to at reboot 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_INSTALLER_UID = "installerUid"; 119 private static final String ATTR_CREATED_MILLIS = "createdMillis"; 120 private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir"; 121 private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid"; 122 private static final String ATTR_PREPARED = "prepared"; 123 private static final String ATTR_SEALED = "sealed"; 124 private static final String ATTR_MODE = "mode"; 125 private static final String ATTR_INSTALL_FLAGS = "installFlags"; 126 private static final String ATTR_INSTALL_LOCATION = "installLocation"; 127 private static final String ATTR_SIZE_BYTES = "sizeBytes"; 128 private static final String ATTR_APP_PACKAGE_NAME = "appPackageName"; 129 @Deprecated 130 private static final String ATTR_APP_ICON = "appIcon"; 131 private static final String ATTR_APP_LABEL = "appLabel"; 132 private static final String ATTR_ORIGINATING_URI = "originatingUri"; 133 private static final String ATTR_REFERRER_URI = "referrerUri"; 134 private static final String ATTR_ABI_OVERRIDE = "abiOverride"; 135 136 /** Automatically destroy sessions older than this */ 137 private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS; 138 /** Upper bound on number of active sessions for a UID */ 139 private static final long MAX_ACTIVE_SESSIONS = 1024; 140 /** Upper bound on number of historical sessions for a UID */ 141 private static final long MAX_HISTORICAL_SESSIONS = 1048576; 142 143 private final Context mContext; 144 private final PackageManagerService mPm; 145 private final AppOpsManager mAppOps; 146 147 private final File mStagingDir; 148 private final HandlerThread mInstallThread; 149 private final Handler mInstallHandler; 150 151 private final Callbacks mCallbacks; 152 153 /** 154 * File storing persisted {@link #mSessions} metadata. 155 */ 156 private final AtomicFile mSessionsFile; 157 158 /** 159 * Directory storing persisted {@link #mSessions} metadata which is too 160 * heavy to store directly in {@link #mSessionsFile}. 161 */ 162 private final File mSessionsDir; 163 164 private final InternalCallback mInternalCallback = new InternalCallback(); 165 166 /** 167 * Used for generating session IDs. Since this is created at boot time, 168 * normal random might be predictable. 169 */ 170 private final Random mRandom = new SecureRandom(); 171 172 @GuardedBy("mSessions") 173 private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>(); 174 175 /** Historical sessions kept around for debugging purposes */ 176 @GuardedBy("mSessions") 177 private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>(); 178 179 /** Sessions allocated to legacy users */ 180 @GuardedBy("mSessions") 181 private final SparseBooleanArray mLegacySessions = new SparseBooleanArray(); 182 183 private static final FilenameFilter sStageFilter = new FilenameFilter() { 184 @Override 185 public boolean accept(File dir, String name) { 186 return isStageName(name); 187 } 188 }; 189 PackageInstallerService(Context context, PackageManagerService pm, File stagingDir)190 public PackageInstallerService(Context context, PackageManagerService pm, File stagingDir) { 191 mContext = context; 192 mPm = pm; 193 mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 194 195 mStagingDir = stagingDir; 196 197 mInstallThread = new HandlerThread(TAG); 198 mInstallThread.start(); 199 200 mInstallHandler = new Handler(mInstallThread.getLooper()); 201 202 mCallbacks = new Callbacks(mInstallThread.getLooper()); 203 204 mSessionsFile = new AtomicFile( 205 new File(Environment.getSystemSecureDirectory(), "install_sessions.xml")); 206 mSessionsDir = new File(Environment.getSystemSecureDirectory(), "install_sessions"); 207 mSessionsDir.mkdirs(); 208 209 synchronized (mSessions) { 210 readSessionsLocked(); 211 212 final ArraySet<File> unclaimedStages = Sets.newArraySet( 213 mStagingDir.listFiles(sStageFilter)); 214 final ArraySet<File> unclaimedIcons = Sets.newArraySet( 215 mSessionsDir.listFiles()); 216 217 // Ignore stages and icons claimed by active sessions 218 for (int i = 0; i < mSessions.size(); i++) { 219 final PackageInstallerSession session = mSessions.valueAt(i); 220 unclaimedStages.remove(session.stageDir); 221 unclaimedIcons.remove(buildAppIconFile(session.sessionId)); 222 } 223 224 // Clean up orphaned staging directories 225 for (File stage : unclaimedStages) { 226 Slog.w(TAG, "Deleting orphan stage " + stage); 227 if (stage.isDirectory()) { 228 FileUtils.deleteContents(stage); 229 } 230 stage.delete(); 231 } 232 233 // Clean up orphaned icons 234 for (File icon : unclaimedIcons) { 235 Slog.w(TAG, "Deleting orphan icon " + icon); 236 icon.delete(); 237 } 238 } 239 } 240 onSecureContainersAvailable()241 public void onSecureContainersAvailable() { 242 synchronized (mSessions) { 243 final ArraySet<String> unclaimed = new ArraySet<>(); 244 for (String cid : PackageHelper.getSecureContainerList()) { 245 if (isStageName(cid)) { 246 unclaimed.add(cid); 247 } 248 } 249 250 // Ignore stages claimed by active sessions 251 for (int i = 0; i < mSessions.size(); i++) { 252 final PackageInstallerSession session = mSessions.valueAt(i); 253 final String cid = session.stageCid; 254 255 if (unclaimed.remove(cid)) { 256 // Claimed by active session, mount it 257 PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(), 258 Process.SYSTEM_UID); 259 } 260 } 261 262 // Clean up orphaned staging containers 263 for (String cid : unclaimed) { 264 Slog.w(TAG, "Deleting orphan container " + cid); 265 PackageHelper.destroySdDir(cid); 266 } 267 } 268 } 269 isStageName(String name)270 public static boolean isStageName(String name) { 271 final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp"); 272 final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp"); 273 final boolean isLegacyContainer = name.startsWith("smdl2tmp"); 274 return isFile || isContainer || isLegacyContainer; 275 } 276 277 @Deprecated allocateInternalStageDirLegacy()278 public File allocateInternalStageDirLegacy() throws IOException { 279 synchronized (mSessions) { 280 try { 281 final int sessionId = allocateSessionIdLocked(); 282 mLegacySessions.put(sessionId, true); 283 final File stageDir = buildInternalStageDir(sessionId); 284 prepareInternalStageDir(stageDir); 285 return stageDir; 286 } catch (IllegalStateException e) { 287 throw new IOException(e); 288 } 289 } 290 } 291 292 @Deprecated allocateExternalStageCidLegacy()293 public String allocateExternalStageCidLegacy() { 294 synchronized (mSessions) { 295 final int sessionId = allocateSessionIdLocked(); 296 mLegacySessions.put(sessionId, true); 297 return "smdl" + sessionId + ".tmp"; 298 } 299 } 300 readSessionsLocked()301 private void readSessionsLocked() { 302 if (LOGD) Slog.v(TAG, "readSessionsLocked()"); 303 304 mSessions.clear(); 305 306 FileInputStream fis = null; 307 try { 308 fis = mSessionsFile.openRead(); 309 final XmlPullParser in = Xml.newPullParser(); 310 in.setInput(fis, null); 311 312 int type; 313 while ((type = in.next()) != END_DOCUMENT) { 314 if (type == START_TAG) { 315 final String tag = in.getName(); 316 if (TAG_SESSION.equals(tag)) { 317 final PackageInstallerSession session = readSessionLocked(in); 318 final long age = System.currentTimeMillis() - session.createdMillis; 319 320 final boolean valid; 321 if (age >= MAX_AGE_MILLIS) { 322 Slog.w(TAG, "Abandoning old session first created at " 323 + session.createdMillis); 324 valid = false; 325 } else if (session.stageDir != null 326 && !session.stageDir.exists()) { 327 Slog.w(TAG, "Abandoning internal session with missing stage " 328 + session.stageDir); 329 valid = false; 330 } else { 331 valid = true; 332 } 333 334 if (valid) { 335 mSessions.put(session.sessionId, session); 336 } else { 337 // Since this is early during boot we don't send 338 // any observer events about the session, but we 339 // keep details around for dumpsys. 340 mHistoricalSessions.put(session.sessionId, session); 341 } 342 } 343 } 344 } 345 } catch (FileNotFoundException e) { 346 // Missing sessions are okay, probably first boot 347 } catch (IOException e) { 348 Slog.wtf(TAG, "Failed reading install sessions", e); 349 } catch (XmlPullParserException e) { 350 Slog.wtf(TAG, "Failed reading install sessions", e); 351 } finally { 352 IoUtils.closeQuietly(fis); 353 } 354 } 355 readSessionLocked(XmlPullParser in)356 private PackageInstallerSession readSessionLocked(XmlPullParser in) throws IOException { 357 final int sessionId = readIntAttribute(in, ATTR_SESSION_ID); 358 final int userId = readIntAttribute(in, ATTR_USER_ID); 359 final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME); 360 final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, 361 mPm.getPackageUid(installerPackageName, userId)); 362 final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS); 363 final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR); 364 final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null; 365 final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID); 366 final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true); 367 final boolean sealed = readBooleanAttribute(in, ATTR_SEALED); 368 369 final SessionParams params = new SessionParams( 370 SessionParams.MODE_INVALID); 371 params.mode = readIntAttribute(in, ATTR_MODE); 372 params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS); 373 params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION); 374 params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES); 375 params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME); 376 params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON); 377 params.appLabel = readStringAttribute(in, ATTR_APP_LABEL); 378 params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI); 379 params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI); 380 params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE); 381 382 final File appIconFile = buildAppIconFile(sessionId); 383 if (appIconFile.exists()) { 384 params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath()); 385 params.appIconLastModified = appIconFile.lastModified(); 386 } 387 388 return new PackageInstallerSession(mInternalCallback, mContext, mPm, 389 mInstallThread.getLooper(), sessionId, userId, installerPackageName, installerUid, 390 params, createdMillis, stageDir, stageCid, prepared, sealed); 391 } 392 writeSessionsLocked()393 private void writeSessionsLocked() { 394 if (LOGD) Slog.v(TAG, "writeSessionsLocked()"); 395 396 FileOutputStream fos = null; 397 try { 398 fos = mSessionsFile.startWrite(); 399 400 XmlSerializer out = new FastXmlSerializer(); 401 out.setOutput(fos, "utf-8"); 402 out.startDocument(null, true); 403 out.startTag(null, TAG_SESSIONS); 404 final int size = mSessions.size(); 405 for (int i = 0; i < size; i++) { 406 final PackageInstallerSession session = mSessions.valueAt(i); 407 writeSessionLocked(out, session); 408 } 409 out.endTag(null, TAG_SESSIONS); 410 out.endDocument(); 411 412 mSessionsFile.finishWrite(fos); 413 } catch (IOException e) { 414 if (fos != null) { 415 mSessionsFile.failWrite(fos); 416 } 417 } 418 } 419 writeSessionLocked(XmlSerializer out, PackageInstallerSession session)420 private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session) 421 throws IOException { 422 final SessionParams params = session.params; 423 424 out.startTag(null, TAG_SESSION); 425 426 writeIntAttribute(out, ATTR_SESSION_ID, session.sessionId); 427 writeIntAttribute(out, ATTR_USER_ID, session.userId); 428 writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME, 429 session.installerPackageName); 430 writeIntAttribute(out, ATTR_INSTALLER_UID, session.installerUid); 431 writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis); 432 if (session.stageDir != null) { 433 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR, 434 session.stageDir.getAbsolutePath()); 435 } 436 if (session.stageCid != null) { 437 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid); 438 } 439 writeBooleanAttribute(out, ATTR_PREPARED, session.isPrepared()); 440 writeBooleanAttribute(out, ATTR_SEALED, session.isSealed()); 441 442 writeIntAttribute(out, ATTR_MODE, params.mode); 443 writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags); 444 writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation); 445 writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes); 446 writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName); 447 writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel); 448 writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri); 449 writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri); 450 writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride); 451 452 // Persist app icon if changed since last written 453 final File appIconFile = buildAppIconFile(session.sessionId); 454 if (params.appIcon == null && appIconFile.exists()) { 455 appIconFile.delete(); 456 } else if (params.appIcon != null 457 && appIconFile.lastModified() != params.appIconLastModified) { 458 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile); 459 FileOutputStream os = null; 460 try { 461 os = new FileOutputStream(appIconFile); 462 params.appIcon.compress(CompressFormat.PNG, 90, os); 463 } catch (IOException e) { 464 Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage()); 465 } finally { 466 IoUtils.closeQuietly(os); 467 } 468 469 params.appIconLastModified = appIconFile.lastModified(); 470 } 471 472 out.endTag(null, TAG_SESSION); 473 } 474 buildAppIconFile(int sessionId)475 private File buildAppIconFile(int sessionId) { 476 return new File(mSessionsDir, "app_icon." + sessionId + ".png"); 477 } 478 writeSessionsAsync()479 private void writeSessionsAsync() { 480 IoThread.getHandler().post(new Runnable() { 481 @Override 482 public void run() { 483 synchronized (mSessions) { 484 writeSessionsLocked(); 485 } 486 } 487 }); 488 } 489 490 @Override createSession(SessionParams params, String installerPackageName, int userId)491 public int createSession(SessionParams params, String installerPackageName, int userId) { 492 try { 493 return createSessionInternal(params, installerPackageName, userId); 494 } catch (IOException e) { 495 throw ExceptionUtils.wrap(e); 496 } 497 } 498 createSessionInternal(SessionParams params, String installerPackageName, int userId)499 private int createSessionInternal(SessionParams params, String installerPackageName, int userId) 500 throws IOException { 501 final int callingUid = Binder.getCallingUid(); 502 mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession"); 503 504 if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { 505 throw new SecurityException("User restriction prevents installing"); 506 } 507 508 if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) { 509 params.installFlags |= PackageManager.INSTALL_FROM_ADB; 510 511 } else { 512 mAppOps.checkPackage(callingUid, installerPackageName); 513 514 params.installFlags &= ~PackageManager.INSTALL_FROM_ADB; 515 params.installFlags &= ~PackageManager.INSTALL_ALL_USERS; 516 params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; 517 } 518 519 // Defensively resize giant app icons 520 if (params.appIcon != null) { 521 final ActivityManager am = (ActivityManager) mContext.getSystemService( 522 Context.ACTIVITY_SERVICE); 523 final int iconSize = am.getLauncherLargeIconSize(); 524 if ((params.appIcon.getWidth() > iconSize * 2) 525 || (params.appIcon.getHeight() > iconSize * 2)) { 526 params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize, 527 true); 528 } 529 } 530 531 if (params.mode == SessionParams.MODE_FULL_INSTALL 532 || params.mode == SessionParams.MODE_INHERIT_EXISTING) { 533 // Resolve best location for install, based on combination of 534 // requested install flags, delta size, and manifest settings. 535 final long ident = Binder.clearCallingIdentity(); 536 try { 537 final int resolved = PackageHelper.resolveInstallLocation(mContext, 538 params.appPackageName, params.installLocation, params.sizeBytes, 539 params.installFlags); 540 541 if (resolved == PackageHelper.RECOMMEND_INSTALL_INTERNAL) { 542 params.setInstallFlagsInternal(); 543 } else if (resolved == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) { 544 params.setInstallFlagsExternal(); 545 } else { 546 throw new IOException("No storage with enough free space; res=" + resolved); 547 } 548 } finally { 549 Binder.restoreCallingIdentity(ident); 550 } 551 } else { 552 throw new IllegalArgumentException("Invalid install mode: " + params.mode); 553 } 554 555 final int sessionId; 556 final PackageInstallerSession session; 557 synchronized (mSessions) { 558 // Sanity check that installer isn't going crazy 559 final int activeCount = getSessionCount(mSessions, callingUid); 560 if (activeCount >= MAX_ACTIVE_SESSIONS) { 561 throw new IllegalStateException( 562 "Too many active sessions for UID " + callingUid); 563 } 564 final int historicalCount = getSessionCount(mHistoricalSessions, callingUid); 565 if (historicalCount >= MAX_HISTORICAL_SESSIONS) { 566 throw new IllegalStateException( 567 "Too many historical sessions for UID " + callingUid); 568 } 569 570 final long createdMillis = System.currentTimeMillis(); 571 sessionId = allocateSessionIdLocked(); 572 573 // We're staging to exactly one location 574 File stageDir = null; 575 String stageCid = null; 576 if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) { 577 stageDir = buildInternalStageDir(sessionId); 578 } else { 579 stageCid = buildExternalStageCid(sessionId); 580 } 581 582 session = new PackageInstallerSession(mInternalCallback, mContext, mPm, 583 mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid, 584 params, createdMillis, stageDir, stageCid, false, false); 585 mSessions.put(sessionId, session); 586 } 587 588 mCallbacks.notifySessionCreated(session.sessionId, session.userId); 589 writeSessionsAsync(); 590 return sessionId; 591 } 592 593 @Override updateSessionAppIcon(int sessionId, Bitmap appIcon)594 public void updateSessionAppIcon(int sessionId, Bitmap appIcon) { 595 synchronized (mSessions) { 596 final PackageInstallerSession session = mSessions.get(sessionId); 597 if (session == null || !isCallingUidOwner(session)) { 598 throw new SecurityException("Caller has no access to session " + sessionId); 599 } 600 601 // Defensively resize giant app icons 602 if (appIcon != null) { 603 final ActivityManager am = (ActivityManager) mContext.getSystemService( 604 Context.ACTIVITY_SERVICE); 605 final int iconSize = am.getLauncherLargeIconSize(); 606 if ((appIcon.getWidth() > iconSize * 2) 607 || (appIcon.getHeight() > iconSize * 2)) { 608 appIcon = Bitmap.createScaledBitmap(appIcon, iconSize, iconSize, true); 609 } 610 } 611 612 session.params.appIcon = appIcon; 613 session.params.appIconLastModified = -1; 614 615 mInternalCallback.onSessionBadgingChanged(session); 616 } 617 } 618 619 @Override updateSessionAppLabel(int sessionId, String appLabel)620 public void updateSessionAppLabel(int sessionId, String appLabel) { 621 synchronized (mSessions) { 622 final PackageInstallerSession session = mSessions.get(sessionId); 623 if (session == null || !isCallingUidOwner(session)) { 624 throw new SecurityException("Caller has no access to session " + sessionId); 625 } 626 session.params.appLabel = appLabel; 627 mInternalCallback.onSessionBadgingChanged(session); 628 } 629 } 630 631 @Override abandonSession(int sessionId)632 public void abandonSession(int sessionId) { 633 synchronized (mSessions) { 634 final PackageInstallerSession session = mSessions.get(sessionId); 635 if (session == null || !isCallingUidOwner(session)) { 636 throw new SecurityException("Caller has no access to session " + sessionId); 637 } 638 session.abandon(); 639 } 640 } 641 642 @Override openSession(int sessionId)643 public IPackageInstallerSession openSession(int sessionId) { 644 try { 645 return openSessionInternal(sessionId); 646 } catch (IOException e) { 647 throw ExceptionUtils.wrap(e); 648 } 649 } 650 openSessionInternal(int sessionId)651 private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException { 652 synchronized (mSessions) { 653 final PackageInstallerSession session = mSessions.get(sessionId); 654 if (session == null || !isCallingUidOwner(session)) { 655 throw new SecurityException("Caller has no access to session " + sessionId); 656 } 657 session.open(); 658 return session; 659 } 660 } 661 allocateSessionIdLocked()662 private int allocateSessionIdLocked() { 663 int n = 0; 664 int sessionId; 665 do { 666 sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1; 667 if (mSessions.get(sessionId) == null && mHistoricalSessions.get(sessionId) == null 668 && !mLegacySessions.get(sessionId, false)) { 669 return sessionId; 670 } 671 } while (n++ < 32); 672 673 throw new IllegalStateException("Failed to allocate session ID"); 674 } 675 buildInternalStageDir(int sessionId)676 private File buildInternalStageDir(int sessionId) { 677 return new File(mStagingDir, "vmdl" + sessionId + ".tmp"); 678 } 679 prepareInternalStageDir(File stageDir)680 static void prepareInternalStageDir(File stageDir) throws IOException { 681 if (stageDir.exists()) { 682 throw new IOException("Session dir already exists: " + stageDir); 683 } 684 685 try { 686 Os.mkdir(stageDir.getAbsolutePath(), 0755); 687 Os.chmod(stageDir.getAbsolutePath(), 0755); 688 } catch (ErrnoException e) { 689 // This purposefully throws if directory already exists 690 throw new IOException("Failed to prepare session dir: " + stageDir, e); 691 } 692 693 if (!SELinux.restorecon(stageDir)) { 694 throw new IOException("Failed to restorecon session dir: " + stageDir); 695 } 696 } 697 buildExternalStageCid(int sessionId)698 private String buildExternalStageCid(int sessionId) { 699 return "smdl" + sessionId + ".tmp"; 700 } 701 prepareExternalStageCid(String stageCid, long sizeBytes)702 static void prepareExternalStageCid(String stageCid, long sizeBytes) throws IOException { 703 if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(), 704 Process.SYSTEM_UID, true) == null) { 705 throw new IOException("Failed to create session cid: " + stageCid); 706 } 707 } 708 709 @Override getSessionInfo(int sessionId)710 public SessionInfo getSessionInfo(int sessionId) { 711 synchronized (mSessions) { 712 final PackageInstallerSession session = mSessions.get(sessionId); 713 return session != null ? session.generateInfo() : null; 714 } 715 } 716 717 @Override getAllSessions(int userId)718 public ParceledListSlice<SessionInfo> getAllSessions(int userId) { 719 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getAllSessions"); 720 721 final List<SessionInfo> result = new ArrayList<>(); 722 synchronized (mSessions) { 723 for (int i = 0; i < mSessions.size(); i++) { 724 final PackageInstallerSession session = mSessions.valueAt(i); 725 if (session.userId == userId) { 726 result.add(session.generateInfo()); 727 } 728 } 729 } 730 return new ParceledListSlice<>(result); 731 } 732 733 @Override getMySessions(String installerPackageName, int userId)734 public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) { 735 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getMySessions"); 736 mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName); 737 738 final List<SessionInfo> result = new ArrayList<>(); 739 synchronized (mSessions) { 740 for (int i = 0; i < mSessions.size(); i++) { 741 final PackageInstallerSession session = mSessions.valueAt(i); 742 if (Objects.equals(session.installerPackageName, installerPackageName) 743 && session.userId == userId) { 744 result.add(session.generateInfo()); 745 } 746 } 747 } 748 return new ParceledListSlice<>(result); 749 } 750 751 @Override uninstall(String packageName, int flags, IntentSender statusReceiver, int userId)752 public void uninstall(String packageName, int flags, IntentSender statusReceiver, int userId) { 753 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, true, "uninstall"); 754 755 final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, 756 statusReceiver, packageName); 757 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES) 758 == PackageManager.PERMISSION_GRANTED) { 759 // Sweet, call straight through! 760 mPm.deletePackage(packageName, adapter.getBinder(), userId, flags); 761 762 } else { 763 // Take a short detour to confirm with user 764 final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE); 765 intent.setData(Uri.fromParts("package", packageName, null)); 766 intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder()); 767 adapter.onUserActionRequired(intent); 768 } 769 } 770 771 @Override setPermissionsResult(int sessionId, boolean accepted)772 public void setPermissionsResult(int sessionId, boolean accepted) { 773 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG); 774 775 synchronized (mSessions) { 776 mSessions.get(sessionId).setPermissionsResult(accepted); 777 } 778 } 779 780 @Override registerCallback(IPackageInstallerCallback callback, int userId)781 public void registerCallback(IPackageInstallerCallback callback, int userId) { 782 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback"); 783 mCallbacks.register(callback, userId); 784 } 785 786 @Override unregisterCallback(IPackageInstallerCallback callback)787 public void unregisterCallback(IPackageInstallerCallback callback) { 788 mCallbacks.unregister(callback); 789 } 790 getSessionCount(SparseArray<PackageInstallerSession> sessions, int installerUid)791 private static int getSessionCount(SparseArray<PackageInstallerSession> sessions, 792 int installerUid) { 793 int count = 0; 794 final int size = sessions.size(); 795 for (int i = 0; i < size; i++) { 796 final PackageInstallerSession session = sessions.valueAt(i); 797 if (session.installerUid == installerUid) { 798 count++; 799 } 800 } 801 return count; 802 } 803 isCallingUidOwner(PackageInstallerSession session)804 private boolean isCallingUidOwner(PackageInstallerSession session) { 805 final int callingUid = Binder.getCallingUid(); 806 if (callingUid == Process.ROOT_UID) { 807 return true; 808 } else { 809 return (session != null) && (callingUid == session.installerUid); 810 } 811 } 812 813 static class PackageDeleteObserverAdapter extends PackageDeleteObserver { 814 private final Context mContext; 815 private final IntentSender mTarget; 816 private final String mPackageName; 817 PackageDeleteObserverAdapter(Context context, IntentSender target, String packageName)818 public PackageDeleteObserverAdapter(Context context, IntentSender target, 819 String packageName) { 820 mContext = context; 821 mTarget = target; 822 mPackageName = packageName; 823 } 824 825 @Override onUserActionRequired(Intent intent)826 public void onUserActionRequired(Intent intent) { 827 final Intent fillIn = new Intent(); 828 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName); 829 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, 830 PackageInstaller.STATUS_PENDING_USER_ACTION); 831 fillIn.putExtra(Intent.EXTRA_INTENT, intent); 832 try { 833 mTarget.sendIntent(mContext, 0, fillIn, null, null); 834 } catch (SendIntentException ignored) { 835 } 836 } 837 838 @Override onPackageDeleted(String basePackageName, int returnCode, String msg)839 public void onPackageDeleted(String basePackageName, int returnCode, String msg) { 840 final Intent fillIn = new Intent(); 841 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName); 842 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, 843 PackageManager.deleteStatusToPublicStatus(returnCode)); 844 fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, 845 PackageManager.deleteStatusToString(returnCode, msg)); 846 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode); 847 try { 848 mTarget.sendIntent(mContext, 0, fillIn, null, null); 849 } catch (SendIntentException ignored) { 850 } 851 } 852 } 853 854 static class PackageInstallObserverAdapter extends PackageInstallObserver { 855 private final Context mContext; 856 private final IntentSender mTarget; 857 private final int mSessionId; 858 PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId)859 public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId) { 860 mContext = context; 861 mTarget = target; 862 mSessionId = sessionId; 863 } 864 865 @Override onUserActionRequired(Intent intent)866 public void onUserActionRequired(Intent intent) { 867 final Intent fillIn = new Intent(); 868 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId); 869 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, 870 PackageInstaller.STATUS_PENDING_USER_ACTION); 871 fillIn.putExtra(Intent.EXTRA_INTENT, intent); 872 try { 873 mTarget.sendIntent(mContext, 0, fillIn, null, null); 874 } catch (SendIntentException ignored) { 875 } 876 } 877 878 @Override onPackageInstalled(String basePackageName, int returnCode, String msg, Bundle extras)879 public void onPackageInstalled(String basePackageName, int returnCode, String msg, 880 Bundle extras) { 881 final Intent fillIn = new Intent(); 882 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId); 883 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, 884 PackageManager.installStatusToPublicStatus(returnCode)); 885 fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, 886 PackageManager.installStatusToString(returnCode, msg)); 887 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode); 888 if (extras != null) { 889 final String existing = extras.getString( 890 PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE); 891 if (!TextUtils.isEmpty(existing)) { 892 fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing); 893 } 894 } 895 try { 896 mTarget.sendIntent(mContext, 0, fillIn, null, null); 897 } catch (SendIntentException ignored) { 898 } 899 } 900 } 901 902 private static class Callbacks extends Handler { 903 private static final int MSG_SESSION_CREATED = 1; 904 private static final int MSG_SESSION_BADGING_CHANGED = 2; 905 private static final int MSG_SESSION_ACTIVE_CHANGED = 3; 906 private static final int MSG_SESSION_PROGRESS_CHANGED = 4; 907 private static final int MSG_SESSION_FINISHED = 5; 908 909 private final RemoteCallbackList<IPackageInstallerCallback> 910 mCallbacks = new RemoteCallbackList<>(); 911 Callbacks(Looper looper)912 public Callbacks(Looper looper) { 913 super(looper); 914 } 915 register(IPackageInstallerCallback callback, int userId)916 public void register(IPackageInstallerCallback callback, int userId) { 917 mCallbacks.register(callback, new UserHandle(userId)); 918 } 919 unregister(IPackageInstallerCallback callback)920 public void unregister(IPackageInstallerCallback callback) { 921 mCallbacks.unregister(callback); 922 } 923 924 @Override handleMessage(Message msg)925 public void handleMessage(Message msg) { 926 final int userId = msg.arg2; 927 final int n = mCallbacks.beginBroadcast(); 928 for (int i = 0; i < n; i++) { 929 final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i); 930 final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i); 931 // TODO: dispatch notifications for slave profiles 932 if (userId == user.getIdentifier()) { 933 try { 934 invokeCallback(callback, msg); 935 } catch (RemoteException ignored) { 936 } 937 } 938 } 939 mCallbacks.finishBroadcast(); 940 } 941 invokeCallback(IPackageInstallerCallback callback, Message msg)942 private void invokeCallback(IPackageInstallerCallback callback, Message msg) 943 throws RemoteException { 944 final int sessionId = msg.arg1; 945 switch (msg.what) { 946 case MSG_SESSION_CREATED: 947 callback.onSessionCreated(sessionId); 948 break; 949 case MSG_SESSION_BADGING_CHANGED: 950 callback.onSessionBadgingChanged(sessionId); 951 break; 952 case MSG_SESSION_ACTIVE_CHANGED: 953 callback.onSessionActiveChanged(sessionId, (boolean) msg.obj); 954 break; 955 case MSG_SESSION_PROGRESS_CHANGED: 956 callback.onSessionProgressChanged(sessionId, (float) msg.obj); 957 break; 958 case MSG_SESSION_FINISHED: 959 callback.onSessionFinished(sessionId, (boolean) msg.obj); 960 break; 961 } 962 } 963 notifySessionCreated(int sessionId, int userId)964 private void notifySessionCreated(int sessionId, int userId) { 965 obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget(); 966 } 967 notifySessionBadgingChanged(int sessionId, int userId)968 private void notifySessionBadgingChanged(int sessionId, int userId) { 969 obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget(); 970 } 971 notifySessionActiveChanged(int sessionId, int userId, boolean active)972 private void notifySessionActiveChanged(int sessionId, int userId, boolean active) { 973 obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget(); 974 } 975 notifySessionProgressChanged(int sessionId, int userId, float progress)976 private void notifySessionProgressChanged(int sessionId, int userId, float progress) { 977 obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget(); 978 } 979 notifySessionFinished(int sessionId, int userId, boolean success)980 public void notifySessionFinished(int sessionId, int userId, boolean success) { 981 obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget(); 982 } 983 } 984 dump(IndentingPrintWriter pw)985 void dump(IndentingPrintWriter pw) { 986 synchronized (mSessions) { 987 pw.println("Active install sessions:"); 988 pw.increaseIndent(); 989 int N = mSessions.size(); 990 for (int i = 0; i < N; i++) { 991 final PackageInstallerSession session = mSessions.valueAt(i); 992 session.dump(pw); 993 pw.println(); 994 } 995 pw.println(); 996 pw.decreaseIndent(); 997 998 pw.println("Historical install sessions:"); 999 pw.increaseIndent(); 1000 N = mHistoricalSessions.size(); 1001 for (int i = 0; i < N; i++) { 1002 final PackageInstallerSession session = mHistoricalSessions.valueAt(i); 1003 session.dump(pw); 1004 pw.println(); 1005 } 1006 pw.println(); 1007 pw.decreaseIndent(); 1008 1009 pw.println("Legacy install sessions:"); 1010 pw.increaseIndent(); 1011 pw.println(mLegacySessions.toString()); 1012 pw.decreaseIndent(); 1013 } 1014 } 1015 1016 class InternalCallback { onSessionBadgingChanged(PackageInstallerSession session)1017 public void onSessionBadgingChanged(PackageInstallerSession session) { 1018 mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId); 1019 writeSessionsAsync(); 1020 } 1021 onSessionActiveChanged(PackageInstallerSession session, boolean active)1022 public void onSessionActiveChanged(PackageInstallerSession session, boolean active) { 1023 mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active); 1024 } 1025 onSessionProgressChanged(PackageInstallerSession session, float progress)1026 public void onSessionProgressChanged(PackageInstallerSession session, float progress) { 1027 mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress); 1028 } 1029 onSessionFinished(final PackageInstallerSession session, boolean success)1030 public void onSessionFinished(final PackageInstallerSession session, boolean success) { 1031 mCallbacks.notifySessionFinished(session.sessionId, session.userId, success); 1032 1033 mInstallHandler.post(new Runnable() { 1034 @Override 1035 public void run() { 1036 synchronized (mSessions) { 1037 mSessions.remove(session.sessionId); 1038 mHistoricalSessions.put(session.sessionId, session); 1039 1040 final File appIconFile = buildAppIconFile(session.sessionId); 1041 if (appIconFile.exists()) { 1042 appIconFile.delete(); 1043 } 1044 1045 writeSessionsLocked(); 1046 } 1047 } 1048 }); 1049 } 1050 onSessionPrepared(PackageInstallerSession session)1051 public void onSessionPrepared(PackageInstallerSession session) { 1052 // We prepared the destination to write into; we want to persist 1053 // this, but it's not critical enough to block for. 1054 writeSessionsAsync(); 1055 } 1056 onSessionSealedBlocking(PackageInstallerSession session)1057 public void onSessionSealedBlocking(PackageInstallerSession session) { 1058 // It's very important that we block until we've recorded the 1059 // session as being sealed, since we never want to allow mutation 1060 // after sealing. 1061 synchronized (mSessions) { 1062 writeSessionsLocked(); 1063 } 1064 } 1065 } 1066 } 1067