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