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