PackageInstallerService.java revision 620b32b316fd4f1bab4eef55ec8802d14a55e7dd
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.writeBooleanAttribute; 26import static com.android.internal.util.XmlUtils.writeIntAttribute; 27import static com.android.internal.util.XmlUtils.writeLongAttribute; 28import static com.android.internal.util.XmlUtils.writeStringAttribute; 29import static com.android.internal.util.XmlUtils.writeUriAttribute; 30import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 31import static org.xmlpull.v1.XmlPullParser.START_TAG; 32 33import android.Manifest; 34import android.app.ActivityManager; 35import android.app.AppGlobals; 36import android.app.AppOpsManager; 37import android.app.Notification; 38import android.app.NotificationManager; 39import android.app.PackageDeleteObserver; 40import android.app.PackageInstallObserver; 41import android.app.admin.DevicePolicyManager; 42import android.content.Context; 43import android.content.Intent; 44import android.content.IntentSender; 45import android.content.IntentSender.SendIntentException; 46import android.content.pm.ApplicationInfo; 47import android.content.pm.IPackageInstaller; 48import android.content.pm.IPackageInstallerCallback; 49import android.content.pm.IPackageInstallerSession; 50import android.content.pm.PackageInfo; 51import android.content.pm.PackageInstaller; 52import android.content.pm.PackageInstaller.SessionInfo; 53import android.content.pm.PackageInstaller.SessionParams; 54import android.content.pm.PackageManager; 55import android.content.pm.ParceledListSlice; 56import android.graphics.Bitmap; 57import android.graphics.Bitmap.CompressFormat; 58import android.graphics.BitmapFactory; 59import android.net.Uri; 60import android.os.Binder; 61import android.os.Bundle; 62import android.os.Environment; 63import android.os.Handler; 64import android.os.HandlerThread; 65import android.os.Looper; 66import android.os.Message; 67import android.os.Process; 68import android.os.RemoteCallbackList; 69import android.os.RemoteException; 70import android.os.SELinux; 71import android.os.UserHandle; 72import android.os.UserManager; 73import android.os.storage.StorageManager; 74import android.os.storage.VolumeInfo; 75import android.system.ErrnoException; 76import android.system.Os; 77import android.text.TextUtils; 78import android.text.format.DateUtils; 79import android.util.ArraySet; 80import android.util.AtomicFile; 81import android.util.ExceptionUtils; 82import android.util.Slog; 83import android.util.SparseArray; 84import android.util.SparseBooleanArray; 85import android.util.Xml; 86 87import libcore.io.IoUtils; 88 89import com.android.internal.R; 90import com.android.internal.annotations.GuardedBy; 91import com.android.internal.content.PackageHelper; 92import com.android.internal.util.FastXmlSerializer; 93import com.android.internal.util.ImageUtils; 94import com.android.internal.util.IndentingPrintWriter; 95import com.android.server.IoThread; 96import com.google.android.collect.Sets; 97 98import org.xmlpull.v1.XmlPullParser; 99import org.xmlpull.v1.XmlPullParserException; 100import org.xmlpull.v1.XmlSerializer; 101 102import java.io.File; 103import java.io.FileInputStream; 104import java.io.FileNotFoundException; 105import java.io.FileOutputStream; 106import java.io.FilenameFilter; 107import java.io.IOException; 108import java.security.SecureRandom; 109import java.util.ArrayList; 110import java.util.List; 111import java.util.Objects; 112import java.util.Random; 113 114public class PackageInstallerService extends IPackageInstaller.Stub { 115 private static final String TAG = "PackageInstaller"; 116 private static final boolean LOGD = false; 117 118 // TODO: remove outstanding sessions when installer package goes away 119 // TODO: notify listeners in other users when package has been installed there 120 // TODO: purge expired sessions periodically in addition to at reboot 121 122 /** XML constants used in {@link #mSessionsFile} */ 123 private static final String TAG_SESSIONS = "sessions"; 124 private static final String TAG_SESSION = "session"; 125 private static final String ATTR_SESSION_ID = "sessionId"; 126 private static final String ATTR_USER_ID = "userId"; 127 private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName"; 128 private static final String ATTR_INSTALLER_UID = "installerUid"; 129 private static final String ATTR_CREATED_MILLIS = "createdMillis"; 130 private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir"; 131 private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid"; 132 private static final String ATTR_PREPARED = "prepared"; 133 private static final String ATTR_SEALED = "sealed"; 134 private static final String ATTR_MODE = "mode"; 135 private static final String ATTR_INSTALL_FLAGS = "installFlags"; 136 private static final String ATTR_INSTALL_LOCATION = "installLocation"; 137 private static final String ATTR_SIZE_BYTES = "sizeBytes"; 138 private static final String ATTR_APP_PACKAGE_NAME = "appPackageName"; 139 @Deprecated 140 private static final String ATTR_APP_ICON = "appIcon"; 141 private static final String ATTR_APP_LABEL = "appLabel"; 142 private static final String ATTR_ORIGINATING_URI = "originatingUri"; 143 private static final String ATTR_REFERRER_URI = "referrerUri"; 144 private static final String ATTR_ABI_OVERRIDE = "abiOverride"; 145 private static final String ATTR_VOLUME_UUID = "volumeUuid"; 146 147 /** Automatically destroy sessions older than this */ 148 private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS; 149 /** Upper bound on number of active sessions for a UID */ 150 private static final long MAX_ACTIVE_SESSIONS = 1024; 151 /** Upper bound on number of historical sessions for a UID */ 152 private static final long MAX_HISTORICAL_SESSIONS = 1048576; 153 154 private final Context mContext; 155 private final PackageManagerService mPm; 156 157 private AppOpsManager mAppOps; 158 private StorageManager mStorage; 159 160 private final HandlerThread mInstallThread; 161 private final Handler mInstallHandler; 162 163 private final Callbacks mCallbacks; 164 165 /** 166 * File storing persisted {@link #mSessions} metadata. 167 */ 168 private final AtomicFile mSessionsFile; 169 170 /** 171 * Directory storing persisted {@link #mSessions} metadata which is too 172 * heavy to store directly in {@link #mSessionsFile}. 173 */ 174 private final File mSessionsDir; 175 176 private final InternalCallback mInternalCallback = new InternalCallback(); 177 178 /** 179 * Used for generating session IDs. Since this is created at boot time, 180 * normal random might be predictable. 181 */ 182 private final Random mRandom = new SecureRandom(); 183 184 @GuardedBy("mSessions") 185 private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>(); 186 187 /** Historical sessions kept around for debugging purposes */ 188 @GuardedBy("mSessions") 189 private final SparseArray<PackageInstallerSession> mHistoricalSessions = new SparseArray<>(); 190 191 /** Sessions allocated to legacy users */ 192 @GuardedBy("mSessions") 193 private final SparseBooleanArray mLegacySessions = new SparseBooleanArray(); 194 195 private static final FilenameFilter sStageFilter = new FilenameFilter() { 196 @Override 197 public boolean accept(File dir, String name) { 198 return isStageName(name); 199 } 200 }; 201 202 public PackageInstallerService(Context context, PackageManagerService pm) { 203 mContext = context; 204 mPm = pm; 205 206 mInstallThread = new HandlerThread(TAG); 207 mInstallThread.start(); 208 209 mInstallHandler = new Handler(mInstallThread.getLooper()); 210 211 mCallbacks = new Callbacks(mInstallThread.getLooper()); 212 213 mSessionsFile = new AtomicFile( 214 new File(Environment.getSystemSecureDirectory(), "install_sessions.xml")); 215 mSessionsDir = new File(Environment.getSystemSecureDirectory(), "install_sessions"); 216 mSessionsDir.mkdirs(); 217 218 synchronized (mSessions) { 219 readSessionsLocked(); 220 221 final File internalStagingDir = buildInternalStagingDir(); 222 final ArraySet<File> unclaimedStages = Sets.newArraySet( 223 internalStagingDir.listFiles(sStageFilter)); 224 final ArraySet<File> unclaimedIcons = Sets.newArraySet( 225 mSessionsDir.listFiles()); 226 227 // Ignore stages and icons claimed by active sessions 228 for (int i = 0; i < mSessions.size(); i++) { 229 final PackageInstallerSession session = mSessions.valueAt(i); 230 unclaimedStages.remove(session.stageDir); 231 unclaimedIcons.remove(buildAppIconFile(session.sessionId)); 232 } 233 234 // Clean up orphaned staging directories 235 for (File stage : unclaimedStages) { 236 Slog.w(TAG, "Deleting orphan stage " + stage); 237 if (stage.isDirectory()) { 238 mPm.mInstaller.rmPackageDir(stage.getAbsolutePath()); 239 } else { 240 stage.delete(); 241 } 242 } 243 244 // Clean up orphaned icons 245 for (File icon : unclaimedIcons) { 246 Slog.w(TAG, "Deleting orphan icon " + icon); 247 icon.delete(); 248 } 249 } 250 } 251 252 public void systemReady() { 253 mAppOps = mContext.getSystemService(AppOpsManager.class); 254 mStorage = mContext.getSystemService(StorageManager.class); 255 } 256 257 public void onSecureContainersAvailable() { 258 synchronized (mSessions) { 259 final ArraySet<String> unclaimed = new ArraySet<>(); 260 for (String cid : PackageHelper.getSecureContainerList()) { 261 if (isStageName(cid)) { 262 unclaimed.add(cid); 263 } 264 } 265 266 // Ignore stages claimed by active sessions 267 for (int i = 0; i < mSessions.size(); i++) { 268 final PackageInstallerSession session = mSessions.valueAt(i); 269 final String cid = session.stageCid; 270 271 if (unclaimed.remove(cid)) { 272 // Claimed by active session, mount it 273 PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(), 274 Process.SYSTEM_UID); 275 } 276 } 277 278 // Clean up orphaned staging containers 279 for (String cid : unclaimed) { 280 Slog.w(TAG, "Deleting orphan container " + cid); 281 PackageHelper.destroySdDir(cid); 282 } 283 } 284 } 285 286 public static boolean isStageName(String name) { 287 final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp"); 288 final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp"); 289 final boolean isLegacyContainer = name.startsWith("smdl2tmp"); 290 return isFile || isContainer || isLegacyContainer; 291 } 292 293 @Deprecated 294 public File allocateStageDirLegacy(String volumeUuid) throws IOException { 295 synchronized (mSessions) { 296 try { 297 final int sessionId = allocateSessionIdLocked(); 298 mLegacySessions.put(sessionId, true); 299 final File stageDir = buildStageDir(volumeUuid, sessionId); 300 prepareStageDir(stageDir); 301 return stageDir; 302 } catch (IllegalStateException e) { 303 throw new IOException(e); 304 } 305 } 306 } 307 308 @Deprecated 309 public String allocateExternalStageCidLegacy() { 310 synchronized (mSessions) { 311 final int sessionId = allocateSessionIdLocked(); 312 mLegacySessions.put(sessionId, true); 313 return "smdl" + sessionId + ".tmp"; 314 } 315 } 316 317 private void readSessionsLocked() { 318 if (LOGD) Slog.v(TAG, "readSessionsLocked()"); 319 320 mSessions.clear(); 321 322 FileInputStream fis = null; 323 try { 324 fis = mSessionsFile.openRead(); 325 final XmlPullParser in = Xml.newPullParser(); 326 in.setInput(fis, null); 327 328 int type; 329 while ((type = in.next()) != END_DOCUMENT) { 330 if (type == START_TAG) { 331 final String tag = in.getName(); 332 if (TAG_SESSION.equals(tag)) { 333 final PackageInstallerSession session = readSessionLocked(in); 334 final long age = System.currentTimeMillis() - session.createdMillis; 335 336 final boolean valid; 337 if (age >= MAX_AGE_MILLIS) { 338 Slog.w(TAG, "Abandoning old session first created at " 339 + session.createdMillis); 340 valid = false; 341 } else { 342 valid = true; 343 } 344 345 if (valid) { 346 mSessions.put(session.sessionId, session); 347 } else { 348 // Since this is early during boot we don't send 349 // any observer events about the session, but we 350 // keep details around for dumpsys. 351 mHistoricalSessions.put(session.sessionId, session); 352 } 353 } 354 } 355 } 356 } catch (FileNotFoundException e) { 357 // Missing sessions are okay, probably first boot 358 } catch (IOException e) { 359 Slog.wtf(TAG, "Failed reading install sessions", e); 360 } catch (XmlPullParserException e) { 361 Slog.wtf(TAG, "Failed reading install sessions", e); 362 } finally { 363 IoUtils.closeQuietly(fis); 364 } 365 } 366 367 private PackageInstallerSession readSessionLocked(XmlPullParser in) throws IOException { 368 final int sessionId = readIntAttribute(in, ATTR_SESSION_ID); 369 final int userId = readIntAttribute(in, ATTR_USER_ID); 370 final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME); 371 final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, 372 mPm.getPackageUid(installerPackageName, userId)); 373 final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS); 374 final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR); 375 final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null; 376 final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID); 377 final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true); 378 final boolean sealed = readBooleanAttribute(in, ATTR_SEALED); 379 380 final SessionParams params = new SessionParams( 381 SessionParams.MODE_INVALID); 382 params.mode = readIntAttribute(in, ATTR_MODE); 383 params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS); 384 params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION); 385 params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES); 386 params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME); 387 params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON); 388 params.appLabel = readStringAttribute(in, ATTR_APP_LABEL); 389 params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI); 390 params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI); 391 params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE); 392 params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID); 393 394 final File appIconFile = buildAppIconFile(sessionId); 395 if (appIconFile.exists()) { 396 params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath()); 397 params.appIconLastModified = appIconFile.lastModified(); 398 } 399 400 return new PackageInstallerSession(mInternalCallback, mContext, mPm, 401 mInstallThread.getLooper(), sessionId, userId, installerPackageName, installerUid, 402 params, createdMillis, stageDir, stageCid, prepared, sealed); 403 } 404 405 private void writeSessionsLocked() { 406 if (LOGD) Slog.v(TAG, "writeSessionsLocked()"); 407 408 FileOutputStream fos = null; 409 try { 410 fos = mSessionsFile.startWrite(); 411 412 XmlSerializer out = new FastXmlSerializer(); 413 out.setOutput(fos, "utf-8"); 414 out.startDocument(null, true); 415 out.startTag(null, TAG_SESSIONS); 416 final int size = mSessions.size(); 417 for (int i = 0; i < size; i++) { 418 final PackageInstallerSession session = mSessions.valueAt(i); 419 writeSessionLocked(out, session); 420 } 421 out.endTag(null, TAG_SESSIONS); 422 out.endDocument(); 423 424 mSessionsFile.finishWrite(fos); 425 } catch (IOException e) { 426 if (fos != null) { 427 mSessionsFile.failWrite(fos); 428 } 429 } 430 } 431 432 private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session) 433 throws IOException { 434 final SessionParams params = session.params; 435 436 out.startTag(null, TAG_SESSION); 437 438 writeIntAttribute(out, ATTR_SESSION_ID, session.sessionId); 439 writeIntAttribute(out, ATTR_USER_ID, session.userId); 440 writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME, 441 session.installerPackageName); 442 writeIntAttribute(out, ATTR_INSTALLER_UID, session.installerUid); 443 writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis); 444 if (session.stageDir != null) { 445 writeStringAttribute(out, ATTR_SESSION_STAGE_DIR, 446 session.stageDir.getAbsolutePath()); 447 } 448 if (session.stageCid != null) { 449 writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid); 450 } 451 writeBooleanAttribute(out, ATTR_PREPARED, session.isPrepared()); 452 writeBooleanAttribute(out, ATTR_SEALED, session.isSealed()); 453 454 writeIntAttribute(out, ATTR_MODE, params.mode); 455 writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags); 456 writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation); 457 writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes); 458 writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName); 459 writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel); 460 writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri); 461 writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri); 462 writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride); 463 writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid); 464 465 // Persist app icon if changed since last written 466 final File appIconFile = buildAppIconFile(session.sessionId); 467 if (params.appIcon == null && appIconFile.exists()) { 468 appIconFile.delete(); 469 } else if (params.appIcon != null 470 && appIconFile.lastModified() != params.appIconLastModified) { 471 if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile); 472 FileOutputStream os = null; 473 try { 474 os = new FileOutputStream(appIconFile); 475 params.appIcon.compress(CompressFormat.PNG, 90, os); 476 } catch (IOException e) { 477 Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage()); 478 } finally { 479 IoUtils.closeQuietly(os); 480 } 481 482 params.appIconLastModified = appIconFile.lastModified(); 483 } 484 485 out.endTag(null, TAG_SESSION); 486 } 487 488 private File buildAppIconFile(int sessionId) { 489 return new File(mSessionsDir, "app_icon." + sessionId + ".png"); 490 } 491 492 private void writeSessionsAsync() { 493 IoThread.getHandler().post(new Runnable() { 494 @Override 495 public void run() { 496 synchronized (mSessions) { 497 writeSessionsLocked(); 498 } 499 } 500 }); 501 } 502 503 @Override 504 public int createSession(SessionParams params, String installerPackageName, int userId) { 505 try { 506 return createSessionInternal(params, installerPackageName, userId); 507 } catch (IOException e) { 508 throw ExceptionUtils.wrap(e); 509 } 510 } 511 512 private int createSessionInternal(SessionParams params, String installerPackageName, int userId) 513 throws IOException { 514 final int callingUid = Binder.getCallingUid(); 515 mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession"); 516 517 if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { 518 throw new SecurityException("User restriction prevents installing"); 519 } 520 521 if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) { 522 params.installFlags |= PackageManager.INSTALL_FROM_ADB; 523 524 } else { 525 mAppOps.checkPackage(callingUid, installerPackageName); 526 527 params.installFlags &= ~PackageManager.INSTALL_FROM_ADB; 528 params.installFlags &= ~PackageManager.INSTALL_ALL_USERS; 529 params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; 530 } 531 532 // Only system components can circumvent runtime permissions when installing. 533 if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0 534 && mContext.checkCallingOrSelfPermission(Manifest.permission 535 .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) { 536 throw new SecurityException("You need the " 537 + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission " 538 + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag"); 539 } 540 541 // Defensively resize giant app icons 542 if (params.appIcon != null) { 543 final ActivityManager am = (ActivityManager) mContext.getSystemService( 544 Context.ACTIVITY_SERVICE); 545 final int iconSize = am.getLauncherLargeIconSize(); 546 if ((params.appIcon.getWidth() > iconSize * 2) 547 || (params.appIcon.getHeight() > iconSize * 2)) { 548 params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize, 549 true); 550 } 551 } 552 553 switch (params.mode) { 554 case SessionParams.MODE_FULL_INSTALL: 555 case SessionParams.MODE_INHERIT_EXISTING: 556 break; 557 default: 558 throw new IllegalArgumentException("Invalid install mode: " + params.mode); 559 } 560 561 // If caller requested explicit location, sanity check it, otherwise 562 // resolve the best internal or adopted location. 563 if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) { 564 if (!PackageHelper.fitsOnInternal(mContext, params.sizeBytes)) { 565 throw new IOException("No suitable internal storage available"); 566 } 567 568 } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) { 569 if (!PackageHelper.fitsOnExternal(mContext, params.sizeBytes)) { 570 throw new IOException("No suitable external storage available"); 571 } 572 573 } else { 574 // For now, installs to adopted media are treated as internal from 575 // an install flag point-of-view. 576 params.setInstallFlagsInternal(); 577 578 // Resolve best location for install, based on combination of 579 // requested install flags, delta size, and manifest settings. 580 final long ident = Binder.clearCallingIdentity(); 581 try { 582 params.volumeUuid = PackageHelper.resolveInstallVolume(mContext, 583 params.appPackageName, params.installLocation, params.sizeBytes); 584 } finally { 585 Binder.restoreCallingIdentity(ident); 586 } 587 } 588 589 final int sessionId; 590 final PackageInstallerSession session; 591 synchronized (mSessions) { 592 // Sanity check that installer isn't going crazy 593 final int activeCount = getSessionCount(mSessions, callingUid); 594 if (activeCount >= MAX_ACTIVE_SESSIONS) { 595 throw new IllegalStateException( 596 "Too many active sessions for UID " + callingUid); 597 } 598 final int historicalCount = getSessionCount(mHistoricalSessions, callingUid); 599 if (historicalCount >= MAX_HISTORICAL_SESSIONS) { 600 throw new IllegalStateException( 601 "Too many historical sessions for UID " + callingUid); 602 } 603 604 final long createdMillis = System.currentTimeMillis(); 605 sessionId = allocateSessionIdLocked(); 606 607 // We're staging to exactly one location 608 File stageDir = null; 609 String stageCid = null; 610 if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) { 611 stageDir = buildStageDir(params.volumeUuid, sessionId); 612 } else { 613 stageCid = buildExternalStageCid(sessionId); 614 } 615 616 session = new PackageInstallerSession(mInternalCallback, mContext, mPm, 617 mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid, 618 params, createdMillis, stageDir, stageCid, false, false); 619 mSessions.put(sessionId, session); 620 } 621 622 mCallbacks.notifySessionCreated(session.sessionId, session.userId); 623 writeSessionsAsync(); 624 return sessionId; 625 } 626 627 @Override 628 public void updateSessionAppIcon(int sessionId, Bitmap appIcon) { 629 synchronized (mSessions) { 630 final PackageInstallerSession session = mSessions.get(sessionId); 631 if (session == null || !isCallingUidOwner(session)) { 632 throw new SecurityException("Caller has no access to session " + sessionId); 633 } 634 635 // Defensively resize giant app icons 636 if (appIcon != null) { 637 final ActivityManager am = (ActivityManager) mContext.getSystemService( 638 Context.ACTIVITY_SERVICE); 639 final int iconSize = am.getLauncherLargeIconSize(); 640 if ((appIcon.getWidth() > iconSize * 2) 641 || (appIcon.getHeight() > iconSize * 2)) { 642 appIcon = Bitmap.createScaledBitmap(appIcon, iconSize, iconSize, true); 643 } 644 } 645 646 session.params.appIcon = appIcon; 647 session.params.appIconLastModified = -1; 648 649 mInternalCallback.onSessionBadgingChanged(session); 650 } 651 } 652 653 @Override 654 public void updateSessionAppLabel(int sessionId, String appLabel) { 655 synchronized (mSessions) { 656 final PackageInstallerSession session = mSessions.get(sessionId); 657 if (session == null || !isCallingUidOwner(session)) { 658 throw new SecurityException("Caller has no access to session " + sessionId); 659 } 660 session.params.appLabel = appLabel; 661 mInternalCallback.onSessionBadgingChanged(session); 662 } 663 } 664 665 @Override 666 public void abandonSession(int sessionId) { 667 synchronized (mSessions) { 668 final PackageInstallerSession session = mSessions.get(sessionId); 669 if (session == null || !isCallingUidOwner(session)) { 670 throw new SecurityException("Caller has no access to session " + sessionId); 671 } 672 session.abandon(); 673 } 674 } 675 676 @Override 677 public IPackageInstallerSession openSession(int sessionId) { 678 try { 679 return openSessionInternal(sessionId); 680 } catch (IOException e) { 681 throw ExceptionUtils.wrap(e); 682 } 683 } 684 685 private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException { 686 synchronized (mSessions) { 687 final PackageInstallerSession session = mSessions.get(sessionId); 688 if (session == null || !isCallingUidOwner(session)) { 689 throw new SecurityException("Caller has no access to session " + sessionId); 690 } 691 session.open(); 692 return session; 693 } 694 } 695 696 private int allocateSessionIdLocked() { 697 int n = 0; 698 int sessionId; 699 do { 700 sessionId = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1; 701 if (mSessions.get(sessionId) == null && mHistoricalSessions.get(sessionId) == null 702 && !mLegacySessions.get(sessionId, false)) { 703 return sessionId; 704 } 705 } while (n++ < 32); 706 707 throw new IllegalStateException("Failed to allocate session ID"); 708 } 709 710 private File buildInternalStagingDir() { 711 return new File(Environment.getDataDirectory(), "app"); 712 } 713 714 private File buildStagingDir(String volumeUuid) throws FileNotFoundException { 715 if (volumeUuid == null) { 716 return buildInternalStagingDir(); 717 } else { 718 final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid); 719 if (vol != null && vol.type == VolumeInfo.TYPE_PRIVATE 720 && vol.isMountedWritable()) { 721 return new File(vol.path, "app"); 722 } else { 723 throw new FileNotFoundException("Failed to find volume for UUID " + volumeUuid); 724 } 725 } 726 } 727 728 private File buildStageDir(String volumeUuid, int sessionId) throws FileNotFoundException { 729 final File stagingDir = buildStagingDir(volumeUuid); 730 return new File(stagingDir, "vmdl" + sessionId + ".tmp"); 731 } 732 733 static void prepareStageDir(File stageDir) throws IOException { 734 if (stageDir.exists()) { 735 throw new IOException("Session dir already exists: " + stageDir); 736 } 737 738 try { 739 Os.mkdir(stageDir.getAbsolutePath(), 0755); 740 Os.chmod(stageDir.getAbsolutePath(), 0755); 741 } catch (ErrnoException e) { 742 // This purposefully throws if directory already exists 743 throw new IOException("Failed to prepare session dir: " + stageDir, e); 744 } 745 746 if (!SELinux.restorecon(stageDir)) { 747 throw new IOException("Failed to restorecon session dir: " + stageDir); 748 } 749 } 750 751 private String buildExternalStageCid(int sessionId) { 752 return "smdl" + sessionId + ".tmp"; 753 } 754 755 static void prepareExternalStageCid(String stageCid, long sizeBytes) throws IOException { 756 if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(), 757 Process.SYSTEM_UID, true) == null) { 758 throw new IOException("Failed to create session cid: " + stageCid); 759 } 760 } 761 762 @Override 763 public SessionInfo getSessionInfo(int sessionId) { 764 synchronized (mSessions) { 765 final PackageInstallerSession session = mSessions.get(sessionId); 766 return session != null ? session.generateInfo() : null; 767 } 768 } 769 770 @Override 771 public ParceledListSlice<SessionInfo> getAllSessions(int userId) { 772 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getAllSessions"); 773 774 final List<SessionInfo> result = new ArrayList<>(); 775 synchronized (mSessions) { 776 for (int i = 0; i < mSessions.size(); i++) { 777 final PackageInstallerSession session = mSessions.valueAt(i); 778 if (session.userId == userId) { 779 result.add(session.generateInfo()); 780 } 781 } 782 } 783 return new ParceledListSlice<>(result); 784 } 785 786 @Override 787 public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) { 788 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getMySessions"); 789 mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName); 790 791 final List<SessionInfo> result = new ArrayList<>(); 792 synchronized (mSessions) { 793 for (int i = 0; i < mSessions.size(); i++) { 794 final PackageInstallerSession session = mSessions.valueAt(i); 795 if (Objects.equals(session.installerPackageName, installerPackageName) 796 && session.userId == userId) { 797 result.add(session.generateInfo()); 798 } 799 } 800 } 801 return new ParceledListSlice<>(result); 802 } 803 804 @Override 805 public void uninstall(String packageName, String callerPackageName, int flags, 806 IntentSender statusReceiver, int userId) { 807 final int callingUid = Binder.getCallingUid(); 808 mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall"); 809 if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) { 810 mAppOps.checkPackage(callingUid, callerPackageName); 811 } 812 813 // Check whether the caller is device owner 814 DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService( 815 Context.DEVICE_POLICY_SERVICE); 816 boolean isDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(callerPackageName); 817 818 final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, 819 statusReceiver, packageName, isDeviceOwner, userId); 820 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES) 821 == PackageManager.PERMISSION_GRANTED) { 822 // Sweet, call straight through! 823 mPm.deletePackage(packageName, adapter.getBinder(), userId, flags); 824 } else if (isDeviceOwner) { 825 // Allow the DeviceOwner to silently delete packages 826 // Need to clear the calling identity to get DELETE_PACKAGES permission 827 long ident = Binder.clearCallingIdentity(); 828 try { 829 mPm.deletePackage(packageName, adapter.getBinder(), userId, flags); 830 } finally { 831 Binder.restoreCallingIdentity(ident); 832 } 833 } else { 834 // Take a short detour to confirm with user 835 final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE); 836 intent.setData(Uri.fromParts("package", packageName, null)); 837 intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder()); 838 adapter.onUserActionRequired(intent); 839 } 840 } 841 842 @Override 843 public void setPermissionsResult(int sessionId, boolean accepted) { 844 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG); 845 846 synchronized (mSessions) { 847 mSessions.get(sessionId).setPermissionsResult(accepted); 848 } 849 } 850 851 @Override 852 public void registerCallback(IPackageInstallerCallback callback, int userId) { 853 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback"); 854 mCallbacks.register(callback, userId); 855 } 856 857 @Override 858 public void unregisterCallback(IPackageInstallerCallback callback) { 859 mCallbacks.unregister(callback); 860 } 861 862 private static int getSessionCount(SparseArray<PackageInstallerSession> sessions, 863 int installerUid) { 864 int count = 0; 865 final int size = sessions.size(); 866 for (int i = 0; i < size; i++) { 867 final PackageInstallerSession session = sessions.valueAt(i); 868 if (session.installerUid == installerUid) { 869 count++; 870 } 871 } 872 return count; 873 } 874 875 private boolean isCallingUidOwner(PackageInstallerSession session) { 876 final int callingUid = Binder.getCallingUid(); 877 if (callingUid == Process.ROOT_UID) { 878 return true; 879 } else { 880 return (session != null) && (callingUid == session.installerUid); 881 } 882 } 883 884 static class PackageDeleteObserverAdapter extends PackageDeleteObserver { 885 private final Context mContext; 886 private final IntentSender mTarget; 887 private final String mPackageName; 888 private final Notification mNotification; 889 890 public PackageDeleteObserverAdapter(Context context, IntentSender target, 891 String packageName, boolean showNotification, int userId) { 892 mContext = context; 893 mTarget = target; 894 mPackageName = packageName; 895 if (showNotification) { 896 mNotification = buildSuccessNotification(mContext, 897 mContext.getResources().getString(R.string.package_deleted_device_owner), 898 packageName, 899 userId); 900 } else { 901 mNotification = null; 902 } 903 } 904 905 @Override 906 public void onUserActionRequired(Intent intent) { 907 final Intent fillIn = new Intent(); 908 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName); 909 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, 910 PackageInstaller.STATUS_PENDING_USER_ACTION); 911 fillIn.putExtra(Intent.EXTRA_INTENT, intent); 912 try { 913 mTarget.sendIntent(mContext, 0, fillIn, null, null); 914 } catch (SendIntentException ignored) { 915 } 916 } 917 918 @Override 919 public void onPackageDeleted(String basePackageName, int returnCode, String msg) { 920 if (PackageManager.DELETE_SUCCEEDED == returnCode && mNotification != null) { 921 NotificationManager notificationManager = (NotificationManager) 922 mContext.getSystemService(Context.NOTIFICATION_SERVICE); 923 notificationManager.notify(basePackageName, 0, mNotification); 924 } 925 final Intent fillIn = new Intent(); 926 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, mPackageName); 927 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, 928 PackageManager.deleteStatusToPublicStatus(returnCode)); 929 fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, 930 PackageManager.deleteStatusToString(returnCode, msg)); 931 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode); 932 try { 933 mTarget.sendIntent(mContext, 0, fillIn, null, null); 934 } catch (SendIntentException ignored) { 935 } 936 } 937 } 938 939 static class PackageInstallObserverAdapter extends PackageInstallObserver { 940 private final Context mContext; 941 private final IntentSender mTarget; 942 private final int mSessionId; 943 private final boolean mShowNotification; 944 private final int mUserId; 945 946 public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId, 947 boolean showNotification, int userId) { 948 mContext = context; 949 mTarget = target; 950 mSessionId = sessionId; 951 mShowNotification = showNotification; 952 mUserId = userId; 953 } 954 955 @Override 956 public void onUserActionRequired(Intent intent) { 957 final Intent fillIn = new Intent(); 958 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId); 959 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, 960 PackageInstaller.STATUS_PENDING_USER_ACTION); 961 fillIn.putExtra(Intent.EXTRA_INTENT, intent); 962 try { 963 mTarget.sendIntent(mContext, 0, fillIn, null, null); 964 } catch (SendIntentException ignored) { 965 } 966 } 967 968 @Override 969 public void onPackageInstalled(String basePackageName, int returnCode, String msg, 970 Bundle extras) { 971 if (PackageManager.INSTALL_SUCCEEDED == returnCode && mShowNotification) { 972 Notification notification = buildSuccessNotification(mContext, 973 mContext.getResources().getString(R.string.package_installed_device_owner), 974 basePackageName, 975 mUserId); 976 if (notification != null) { 977 NotificationManager notificationManager = (NotificationManager) 978 mContext.getSystemService(Context.NOTIFICATION_SERVICE); 979 notificationManager.notify(basePackageName, 0, notification); 980 } 981 } 982 final Intent fillIn = new Intent(); 983 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId); 984 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, 985 PackageManager.installStatusToPublicStatus(returnCode)); 986 fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, 987 PackageManager.installStatusToString(returnCode, msg)); 988 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode); 989 if (extras != null) { 990 final String existing = extras.getString( 991 PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE); 992 if (!TextUtils.isEmpty(existing)) { 993 fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, existing); 994 } 995 } 996 try { 997 mTarget.sendIntent(mContext, 0, fillIn, null, null); 998 } catch (SendIntentException ignored) { 999 } 1000 } 1001 } 1002 1003 /** 1004 * Build a notification for package installation / deletion by device owners that is shown if 1005 * the operation succeeds. 1006 */ 1007 private static Notification buildSuccessNotification(Context context, String contentText, 1008 String basePackageName, int userId) { 1009 PackageInfo packageInfo = null; 1010 try { 1011 packageInfo = AppGlobals.getPackageManager().getPackageInfo( 1012 basePackageName, 0, userId); 1013 } catch (RemoteException ignored) { 1014 } 1015 if (packageInfo == null || packageInfo.applicationInfo == null) { 1016 Slog.w(TAG, "Notification not built for package: " + basePackageName); 1017 return null; 1018 } 1019 PackageManager pm = context.getPackageManager(); 1020 Bitmap packageIcon = ImageUtils.buildScaledBitmap( 1021 packageInfo.applicationInfo.loadIcon(pm), 1022 context.getResources().getDimensionPixelSize( 1023 android.R.dimen.notification_large_icon_width), 1024 context.getResources().getDimensionPixelSize( 1025 android.R.dimen.notification_large_icon_height)); 1026 CharSequence packageLabel = packageInfo.applicationInfo.loadLabel(pm); 1027 return new Notification.Builder(context) 1028 .setSmallIcon(R.drawable.ic_check_circle_24px) 1029 .setColor(context.getResources().getColor( 1030 R.color.system_notification_accent_color)) 1031 .setContentTitle(packageLabel) 1032 .setContentText(contentText) 1033 .setLargeIcon(packageIcon) 1034 .build(); 1035 } 1036 1037 private static class Callbacks extends Handler { 1038 private static final int MSG_SESSION_CREATED = 1; 1039 private static final int MSG_SESSION_BADGING_CHANGED = 2; 1040 private static final int MSG_SESSION_ACTIVE_CHANGED = 3; 1041 private static final int MSG_SESSION_PROGRESS_CHANGED = 4; 1042 private static final int MSG_SESSION_FINISHED = 5; 1043 1044 private final RemoteCallbackList<IPackageInstallerCallback> 1045 mCallbacks = new RemoteCallbackList<>(); 1046 1047 public Callbacks(Looper looper) { 1048 super(looper); 1049 } 1050 1051 public void register(IPackageInstallerCallback callback, int userId) { 1052 mCallbacks.register(callback, new UserHandle(userId)); 1053 } 1054 1055 public void unregister(IPackageInstallerCallback callback) { 1056 mCallbacks.unregister(callback); 1057 } 1058 1059 @Override 1060 public void handleMessage(Message msg) { 1061 final int userId = msg.arg2; 1062 final int n = mCallbacks.beginBroadcast(); 1063 for (int i = 0; i < n; i++) { 1064 final IPackageInstallerCallback callback = mCallbacks.getBroadcastItem(i); 1065 final UserHandle user = (UserHandle) mCallbacks.getBroadcastCookie(i); 1066 // TODO: dispatch notifications for slave profiles 1067 if (userId == user.getIdentifier()) { 1068 try { 1069 invokeCallback(callback, msg); 1070 } catch (RemoteException ignored) { 1071 } 1072 } 1073 } 1074 mCallbacks.finishBroadcast(); 1075 } 1076 1077 private void invokeCallback(IPackageInstallerCallback callback, Message msg) 1078 throws RemoteException { 1079 final int sessionId = msg.arg1; 1080 switch (msg.what) { 1081 case MSG_SESSION_CREATED: 1082 callback.onSessionCreated(sessionId); 1083 break; 1084 case MSG_SESSION_BADGING_CHANGED: 1085 callback.onSessionBadgingChanged(sessionId); 1086 break; 1087 case MSG_SESSION_ACTIVE_CHANGED: 1088 callback.onSessionActiveChanged(sessionId, (boolean) msg.obj); 1089 break; 1090 case MSG_SESSION_PROGRESS_CHANGED: 1091 callback.onSessionProgressChanged(sessionId, (float) msg.obj); 1092 break; 1093 case MSG_SESSION_FINISHED: 1094 callback.onSessionFinished(sessionId, (boolean) msg.obj); 1095 break; 1096 } 1097 } 1098 1099 private void notifySessionCreated(int sessionId, int userId) { 1100 obtainMessage(MSG_SESSION_CREATED, sessionId, userId).sendToTarget(); 1101 } 1102 1103 private void notifySessionBadgingChanged(int sessionId, int userId) { 1104 obtainMessage(MSG_SESSION_BADGING_CHANGED, sessionId, userId).sendToTarget(); 1105 } 1106 1107 private void notifySessionActiveChanged(int sessionId, int userId, boolean active) { 1108 obtainMessage(MSG_SESSION_ACTIVE_CHANGED, sessionId, userId, active).sendToTarget(); 1109 } 1110 1111 private void notifySessionProgressChanged(int sessionId, int userId, float progress) { 1112 obtainMessage(MSG_SESSION_PROGRESS_CHANGED, sessionId, userId, progress).sendToTarget(); 1113 } 1114 1115 public void notifySessionFinished(int sessionId, int userId, boolean success) { 1116 obtainMessage(MSG_SESSION_FINISHED, sessionId, userId, success).sendToTarget(); 1117 } 1118 } 1119 1120 void dump(IndentingPrintWriter pw) { 1121 synchronized (mSessions) { 1122 pw.println("Active install sessions:"); 1123 pw.increaseIndent(); 1124 int N = mSessions.size(); 1125 for (int i = 0; i < N; i++) { 1126 final PackageInstallerSession session = mSessions.valueAt(i); 1127 session.dump(pw); 1128 pw.println(); 1129 } 1130 pw.println(); 1131 pw.decreaseIndent(); 1132 1133 pw.println("Historical install sessions:"); 1134 pw.increaseIndent(); 1135 N = mHistoricalSessions.size(); 1136 for (int i = 0; i < N; i++) { 1137 final PackageInstallerSession session = mHistoricalSessions.valueAt(i); 1138 session.dump(pw); 1139 pw.println(); 1140 } 1141 pw.println(); 1142 pw.decreaseIndent(); 1143 1144 pw.println("Legacy install sessions:"); 1145 pw.increaseIndent(); 1146 pw.println(mLegacySessions.toString()); 1147 pw.decreaseIndent(); 1148 } 1149 } 1150 1151 class InternalCallback { 1152 public void onSessionBadgingChanged(PackageInstallerSession session) { 1153 mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId); 1154 writeSessionsAsync(); 1155 } 1156 1157 public void onSessionActiveChanged(PackageInstallerSession session, boolean active) { 1158 mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, active); 1159 } 1160 1161 public void onSessionProgressChanged(PackageInstallerSession session, float progress) { 1162 mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, progress); 1163 } 1164 1165 public void onSessionFinished(final PackageInstallerSession session, boolean success) { 1166 mCallbacks.notifySessionFinished(session.sessionId, session.userId, success); 1167 1168 mInstallHandler.post(new Runnable() { 1169 @Override 1170 public void run() { 1171 synchronized (mSessions) { 1172 mSessions.remove(session.sessionId); 1173 mHistoricalSessions.put(session.sessionId, session); 1174 1175 final File appIconFile = buildAppIconFile(session.sessionId); 1176 if (appIconFile.exists()) { 1177 appIconFile.delete(); 1178 } 1179 1180 writeSessionsLocked(); 1181 } 1182 } 1183 }); 1184 } 1185 1186 public void onSessionPrepared(PackageInstallerSession session) { 1187 // We prepared the destination to write into; we want to persist 1188 // this, but it's not critical enough to block for. 1189 writeSessionsAsync(); 1190 } 1191 1192 public void onSessionSealedBlocking(PackageInstallerSession session) { 1193 // It's very important that we block until we've recorded the 1194 // session as being sealed, since we never want to allow mutation 1195 // after sealing. 1196 synchronized (mSessions) { 1197 writeSessionsLocked(); 1198 } 1199 } 1200 } 1201} 1202