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 rs.clearBcb(); 678 throw new IOException("Failed to allocate space for update " 679 + packageFile.getAbsolutePath()); 680 } 681 } catch (RemoteException e) { 682 rs.clearBcb(); 683 e.rethrowAsRuntimeException(); 684 } 685 686 // Having set up the BCB (bootloader control block), go ahead and reboot 687 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 688 String reason = PowerManager.REBOOT_RECOVERY_UPDATE; 689 690 // On TV, reboot quiescently if the screen is off 691 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { 692 DisplayManager dm = context.getSystemService(DisplayManager.class); 693 if (dm.getDisplay(DEFAULT_DISPLAY).getState() != Display.STATE_ON) { 694 reason += ",quiescent"; 695 } 696 } 697 pm.reboot(reason); 698 699 throw new IOException("Reboot failed (no permissions?)"); 700 } 701 } 702 703 /** 704 * Prepare to apply an unattended update by asking the user for their Lock Screen Knowledge 705 * Factor (LSKF). If supplied, the {@code intentSender} will be called when the system is setup 706 * and ready to apply the OTA. <p> 707 * 708 * <p> If the device doesn't setup a lock screen, i.e. by checking 709 * {@link KeyguardManager#isKeyguardSecure()}, this API call will fail and throw an exception. 710 * Callers are expected to use {@link PowerManager#reboot(String)} directly without going 711 * through the RoR flow. <p> 712 * 713 * <p> This API is expected to handle requests from multiple clients simultaneously, e.g. 714 * from ota and mainline. The behavior of multi-client Resume on Reboot works as follows 715 * <li> Each client should call this function to prepare for Resume on Reboot before calling 716 * {@link #rebootAndApply(Context, String, boolean)} </li> 717 * <li> One client cannot clear the Resume on Reboot preparation of another client. </li> 718 * <li> If multiple clients have prepared for Resume on Reboot, the subsequent reboot will be 719 * first come, first served. </li> 720 * 721 * @param context the Context to use. 722 * @param updateToken this parameter is deprecated and won't be used. Callers can supply with 723 * an empty string. See details in 724 * <a href="http://go/multi-client-ror">http://go/multi-client-ror</a> 725 * TODO(xunchang) update the link of document with the public doc. 726 * @param intentSender the intent to call when the update is prepared; may be {@code null} 727 * @throws IOException if there were any errors setting up unattended update 728 * @hide 729 */ 730 @SystemApi 731 @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY, 732 android.Manifest.permission.REBOOT}) prepareForUnattendedUpdate(@onNull Context context, @NonNull String updateToken, @Nullable IntentSender intentSender)733 public static void prepareForUnattendedUpdate(@NonNull Context context, 734 @NonNull String updateToken, @Nullable IntentSender intentSender) throws IOException { 735 if (updateToken == null) { 736 throw new NullPointerException("updateToken == null"); 737 } 738 739 KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class); 740 if (keyguardManager == null || !keyguardManager.isDeviceSecure()) { 741 throw new IOException("Failed to request LSKF because the device doesn't have a" 742 + " lock screen. "); 743 } 744 745 RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); 746 if (!rs.requestLskf(context.getPackageName(), intentSender)) { 747 throw new IOException("preparation for update failed"); 748 } 749 } 750 751 /** 752 * Request that any previously requested Lock Screen Knowledge Factor (LSKF) is cleared and 753 * the preparation for unattended update is reset. 754 * 755 * <p> Note that the API won't clear the underlying Resume on Reboot preparation state if 756 * another client has requested. So the reboot call from the other client can still succeed. 757 * 758 * @param context the Context to use. 759 * @throws IOException if there were any errors clearing the unattended update state 760 * @hide 761 */ 762 @SystemApi 763 @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY, 764 android.Manifest.permission.REBOOT}) clearPrepareForUnattendedUpdate(@onNull Context context)765 public static void clearPrepareForUnattendedUpdate(@NonNull Context context) 766 throws IOException { 767 RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); 768 if (!rs.clearLskf(context.getPackageName())) { 769 throw new IOException("could not reset unattended update state"); 770 } 771 } 772 773 /** 774 * Request that the device reboot and apply the update that has been prepared. This API is 775 * deprecated, and is expected to be used by OTA only on devices running Android 11. 776 * 777 * @param context the Context to use. 778 * @param updateToken this parameter is deprecated and won't be used. See details in 779 * <a href="http://go/multi-client-ror">http://go/multi-client-ror</a> 780 * TODO(xunchang) update the link of document with the public doc. 781 * @param reason the reboot reason to give to the {@link PowerManager} 782 * @throws IOException if the reboot couldn't proceed because the device wasn't ready for an 783 * unattended reboot or if the {@code updateToken} did not match the previously 784 * given token 785 * @hide 786 * @deprecated Use {@link #rebootAndApply(Context, String, boolean)} instead 787 */ 788 @SystemApi 789 @RequiresPermission(android.Manifest.permission.RECOVERY) rebootAndApply(@onNull Context context, @NonNull String updateToken, @NonNull String reason)790 public static void rebootAndApply(@NonNull Context context, @NonNull String updateToken, 791 @NonNull String reason) throws IOException { 792 if (updateToken == null) { 793 throw new NullPointerException("updateToken == null"); 794 } 795 RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); 796 // OTA is the sole user, who expects a slot switch. 797 if (rs.rebootWithLskfAssumeSlotSwitch(context.getPackageName(), reason) 798 != RESUME_ON_REBOOT_REBOOT_ERROR_NONE) { 799 throw new IOException("system not prepared to apply update"); 800 } 801 } 802 803 /** 804 * Query if Resume on Reboot has been prepared for a given caller. 805 * 806 * @param context the Context to use. 807 * @throws IOException if there were any errors connecting to the service or querying the state. 808 * @hide 809 */ 810 @SystemApi 811 @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY, 812 android.Manifest.permission.REBOOT}) isPreparedForUnattendedUpdate(@onNull Context context)813 public static boolean isPreparedForUnattendedUpdate(@NonNull Context context) 814 throws IOException { 815 RecoverySystem rs = context.getSystemService(RecoverySystem.class); 816 return rs.isLskfCaptured(context.getPackageName()); 817 } 818 819 /** 820 * Request that the device reboot and apply the update that has been prepared. 821 * {@link #prepareForUnattendedUpdate} must be called before for the given client, 822 * otherwise the function call will fail. 823 * 824 * @param context the Context to use. 825 * @param reason the reboot reason to give to the {@link PowerManager} 826 * @param slotSwitch true if the caller expects the slot to be switched on A/B devices. 827 * 828 * @return 0 on success, and a non-zero error code if the reboot couldn't proceed because the 829 * device wasn't ready for an unattended reboot. 830 * @throws IOException on remote exceptions from the RecoverySystemService 831 * @hide 832 */ 833 @SystemApi 834 @RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY, 835 android.Manifest.permission.REBOOT}) rebootAndApply(@onNull Context context, @NonNull String reason, boolean slotSwitch)836 public static @ResumeOnRebootRebootErrorCode int rebootAndApply(@NonNull Context context, 837 @NonNull String reason, boolean slotSwitch) throws IOException { 838 RecoverySystem rs = context.getSystemService(RecoverySystem.class); 839 return rs.rebootWithLskf(context.getPackageName(), reason, slotSwitch); 840 } 841 842 /** 843 * Schedule to install the given package on next boot. The caller needs to ensure that the 844 * package must have been processed (uncrypt'd) if needed. It sets up the command in BCB 845 * (bootloader control block), which will be read by the bootloader and the recovery image. 846 * 847 * @param context the Context to use. 848 * @param packageFile the package to be installed. 849 * @throws IOException if there were any errors setting up the BCB. 850 * @hide 851 */ 852 @SystemApi 853 @RequiresPermission(android.Manifest.permission.RECOVERY) scheduleUpdateOnBoot(Context context, File packageFile)854 public static void scheduleUpdateOnBoot(Context context, File packageFile) throws IOException { 855 String filename = packageFile.getCanonicalPath(); 856 boolean securityUpdate = filename.endsWith("_s.zip"); 857 858 // If the package is on the /data partition, use the block map file as 859 // the package name instead. 860 if (filename.startsWith("/data/")) { 861 filename = "@/cache/recovery/block.map"; 862 } 863 864 final String filenameArg = "--update_package=" + filename + "\n"; 865 final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() + "\n"; 866 final String securityArg = "--security\n"; 867 868 String command = filenameArg + localeArg; 869 if (securityUpdate) { 870 command += securityArg; 871 } 872 873 RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); 874 if (!rs.setupBcb(command)) { 875 throw new IOException("schedule update on boot failed"); 876 } 877 } 878 879 /** 880 * Cancel any scheduled update by clearing up the BCB (bootloader control 881 * block). 882 * 883 * @param Context the Context to use. 884 * 885 * @throws IOException if there were any errors clearing up the BCB. 886 * 887 * @hide 888 */ 889 @SystemApi 890 @RequiresPermission(android.Manifest.permission.RECOVERY) cancelScheduledUpdate(Context context)891 public static void cancelScheduledUpdate(Context context) 892 throws IOException { 893 RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); 894 if (!rs.clearBcb()) { 895 throw new IOException("cancel scheduled update failed"); 896 } 897 } 898 899 /** 900 * Reboots the device and wipes the user data and cache 901 * partitions. This is sometimes called a "factory reset", which 902 * is something of a misnomer because the system partition is not 903 * restored to its factory state. Requires the 904 * {@link android.Manifest.permission#REBOOT} permission. 905 * 906 * @param context the Context to use 907 * 908 * @throws IOException if writing the recovery command file 909 * fails, or if the reboot itself fails. 910 * @throws SecurityException if the current user is not allowed to wipe data. 911 */ rebootWipeUserData(Context context)912 public static void rebootWipeUserData(Context context) throws IOException { 913 rebootWipeUserData(context, false /* shutdown */, context.getPackageName(), 914 false /* force */, false /* wipeEuicc */); 915 } 916 917 /** {@hide} */ rebootWipeUserData(Context context, String reason)918 public static void rebootWipeUserData(Context context, String reason) throws IOException { 919 rebootWipeUserData(context, false /* shutdown */, reason, false /* force */, 920 false /* wipeEuicc */); 921 } 922 923 /** {@hide} */ rebootWipeUserData(Context context, boolean shutdown)924 public static void rebootWipeUserData(Context context, boolean shutdown) 925 throws IOException { 926 rebootWipeUserData(context, shutdown, context.getPackageName(), false /* force */, 927 false /* wipeEuicc */); 928 } 929 930 /** {@hide} */ rebootWipeUserData(Context context, boolean shutdown, String reason, boolean force)931 public static void rebootWipeUserData(Context context, boolean shutdown, String reason, 932 boolean force) throws IOException { 933 rebootWipeUserData(context, shutdown, reason, force, false /* wipeEuicc */); 934 } 935 936 /** 937 * Reboots the device and wipes the user data and cache 938 * partitions. This is sometimes called a "factory reset", which 939 * is something of a misnomer because the system partition is not 940 * restored to its factory state. Requires the 941 * {@link android.Manifest.permission#REBOOT} permission. 942 * 943 * @param context the Context to use 944 * @param shutdown if true, the device will be powered down after 945 * the wipe completes, rather than being rebooted 946 * back to the regular system. 947 * @param reason the reason for the wipe that is visible in the logs 948 * @param force whether the {@link UserManager.DISALLOW_FACTORY_RESET} user restriction 949 * should be ignored 950 * @param wipeEuicc whether wipe the euicc data 951 * 952 * @throws IOException if writing the recovery command file 953 * fails, or if the reboot itself fails. 954 * @throws SecurityException if the current user is not allowed to wipe data. 955 * 956 * @hide 957 */ rebootWipeUserData(Context context, boolean shutdown, String reason, boolean force, boolean wipeEuicc)958 public static void rebootWipeUserData(Context context, boolean shutdown, String reason, 959 boolean force, boolean wipeEuicc) throws IOException { 960 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 961 if (!force && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) { 962 throw new SecurityException("Wiping data is not allowed for this user."); 963 } 964 final ConditionVariable condition = new ConditionVariable(); 965 966 Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION"); 967 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND 968 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 969 context.sendOrderedBroadcastAsUser(intent, UserHandle.SYSTEM, 970 android.Manifest.permission.MASTER_CLEAR, 971 new BroadcastReceiver() { 972 @Override 973 public void onReceive(Context context, Intent intent) { 974 condition.open(); 975 } 976 }, null, 0, null, null); 977 978 // Block until the ordered broadcast has completed. 979 condition.block(); 980 981 EuiccManager euiccManager = context.getSystemService(EuiccManager.class); 982 if (wipeEuicc) { 983 wipeEuiccData(context, PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK); 984 } else { 985 removeEuiccInvisibleSubs(context, euiccManager); 986 } 987 988 String shutdownArg = null; 989 if (shutdown) { 990 shutdownArg = "--shutdown_after"; 991 } 992 993 String reasonArg = null; 994 if (!TextUtils.isEmpty(reason)) { 995 String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString(); 996 reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp); 997 } 998 999 final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ; 1000 bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg); 1001 } 1002 1003 /** 1004 * Returns whether wipe Euicc data successfully or not. 1005 * 1006 * @param packageName the package name of the caller app. 1007 * 1008 * @hide 1009 */ wipeEuiccData(Context context, final String packageName)1010 public static boolean wipeEuiccData(Context context, final String packageName) { 1011 ContentResolver cr = context.getContentResolver(); 1012 if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) { 1013 // If the eUICC isn't provisioned, there's no reason to either wipe or retain profiles, 1014 // as there's nothing to wipe nor retain. 1015 Log.d(TAG, "Skipping eUICC wipe/retain as it is not provisioned"); 1016 return true; 1017 } 1018 1019 EuiccManager euiccManager = (EuiccManager) context.getSystemService( 1020 Context.EUICC_SERVICE); 1021 if (euiccManager != null && euiccManager.isEnabled()) { 1022 CountDownLatch euiccFactoryResetLatch = new CountDownLatch(1); 1023 final AtomicBoolean wipingSucceeded = new AtomicBoolean(false); 1024 1025 BroadcastReceiver euiccWipeFinishReceiver = new BroadcastReceiver() { 1026 @Override 1027 public void onReceive(Context context, Intent intent) { 1028 if (ACTION_EUICC_FACTORY_RESET.equals(intent.getAction())) { 1029 if (getResultCode() != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) { 1030 int detailedCode = intent.getIntExtra( 1031 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0); 1032 Log.e(TAG, "Error wiping euicc data, Detailed code = " 1033 + detailedCode); 1034 } else { 1035 Log.d(TAG, "Successfully wiped euicc data."); 1036 wipingSucceeded.set(true /* newValue */); 1037 } 1038 euiccFactoryResetLatch.countDown(); 1039 } 1040 } 1041 }; 1042 1043 Intent intent = new Intent(ACTION_EUICC_FACTORY_RESET); 1044 intent.setPackage(packageName); 1045 PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser( 1046 context, 1047 0, 1048 intent, 1049 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, 1050 UserHandle.SYSTEM); 1051 IntentFilter filterConsent = new IntentFilter(); 1052 filterConsent.addAction(ACTION_EUICC_FACTORY_RESET); 1053 HandlerThread euiccHandlerThread = new HandlerThread("euiccWipeFinishReceiverThread"); 1054 euiccHandlerThread.start(); 1055 Handler euiccHandler = new Handler(euiccHandlerThread.getLooper()); 1056 context.getApplicationContext() 1057 .registerReceiver(euiccWipeFinishReceiver, filterConsent, null, euiccHandler); 1058 euiccManager.eraseSubscriptions(callbackIntent); 1059 try { 1060 long waitingTimeMillis = Settings.Global.getLong( 1061 context.getContentResolver(), 1062 Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS, 1063 DEFAULT_EUICC_FACTORY_RESET_TIMEOUT_MILLIS); 1064 if (waitingTimeMillis < MIN_EUICC_FACTORY_RESET_TIMEOUT_MILLIS) { 1065 waitingTimeMillis = MIN_EUICC_FACTORY_RESET_TIMEOUT_MILLIS; 1066 } else if (waitingTimeMillis > MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS) { 1067 waitingTimeMillis = MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS; 1068 } 1069 if (!euiccFactoryResetLatch.await(waitingTimeMillis, TimeUnit.MILLISECONDS)) { 1070 Log.e(TAG, "Timeout wiping eUICC data."); 1071 return false; 1072 } 1073 } catch (InterruptedException e) { 1074 Thread.currentThread().interrupt(); 1075 Log.e(TAG, "Wiping eUICC data interrupted", e); 1076 return false; 1077 } finally { 1078 context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver); 1079 } 1080 return wipingSucceeded.get(); 1081 } 1082 return false; 1083 } 1084 removeEuiccInvisibleSubs( Context context, EuiccManager euiccManager)1085 private static void removeEuiccInvisibleSubs( 1086 Context context, EuiccManager euiccManager) { 1087 ContentResolver cr = context.getContentResolver(); 1088 if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) { 1089 // If the eUICC isn't provisioned, there's no need to remove euicc invisible profiles, 1090 // as there's nothing to be removed. 1091 Log.i(TAG, "Skip removing eUICC invisible profiles as it is not provisioned."); 1092 return; 1093 } else if (euiccManager == null || !euiccManager.isEnabled()) { 1094 Log.i(TAG, "Skip removing eUICC invisible profiles as eUICC manager is not available."); 1095 return; 1096 } 1097 SubscriptionManager subscriptionManager = 1098 context.getSystemService(SubscriptionManager.class); 1099 List<SubscriptionInfo> availableSubs = 1100 subscriptionManager.getAvailableSubscriptionInfoList(); 1101 if (availableSubs == null || availableSubs.isEmpty()) { 1102 Log.i(TAG, "Skip removing eUICC invisible profiles as no available profiles found."); 1103 return; 1104 } 1105 List<SubscriptionInfo> invisibleSubs = new ArrayList<>(); 1106 for (SubscriptionInfo sub : availableSubs) { 1107 if (sub.isEmbedded() && sub.getGroupUuid() != null && sub.isOpportunistic()) { 1108 invisibleSubs.add(sub); 1109 } 1110 } 1111 removeEuiccInvisibleSubs(context, invisibleSubs, euiccManager); 1112 } 1113 removeEuiccInvisibleSubs( Context context, List<SubscriptionInfo> subscriptionInfos, EuiccManager euiccManager)1114 private static boolean removeEuiccInvisibleSubs( 1115 Context context, List<SubscriptionInfo> subscriptionInfos, EuiccManager euiccManager) { 1116 if (subscriptionInfos == null || subscriptionInfos.isEmpty()) { 1117 Log.i(TAG, "There are no eUICC invisible profiles needed to be removed."); 1118 return true; 1119 } 1120 CountDownLatch removeSubsLatch = new CountDownLatch(subscriptionInfos.size()); 1121 final AtomicInteger removedSubsCount = new AtomicInteger(0); 1122 1123 BroadcastReceiver removeEuiccSubsReceiver = new BroadcastReceiver() { 1124 @Override 1125 public void onReceive(Context context, Intent intent) { 1126 if (ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS.equals(intent.getAction())) { 1127 if (getResultCode() != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) { 1128 int detailedCode = intent.getIntExtra( 1129 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0); 1130 Log.e(TAG, "Error removing euicc opportunistic profile, Detailed code = " 1131 + detailedCode); 1132 } else { 1133 Log.e(TAG, "Successfully remove euicc opportunistic profile."); 1134 removedSubsCount.incrementAndGet(); 1135 } 1136 removeSubsLatch.countDown(); 1137 } 1138 } 1139 }; 1140 1141 Intent intent = new Intent(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS); 1142 intent.setPackage(PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK); 1143 PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser( 1144 context, 1145 0, 1146 intent, 1147 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, 1148 UserHandle.SYSTEM); 1149 IntentFilter intentFilter = new IntentFilter(); 1150 intentFilter.addAction(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS); 1151 HandlerThread euiccHandlerThread = 1152 new HandlerThread("euiccRemovingSubsReceiverThread"); 1153 euiccHandlerThread.start(); 1154 Handler euiccHandler = new Handler(euiccHandlerThread.getLooper()); 1155 context.getApplicationContext() 1156 .registerReceiver( 1157 removeEuiccSubsReceiver, intentFilter, null, euiccHandler); 1158 for (SubscriptionInfo subscriptionInfo : subscriptionInfos) { 1159 Log.i( 1160 TAG, 1161 "Remove invisible subscription " + subscriptionInfo.getSubscriptionId() 1162 + " from card " + subscriptionInfo.getCardId()); 1163 euiccManager.createForCardId(subscriptionInfo.getCardId()) 1164 .deleteSubscription(subscriptionInfo.getSubscriptionId(), callbackIntent); 1165 } 1166 try { 1167 long waitingTimeMillis = Settings.Global.getLong( 1168 context.getContentResolver(), 1169 Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS, 1170 DEFAULT_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS); 1171 if (waitingTimeMillis < MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS) { 1172 waitingTimeMillis = MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS; 1173 } else if (waitingTimeMillis > MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS) { 1174 waitingTimeMillis = MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS; 1175 } 1176 if (!removeSubsLatch.await(waitingTimeMillis, TimeUnit.MILLISECONDS)) { 1177 Log.e(TAG, "Timeout removing invisible euicc profiles."); 1178 return false; 1179 } 1180 } catch (InterruptedException e) { 1181 Thread.currentThread().interrupt(); 1182 Log.e(TAG, "Removing invisible euicc profiles interrupted", e); 1183 return false; 1184 } finally { 1185 context.getApplicationContext().unregisterReceiver(removeEuiccSubsReceiver); 1186 if (euiccHandlerThread != null) { 1187 euiccHandlerThread.quit(); 1188 } 1189 } 1190 return removedSubsCount.get() == subscriptionInfos.size(); 1191 } 1192 1193 /** {@hide} */ rebootPromptAndWipeUserData(Context context, String reason)1194 public static void rebootPromptAndWipeUserData(Context context, String reason) 1195 throws IOException { 1196 boolean checkpointing = false; 1197 boolean needReboot = false; 1198 IVold vold = null; 1199 try { 1200 vold = IVold.Stub.asInterface(ServiceManager.checkService("vold")); 1201 if (vold != null) { 1202 checkpointing = vold.needsCheckpoint(); 1203 } else { 1204 Log.w(TAG, "Failed to get vold"); 1205 } 1206 } catch (Exception e) { 1207 Log.w(TAG, "Failed to check for checkpointing"); 1208 } 1209 1210 // If we are running in checkpointing mode, we should not prompt a wipe. 1211 // Checkpointing may save us. If it doesn't, we will wind up here again. 1212 if (checkpointing) { 1213 try { 1214 vold.abortChanges("rescueparty", false); 1215 Log.i(TAG, "Rescue Party requested wipe. Aborting update"); 1216 } catch (Exception e) { 1217 Log.i(TAG, "Rescue Party requested wipe. Rebooting instead."); 1218 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 1219 pm.reboot("rescueparty"); 1220 } 1221 return; 1222 } 1223 1224 String reasonArg = null; 1225 if (!TextUtils.isEmpty(reason)) { 1226 reasonArg = "--reason=" + sanitizeArg(reason); 1227 } 1228 1229 final String localeArg = "--locale=" + Locale.getDefault().toString(); 1230 bootCommand(context, null, "--prompt_and_wipe_data", reasonArg, localeArg); 1231 } 1232 1233 /** 1234 * Reboot into the recovery system to wipe the /cache partition. 1235 * @throws IOException if something goes wrong. 1236 */ rebootWipeCache(Context context)1237 public static void rebootWipeCache(Context context) throws IOException { 1238 rebootWipeCache(context, context.getPackageName()); 1239 } 1240 1241 /** {@hide} */ rebootWipeCache(Context context, String reason)1242 public static void rebootWipeCache(Context context, String reason) throws IOException { 1243 String reasonArg = null; 1244 if (!TextUtils.isEmpty(reason)) { 1245 reasonArg = "--reason=" + sanitizeArg(reason); 1246 } 1247 1248 final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ; 1249 bootCommand(context, "--wipe_cache", reasonArg, localeArg); 1250 } 1251 1252 /** 1253 * Reboot into recovery and wipe the A/B device. 1254 * 1255 * @param Context the Context to use. 1256 * @param packageFile the wipe package to be applied. 1257 * @param reason the reason to wipe. 1258 * 1259 * @throws IOException if something goes wrong. 1260 * 1261 * @hide 1262 */ 1263 @SystemApi 1264 @RequiresPermission(allOf = { 1265 android.Manifest.permission.RECOVERY, 1266 android.Manifest.permission.REBOOT 1267 }) rebootWipeAb(Context context, File packageFile, String reason)1268 public static void rebootWipeAb(Context context, File packageFile, String reason) 1269 throws IOException { 1270 String reasonArg = null; 1271 if (!TextUtils.isEmpty(reason)) { 1272 reasonArg = "--reason=" + sanitizeArg(reason); 1273 } 1274 1275 final String filename = packageFile.getCanonicalPath(); 1276 final String filenameArg = "--wipe_package=" + filename; 1277 final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ; 1278 bootCommand(context, "--wipe_ab", filenameArg, reasonArg, localeArg); 1279 } 1280 1281 /** 1282 * Reboot into the recovery system with the supplied argument. 1283 * @param args to pass to the recovery utility. 1284 * @throws IOException if something goes wrong. 1285 */ bootCommand(Context context, String... args)1286 private static void bootCommand(Context context, String... args) throws IOException { 1287 LOG_FILE.delete(); 1288 1289 StringBuilder command = new StringBuilder(); 1290 for (String arg : args) { 1291 if (!TextUtils.isEmpty(arg)) { 1292 command.append(arg); 1293 command.append("\n"); 1294 } 1295 } 1296 1297 // Write the command into BCB (bootloader control block) and boot from 1298 // there. Will not return unless failed. 1299 RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE); 1300 rs.rebootRecoveryWithCommand(command.toString()); 1301 1302 throw new IOException("Reboot failed (no permissions?)"); 1303 } 1304 1305 /** 1306 * Called after booting to process and remove recovery-related files. 1307 * @return the log file from recovery, or null if none was found. 1308 * 1309 * @hide 1310 */ handleAftermath(Context context)1311 public static String handleAftermath(Context context) { 1312 // Record the tail of the LOG_FILE 1313 String log = null; 1314 try { 1315 log = FileUtils.readTextFile(LOG_FILE, -LOG_FILE_MAX_LENGTH, "...\n"); 1316 } catch (FileNotFoundException e) { 1317 Log.i(TAG, "No recovery log file"); 1318 } catch (IOException e) { 1319 Log.e(TAG, "Error reading recovery log", e); 1320 } 1321 1322 1323 // Only remove the OTA package if it's partially processed (uncrypt'd). 1324 boolean reservePackage = BLOCK_MAP_FILE.exists(); 1325 if (!reservePackage && UNCRYPT_PACKAGE_FILE.exists()) { 1326 String filename = null; 1327 try { 1328 filename = FileUtils.readTextFile(UNCRYPT_PACKAGE_FILE, 0, null); 1329 } catch (IOException e) { 1330 Log.e(TAG, "Error reading uncrypt file", e); 1331 } 1332 1333 // Remove the OTA package on /data that has been (possibly 1334 // partially) processed. (Bug: 24973532) 1335 if (filename != null && filename.startsWith("/data")) { 1336 if (UNCRYPT_PACKAGE_FILE.delete()) { 1337 Log.i(TAG, "Deleted: " + filename); 1338 } else { 1339 Log.e(TAG, "Can't delete: " + filename); 1340 } 1341 } 1342 } 1343 1344 // We keep the update logs (beginning with LAST_PREFIX), and optionally 1345 // the block map file (BLOCK_MAP_FILE) for a package. BLOCK_MAP_FILE 1346 // will be created at the end of a successful uncrypt. If seeing this 1347 // file, we keep the block map file and the file that contains the 1348 // package name (UNCRYPT_PACKAGE_FILE). This is to reduce the work for 1349 // GmsCore to avoid re-downloading everything again. 1350 String[] names = RECOVERY_DIR.list(); 1351 for (int i = 0; names != null && i < names.length; i++) { 1352 // Do not remove the last_install file since the recovery-persist takes care of it. 1353 if (names[i].startsWith(LAST_PREFIX) || names[i].equals(LAST_INSTALL_PATH)) continue; 1354 if (reservePackage && names[i].equals(BLOCK_MAP_FILE.getName())) continue; 1355 if (reservePackage && names[i].equals(UNCRYPT_PACKAGE_FILE.getName())) continue; 1356 1357 recursiveDelete(new File(RECOVERY_DIR, names[i])); 1358 } 1359 1360 return log; 1361 } 1362 1363 /** 1364 * Internally, delete a given file or directory recursively. 1365 */ recursiveDelete(File name)1366 private static void recursiveDelete(File name) { 1367 if (name.isDirectory()) { 1368 String[] files = name.list(); 1369 for (int i = 0; files != null && i < files.length; i++) { 1370 File f = new File(name, files[i]); 1371 recursiveDelete(f); 1372 } 1373 } 1374 1375 if (!name.delete()) { 1376 Log.e(TAG, "Can't delete: " + name); 1377 } else { 1378 Log.i(TAG, "Deleted: " + name); 1379 } 1380 } 1381 1382 /** 1383 * Talks to RecoverySystemService via Binder to trigger uncrypt. 1384 */ uncrypt(String packageFile, IRecoverySystemProgressListener listener)1385 private boolean uncrypt(String packageFile, IRecoverySystemProgressListener listener) { 1386 try { 1387 return mService.uncrypt(packageFile, listener); 1388 } catch (RemoteException unused) { 1389 } 1390 return false; 1391 } 1392 1393 /** 1394 * Talks to RecoverySystemService via Binder to set up the BCB. 1395 */ setupBcb(String command)1396 private boolean setupBcb(String command) { 1397 try { 1398 return mService.setupBcb(command); 1399 } catch (RemoteException unused) { 1400 } 1401 return false; 1402 } 1403 1404 /** 1405 * Talks to RecoverySystemService via Binder to allocate space 1406 */ allocateSpaceForUpdate(File packageFile)1407 private boolean allocateSpaceForUpdate(File packageFile) throws RemoteException { 1408 return mService.allocateSpaceForUpdate(packageFile.getAbsolutePath()); 1409 } 1410 1411 /** 1412 * Talks to RecoverySystemService via Binder to clear up the BCB. 1413 */ clearBcb()1414 private boolean clearBcb() { 1415 try { 1416 return mService.clearBcb(); 1417 } catch (RemoteException unused) { 1418 } 1419 return false; 1420 } 1421 1422 /** 1423 * Talks to RecoverySystemService via Binder to set up the BCB command and 1424 * reboot into recovery accordingly. 1425 */ rebootRecoveryWithCommand(String command)1426 private void rebootRecoveryWithCommand(String command) { 1427 try { 1428 mService.rebootRecoveryWithCommand(command); 1429 } catch (RemoteException ignored) { 1430 } 1431 } 1432 1433 /** 1434 * Begins the process of asking the user for the Lock Screen Knowledge Factor. 1435 * 1436 * @param packageName the package name of the caller who requests Resume on Reboot 1437 * @return true if the request was correct 1438 * @throws IOException if the recovery system service could not be contacted 1439 */ requestLskf(String packageName, IntentSender sender)1440 private boolean requestLskf(String packageName, IntentSender sender) throws IOException { 1441 try { 1442 return mService.requestLskf(packageName, sender); 1443 } catch (RemoteException | SecurityException e) { 1444 throw new IOException("could not request LSKF capture", e); 1445 } 1446 } 1447 1448 /** 1449 * Calls the recovery system service and clears the setup for the OTA. 1450 * 1451 * @return true if the setup for OTA was cleared 1452 * @throws IOException if the recovery system service could not be contacted 1453 */ clearLskf(String packageName)1454 private boolean clearLskf(String packageName) throws IOException { 1455 try { 1456 return mService.clearLskf(packageName); 1457 } catch (RemoteException | SecurityException e) { 1458 throw new IOException("could not clear LSKF", e); 1459 } 1460 } 1461 1462 /** 1463 * Queries if the Resume on Reboot has been prepared for a given caller. 1464 * 1465 * @param packageName the identifier of the caller who requests Resume on Reboot 1466 * @return true if Resume on Reboot is prepared. 1467 * @throws IOException if the recovery system service could not be contacted 1468 */ isLskfCaptured(String packageName)1469 private boolean isLskfCaptured(String packageName) throws IOException { 1470 try { 1471 return mService.isLskfCaptured(packageName); 1472 } catch (RemoteException | SecurityException e) { 1473 throw new IOException("could not get LSKF capture state", e); 1474 } 1475 } 1476 1477 /** 1478 * Calls the recovery system service to reboot and apply update. 1479 * 1480 */ rebootWithLskf(String packageName, String reason, boolean slotSwitch)1481 private @ResumeOnRebootRebootErrorCode int rebootWithLskf(String packageName, String reason, 1482 boolean slotSwitch) throws IOException { 1483 try { 1484 return mService.rebootWithLskf(packageName, reason, slotSwitch); 1485 } catch (RemoteException | SecurityException e) { 1486 throw new IOException("could not reboot for update", e); 1487 } 1488 } 1489 1490 /** 1491 * Calls the recovery system service to reboot and apply update. This is the legacy API and 1492 * expects a slot switch for A/B devices. 1493 * 1494 */ rebootWithLskfAssumeSlotSwitch(String packageName, String reason)1495 private @ResumeOnRebootRebootErrorCode int rebootWithLskfAssumeSlotSwitch(String packageName, 1496 String reason) throws IOException { 1497 try { 1498 return mService.rebootWithLskfAssumeSlotSwitch(packageName, reason); 1499 } catch (RemoteException | RuntimeException e) { 1500 throw new IOException("could not reboot for update", e); 1501 } 1502 } 1503 1504 /** 1505 * Internally, recovery treats each line of the command file as a separate 1506 * argv, so we only need to protect against newlines and nulls. 1507 */ sanitizeArg(String arg)1508 private static String sanitizeArg(String arg) { 1509 arg = arg.replace('\0', '?'); 1510 arg = arg.replace('\n', '?'); 1511 return arg; 1512 } 1513 1514 1515 /** 1516 * @removed Was previously made visible by accident. 1517 */ RecoverySystem()1518 public RecoverySystem() { 1519 mService = null; 1520 } 1521 1522 /** 1523 * @hide 1524 */ RecoverySystem(IRecoverySystem service)1525 public RecoverySystem(IRecoverySystem service) { 1526 mService = service; 1527 } 1528 } 1529