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