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