1 /* 2 * Copyright (C) 2010 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 android.os; 18 19 import static android.view.Display.DEFAULT_DISPLAY; 20 21 import static java.nio.charset.StandardCharsets.UTF_8; 22 23 import android.annotation.IntDef; 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.RequiresPermission; 27 import android.annotation.SuppressLint; 28 import android.annotation.SystemApi; 29 import android.annotation.SystemService; 30 import android.app.KeyguardManager; 31 import android.app.PendingIntent; 32 import android.compat.annotation.UnsupportedAppUsage; 33 import android.content.BroadcastReceiver; 34 import android.content.ContentResolver; 35 import android.content.Context; 36 import android.content.Intent; 37 import android.content.IntentFilter; 38 import android.content.IntentSender; 39 import android.content.pm.PackageManager; 40 import android.hardware.display.DisplayManager; 41 import android.provider.Settings; 42 import android.telephony.SubscriptionInfo; 43 import android.telephony.SubscriptionManager; 44 import android.telephony.euicc.EuiccManager; 45 import android.text.TextUtils; 46 import android.text.format.DateFormat; 47 import android.util.Log; 48 import android.view.Display; 49 50 import libcore.io.Streams; 51 52 import java.io.ByteArrayInputStream; 53 import java.io.File; 54 import java.io.FileInputStream; 55 import java.io.FileNotFoundException; 56 import java.io.FileWriter; 57 import java.io.IOException; 58 import java.io.InputStream; 59 import java.io.RandomAccessFile; 60 import java.security.GeneralSecurityException; 61 import java.security.PublicKey; 62 import java.security.SignatureException; 63 import java.security.cert.CertificateFactory; 64 import java.security.cert.X509Certificate; 65 import java.util.ArrayList; 66 import java.util.Enumeration; 67 import java.util.HashSet; 68 import java.util.List; 69 import java.util.Locale; 70 import java.util.concurrent.CountDownLatch; 71 import java.util.concurrent.TimeUnit; 72 import java.util.concurrent.atomic.AtomicBoolean; 73 import java.util.concurrent.atomic.AtomicInteger; 74 import java.util.zip.ZipEntry; 75 import java.util.zip.ZipFile; 76 import java.util.zip.ZipInputStream; 77 78 import sun.security.pkcs.PKCS7; 79 import sun.security.pkcs.SignerInfo; 80 81 /** 82 * RecoverySystem contains methods for interacting with the Android 83 * recovery system (the separate partition that can be used to install 84 * system updates, wipe user data, etc.) 85 */ 86 @SystemService(Context.RECOVERY_SERVICE) 87 public class RecoverySystem { 88 private static final String TAG = "RecoverySystem"; 89 90 /** 91 * Default location of zip file containing public keys (X509 92 * certs) authorized to sign OTA updates. 93 */ 94 private static final File DEFAULT_KEYSTORE = 95 new File("/system/etc/security/otacerts.zip"); 96 97 /** Send progress to listeners no more often than this (in ms). */ 98 private static final long PUBLISH_PROGRESS_INTERVAL_MS = 500; 99 100 private static final long DEFAULT_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 30000L; // 30 s 101 private static final long MIN_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 5000L; // 5 s 102 private static final long MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 60000L; // 60 s 103 104 private static final long DEFAULT_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = 105 45000L; // 45 s 106 private static final long MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = 15000L; // 15 s 107 private static final long MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = 90000L; // 90 s 108 109 /** Used to communicate with recovery. See bootable/recovery/recovery.cpp. */ 110 private static final File RECOVERY_DIR = new File("/cache/recovery"); 111 private static final File LOG_FILE = new File(RECOVERY_DIR, "log"); 112 private static final String LAST_INSTALL_PATH = "last_install"; 113 private static final String LAST_PREFIX = "last_"; 114 private static final String ACTION_EUICC_FACTORY_RESET = 115 "com.android.internal.action.EUICC_FACTORY_RESET"; 116 private static final String ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS = 117 "com.android.internal.action.EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS"; 118 119 /** 120 * Used in {@link #wipeEuiccData} & {@link #removeEuiccInvisibleSubs} as package name of 121 * callback intent. 122 */ 123 private static final String PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK = "android"; 124 125 /** 126 * The recovery image uses this file to identify the location (i.e. blocks) 127 * of an OTA package on the /data partition. The block map file is 128 * generated by uncrypt. 129 * 130 * @hide 131 */ 132 public static final File BLOCK_MAP_FILE = new File(RECOVERY_DIR, "block.map"); 133 134 /** 135 * UNCRYPT_PACKAGE_FILE stores the filename to be uncrypt'd, which will be 136 * read by uncrypt. 137 * 138 * @hide 139 */ 140 public static final File UNCRYPT_PACKAGE_FILE = new File(RECOVERY_DIR, "uncrypt_file"); 141 142 /** 143 * UNCRYPT_STATUS_FILE stores the time cost (and error code in the case of a failure) 144 * of uncrypt. 145 * 146 * @hide 147 */ 148 public static final File UNCRYPT_STATUS_FILE = new File(RECOVERY_DIR, "uncrypt_status"); 149 150 // Length limits for reading files. 151 private static final int LOG_FILE_MAX_LENGTH = 64 * 1024; 152 153 // Prevent concurrent execution of requests. 154 private static final Object sRequestLock = new Object(); 155 156 private final IRecoverySystem mService; 157 158 /** 159 * The error codes for reboots initiated by resume on reboot clients. 160 * @hide 161 */ 162 @IntDef(prefix = { "RESUME_ON_REBOOT_REBOOT_ERROR_" }, value = { 163 RESUME_ON_REBOOT_REBOOT_ERROR_NONE, 164 RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED, 165 RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME, 166 RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED, 167 RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH, 168 RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE}) 169 public @interface ResumeOnRebootRebootErrorCode {} 170 171 /** 172 * The preparation of resume on reboot succeeds. 173 * 174 * <p> Don't expose it because a successful reboot should just reboot the device. 175 * @hide 176 */ 177 public static final int RESUME_ON_REBOOT_REBOOT_ERROR_NONE = 0; 178 179 /** 180 * The resume on reboot fails due to an unknown reason. 181 * @hide 182 */ 183 @SystemApi 184 public static final int RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED = 1000; 185 186 /** 187 * The resume on reboot fails because the package name of the client is invalid, e.g. null 188 * packageName, name contains invalid characters, etc. 189 * @hide 190 */ 191 @SystemApi 192 public static final int RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME = 2000; 193 194 /** 195 * The resume on reboot fails because the Lock Screen Knowledge Factor hasn't been captured. 196 * This error is also reported if the client attempts to reboot without preparing RoR. 197 * @hide 198 */ 199 @SystemApi 200 public static final int RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED = 3000; 201 202 /** 203 * The resume on reboot fails because the client expects a different boot slot for the next boot 204 * on A/B devices. 205 * @hide 206 */ 207 @SystemApi 208 public static final int RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH = 4000; 209 210 /** 211 * The resume on reboot fails because the resume on reboot provider, e.g. HAL / server based, 212 * fails to arm/store the escrow key. 213 * @hide 214 */ 215 @SystemApi 216 public static final int RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE = 5000; 217 218 /** 219 * Interface definition for a callback to be invoked regularly as 220 * verification proceeds. 221 */ 222 public interface ProgressListener { 223 /** 224 * Called periodically as the verification progresses. 225 * 226 * @param progress the approximate percentage of the 227 * verification that has been completed, ranging from 0 228 * to 100 (inclusive). 229 */ onProgress(int progress)230 public void onProgress(int progress); 231 } 232 233 /** @return the set of certs that can be used to sign an OTA package. */ getTrustedCerts(File keystore)234 private static HashSet<X509Certificate> getTrustedCerts(File keystore) 235 throws IOException, GeneralSecurityException { 236 HashSet<X509Certificate> trusted = new HashSet<X509Certificate>(); 237 if (keystore == null) { 238 keystore = DEFAULT_KEYSTORE; 239 } 240 ZipFile zip = new ZipFile(keystore); 241 try { 242 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 243 Enumeration<? extends ZipEntry> entries = zip.entries(); 244 while (entries.hasMoreElements()) { 245 ZipEntry entry = entries.nextElement(); 246 InputStream is = zip.getInputStream(entry); 247 try { 248 trusted.add((X509Certificate) cf.generateCertificate(is)); 249 } finally { 250 is.close(); 251 } 252 } 253 } finally { 254 zip.close(); 255 } 256 return trusted; 257 } 258 259 /** 260 * Verify the cryptographic signature of a system update package 261 * before installing it. Note that the package is also verified 262 * separately by the installer once the device is rebooted into 263 * the recovery system. This function will return only if the 264 * package was successfully verified; otherwise it will throw an 265 * exception. 266 * 267 * Verification of a package can take significant time, so this 268 * function should not be called from a UI thread. Interrupting 269 * the thread while this function is in progress will result in a 270 * SecurityException being thrown (and the thread's interrupt flag 271 * will be cleared). 272 * 273 * @param packageFile the package to be verified 274 * @param listener an object to receive periodic progress 275 * updates as verification proceeds. May be null. 276 * @param deviceCertsZipFile the zip file of certificates whose 277 * public keys we will accept. Verification succeeds if the 278 * package is signed by the private key corresponding to any 279 * public key in this file. May be null to use the system default 280 * file (currently "/system/etc/security/otacerts.zip"). 281 * 282 * @throws IOException if there were any errors reading the 283 * package or certs files. 284 * @throws GeneralSecurityException if verification failed 285 */ verifyPackage(File packageFile, ProgressListener listener, File deviceCertsZipFile)286 public static void verifyPackage(File packageFile, 287 ProgressListener listener, 288 File deviceCertsZipFile) 289 throws IOException, GeneralSecurityException { 290 final long fileLen = packageFile.length(); 291 292 final RandomAccessFile raf = new RandomAccessFile(packageFile, "r"); 293 try { 294 final long startTimeMillis = System.currentTimeMillis(); 295 if (listener != null) { 296 listener.onProgress(0); 297 } 298 299 raf.seek(fileLen - 6); 300 byte[] footer = new byte[6]; 301 raf.readFully(footer); 302 303 if (footer[2] != (byte)0xff || footer[3] != (byte)0xff) { 304 throw new SignatureException("no signature in file (no footer)"); 305 } 306 307 final int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8); 308 final int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8); 309 310 byte[] eocd = new byte[commentSize + 22]; 311 raf.seek(fileLen - (commentSize + 22)); 312 raf.readFully(eocd); 313 314 // Check that we have found the start of the 315 // end-of-central-directory record. 316 if (eocd[0] != (byte)0x50 || eocd[1] != (byte)0x4b || 317 eocd[2] != (byte)0x05 || eocd[3] != (byte)0x06) { 318 throw new SignatureException("no signature in file (bad footer)"); 319 } 320 321 for (int i = 4; i < eocd.length-3; ++i) { 322 if (eocd[i ] == (byte)0x50 && eocd[i+1] == (byte)0x4b && 323 eocd[i+2] == (byte)0x05 && eocd[i+3] == (byte)0x06) { 324 throw new SignatureException("EOCD marker found after start of EOCD"); 325 } 326 } 327 328 // Parse the signature 329 PKCS7 block = 330 new PKCS7(new ByteArrayInputStream(eocd, commentSize+22-signatureStart, signatureStart)); 331 332 // Take the first certificate from the signature (packages 333 // should contain only one). 334 X509Certificate[] certificates = block.getCertificates(); 335 if (certificates == null || certificates.length == 0) { 336 throw new SignatureException("signature contains no certificates"); 337 } 338 X509Certificate cert = certificates[0]; 339 PublicKey signatureKey = cert.getPublicKey(); 340 341 SignerInfo[] signerInfos = block.getSignerInfos(); 342 if (signerInfos == null || signerInfos.length == 0) { 343 throw new SignatureException("signature contains no signedData"); 344 } 345 SignerInfo signerInfo = signerInfos[0]; 346 347 // Check that the public key of the certificate contained 348 // in the package equals one of our trusted public keys. 349 boolean verified = false; 350 HashSet<X509Certificate> trusted = getTrustedCerts( 351 deviceCertsZipFile == null ? DEFAULT_KEYSTORE : deviceCertsZipFile); 352 for (X509Certificate c : trusted) { 353 if (c.getPublicKey().equals(signatureKey)) { 354 verified = true; 355 break; 356 } 357 } 358 if (!verified) { 359 throw new SignatureException("signature doesn't match any trusted key"); 360 } 361 362 // The signature cert matches a trusted key. Now verify that 363 // the digest in the cert matches the actual file data. 364 raf.seek(0); 365 final ProgressListener listenerForInner = listener; 366 SignerInfo verifyResult = block.verify(signerInfo, new InputStream() { 367 // The signature covers all of the OTA package except the 368 // archive comment and its 2-byte length. 369 long toRead = fileLen - commentSize - 2; 370 long soFar = 0; 371 372 int lastPercent = 0; 373 long lastPublishTime = startTimeMillis; 374 375 @Override 376 public int read() throws IOException { 377 throw new UnsupportedOperationException(); 378 } 379 380 @Override 381 public int read(byte[] b, int off, int len) throws IOException { 382 if (soFar >= toRead) { 383 return -1; 384 } 385 if (Thread.currentThread().isInterrupted()) { 386 return -1; 387 } 388 389 int size = len; 390 if (soFar + size > toRead) { 391 size = (int)(toRead - soFar); 392 } 393 int read = raf.read(b, off, size); 394 soFar += read; 395 396 if (listenerForInner != null) { 397 long now = System.currentTimeMillis(); 398 int p = (int)(soFar * 100 / toRead); 399 if (p > lastPercent && 400 now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) { 401 lastPercent = p; 402 lastPublishTime = now; 403 listenerForInner.onProgress(lastPercent); 404 } 405 } 406 407 return read; 408 } 409 }); 410 411 final boolean interrupted = Thread.interrupted(); 412 if (listener != null) { 413 listener.onProgress(100); 414 } 415 416 if (interrupted) { 417 throw new SignatureException("verification was interrupted"); 418 } 419 420 if (verifyResult == null) { 421 throw new SignatureException("signature digest verification failed"); 422 } 423 } finally { 424 raf.close(); 425 } 426 427 // Additionally verify the package compatibility. 428 if (!readAndVerifyPackageCompatibilityEntry(packageFile)) { 429 throw new SignatureException("package compatibility verification failed"); 430 } 431 } 432 433 /** 434 * Verifies the compatibility entry from an {@link InputStream}. 435 * 436 * @return the verification result. 437 */ 438 @UnsupportedAppUsage verifyPackageCompatibility(InputStream inputStream)439 private static boolean verifyPackageCompatibility(InputStream inputStream) throws IOException { 440 ArrayList<String> list = new ArrayList<>(); 441 ZipInputStream zis = new ZipInputStream(inputStream); 442 ZipEntry entry; 443 while ((entry = zis.getNextEntry()) != null) { 444 long entrySize = entry.getSize(); 445 if (entrySize > Integer.MAX_VALUE || entrySize < 0) { 446 throw new IOException( 447 "invalid entry size (" + entrySize + ") in the compatibility file"); 448 } 449 byte[] bytes = new byte[(int) entrySize]; 450 Streams.readFully(zis, bytes); 451 list.add(new String(bytes, UTF_8)); 452 } 453 if (list.isEmpty()) { 454 throw new IOException("no entries found in the compatibility file"); 455 } 456 return (VintfObject.verify(list.toArray(new String[list.size()])) == 0); 457 } 458 459 /** 460 * Reads and verifies the compatibility entry in an OTA zip package. The compatibility entry is 461 * a zip file (inside the OTA package zip). 462 * 463 * @return {@code true} if the entry doesn't exist or verification passes. 464 */ readAndVerifyPackageCompatibilityEntry(File packageFile)465 private static boolean readAndVerifyPackageCompatibilityEntry(File packageFile) 466 throws IOException { 467 try (ZipFile zip = new ZipFile(packageFile)) { 468 ZipEntry entry = zip.getEntry("compatibility.zip"); 469 if (entry == null) { 470 return true; 471 } 472 InputStream inputStream = zip.getInputStream(entry); 473 return verifyPackageCompatibility(inputStream); 474 } 475 } 476 477 /** 478 * Verifies the package compatibility info against the current system. 479 * 480 * @param compatibilityFile the {@link File} that contains the package compatibility info. 481 * @throws IOException if there were any errors reading the compatibility file. 482 * @return the compatibility verification result. 483 * 484 * {@hide} 485 */ 486 @SystemApi 487 @SuppressLint("RequiresPermission") verifyPackageCompatibility(File compatibilityFile)488 public static boolean verifyPackageCompatibility(File compatibilityFile) throws IOException { 489 try (InputStream inputStream = new FileInputStream(compatibilityFile)) { 490 return verifyPackageCompatibility(inputStream); 491 } 492 } 493 494 /** 495 * Process a given package with uncrypt. No-op if the package is not on the 496 * /data partition. 497 * 498 * @param Context the Context to use 499 * @param packageFile the package to be processed 500 * @param listener an object to receive periodic progress updates as 501 * processing proceeds. May be null. 502 * @param handler the Handler upon which the callbacks will be 503 * executed. 504 * 505 * @throws IOException if there were any errors processing the package file. 506 * 507 * @hide 508 */ 509 @SystemApi 510 @RequiresPermission(android.Manifest.permission.RECOVERY) processPackage(Context context, File packageFile, final ProgressListener listener, final Handler handler)511 public static void processPackage(Context context, 512 File packageFile, 513 final ProgressListener listener, 514 final Handler handler) 515 throws IOException { 516 String filename = packageFile.getCanonicalPath(); 517 if (!filename.startsWith("/data/")) { 518 return; 519 } 520 521 RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); 522 IRecoverySystemProgressListener progressListener = null; 523 if (listener != null) { 524 final Handler progressHandler; 525 if (handler != null) { 526 progressHandler = handler; 527 } else { 528 progressHandler = new Handler(context.getMainLooper()); 529 } 530 progressListener = new IRecoverySystemProgressListener.Stub() { 531 int lastProgress = 0; 532 long lastPublishTime = System.currentTimeMillis(); 533 534 @Override 535 public void onProgress(final int progress) { 536 final long now = System.currentTimeMillis(); 537 progressHandler.post(new Runnable() { 538 @Override 539 public void run() { 540 if (progress > lastProgress && 541 now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) { 542 lastProgress = progress; 543 lastPublishTime = now; 544 listener.onProgress(progress); 545 } 546 } 547 }); 548 } 549 }; 550 } 551 552 if (!rs.uncrypt(filename, progressListener)) { 553 throw new IOException("process package failed"); 554 } 555 } 556 557 /** 558 * Process a given package with uncrypt. No-op if the package is not on the 559 * /data partition. 560 * 561 * @param Context the Context to use 562 * @param packageFile the package to be processed 563 * @param listener an object to receive periodic progress updates as 564 * processing proceeds. May be null. 565 * 566 * @throws IOException if there were any errors processing the package file. 567 * 568 * @hide 569 */ 570 @SystemApi 571 @RequiresPermission(android.Manifest.permission.RECOVERY) processPackage(Context context, File packageFile, final ProgressListener listener)572 public static void processPackage(Context context, 573 File packageFile, 574 final ProgressListener listener) 575 throws IOException { 576 processPackage(context, packageFile, listener, null); 577 } 578 579 /** 580 * Reboots the device in order to install the given update 581 * package. 582 * Requires the {@link android.Manifest.permission#REBOOT} permission. 583 * 584 * @param context the Context to use 585 * @param packageFile the update package to install. Must be on 586 * a partition mountable by recovery. (The set of partitions 587 * known to recovery may vary from device to device. Generally, 588 * /cache and /data are safe.) 589 * 590 * @throws IOException if writing the recovery command file 591 * fails, or if the reboot itself fails. 592 */ 593 @RequiresPermission(android.Manifest.permission.RECOVERY) installPackage(Context context, File packageFile)594 public static void installPackage(Context context, File packageFile) 595 throws IOException { 596 installPackage(context, packageFile, false); 597 } 598 599 /** 600 * If the package hasn't been processed (i.e. uncrypt'd), set up 601 * UNCRYPT_PACKAGE_FILE and delete BLOCK_MAP_FILE to trigger uncrypt during the 602 * reboot. 603 * 604 * @param context the Context to use 605 * @param packageFile the update package to install. Must be on a 606 * partition mountable by recovery. 607 * @param processed if the package has been processed (uncrypt'd). 608 * 609 * @throws IOException if writing the recovery command file fails, or if 610 * the reboot itself fails. 611 * 612 * @hide 613 */ 614 @SystemApi 615 @RequiresPermission(android.Manifest.permission.RECOVERY) installPackage(Context context, File packageFile, boolean processed)616 public static void installPackage(Context context, File packageFile, boolean processed) 617 throws IOException { 618 synchronized (sRequestLock) { 619 LOG_FILE.delete(); 620 // Must delete the file in case it was created by system server. 621 UNCRYPT_PACKAGE_FILE.delete(); 622 623 String filename = packageFile.getCanonicalPath(); 624 Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!"); 625 626 // If the package name ends with "_s.zip", it's a security update. 627 boolean securityUpdate = filename.endsWith("_s.zip"); 628 629 // If the package is on the /data partition, the package needs to 630 // be processed (i.e. uncrypt'd). The caller specifies if that has 631 // been done in 'processed' parameter. 632 if (filename.startsWith("/data/")) { 633 if (processed) { 634 if (!BLOCK_MAP_FILE.exists()) { 635 Log.e(TAG, "Package claimed to have been processed but failed to find " 636 + "the block map file."); 637 throw new IOException("Failed to find block map file"); 638 } 639 } else { 640 FileWriter uncryptFile = new FileWriter(UNCRYPT_PACKAGE_FILE); 641 try { 642 uncryptFile.write(filename + "\n"); 643 } finally { 644 uncryptFile.close(); 645 } 646 // UNCRYPT_PACKAGE_FILE needs to be readable and writable 647 // by system server. 648 if (!UNCRYPT_PACKAGE_FILE.setReadable(true, false) 649 || !UNCRYPT_PACKAGE_FILE.setWritable(true, false)) { 650 Log.e(TAG, "Error setting permission for " + UNCRYPT_PACKAGE_FILE); 651 } 652 653 BLOCK_MAP_FILE.delete(); 654 } 655 656 // If the package is on the /data partition, use the block map 657 // file as the package name instead. 658 filename = "@/cache/recovery/block.map"; 659 } 660 661 final String filenameArg = "--update_package=" + filename + "\n"; 662 final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() + "\n"; 663 final String securityArg = "--security\n"; 664 665 String command = filenameArg + localeArg; 666 if (securityUpdate) { 667 command += securityArg; 668 } 669 670 RecoverySystem rs = (RecoverySystem) context.getSystemService( 671 Context.RECOVERY_SERVICE); 672 if (!rs.setupBcb(command)) { 673 throw new IOException("Setup BCB failed"); 674 } 675 try { 676 if (!rs.allocateSpaceForUpdate(packageFile)) { 677 throw new IOException("Failed to allocate space for update " 678 + packageFile.getAbsolutePath()); 679 } 680 } catch (RemoteException e) { 681 e.rethrowAsRuntimeException(); 682 } 683 684 // Having set up the BCB (bootloader control block), go ahead and reboot 685 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 686 String reason = PowerManager.REBOOT_RECOVERY_UPDATE; 687 688 // On TV, reboot quiescently if the screen is off 689 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { 690 DisplayManager dm = context.getSystemService(DisplayManager.class); 691 if (dm.getDisplay(DEFAULT_DISPLAY).getState() != Display.STATE_ON) { 692 reason += ",quiescent"; 693 } 694 } 695 pm.reboot(reason); 696 697 throw new IOException("Reboot failed (no permissions?)"); 698 } 699 } 700 701 /** 702 * Prepare to apply an unattended update by asking the user for their Lock Screen Knowledge 703 * Factor (LSKF). If supplied, the {@code intentSender} will be called when the system is setup 704 * and ready to apply the OTA. <p> 705 * 706 * <p> If the device doesn't setup a lock screen, i.e. by checking 707 * {@link KeyguardManager#isKeyguardSecure()}, this API call will fail and throw an exception. 708 * Callers are expected to use {@link PowerManager#reboot(String)} directly without going 709 * through the RoR flow. <p> 710 * 711 * <p> This API is expected to handle requests from multiple clients simultaneously, e.g. 712 * from ota and mainline. The behavior of multi-client Resume on Reboot works as follows 713 * <li> Each client should call this function to prepare for Resume on Reboot before calling 714 * {@link #rebootAndApply(Context, String, boolean)} </li> 715 * <li> One client cannot clear the Resume on Reboot preparation of another client. </li> 716 * <li> If multiple clients have prepared for Resume on Reboot, the subsequent reboot will be 717 * first come, first served. </li> 718 * 719 * @param context the Context to use. 720 * @param updateToken this parameter is deprecated and won't be used. Callers can supply with 721 * an empty string. See details in 722 * <a href="http://go/multi-client-ror">http://go/multi-client-ror</a> 723 * TODO(xunchang) update the link of document with the public doc. 724 * @param intentSender the intent to call when the update is prepared; may be {@code null} 725 * @throws IOException if there were any errors setting up unattended update 726 * @hide 727 */ 728 @SystemApi 729 @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY, 730 android.Manifest.permission.REBOOT}) prepareForUnattendedUpdate(@onNull Context context, @NonNull String updateToken, @Nullable IntentSender intentSender)731 public static void prepareForUnattendedUpdate(@NonNull Context context, 732 @NonNull String updateToken, @Nullable IntentSender intentSender) throws IOException { 733 if (updateToken == null) { 734 throw new NullPointerException("updateToken == null"); 735 } 736 737 KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class); 738 if (keyguardManager == null || !keyguardManager.isDeviceSecure()) { 739 throw new IOException("Failed to request LSKF because the device doesn't have a" 740 + " lock screen. "); 741 } 742 743 RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); 744 if (!rs.requestLskf(context.getPackageName(), intentSender)) { 745 throw new IOException("preparation for update failed"); 746 } 747 } 748 749 /** 750 * Request that any previously requested Lock Screen Knowledge Factor (LSKF) is cleared and 751 * the preparation for unattended update is reset. 752 * 753 * <p> Note that the API won't clear the underlying Resume on Reboot preparation state if 754 * another client has requested. So the reboot call from the other client can still succeed. 755 * 756 * @param context the Context to use. 757 * @throws IOException if there were any errors clearing the unattended update state 758 * @hide 759 */ 760 @SystemApi 761 @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY, 762 android.Manifest.permission.REBOOT}) clearPrepareForUnattendedUpdate(@onNull Context context)763 public static void clearPrepareForUnattendedUpdate(@NonNull Context context) 764 throws IOException { 765 RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); 766 if (!rs.clearLskf(context.getPackageName())) { 767 throw new IOException("could not reset unattended update state"); 768 } 769 } 770 771 /** 772 * Request that the device reboot and apply the update that has been prepared. This API is 773 * deprecated, and is expected to be used by OTA only on devices running Android 11. 774 * 775 * @param context the Context to use. 776 * @param updateToken this parameter is deprecated and won't be used. See details in 777 * <a href="http://go/multi-client-ror">http://go/multi-client-ror</a> 778 * TODO(xunchang) update the link of document with the public doc. 779 * @param reason the reboot reason to give to the {@link PowerManager} 780 * @throws IOException if the reboot couldn't proceed because the device wasn't ready for an 781 * unattended reboot or if the {@code updateToken} did not match the previously 782 * given token 783 * @hide 784 * @deprecated Use {@link #rebootAndApply(Context, String, boolean)} instead 785 */ 786 @SystemApi 787 @RequiresPermission(android.Manifest.permission.RECOVERY) rebootAndApply(@onNull Context context, @NonNull String updateToken, @NonNull String reason)788 public static void rebootAndApply(@NonNull Context context, @NonNull String updateToken, 789 @NonNull String reason) throws IOException { 790 if (updateToken == null) { 791 throw new NullPointerException("updateToken == null"); 792 } 793 RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); 794 // OTA is the sole user, who expects a slot switch. 795 if (rs.rebootWithLskfAssumeSlotSwitch(context.getPackageName(), reason) 796 != RESUME_ON_REBOOT_REBOOT_ERROR_NONE) { 797 throw new IOException("system not prepared to apply update"); 798 } 799 } 800 801 /** 802 * Query if Resume on Reboot has been prepared for a given caller. 803 * 804 * @param context the Context to use. 805 * @throws IOException if there were any errors connecting to the service or querying the state. 806 * @hide 807 */ 808 @SystemApi 809 @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY, 810 android.Manifest.permission.REBOOT}) isPreparedForUnattendedUpdate(@onNull Context context)811 public static boolean isPreparedForUnattendedUpdate(@NonNull Context context) 812 throws IOException { 813 RecoverySystem rs = context.getSystemService(RecoverySystem.class); 814 return rs.isLskfCaptured(context.getPackageName()); 815 } 816 817 /** 818 * Request that the device reboot and apply the update that has been prepared. 819 * {@link #prepareForUnattendedUpdate} must be called before for the given client, 820 * otherwise the function call will fail. 821 * 822 * @param context the Context to use. 823 * @param reason the reboot reason to give to the {@link PowerManager} 824 * @param slotSwitch true if the caller expects the slot to be switched on A/B devices. 825 * 826 * @return 0 on success, and a non-zero error code if the reboot couldn't proceed because the 827 * device wasn't ready for an unattended reboot. 828 * @throws IOException on remote exceptions from the RecoverySystemService 829 * @hide 830 */ 831 @SystemApi 832 @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY, 833 android.Manifest.permission.REBOOT}) rebootAndApply(@onNull Context context, @NonNull String reason, boolean slotSwitch)834 public static @ResumeOnRebootRebootErrorCode int rebootAndApply(@NonNull Context context, 835 @NonNull String reason, boolean slotSwitch) throws IOException { 836 RecoverySystem rs = context.getSystemService(RecoverySystem.class); 837 return rs.rebootWithLskf(context.getPackageName(), reason, slotSwitch); 838 } 839 840 /** 841 * Schedule to install the given package on next boot. The caller needs to ensure that the 842 * package must have been processed (uncrypt'd) if needed. It sets up the command in BCB 843 * (bootloader control block), which will be read by the bootloader and the recovery image. 844 * 845 * @param context the Context to use. 846 * @param packageFile the package to be installed. 847 * @throws IOException if there were any errors setting up the BCB. 848 * @hide 849 */ 850 @SystemApi 851 @RequiresPermission(android.Manifest.permission.RECOVERY) scheduleUpdateOnBoot(Context context, File packageFile)852 public static void scheduleUpdateOnBoot(Context context, File packageFile) throws IOException { 853 String filename = packageFile.getCanonicalPath(); 854 boolean securityUpdate = filename.endsWith("_s.zip"); 855 856 // If the package is on the /data partition, use the block map file as 857 // the package name instead. 858 if (filename.startsWith("/data/")) { 859 filename = "@/cache/recovery/block.map"; 860 } 861 862 final String filenameArg = "--update_package=" + filename + "\n"; 863 final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() + "\n"; 864 final String securityArg = "--security\n"; 865 866 String command = filenameArg + localeArg; 867 if (securityUpdate) { 868 command += securityArg; 869 } 870 871 RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); 872 if (!rs.setupBcb(command)) { 873 throw new IOException("schedule update on boot failed"); 874 } 875 } 876 877 /** 878 * Cancel any scheduled update by clearing up the BCB (bootloader control 879 * block). 880 * 881 * @param Context the Context to use. 882 * 883 * @throws IOException if there were any errors clearing up the BCB. 884 * 885 * @hide 886 */ 887 @SystemApi 888 @RequiresPermission(android.Manifest.permission.RECOVERY) cancelScheduledUpdate(Context context)889 public static void cancelScheduledUpdate(Context context) 890 throws IOException { 891 RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); 892 if (!rs.clearBcb()) { 893 throw new IOException("cancel scheduled update failed"); 894 } 895 } 896 897 /** 898 * Reboots the device and wipes the user data and cache 899 * partitions. This is sometimes called a "factory reset", which 900 * is something of a misnomer because the system partition is not 901 * restored to its factory state. Requires the 902 * {@link android.Manifest.permission#REBOOT} permission. 903 * 904 * @param context the Context to use 905 * 906 * @throws IOException if writing the recovery command file 907 * fails, or if the reboot itself fails. 908 * @throws SecurityException if the current user is not allowed to wipe data. 909 */ rebootWipeUserData(Context context)910 public static void rebootWipeUserData(Context context) throws IOException { 911 rebootWipeUserData(context, false /* shutdown */, context.getPackageName(), 912 false /* force */, false /* wipeEuicc */); 913 } 914 915 /** {@hide} */ rebootWipeUserData(Context context, String reason)916 public static void rebootWipeUserData(Context context, String reason) throws IOException { 917 rebootWipeUserData(context, false /* shutdown */, reason, false /* force */, 918 false /* wipeEuicc */); 919 } 920 921 /** {@hide} */ rebootWipeUserData(Context context, boolean shutdown)922 public static void rebootWipeUserData(Context context, boolean shutdown) 923 throws IOException { 924 rebootWipeUserData(context, shutdown, context.getPackageName(), false /* force */, 925 false /* wipeEuicc */); 926 } 927 928 /** {@hide} */ rebootWipeUserData(Context context, boolean shutdown, String reason, boolean force)929 public static void rebootWipeUserData(Context context, boolean shutdown, String reason, 930 boolean force) throws IOException { 931 rebootWipeUserData(context, shutdown, reason, force, false /* wipeEuicc */); 932 } 933 934 /** 935 * Reboots the device and wipes the user data and cache 936 * partitions. This is sometimes called a "factory reset", which 937 * is something of a misnomer because the system partition is not 938 * restored to its factory state. Requires the 939 * {@link android.Manifest.permission#REBOOT} permission. 940 * 941 * @param context the Context to use 942 * @param shutdown if true, the device will be powered down after 943 * the wipe completes, rather than being rebooted 944 * back to the regular system. 945 * @param reason the reason for the wipe that is visible in the logs 946 * @param force whether the {@link UserManager.DISALLOW_FACTORY_RESET} user restriction 947 * should be ignored 948 * @param wipeEuicc whether wipe the euicc data 949 * 950 * @throws IOException if writing the recovery command file 951 * fails, or if the reboot itself fails. 952 * @throws SecurityException if the current user is not allowed to wipe data. 953 * 954 * @hide 955 */ rebootWipeUserData(Context context, boolean shutdown, String reason, boolean force, boolean wipeEuicc)956 public static void rebootWipeUserData(Context context, boolean shutdown, String reason, 957 boolean force, boolean wipeEuicc) throws IOException { 958 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 959 if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) { 960 throw new SecurityException("Wiping data is not allowed for this user."); 961 } 962 final ConditionVariable condition = new ConditionVariable(); 963 964 Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION"); 965 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND 966 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 967 context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM, 968 android.Manifest.permission.MASTER_CLEAR, 969 new BroadcastReceiver() { 970 @Override 971 public void onReceive(Context context, Intent intent) { 972 condition.open(); 973 } 974 }, null, 0, null, null); 975 976 // Block until the ordered broadcast has completed. 977 condition.block(); 978 979 EuiccManager euiccManager = context.getSystemService(EuiccManager.class); 980 if (wipeEuicc) { 981 wipeEuiccData(context, PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK); 982 } else { 983 removeEuiccInvisibleSubs(context, euiccManager); 984 } 985 986 String shutdownArg = null; 987 if (shutdown) { 988 shutdownArg = "--shutdown_after"; 989 } 990 991 String reasonArg = null; 992 if (!TextUtils.isEmpty(reason)) { 993 String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString(); 994 reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp); 995 } 996 997 final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ; 998 bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg); 999 } 1000 1001 /** 1002 * Returns whether wipe Euicc data successfully or not. 1003 * 1004 * @param packageName the package name of the caller app. 1005 * 1006 * @hide 1007 */ wipeEuiccData(Context context, final String packageName)1008 public static boolean wipeEuiccData(Context context, final String packageName) { 1009 ContentResolver cr = context.getContentResolver(); 1010 if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) { 1011 // If the eUICC isn't provisioned, there's no reason to either wipe or retain profiles, 1012 // as there's nothing to wipe nor retain. 1013 Log.d(TAG, "Skipping eUICC wipe/retain as it is not provisioned"); 1014 return true; 1015 } 1016 1017 EuiccManager euiccManager = (EuiccManager) context.getSystemService( 1018 Context.EUICC_SERVICE); 1019 if (euiccManager != null && euiccManager.isEnabled()) { 1020 CountDownLatch euiccFactoryResetLatch = new CountDownLatch(1); 1021 final AtomicBoolean wipingSucceeded = new AtomicBoolean(false); 1022 1023 BroadcastReceiver euiccWipeFinishReceiver = new BroadcastReceiver() { 1024 @Override 1025 public void onReceive(Context context, Intent intent) { 1026 if (ACTION_EUICC_FACTORY_RESET.equals(intent.getAction())) { 1027 if (getResultCode() != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) { 1028 int detailedCode = intent.getIntExtra( 1029 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0); 1030 Log.e(TAG, "Error wiping euicc data, Detailed code = " 1031 + detailedCode); 1032 } else { 1033 Log.d(TAG, "Successfully wiped euicc data."); 1034 wipingSucceeded.set(true /* newValue */); 1035 } 1036 euiccFactoryResetLatch.countDown(); 1037 } 1038 } 1039 }; 1040 1041 Intent intent = new Intent(ACTION_EUICC_FACTORY_RESET); 1042 intent.setPackage(packageName); 1043 PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser( 1044 context, 1045 0, 1046 intent, 1047 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, 1048 UserHandle.SYSTEM); 1049 IntentFilter filterConsent = new IntentFilter(); 1050 filterConsent.addAction(ACTION_EUICC_FACTORY_RESET); 1051 HandlerThread euiccHandlerThread = new HandlerThread("euiccWipeFinishReceiverThread"); 1052 euiccHandlerThread.start(); 1053 Handler euiccHandler = new Handler(euiccHandlerThread.getLooper()); 1054 context.getApplicationContext() 1055 .registerReceiver(euiccWipeFinishReceiver, filterConsent, null, euiccHandler); 1056 euiccManager.eraseSubscriptions(callbackIntent); 1057 try { 1058 long waitingTimeMillis = Settings.Global.getLong( 1059 context.getContentResolver(), 1060 Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS, 1061 DEFAULT_EUICC_FACTORY_RESET_TIMEOUT_MILLIS); 1062 if (waitingTimeMillis < MIN_EUICC_FACTORY_RESET_TIMEOUT_MILLIS) { 1063 waitingTimeMillis = MIN_EUICC_FACTORY_RESET_TIMEOUT_MILLIS; 1064 } else if (waitingTimeMillis > MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS) { 1065 waitingTimeMillis = MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS; 1066 } 1067 if (!euiccFactoryResetLatch.await(waitingTimeMillis, TimeUnit.MILLISECONDS)) { 1068 Log.e(TAG, "Timeout wiping eUICC data."); 1069 return false; 1070 } 1071 } catch (InterruptedException e) { 1072 Thread.currentThread().interrupt(); 1073 Log.e(TAG, "Wiping eUICC data interrupted", e); 1074 return false; 1075 } finally { 1076 context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver); 1077 } 1078 return wipingSucceeded.get(); 1079 } 1080 return false; 1081 } 1082 removeEuiccInvisibleSubs( Context context, EuiccManager euiccManager)1083 private static void removeEuiccInvisibleSubs( 1084 Context context, EuiccManager euiccManager) { 1085 ContentResolver cr = context.getContentResolver(); 1086 if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) { 1087 // If the eUICC isn't provisioned, there's no need to remove euicc invisible profiles, 1088 // as there's nothing to be removed. 1089 Log.i(TAG, "Skip removing eUICC invisible profiles as it is not provisioned."); 1090 return; 1091 } else if (euiccManager == null || !euiccManager.isEnabled()) { 1092 Log.i(TAG, "Skip removing eUICC invisible profiles as eUICC manager is not available."); 1093 return; 1094 } 1095 SubscriptionManager subscriptionManager = 1096 context.getSystemService(SubscriptionManager.class); 1097 List<SubscriptionInfo> availableSubs = 1098 subscriptionManager.getAvailableSubscriptionInfoList(); 1099 if (availableSubs == null || availableSubs.isEmpty()) { 1100 Log.i(TAG, "Skip removing eUICC invisible profiles as no available profiles found."); 1101 return; 1102 } 1103 List<SubscriptionInfo> invisibleSubs = new ArrayList<>(); 1104 for (SubscriptionInfo sub : availableSubs) { 1105 if (sub.isEmbedded() && sub.getGroupUuid() != null && sub.isOpportunistic()) { 1106 invisibleSubs.add(sub); 1107 } 1108 } 1109 removeEuiccInvisibleSubs(context, invisibleSubs, euiccManager); 1110 } 1111 removeEuiccInvisibleSubs( Context context, List<SubscriptionInfo> subscriptionInfos, EuiccManager euiccManager)1112 private static boolean removeEuiccInvisibleSubs( 1113 Context context, List<SubscriptionInfo> subscriptionInfos, EuiccManager euiccManager) { 1114 if (subscriptionInfos == null || subscriptionInfos.isEmpty()) { 1115 Log.i(TAG, "There are no eUICC invisible profiles needed to be removed."); 1116 return true; 1117 } 1118 CountDownLatch removeSubsLatch = new CountDownLatch(subscriptionInfos.size()); 1119 final AtomicInteger removedSubsCount = new AtomicInteger(0); 1120 1121 BroadcastReceiver removeEuiccSubsReceiver = new BroadcastReceiver() { 1122 @Override 1123 public void onReceive(Context context, Intent intent) { 1124 if (ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS.equals(intent.getAction())) { 1125 if (getResultCode() != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) { 1126 int detailedCode = intent.getIntExtra( 1127 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0); 1128 Log.e(TAG, "Error removing euicc opportunistic profile, Detailed code = " 1129 + detailedCode); 1130 } else { 1131 Log.e(TAG, "Successfully remove euicc opportunistic profile."); 1132 removedSubsCount.incrementAndGet(); 1133 } 1134 removeSubsLatch.countDown(); 1135 } 1136 } 1137 }; 1138 1139 Intent intent = new Intent(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS); 1140 intent.setPackage(PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK); 1141 PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser( 1142 context, 1143 0, 1144 intent, 1145 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, 1146 UserHandle.SYSTEM); 1147 IntentFilter intentFilter = new IntentFilter(); 1148 intentFilter.addAction(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS); 1149 HandlerThread euiccHandlerThread = 1150 new HandlerThread("euiccRemovingSubsReceiverThread"); 1151 euiccHandlerThread.start(); 1152 Handler euiccHandler = new Handler(euiccHandlerThread.getLooper()); 1153 context.getApplicationContext() 1154 .registerReceiver( 1155 removeEuiccSubsReceiver, intentFilter, null, euiccHandler); 1156 for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { 1157 Log.i( 1158 TAG, 1159 "Remove invisible subscription " + subscriptionInfo.getSubscriptionId() 1160 + " from card " + subscriptionInfo.getCardId()); 1161 euiccManager.createForCardId(subscriptionInfo.getCardId()) 1162 .deleteSubscription(subscriptionInfo.getSubscriptionId(), callbackIntent); 1163 } 1164 try { 1165 long waitingTimeMillis = Settings.Global.getLong( 1166 context.getContentResolver(), 1167 Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS, 1168 DEFAULT_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS); 1169 if (waitingTimeMillis < MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS) { 1170 waitingTimeMillis = MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS; 1171 } else if (waitingTimeMillis > MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS) { 1172 waitingTimeMillis = MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS; 1173 } 1174 if (!removeSubsLatch.await(waitingTimeMillis, TimeUnit.MILLISECONDS)) { 1175 Log.e(TAG, "Timeout removing invisible euicc profiles."); 1176 return false; 1177 } 1178 } catch (InterruptedException e) { 1179 Thread.currentThread().interrupt(); 1180 Log.e(TAG, "Removing invisible euicc profiles interrupted", e); 1181 return false; 1182 } finally { 1183 context.getApplicationContext().unregisterReceiver(removeEuiccSubsReceiver); 1184 if (euiccHandlerThread != null) { 1185 euiccHandlerThread.quit(); 1186 } 1187 } 1188 return removedSubsCount.get() == subscriptionInfos.size(); 1189 } 1190 1191 /** {@hide} */ rebootPromptAndWipeUserData(Context context, String reason)1192 public static void rebootPromptAndWipeUserData(Context context, String reason) 1193 throws IOException { 1194 boolean checkpointing = false; 1195 boolean needReboot = false; 1196 IVold vold = null; 1197 try { 1198 vold = IVold.Stub.asInterface(ServiceManager.checkService("vold")); 1199 if (vold != null) { 1200 checkpointing = vold.needsCheckpoint(); 1201 } else { 1202 Log.w(TAG, "Failed to get vold"); 1203 } 1204 } catch (Exception e) { 1205 Log.w(TAG, "Failed to check for checkpointing"); 1206 } 1207 1208 // If we are running in checkpointing mode, we should not prompt a wipe. 1209 // Checkpointing may save us. If it doesn't, we will wind up here again. 1210 if (checkpointing) { 1211 try { 1212 vold.abortChanges("rescueparty", false); 1213 Log.i(TAG, "Rescue Party requested wipe. Aborting update"); 1214 } catch (Exception e) { 1215 Log.i(TAG, "Rescue Party requested wipe. Rebooting instead."); 1216 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 1217 pm.reboot("rescueparty"); 1218 } 1219 return; 1220 } 1221 1222 String reasonArg = null; 1223 if (!TextUtils.isEmpty(reason)) { 1224 reasonArg = "--reason=" + sanitizeArg(reason); 1225 } 1226 1227 final String localeArg = "--locale=" + Locale.getDefault().toString(); 1228 bootCommand(context, null, "--prompt_and_wipe_data", reasonArg, localeArg); 1229 } 1230 1231 /** 1232 * Reboot into the recovery system to wipe the /cache partition. 1233 * @throws IOException if something goes wrong. 1234 */ rebootWipeCache(Context context)1235 public static void rebootWipeCache(Context context) throws IOException { 1236 rebootWipeCache(context, context.getPackageName()); 1237 } 1238 1239 /** {@hide} */ rebootWipeCache(Context context, String reason)1240 public static void rebootWipeCache(Context context, String reason) throws IOException { 1241 String reasonArg = null; 1242 if (!TextUtils.isEmpty(reason)) { 1243 reasonArg = "--reason=" + sanitizeArg(reason); 1244 } 1245 1246 final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ; 1247 bootCommand(context, "--wipe_cache", reasonArg, localeArg); 1248 } 1249 1250 /** 1251 * Reboot into recovery and wipe the A/B device. 1252 * 1253 * @param Context the Context to use. 1254 * @param packageFile the wipe package to be applied. 1255 * @param reason the reason to wipe. 1256 * 1257 * @throws IOException if something goes wrong. 1258 * 1259 * @hide 1260 */ 1261 @SystemApi 1262 @RequiresPermission(allOf = { 1263 android.Manifest.permission.RECOVERY, 1264 android.Manifest.permission.REBOOT 1265 }) rebootWipeAb(Context context, File packageFile, String reason)1266 public static void rebootWipeAb(Context context, File packageFile, String reason) 1267 throws IOException { 1268 String reasonArg = null; 1269 if (!TextUtils.isEmpty(reason)) { 1270 reasonArg = "--reason=" + sanitizeArg(reason); 1271 } 1272 1273 final String filename = packageFile.getCanonicalPath(); 1274 final String filenameArg = "--wipe_package=" + filename; 1275 final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ; 1276 bootCommand(context, "--wipe_ab", filenameArg, reasonArg, localeArg); 1277 } 1278 1279 /** 1280 * Reboot into the recovery system with the supplied argument. 1281 * @param args to pass to the recovery utility. 1282 * @throws IOException if something goes wrong. 1283 */ bootCommand(Context context, String... args)1284 private static void bootCommand(Context context, String... args) throws IOException { 1285 LOG_FILE.delete(); 1286 1287 StringBuilder command = new StringBuilder(); 1288 for (String arg : args) { 1289 if (!TextUtils.isEmpty(arg)) { 1290 command.append(arg); 1291 command.append("\n"); 1292 } 1293 } 1294 1295 // Write the command into BCB (bootloader control block) and boot from 1296 // there. Will not return unless failed. 1297 RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); 1298 rs.rebootRecoveryWithCommand(command.toString()); 1299 1300 throw new IOException("Reboot failed (no permissions?)"); 1301 } 1302 1303 /** 1304 * Called after booting to process and remove recovery-related files. 1305 * @return the log file from recovery, or null if none was found. 1306 * 1307 * @hide 1308 */ handleAftermath(Context context)1309 public static String handleAftermath(Context context) { 1310 // Record the tail of the LOG_FILE 1311 String log = null; 1312 try { 1313 log = FileUtils.readTextFile(LOG_FILE, -LOG_FILE_MAX_LENGTH, "...\n"); 1314 } catch (FileNotFoundException e) { 1315 Log.i(TAG, "No recovery log file"); 1316 } catch (IOException e) { 1317 Log.e(TAG, "Error reading recovery log", e); 1318 } 1319 1320 1321 // Only remove the OTA package if it's partially processed (uncrypt'd). 1322 boolean reservePackage = BLOCK_MAP_FILE.exists(); 1323 if (!reservePackage && UNCRYPT_PACKAGE_FILE.exists()) { 1324 String filename = null; 1325 try { 1326 filename = FileUtils.readTextFile(UNCRYPT_PACKAGE_FILE, 0, null); 1327 } catch (IOException e) { 1328 Log.e(TAG, "Error reading uncrypt file", e); 1329 } 1330 1331 // Remove the OTA package on /data that has been (possibly 1332 // partially) processed. (Bug: 24973532) 1333 if (filename != null && filename.startsWith("/data")) { 1334 if (UNCRYPT_PACKAGE_FILE.delete()) { 1335 Log.i(TAG, "Deleted: " + filename); 1336 } else { 1337 Log.e(TAG, "Can't delete: " + filename); 1338 } 1339 } 1340 } 1341 1342 // We keep the update logs (beginning with LAST_PREFIX), and optionally 1343 // the block map file (BLOCK_MAP_FILE) for a package. BLOCK_MAP_FILE 1344 // will be created at the end of a successful uncrypt. If seeing this 1345 // file, we keep the block map file and the file that contains the 1346 // package name (UNCRYPT_PACKAGE_FILE). This is to reduce the work for 1347 // GmsCore to avoid re-downloading everything again. 1348 String[] names = RECOVERY_DIR.list(); 1349 for (int i = 0; names != null && i < names.length; i++) { 1350 // Do not remove the last_install file since the recovery-persist takes care of it. 1351 if (names[i].startsWith(LAST_PREFIX) || names[i].equals(LAST_INSTALL_PATH)) continue; 1352 if (reservePackage && names[i].equals(BLOCK_MAP_FILE.getName())) continue; 1353 if (reservePackage && names[i].equals(UNCRYPT_PACKAGE_FILE.getName())) continue; 1354 1355 recursiveDelete(new File(RECOVERY_DIR, names[i])); 1356 } 1357 1358 return log; 1359 } 1360 1361 /** 1362 * Internally, delete a given file or directory recursively. 1363 */ recursiveDelete(File name)1364 private static void recursiveDelete(File name) { 1365 if (name.isDirectory()) { 1366 String[] files = name.list(); 1367 for (int i = 0; files != null && i < files.length; i++) { 1368 File f = new File(name, files[i]); 1369 recursiveDelete(f); 1370 } 1371 } 1372 1373 if (!name.delete()) { 1374 Log.e(TAG, "Can't delete: " + name); 1375 } else { 1376 Log.i(TAG, "Deleted: " + name); 1377 } 1378 } 1379 1380 /** 1381 * Talks to RecoverySystemService via Binder to trigger uncrypt. 1382 */ uncrypt(String packageFile, IRecoverySystemProgressListener listener)1383 private boolean uncrypt(String packageFile, IRecoverySystemProgressListener listener) { 1384 try { 1385 return mService.uncrypt(packageFile, listener); 1386 } catch (RemoteException unused) { 1387 } 1388 return false; 1389 } 1390 1391 /** 1392 * Talks to RecoverySystemService via Binder to set up the BCB. 1393 */ setupBcb(String command)1394 private boolean setupBcb(String command) { 1395 try { 1396 return mService.setupBcb(command); 1397 } catch (RemoteException unused) { 1398 } 1399 return false; 1400 } 1401 1402 /** 1403 * Talks to RecoverySystemService via Binder to allocate space 1404 */ allocateSpaceForUpdate(File packageFile)1405 private boolean allocateSpaceForUpdate(File packageFile) throws RemoteException { 1406 return mService.allocateSpaceForUpdate(packageFile.getAbsolutePath()); 1407 } 1408 1409 /** 1410 * Talks to RecoverySystemService via Binder to clear up the BCB. 1411 */ clearBcb()1412 private boolean clearBcb() { 1413 try { 1414 return mService.clearBcb(); 1415 } catch (RemoteException unused) { 1416 } 1417 return false; 1418 } 1419 1420 /** 1421 * Talks to RecoverySystemService via Binder to set up the BCB command and 1422 * reboot into recovery accordingly. 1423 */ rebootRecoveryWithCommand(String command)1424 private void rebootRecoveryWithCommand(String command) { 1425 try { 1426 mService.rebootRecoveryWithCommand(command); 1427 } catch (RemoteException ignored) { 1428 } 1429 } 1430 1431 /** 1432 * Begins the process of asking the user for the Lock Screen Knowledge Factor. 1433 * 1434 * @param packageName the package name of the caller who requests Resume on Reboot 1435 * @return true if the request was correct 1436 * @throws IOException if the recovery system service could not be contacted 1437 */ requestLskf(String packageName, IntentSender sender)1438 private boolean requestLskf(String packageName, IntentSender sender) throws IOException { 1439 try { 1440 return mService.requestLskf(packageName, sender); 1441 } catch (RemoteException | SecurityException e) { 1442 throw new IOException("could not request LSKF capture", e); 1443 } 1444 } 1445 1446 /** 1447 * Calls the recovery system service and clears the setup for the OTA. 1448 * 1449 * @return true if the setup for OTA was cleared 1450 * @throws IOException if the recovery system service could not be contacted 1451 */ clearLskf(String packageName)1452 private boolean clearLskf(String packageName) throws IOException { 1453 try { 1454 return mService.clearLskf(packageName); 1455 } catch (RemoteException | SecurityException e) { 1456 throw new IOException("could not clear LSKF", e); 1457 } 1458 } 1459 1460 /** 1461 * Queries if the Resume on Reboot has been prepared for a given caller. 1462 * 1463 * @param packageName the identifier of the caller who requests Resume on Reboot 1464 * @return true if Resume on Reboot is prepared. 1465 * @throws IOException if the recovery system service could not be contacted 1466 */ isLskfCaptured(String packageName)1467 private boolean isLskfCaptured(String packageName) throws IOException { 1468 try { 1469 return mService.isLskfCaptured(packageName); 1470 } catch (RemoteException | SecurityException e) { 1471 throw new IOException("could not get LSKF capture state", e); 1472 } 1473 } 1474 1475 /** 1476 * Calls the recovery system service to reboot and apply update. 1477 * 1478 */ rebootWithLskf(String packageName, String reason, boolean slotSwitch)1479 private @ResumeOnRebootRebootErrorCode int rebootWithLskf(String packageName, String reason, 1480 boolean slotSwitch) throws IOException { 1481 try { 1482 return mService.rebootWithLskf(packageName, reason, slotSwitch); 1483 } catch (RemoteException | SecurityException e) { 1484 throw new IOException("could not reboot for update", e); 1485 } 1486 } 1487 1488 /** 1489 * Calls the recovery system service to reboot and apply update. This is the legacy API and 1490 * expects a slot switch for A/B devices. 1491 * 1492 */ rebootWithLskfAssumeSlotSwitch(String packageName, String reason)1493 private @ResumeOnRebootRebootErrorCode int rebootWithLskfAssumeSlotSwitch(String packageName, 1494 String reason) throws IOException { 1495 try { 1496 return mService.rebootWithLskfAssumeSlotSwitch(packageName, reason); 1497 } catch (RemoteException | RuntimeException e) { 1498 throw new IOException("could not reboot for update", e); 1499 } 1500 } 1501 1502 /** 1503 * Internally, recovery treats each line of the command file as a separate 1504 * argv, so we only need to protect against newlines and nulls. 1505 */ sanitizeArg(String arg)1506 private static String sanitizeArg(String arg) { 1507 arg = arg.replace('\0', '?'); 1508 arg = arg.replace('\n', '?'); 1509 return arg; 1510 } 1511 1512 1513 /** 1514 * @removed Was previously made visible by accident. 1515 */ RecoverySystem()1516 public RecoverySystem() { 1517 mService = null; 1518 } 1519 1520 /** 1521 * @hide 1522 */ RecoverySystem(IRecoverySystem service)1523 public RecoverySystem(IRecoverySystem service) { 1524 mService = service; 1525 } 1526 } 1527