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