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