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