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