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