1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.recoverysystem;
18 
19 import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME;
20 import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED;
21 import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_NONE;
22 import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE;
23 import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH;
24 import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED;
25 import static android.os.RecoverySystem.ResumeOnRebootRebootErrorCode;
26 import static android.os.UserHandle.USER_SYSTEM;
27 import static android.ota.nano.OtaPackageMetadata.ApexMetadata;
28 
29 import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NONE;
30 import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NO_PROVIDER;
31 
32 import android.annotation.IntDef;
33 import android.annotation.Nullable;
34 import android.apex.CompressedApexInfo;
35 import android.apex.CompressedApexInfoList;
36 import android.content.Context;
37 import android.content.IntentSender;
38 import android.content.SharedPreferences;
39 import android.content.pm.PackageManager;
40 import android.hardware.boot.IBootControl;
41 import android.hardware.security.secretkeeper.ISecretkeeper;
42 import android.net.LocalSocket;
43 import android.net.LocalSocketAddress;
44 import android.os.Binder;
45 import android.os.Environment;
46 import android.os.IRecoverySystem;
47 import android.os.IRecoverySystemProgressListener;
48 import android.os.PowerManager;
49 import android.os.Process;
50 import android.os.RecoverySystem;
51 import android.os.RemoteException;
52 import android.os.ResultReceiver;
53 import android.os.ServiceManager;
54 import android.os.ShellCallback;
55 import android.os.SystemProperties;
56 import android.provider.DeviceConfig;
57 import android.security.AndroidKeyStoreMaintenance;
58 import android.util.ArrayMap;
59 import android.util.ArraySet;
60 import android.util.FastImmutableArraySet;
61 import android.util.Log;
62 import android.util.Slog;
63 
64 import com.android.internal.annotations.GuardedBy;
65 import com.android.internal.annotations.VisibleForTesting;
66 import com.android.internal.util.FrameworkStatsLog;
67 import com.android.internal.widget.LockSettingsInternal;
68 import com.android.internal.widget.RebootEscrowListener;
69 import com.android.server.LocalServices;
70 import com.android.server.SystemService;
71 import com.android.server.Watchdog;
72 import com.android.server.pm.ApexManager;
73 import com.android.server.recoverysystem.hal.BootControlHIDL;
74 import com.android.server.utils.Slogf;
75 
76 import libcore.io.IoUtils;
77 
78 import java.io.DataInputStream;
79 import java.io.DataOutputStream;
80 import java.io.File;
81 import java.io.FileDescriptor;
82 import java.io.FileWriter;
83 import java.io.IOException;
84 import java.io.InputStream;
85 import java.nio.charset.StandardCharsets;
86 import java.util.ArrayList;
87 import java.util.Arrays;
88 import java.util.List;
89 import java.util.zip.ZipEntry;
90 import java.util.zip.ZipFile;
91 
92 /**
93  * The recovery system service is responsible for coordinating recovery related
94  * functions on the device. It sets up (or clears) the bootloader control block
95  * (BCB), which will be read by the bootloader and the recovery image. It also
96  * triggers /system/bin/uncrypt via init to de-encrypt an OTA package on the
97  * /data partition so that it can be accessed under the recovery image.
98  */
99 public class RecoverySystemService extends IRecoverySystem.Stub implements RebootEscrowListener {
100     private static final String TAG = "RecoverySystemService";
101     private static final boolean DEBUG = false;
102 
103     // The socket at /dev/socket/uncrypt to communicate with uncrypt.
104     private static final String UNCRYPT_SOCKET = "uncrypt";
105 
106     // The init services that communicate with /system/bin/uncrypt.
107     @VisibleForTesting
108     static final String INIT_SERVICE_UNCRYPT = "init.svc.uncrypt";
109     @VisibleForTesting
110     static final String INIT_SERVICE_SETUP_BCB = "init.svc.setup-bcb";
111     @VisibleForTesting
112     static final String INIT_SERVICE_CLEAR_BCB = "init.svc.clear-bcb";
113     @VisibleForTesting
114     static final String AB_UPDATE = "ro.build.ab_update";
115 
116     private static final Object sRequestLock = new Object();
117 
118     private static final int SOCKET_CONNECTION_MAX_RETRY = 30;
119 
120     /** How long to pause the watchdog for when rebooting the device. */
121     private static final int REBOOT_WATCHDOG_PAUSE_DURATION_MS = 20_000;
122 
123     static final String REQUEST_LSKF_TIMESTAMP_PREF_SUFFIX = "_request_lskf_timestamp";
124     static final String REQUEST_LSKF_COUNT_PREF_SUFFIX = "_request_lskf_count";
125 
126     static final String LSKF_CAPTURED_TIMESTAMP_PREF = "lskf_captured_timestamp";
127     static final String LSKF_CAPTURED_COUNT_PREF = "lskf_captured_count";
128 
129     static final String RECOVERY_WIPE_DATA_COMMAND = "--wipe_data";
130 
131     private final Injector mInjector;
132     private final Context mContext;
133 
134     @GuardedBy("this")
135     private final ArrayMap<String, IntentSender> mCallerPendingRequest = new ArrayMap<>();
136     @GuardedBy("this")
137     private final ArraySet<String> mCallerPreparedForReboot = new ArraySet<>();
138 
139     /**
140      * Need to prepare for resume on reboot.
141      */
142     private static final int ROR_NEED_PREPARATION = 0;
143     /**
144      * Resume on reboot has been prepared, notify the caller.
145      */
146     private static final int ROR_SKIP_PREPARATION_AND_NOTIFY = 1;
147     /**
148      * Resume on reboot has been requested. Caller won't be notified until the preparation is done.
149      */
150     private static final int ROR_SKIP_PREPARATION_NOT_NOTIFY = 2;
151 
152     /**
153      * The caller never requests for resume on reboot, no need for clear.
154      */
155     private static final int ROR_NOT_REQUESTED = 0;
156     /**
157      * Clear the resume on reboot preparation state.
158      */
159     private static final int ROR_REQUESTED_NEED_CLEAR = 1;
160     /**
161      * The caller has requested for resume on reboot. No need for clear since other callers may
162      * exist.
163      */
164     private static final int ROR_REQUESTED_SKIP_CLEAR = 2;
165 
166     /**
167      * The action to perform upon new resume on reboot prepare request for a given client.
168      */
169     @IntDef({ROR_NEED_PREPARATION,
170             ROR_SKIP_PREPARATION_AND_NOTIFY,
171             ROR_SKIP_PREPARATION_NOT_NOTIFY})
172     private @interface ResumeOnRebootActionsOnRequest {
173     }
174 
175     /**
176      * The action to perform upon resume on reboot clear request for a given client.
177      */
178     @IntDef({ROR_NOT_REQUESTED,
179             ROR_REQUESTED_NEED_CLEAR,
180             ROR_REQUESTED_SKIP_CLEAR})
181     private @interface ResumeOnRebootActionsOnClear {
182     }
183 
184     /**
185      * Fatal arm escrow errors from lock settings that means the RoR is in a bad state. So clients
186      * need to prepare RoR again.
187      */
188     static final FastImmutableArraySet<Integer> FATAL_ARM_ESCROW_ERRORS =
189             new FastImmutableArraySet<>(new Integer[]{
190                     LockSettingsInternal.ARM_REBOOT_ERROR_ESCROW_NOT_READY,
191                     LockSettingsInternal.ARM_REBOOT_ERROR_NO_PROVIDER,
192                     LockSettingsInternal.ARM_REBOOT_ERROR_PROVIDER_MISMATCH,
193                     LockSettingsInternal.ARM_REBOOT_ERROR_NO_ESCROW_KEY,
194                     LockSettingsInternal.ARM_REBOOT_ERROR_KEYSTORE_FAILURE,
195             });
196 
197     /**
198      * The error details for ArmRebootEscrow. It contains error codes from RecoverySystemService
199      * and LockSettingsService.
200      */
201     static class RebootPreparationError {
202         final @ResumeOnRebootRebootErrorCode int mRebootErrorCode;
203         final int mProviderErrorCode;  // The supplemental error code from lock settings
204 
RebootPreparationError(int rebootErrorCode, int providerErrorCode)205         RebootPreparationError(int rebootErrorCode, int providerErrorCode) {
206             mRebootErrorCode = rebootErrorCode;
207             mProviderErrorCode = providerErrorCode;
208         }
209 
getErrorCodeForMetrics()210         int getErrorCodeForMetrics() {
211             // The ResumeOnRebootRebootErrorCode are aligned with 1000; so it's safe to add them
212             // for metrics purpose.
213             return mRebootErrorCode + mProviderErrorCode;
214         }
215     }
216 
217     /**
218      * Manages shared preference, i.e. the storage used for metrics reporting.
219      */
220     public static class PreferencesManager {
221         private static final String METRICS_DIR = "recovery_system";
222         private static final String METRICS_PREFS_FILE = "RecoverySystemMetricsPrefs.xml";
223 
224         protected final SharedPreferences mSharedPreferences;
225         private final File mMetricsPrefsFile;
226 
PreferencesManager(Context context)227         PreferencesManager(Context context) {
228             File prefsDir = new File(Environment.getDataSystemCeDirectory(USER_SYSTEM),
229                     METRICS_DIR);
230             mMetricsPrefsFile = new File(prefsDir, METRICS_PREFS_FILE);
231             mSharedPreferences = context.getSharedPreferences(mMetricsPrefsFile, 0);
232         }
233 
234         /** Reads the value of a given key with type long. **/
getLong(String key, long defaultValue)235         public long getLong(String key, long defaultValue) {
236             return mSharedPreferences.getLong(key, defaultValue);
237         }
238 
239         /** Reads the value of a given key with type int. **/
getInt(String key, int defaultValue)240         public int getInt(String key, int defaultValue) {
241             return mSharedPreferences.getInt(key, defaultValue);
242         }
243 
244         /** Stores the value of a given key with type long. **/
putLong(String key, long value)245         public void putLong(String key, long value) {
246             mSharedPreferences.edit().putLong(key, value).commit();
247         }
248 
249         /** Stores the value of a given key with type int. **/
putInt(String key, int value)250         public void putInt(String key, int value) {
251             mSharedPreferences.edit().putInt(key, value).commit();
252         }
253 
254         /** Increments the value of a given key with type int. **/
incrementIntKey(String key, int defaultInitialValue)255         public synchronized void incrementIntKey(String key, int defaultInitialValue) {
256             int oldValue = getInt(key, defaultInitialValue);
257             putInt(key, oldValue + 1);
258         }
259 
260         /** Delete the preference file and cleanup all metrics storage. **/
deletePrefsFile()261         public void deletePrefsFile() {
262             if (!mMetricsPrefsFile.delete()) {
263                 Slog.w(TAG, "Failed to delete metrics prefs");
264             }
265         }
266     }
267 
268     static class Injector {
269         protected final Context mContext;
270         protected final PreferencesManager mPrefs;
271 
Injector(Context context)272         Injector(Context context) {
273             mContext = context;
274             mPrefs = new PreferencesManager(context);
275         }
276 
getContext()277         public Context getContext() {
278             return mContext;
279         }
280 
getLockSettingsService()281         public LockSettingsInternal getLockSettingsService() {
282             return LocalServices.getService(LockSettingsInternal.class);
283         }
284 
getPowerManager()285         public PowerManager getPowerManager() {
286             return (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
287         }
288 
systemPropertiesGet(String key)289         public String systemPropertiesGet(String key) {
290             return SystemProperties.get(key);
291         }
292 
systemPropertiesSet(String key, String value)293         public void systemPropertiesSet(String key, String value) {
294             SystemProperties.set(key, value);
295         }
296 
uncryptPackageFileDelete()297         public boolean uncryptPackageFileDelete() {
298             return RecoverySystem.UNCRYPT_PACKAGE_FILE.delete();
299         }
300 
getUncryptPackageFileName()301         public String getUncryptPackageFileName() {
302             return RecoverySystem.UNCRYPT_PACKAGE_FILE.getName();
303         }
304 
getUncryptPackageFileWriter()305         public FileWriter getUncryptPackageFileWriter() throws IOException {
306             return new FileWriter(RecoverySystem.UNCRYPT_PACKAGE_FILE);
307         }
308 
connectService()309         public UncryptSocket connectService() {
310             UncryptSocket socket = new UncryptSocket();
311             if (!socket.connectService()) {
312                 socket.close();
313                 return null;
314             }
315             return socket;
316         }
317 
318         /**
319          * Throws remote exception if there's an error getting the boot control HAL.
320          * Returns null if the boot control HAL's version is older than V1_2.
321          */
getBootControl()322         public IBootControl getBootControl() throws RemoteException {
323             String serviceName = IBootControl.DESCRIPTOR + "/default";
324             if (ServiceManager.isDeclared(serviceName)) {
325                 Slog.i(TAG,
326                         "AIDL version of BootControl HAL present, using instance " + serviceName);
327                 return IBootControl.Stub.asInterface(
328                         ServiceManager.waitForDeclaredService(serviceName));
329             }
330 
331             IBootControl bootcontrol = BootControlHIDL.getService();
332             if (!BootControlHIDL.isServicePresent()) {
333                 Slog.e(TAG, "Neither AIDL nor HIDL version of the BootControl HAL is present.");
334                 return null;
335             }
336 
337             if (!BootControlHIDL.isV1_2ServicePresent()) {
338                 Slog.w(TAG, "Device doesn't implement boot control HAL V1_2.");
339                 return null;
340             }
341             return bootcontrol;
342         }
343 
threadSleep(long millis)344         public void threadSleep(long millis) throws InterruptedException {
345             Thread.sleep(millis);
346         }
347 
getUidFromPackageName(String packageName)348         public int getUidFromPackageName(String packageName) {
349             try {
350                 return mContext.getPackageManager().getPackageUidAsUser(packageName, USER_SYSTEM);
351             } catch (PackageManager.NameNotFoundException e) {
352                 Slog.w(TAG, "Failed to find uid for " + packageName);
353             }
354             return -1;
355         }
356 
getMetricsPrefs()357         public PreferencesManager getMetricsPrefs() {
358             return mPrefs;
359         }
360 
getCurrentTimeMillis()361         public long getCurrentTimeMillis() {
362             return System.currentTimeMillis();
363         }
364 
reportRebootEscrowPreparationMetrics(int uid, @ResumeOnRebootActionsOnRequest int requestResult, int requestedClientCount)365         public void reportRebootEscrowPreparationMetrics(int uid,
366                 @ResumeOnRebootActionsOnRequest int requestResult, int requestedClientCount) {
367             FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_PREPARATION_REPORTED, uid,
368                     requestResult, requestedClientCount);
369         }
370 
reportRebootEscrowLskfCapturedMetrics(int uid, int requestedClientCount, int requestedToLskfCapturedDurationInSeconds)371         public void reportRebootEscrowLskfCapturedMetrics(int uid, int requestedClientCount,
372                 int requestedToLskfCapturedDurationInSeconds) {
373             FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_LSKF_CAPTURE_REPORTED, uid,
374                     requestedClientCount, requestedToLskfCapturedDurationInSeconds);
375         }
376 
reportRebootEscrowRebootMetrics(int errorCode, int uid, int preparedClientCount, int requestCount, boolean slotSwitch, boolean serverBased, int lskfCapturedToRebootDurationInSeconds, int lskfCapturedCounts)377         public void reportRebootEscrowRebootMetrics(int errorCode, int uid,
378                 int preparedClientCount, int requestCount, boolean slotSwitch, boolean serverBased,
379                 int lskfCapturedToRebootDurationInSeconds, int lskfCapturedCounts) {
380             FrameworkStatsLog.write(FrameworkStatsLog.REBOOT_ESCROW_REBOOT_REPORTED, errorCode,
381                     uid, preparedClientCount, requestCount, slotSwitch, serverBased,
382                     lskfCapturedToRebootDurationInSeconds, lskfCapturedCounts);
383         }
384     }
385 
386     /**
387      * Handles the lifecycle events for the RecoverySystemService.
388      */
389     public static final class Lifecycle extends SystemService {
390         private RecoverySystemService mRecoverySystemService;
391 
Lifecycle(Context context)392         public Lifecycle(Context context) {
393             super(context);
394         }
395 
396         @Override
onBootPhase(int phase)397         public void onBootPhase(int phase) {
398             if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
399                 mRecoverySystemService.onSystemServicesReady();
400             }
401         }
402 
403         @Override
onStart()404         public void onStart() {
405             mRecoverySystemService = new RecoverySystemService(getContext());
406             publishBinderService(Context.RECOVERY_SERVICE, mRecoverySystemService);
407         }
408     }
409 
RecoverySystemService(Context context)410     private RecoverySystemService(Context context) {
411         this(new Injector(context));
412     }
413 
414     @VisibleForTesting
RecoverySystemService(Injector injector)415     RecoverySystemService(Injector injector) {
416         mInjector = injector;
417         mContext = injector.getContext();
418     }
419 
420     @VisibleForTesting
onSystemServicesReady()421     void onSystemServicesReady() {
422         LockSettingsInternal lockSettings = mInjector.getLockSettingsService();
423         if (lockSettings == null) {
424             Slog.e(TAG, "Failed to get lock settings service, skipping set"
425                     + " RebootEscrowListener");
426             return;
427         }
428         lockSettings.setRebootEscrowListener(this);
429     }
430 
431     @Override // Binder call
uncrypt(String filename, IRecoverySystemProgressListener listener)432     public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) {
433         if (DEBUG) Slog.d(TAG, "uncrypt: " + filename);
434 
435         synchronized (sRequestLock) {
436             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
437 
438             if (!checkAndWaitForUncryptService()) {
439                 Slog.e(TAG, "uncrypt service is unavailable.");
440                 return false;
441             }
442 
443             // Write the filename into uncrypt package file to be read by
444             // uncrypt.
445             mInjector.uncryptPackageFileDelete();
446 
447             try (FileWriter uncryptFile = mInjector.getUncryptPackageFileWriter()) {
448                 uncryptFile.write(filename + "\n");
449             } catch (IOException e) {
450                 Slog.e(TAG, "IOException when writing \""
451                         + mInjector.getUncryptPackageFileName() + "\":", e);
452                 return false;
453             }
454 
455             // Trigger uncrypt via init.
456             mInjector.systemPropertiesSet("ctl.start", "uncrypt");
457 
458             // Connect to the uncrypt service socket.
459             UncryptSocket socket = mInjector.connectService();
460             if (socket == null) {
461                 Slog.e(TAG, "Failed to connect to uncrypt socket");
462                 return false;
463             }
464 
465             // Read the status from the socket.
466             try {
467                 int lastStatus = Integer.MIN_VALUE;
468                 while (true) {
469                     int status = socket.getPercentageUncrypted();
470                     // Avoid flooding the log with the same message.
471                     if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
472                         continue;
473                     }
474                     lastStatus = status;
475 
476                     if (status >= 0 && status <= 100) {
477                         // Update status
478                         Slog.i(TAG, "uncrypt read status: " + status);
479                         if (listener != null) {
480                             try {
481                                 listener.onProgress(status);
482                             } catch (RemoteException ignored) {
483                                 Slog.w(TAG, "RemoteException when posting progress");
484                             }
485                         }
486                         if (status == 100) {
487                             Slog.i(TAG, "uncrypt successfully finished.");
488                             // Ack receipt of the final status code. uncrypt
489                             // waits for the ack so the socket won't be
490                             // destroyed before we receive the code.
491                             socket.sendAck();
492                             break;
493                         }
494                     } else {
495                         // Error in /system/bin/uncrypt.
496                         Slog.e(TAG, "uncrypt failed with status: " + status);
497                         // Ack receipt of the final status code. uncrypt waits
498                         // for the ack so the socket won't be destroyed before
499                         // we receive the code.
500                         socket.sendAck();
501                         return false;
502                     }
503                 }
504             } catch (IOException e) {
505                 Slog.e(TAG, "IOException when reading status: ", e);
506                 return false;
507             } finally {
508                 socket.close();
509             }
510 
511             return true;
512         }
513     }
514 
515     @Override // Binder call
clearBcb()516     public boolean clearBcb() {
517         if (DEBUG) Slog.d(TAG, "clearBcb");
518         synchronized (sRequestLock) {
519             return setupOrClearBcb(false, null);
520         }
521     }
522 
523     @Override // Binder call
setupBcb(String command)524     public boolean setupBcb(String command) {
525         if (DEBUG) Slog.d(TAG, "setupBcb: [" + command + "]");
526         synchronized (sRequestLock) {
527             return setupOrClearBcb(true, command);
528         }
529     }
530 
531     @Override // Binder call
rebootRecoveryWithCommand(String command)532     public void rebootRecoveryWithCommand(String command) {
533         if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
534 
535         boolean isForcedWipe = command != null && command.contains(RECOVERY_WIPE_DATA_COMMAND);
536         synchronized (sRequestLock) {
537             if (!setupOrClearBcb(true, command)) {
538                 Slog.e(TAG, "rebootRecoveryWithCommand failed to setup BCB");
539                 return;
540             }
541 
542             if (isForcedWipe) {
543                 deleteSecrets();
544                 // TODO: consider adding a dedicated forced-wipe-reboot method to PowerManager and
545                 // calling here.
546             }
547 
548             // Having set up the BCB, go ahead and reboot.
549             PowerManager pm = mInjector.getPowerManager();
550             pm.reboot(PowerManager.REBOOT_RECOVERY);
551         }
552     }
553 
deleteSecrets()554     private static void deleteSecrets() {
555         Slogf.w(TAG, "deleteSecrets");
556         try {
557             AndroidKeyStoreMaintenance.deleteAllKeys();
558         } catch (android.security.KeyStoreException e) {
559             Log.wtf(TAG, "Failed to delete all keys from keystore.", e);
560         }
561 
562         try {
563             ISecretkeeper secretKeeper = getSecretKeeper();
564             if (secretKeeper != null) {
565                 Slogf.i(TAG, "ISecretkeeper.deleteAll();");
566                 secretKeeper.deleteAll();
567             }
568         } catch (RemoteException e) {
569             Log.wtf(TAG, "Failed to delete all secrets from secretkeeper.", e);
570         }
571     }
572 
getSecretKeeper()573     private static @Nullable ISecretkeeper getSecretKeeper() {
574         ISecretkeeper result = null;
575         try {
576             result = ISecretkeeper.Stub.asInterface(
577                 ServiceManager.waitForDeclaredService(ISecretkeeper.DESCRIPTOR + "/default"));
578         } catch (SecurityException e) {
579             Slog.w(TAG, "Does not have permissions to get AIDL secretkeeper service");
580         }
581 
582         return result;
583     }
584 
enforcePermissionForResumeOnReboot()585     private void enforcePermissionForResumeOnReboot() {
586         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.RECOVERY)
587                 != PackageManager.PERMISSION_GRANTED
588                 && mContext.checkCallingOrSelfPermission(android.Manifest.permission.REBOOT)
589                 != PackageManager.PERMISSION_GRANTED) {
590             throw new SecurityException("Caller must have " + android.Manifest.permission.RECOVERY
591                     + " or " + android.Manifest.permission.REBOOT + " for resume on reboot.");
592         }
593     }
594 
reportMetricsOnRequestLskf(String packageName, int requestResult)595     private void reportMetricsOnRequestLskf(String packageName, int requestResult) {
596         int uid = mInjector.getUidFromPackageName(packageName);
597         int pendingRequestCount;
598         synchronized (this) {
599             pendingRequestCount = mCallerPendingRequest.size();
600         }
601 
602         // Save the timestamp and request count for new ror request
603         PreferencesManager prefs = mInjector.getMetricsPrefs();
604         prefs.putLong(packageName + REQUEST_LSKF_TIMESTAMP_PREF_SUFFIX,
605                 mInjector.getCurrentTimeMillis());
606         prefs.incrementIntKey(packageName + REQUEST_LSKF_COUNT_PREF_SUFFIX, 0);
607 
608         mInjector.reportRebootEscrowPreparationMetrics(uid, requestResult, pendingRequestCount);
609     }
610 
611     @Override // Binder call
requestLskf(String packageName, IntentSender intentSender)612     public boolean requestLskf(String packageName, IntentSender intentSender) {
613         enforcePermissionForResumeOnReboot();
614 
615         if (packageName == null) {
616             Slog.w(TAG, "Missing packageName when requesting lskf.");
617             return false;
618         }
619 
620         @ResumeOnRebootActionsOnRequest int action = updateRoRPreparationStateOnNewRequest(
621                 packageName, intentSender);
622         reportMetricsOnRequestLskf(packageName, action);
623 
624         switch (action) {
625             case ROR_SKIP_PREPARATION_AND_NOTIFY:
626                 // We consider the preparation done if someone else has prepared.
627                 sendPreparedForRebootIntentIfNeeded(intentSender);
628                 return true;
629             case ROR_SKIP_PREPARATION_NOT_NOTIFY:
630                 return true;
631             case ROR_NEED_PREPARATION:
632                 final long origId = Binder.clearCallingIdentity();
633                 try {
634                     LockSettingsInternal lockSettings = mInjector.getLockSettingsService();
635                     if (lockSettings == null) {
636                         Slog.e(TAG, "Failed to get lock settings service, skipping"
637                                 + " prepareRebootEscrow");
638                         return false;
639                     }
640                     // Clear the RoR preparation state if lock settings reports an failure.
641                     if (!lockSettings.prepareRebootEscrow()) {
642                         clearRoRPreparationState();
643                         return false;
644                     }
645                     return true;
646                 } finally {
647                     Binder.restoreCallingIdentity(origId);
648                 }
649             default:
650                 throw new IllegalStateException("Unsupported action type on new request " + action);
651         }
652     }
653 
654     // Checks and updates the resume on reboot preparation state.
updateRoRPreparationStateOnNewRequest( String packageName, IntentSender intentSender)655     private synchronized @ResumeOnRebootActionsOnRequest int updateRoRPreparationStateOnNewRequest(
656             String packageName, IntentSender intentSender) {
657         if (!mCallerPreparedForReboot.isEmpty()) {
658             if (mCallerPreparedForReboot.contains(packageName)) {
659                 Slog.i(TAG, "RoR already has prepared for " + packageName);
660             }
661 
662             // Someone else has prepared. Consider the preparation done, and send back the intent.
663             mCallerPreparedForReboot.add(packageName);
664             return ROR_SKIP_PREPARATION_AND_NOTIFY;
665         }
666 
667         boolean needPreparation = mCallerPendingRequest.isEmpty();
668         if (mCallerPendingRequest.containsKey(packageName)) {
669             Slog.i(TAG, "Duplicate RoR preparation request for " + packageName);
670         }
671         // Update the request with the new intentSender.
672         mCallerPendingRequest.put(packageName, intentSender);
673         return needPreparation ? ROR_NEED_PREPARATION : ROR_SKIP_PREPARATION_NOT_NOTIFY;
674     }
675 
reportMetricsOnPreparedForReboot()676     private void reportMetricsOnPreparedForReboot() {
677         long currentTimestamp = mInjector.getCurrentTimeMillis();
678 
679         List<String> preparedClients;
680         synchronized (this) {
681             preparedClients = new ArrayList<>(mCallerPreparedForReboot);
682         }
683 
684         // Save the timestamp & lskf capture count for lskf capture
685         PreferencesManager prefs = mInjector.getMetricsPrefs();
686         prefs.putLong(LSKF_CAPTURED_TIMESTAMP_PREF, currentTimestamp);
687         prefs.incrementIntKey(LSKF_CAPTURED_COUNT_PREF, 0);
688 
689         for (String packageName : preparedClients) {
690             int uid = mInjector.getUidFromPackageName(packageName);
691 
692             int durationSeconds = -1;
693             long requestLskfTimestamp = prefs.getLong(
694                     packageName + REQUEST_LSKF_TIMESTAMP_PREF_SUFFIX, -1);
695             if (requestLskfTimestamp != -1 && currentTimestamp > requestLskfTimestamp) {
696                 durationSeconds = (int) (currentTimestamp - requestLskfTimestamp) / 1000;
697             }
698             Slog.i(TAG, String.format("Reporting lskf captured, lskf capture takes %d seconds for"
699                     + " package %s", durationSeconds, packageName));
700             mInjector.reportRebootEscrowLskfCapturedMetrics(uid, preparedClients.size(),
701                     durationSeconds);
702         }
703     }
704 
705     @Override
onPreparedForReboot(boolean ready)706     public void onPreparedForReboot(boolean ready) {
707         if (!ready) {
708             return;
709         }
710         updateRoRPreparationStateOnPreparedForReboot();
711         reportMetricsOnPreparedForReboot();
712     }
713 
updateRoRPreparationStateOnPreparedForReboot()714     private synchronized void updateRoRPreparationStateOnPreparedForReboot() {
715         if (!mCallerPreparedForReboot.isEmpty()) {
716             Slog.w(TAG, "onPreparedForReboot called when some clients have prepared.");
717         }
718 
719         if (mCallerPendingRequest.isEmpty()) {
720             Slog.w(TAG, "onPreparedForReboot called but no client has requested.");
721         }
722 
723         // Send intents to notify callers
724         for (int i = 0; i < mCallerPendingRequest.size(); i++) {
725             sendPreparedForRebootIntentIfNeeded(mCallerPendingRequest.valueAt(i));
726             mCallerPreparedForReboot.add(mCallerPendingRequest.keyAt(i));
727         }
728         mCallerPendingRequest.clear();
729     }
730 
sendPreparedForRebootIntentIfNeeded(IntentSender intentSender)731     private void sendPreparedForRebootIntentIfNeeded(IntentSender intentSender) {
732         if (intentSender != null) {
733             try {
734                 intentSender.sendIntent(null, 0, null, null, null);
735             } catch (IntentSender.SendIntentException e) {
736                 Slog.w(TAG, "Could not send intent for prepared reboot: " + e.getMessage());
737             }
738         }
739     }
740 
741     @Override // Binder call
clearLskf(String packageName)742     public boolean clearLskf(String packageName) {
743         enforcePermissionForResumeOnReboot();
744         if (packageName == null) {
745             Slog.w(TAG, "Missing packageName when clearing lskf.");
746             return false;
747         }
748         // TODO(179105110) Clear the RoR metrics for the given packageName.
749 
750         @ResumeOnRebootActionsOnClear int action = updateRoRPreparationStateOnClear(packageName);
751         switch (action) {
752             case ROR_NOT_REQUESTED:
753                 Slog.w(TAG, "RoR clear called before preparation for caller " + packageName);
754                 return true;
755             case ROR_REQUESTED_SKIP_CLEAR:
756                 return true;
757             case ROR_REQUESTED_NEED_CLEAR:
758                 final long origId = Binder.clearCallingIdentity();
759                 try {
760                     LockSettingsInternal lockSettings = mInjector.getLockSettingsService();
761                     if (lockSettings == null) {
762                         Slog.e(TAG, "Failed to get lock settings service, skipping"
763                                 + " clearRebootEscrow");
764                         return false;
765                     }
766 
767                     return lockSettings.clearRebootEscrow();
768                 } finally {
769                     Binder.restoreCallingIdentity(origId);
770                 }
771             default:
772                 throw new IllegalStateException("Unsupported action type on clear " + action);
773         }
774     }
775 
updateRoRPreparationStateOnClear( String packageName)776     private synchronized @ResumeOnRebootActionsOnClear int updateRoRPreparationStateOnClear(
777             String packageName) {
778         if (!mCallerPreparedForReboot.contains(packageName) && !mCallerPendingRequest.containsKey(
779                 packageName)) {
780             Slog.w(TAG, packageName + " hasn't prepared for resume on reboot");
781             return ROR_NOT_REQUESTED;
782         }
783         mCallerPendingRequest.remove(packageName);
784         mCallerPreparedForReboot.remove(packageName);
785 
786         // Check if others have prepared ROR.
787         boolean needClear = mCallerPendingRequest.isEmpty() && mCallerPreparedForReboot.isEmpty();
788         return needClear ? ROR_REQUESTED_NEED_CLEAR : ROR_REQUESTED_SKIP_CLEAR;
789     }
790 
isAbDevice()791     private boolean isAbDevice() {
792         return "true".equalsIgnoreCase(mInjector.systemPropertiesGet(AB_UPDATE));
793     }
794 
verifySlotForNextBoot(boolean slotSwitch)795     private boolean verifySlotForNextBoot(boolean slotSwitch) {
796         if (!isAbDevice()) {
797             Slog.w(TAG, "Device isn't a/b, skipping slot verification.");
798             return true;
799         }
800 
801         IBootControl bootControl;
802         try {
803             bootControl = mInjector.getBootControl();
804         } catch (RemoteException e) {
805             Slog.w(TAG, "Failed to get the boot control HAL " + e);
806             return false;
807         }
808 
809         // TODO(xunchang) enforce boot control V1_2 HAL on devices using multi client RoR
810         if (bootControl == null) {
811             Slog.w(TAG, "Cannot get the boot control HAL, skipping slot verification.");
812             return true;
813         }
814 
815         int current_slot;
816         int next_active_slot;
817         try {
818             current_slot = bootControl.getCurrentSlot();
819             if (current_slot != 0 && current_slot != 1) {
820                 throw new IllegalStateException("Current boot slot should be 0 or 1, got "
821                         + current_slot);
822             }
823             next_active_slot = bootControl.getActiveBootSlot();
824         } catch (RemoteException e) {
825             Slog.w(TAG, "Failed to query the active slots", e);
826             return false;
827         }
828 
829         int expected_active_slot = current_slot;
830         if (slotSwitch) {
831             expected_active_slot = current_slot == 0 ? 1 : 0;
832         }
833         if (next_active_slot != expected_active_slot) {
834             Slog.w(TAG, "The next active boot slot doesn't match the expected value, "
835                     + "expected " + expected_active_slot + ", got " + next_active_slot);
836             return false;
837         }
838         return true;
839     }
840 
armRebootEscrow(String packageName, boolean slotSwitch)841     private RebootPreparationError armRebootEscrow(String packageName,
842             boolean slotSwitch) {
843         if (packageName == null) {
844             Slog.w(TAG, "Missing packageName when rebooting with lskf.");
845             return new RebootPreparationError(
846                     RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME, ARM_REBOOT_ERROR_NONE);
847         }
848         if (!isLskfCaptured(packageName)) {
849             return new RebootPreparationError(RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
850                     ARM_REBOOT_ERROR_NONE);
851         }
852 
853         if (!verifySlotForNextBoot(slotSwitch)) {
854             return new RebootPreparationError(RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH,
855                     ARM_REBOOT_ERROR_NONE);
856         }
857 
858         final long origId = Binder.clearCallingIdentity();
859         int providerErrorCode;
860         try {
861             LockSettingsInternal lockSettings = mInjector.getLockSettingsService();
862             if (lockSettings == null) {
863                 Slog.e(TAG, "Failed to get lock settings service, skipping"
864                         + " armRebootEscrow");
865                 return new RebootPreparationError(
866                         RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE,
867                         ARM_REBOOT_ERROR_NO_PROVIDER);
868             }
869             providerErrorCode = lockSettings.armRebootEscrow();
870         } finally {
871             Binder.restoreCallingIdentity(origId);
872         }
873 
874         if (providerErrorCode != ARM_REBOOT_ERROR_NONE) {
875             Slog.w(TAG, "Failure to escrow key for reboot, providerErrorCode: "
876                     + providerErrorCode);
877             return new RebootPreparationError(
878                     RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE, providerErrorCode);
879         }
880 
881         return new RebootPreparationError(RESUME_ON_REBOOT_REBOOT_ERROR_NONE,
882                 ARM_REBOOT_ERROR_NONE);
883     }
884 
useServerBasedRoR()885     private boolean useServerBasedRoR() {
886         final long origId = Binder.clearCallingIdentity();
887         try {
888             return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
889                     "server_based_ror_enabled", false);
890         } finally {
891             Binder.restoreCallingIdentity(origId);
892         }
893     }
894 
reportMetricsOnRebootWithLskf(String packageName, boolean slotSwitch, RebootPreparationError escrowError)895     private void reportMetricsOnRebootWithLskf(String packageName, boolean slotSwitch,
896             RebootPreparationError escrowError) {
897         int uid = mInjector.getUidFromPackageName(packageName);
898         boolean serverBased = useServerBasedRoR();
899         int preparedClientCount;
900         synchronized (this) {
901             preparedClientCount = mCallerPreparedForReboot.size();
902         }
903 
904         long currentTimestamp = mInjector.getCurrentTimeMillis();
905         int durationSeconds = -1;
906         PreferencesManager prefs = mInjector.getMetricsPrefs();
907         long lskfCapturedTimestamp = prefs.getLong(LSKF_CAPTURED_TIMESTAMP_PREF, -1);
908         if (lskfCapturedTimestamp != -1 && currentTimestamp > lskfCapturedTimestamp) {
909             durationSeconds = (int) (currentTimestamp - lskfCapturedTimestamp) / 1000;
910         }
911 
912         int requestCount = prefs.getInt(packageName + REQUEST_LSKF_COUNT_PREF_SUFFIX, -1);
913         int lskfCapturedCount = prefs.getInt(LSKF_CAPTURED_COUNT_PREF, -1);
914 
915         Slog.i(TAG, String.format("Reporting reboot with lskf, package name %s, client count %d,"
916                         + " request count %d, lskf captured count %d, duration since lskf captured"
917                         + " %d seconds.", packageName, preparedClientCount, requestCount,
918                 lskfCapturedCount, durationSeconds));
919         mInjector.reportRebootEscrowRebootMetrics(escrowError.getErrorCodeForMetrics(), uid,
920                 preparedClientCount, requestCount, slotSwitch, serverBased, durationSeconds,
921                 lskfCapturedCount);
922     }
923 
clearRoRPreparationState()924     private synchronized void clearRoRPreparationState() {
925         mCallerPendingRequest.clear();
926         mCallerPreparedForReboot.clear();
927     }
928 
clearRoRPreparationStateOnRebootFailure(RebootPreparationError escrowError)929     private void clearRoRPreparationStateOnRebootFailure(RebootPreparationError escrowError) {
930         if (!FATAL_ARM_ESCROW_ERRORS.contains(escrowError.mProviderErrorCode)) {
931             return;
932         }
933 
934         Slog.w(TAG, "Clearing resume on reboot states for all clients on arm escrow error: "
935                 + escrowError.mProviderErrorCode);
936         clearRoRPreparationState();
937     }
938 
rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch)939     private @ResumeOnRebootRebootErrorCode int rebootWithLskfImpl(String packageName, String reason,
940             boolean slotSwitch) {
941         RebootPreparationError escrowError = armRebootEscrow(packageName, slotSwitch);
942         reportMetricsOnRebootWithLskf(packageName, slotSwitch, escrowError);
943         clearRoRPreparationStateOnRebootFailure(escrowError);
944 
945         @ResumeOnRebootRebootErrorCode int errorCode = escrowError.mRebootErrorCode;
946         if (errorCode != RESUME_ON_REBOOT_REBOOT_ERROR_NONE) {
947             return errorCode;
948         }
949 
950         // Clear the metrics prefs after a successful RoR reboot.
951         mInjector.getMetricsPrefs().deletePrefsFile();
952         Watchdog.getInstance().pauseWatchingCurrentThreadFor(
953                 REBOOT_WATCHDOG_PAUSE_DURATION_MS, "reboot can be slow");
954         PowerManager pm = mInjector.getPowerManager();
955         pm.reboot(reason);
956         return RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED;
957     }
958 
959     @android.annotation.EnforcePermission(android.Manifest.permission.RECOVERY)
960     @Override // Binder call for the legacy rebootWithLskf
rebootWithLskfAssumeSlotSwitch(String packageName, String reason)961     public @ResumeOnRebootRebootErrorCode int rebootWithLskfAssumeSlotSwitch(String packageName,
962             String reason) {
963         rebootWithLskfAssumeSlotSwitch_enforcePermission();
964         return rebootWithLskfImpl(packageName, reason, true);
965     }
966 
967     @Override // Binder call
rebootWithLskf(String packageName, String reason, boolean slotSwitch)968     public @ResumeOnRebootRebootErrorCode int rebootWithLskf(String packageName, String reason,
969             boolean slotSwitch) {
970         enforcePermissionForResumeOnReboot();
971         return rebootWithLskfImpl(packageName, reason, slotSwitch);
972     }
973 
974     // Metadata should be no more than few MB, if it's larger than 100MB something is wrong.
975     private static final long APEX_INFO_SIZE_LIMIT = 24 * 1024 * 100;
976 
getCompressedApexInfoList(String packageFile)977     private static CompressedApexInfoList getCompressedApexInfoList(String packageFile)
978             throws IOException {
979         try (ZipFile zipFile = new ZipFile(packageFile)) {
980             final ZipEntry entry = zipFile.getEntry("apex_info.pb");
981             if (entry == null) {
982                 return null;
983             }
984             if (entry.getSize() >= APEX_INFO_SIZE_LIMIT) {
985                 throw new IllegalArgumentException("apex_info.pb has size "
986                         + entry.getSize()
987                         + " which is larger than the permitted limit" + APEX_INFO_SIZE_LIMIT);
988             }
989             if (entry.getSize() == 0) {
990                 CompressedApexInfoList infoList = new CompressedApexInfoList();
991                 infoList.apexInfos = new CompressedApexInfo[0];
992                 return infoList;
993             }
994             Log.i(TAG, "Allocating " + entry.getSize()
995                     + " bytes of memory to store OTA Metadata");
996             byte[] data = new byte[(int) entry.getSize()];
997 
998             try (InputStream is = zipFile.getInputStream(entry)) {
999                 int bytesRead = is.read(data);
1000                 String msg = "Read " + bytesRead + " when expecting " + data.length;
1001                 Log.e(TAG, msg);
1002                 if (bytesRead != data.length) {
1003                     throw new IOException(msg);
1004                 }
1005             }
1006             ApexMetadata metadata = ApexMetadata.parseFrom(data);
1007             CompressedApexInfoList apexInfoList = new CompressedApexInfoList();
1008             apexInfoList.apexInfos =
1009                     Arrays.stream(metadata.apexInfo).filter(apex -> apex.isCompressed).map(apex -> {
1010                         CompressedApexInfo info = new CompressedApexInfo();
1011                         info.moduleName = apex.packageName;
1012                         info.decompressedSize = apex.decompressedSize;
1013                         info.versionCode = apex.version;
1014                         return info;
1015                     }).toArray(CompressedApexInfo[]::new);
1016             return apexInfoList;
1017         }
1018     }
1019 
1020     @android.annotation.EnforcePermission(android.Manifest.permission.RECOVERY)
1021     @Override
allocateSpaceForUpdate(String packageFile)1022     public boolean allocateSpaceForUpdate(String packageFile) {
1023         allocateSpaceForUpdate_enforcePermission();
1024         final long token = Binder.clearCallingIdentity();
1025         try {
1026             CompressedApexInfoList apexInfoList = getCompressedApexInfoList(packageFile);
1027             if (apexInfoList == null) {
1028                 Log.i(TAG, "apex_info.pb not present in OTA package. "
1029                         + "Assuming device doesn't support compressed"
1030                         + "APEX, continueing without allocating space.");
1031                 return true;
1032             }
1033             ApexManager apexManager = ApexManager.getInstance();
1034             apexManager.reserveSpaceForCompressedApex(apexInfoList);
1035             return true;
1036         } catch (RemoteException e) {
1037             e.rethrowAsRuntimeException();
1038         } catch (IOException | UnsupportedOperationException e) {
1039             Slog.e(TAG, "Failed to reserve space for compressed apex: ", e);
1040         } finally {
1041             Binder.restoreCallingIdentity(token);
1042         }
1043         return false;
1044     }
1045 
1046     @Override // Binder call
isLskfCaptured(String packageName)1047     public boolean isLskfCaptured(String packageName) {
1048         enforcePermissionForResumeOnReboot();
1049         boolean captured;
1050         synchronized (this) {
1051             captured = mCallerPreparedForReboot.contains(packageName);
1052         }
1053 
1054         if (!captured) {
1055             Slog.i(TAG, "Reboot requested before prepare completed for caller "
1056                     + packageName);
1057             return false;
1058         }
1059         return true;
1060     }
1061 
1062     /**
1063      * Check if any of the init services is still running. If so, we cannot
1064      * start a new uncrypt/setup-bcb/clear-bcb service right away; otherwise
1065      * it may break the socket communication since init creates / deletes
1066      * the socket (/dev/socket/uncrypt) on service start / exit.
1067      */
checkAndWaitForUncryptService()1068     private boolean checkAndWaitForUncryptService() {
1069         for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
1070             final String uncryptService = mInjector.systemPropertiesGet(INIT_SERVICE_UNCRYPT);
1071             final String setupBcbService = mInjector.systemPropertiesGet(INIT_SERVICE_SETUP_BCB);
1072             final String clearBcbService = mInjector.systemPropertiesGet(INIT_SERVICE_CLEAR_BCB);
1073             final boolean busy = "running".equals(uncryptService)
1074                     || "running".equals(setupBcbService) || "running".equals(clearBcbService);
1075             if (DEBUG) {
1076                 Slog.i(TAG, "retry: " + retry + " busy: " + busy
1077                         + " uncrypt: [" + uncryptService + "]"
1078                         + " setupBcb: [" + setupBcbService + "]"
1079                         + " clearBcb: [" + clearBcbService + "]");
1080             }
1081 
1082             if (!busy) {
1083                 return true;
1084             }
1085 
1086             try {
1087                 mInjector.threadSleep(1000);
1088             } catch (InterruptedException e) {
1089                 Slog.w(TAG, "Interrupted:", e);
1090             }
1091         }
1092 
1093         return false;
1094     }
1095 
setupOrClearBcb(boolean isSetup, String command)1096     private boolean setupOrClearBcb(boolean isSetup, String command) {
1097         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
1098 
1099         final boolean available = checkAndWaitForUncryptService();
1100         if (!available) {
1101             Slog.e(TAG, "uncrypt service is unavailable.");
1102             return false;
1103         }
1104 
1105         if (isSetup) {
1106             mInjector.systemPropertiesSet("ctl.start", "setup-bcb");
1107         } else {
1108             mInjector.systemPropertiesSet("ctl.start", "clear-bcb");
1109         }
1110 
1111         // Connect to the uncrypt service socket.
1112         UncryptSocket socket = mInjector.connectService();
1113         if (socket == null) {
1114             Slog.e(TAG, "Failed to connect to uncrypt socket");
1115             return false;
1116         }
1117 
1118         try {
1119             // Send the BCB commands if it's to setup BCB.
1120             if (isSetup) {
1121                 socket.sendCommand(command);
1122             }
1123 
1124             // Read the status from the socket.
1125             int status = socket.getPercentageUncrypted();
1126 
1127             // Ack receipt of the status code. uncrypt waits for the ack so
1128             // the socket won't be destroyed before we receive the code.
1129             socket.sendAck();
1130 
1131             if (status == 100) {
1132                 Slog.i(TAG, "uncrypt " + (isSetup ? "setup" : "clear")
1133                         + " bcb successfully finished.");
1134             } else {
1135                 // Error in /system/bin/uncrypt.
1136                 Slog.e(TAG, "uncrypt failed with status: " + status);
1137                 return false;
1138             }
1139         } catch (IOException e) {
1140             Slog.e(TAG, "IOException when communicating with uncrypt:", e);
1141             return false;
1142         } finally {
1143             socket.close();
1144         }
1145 
1146         return true;
1147     }
1148 
1149     /**
1150      * Provides a wrapper for the low-level details of framing packets sent to the uncrypt
1151      * socket.
1152      */
1153     public static class UncryptSocket {
1154         private LocalSocket mLocalSocket;
1155         private DataInputStream mInputStream;
1156         private DataOutputStream mOutputStream;
1157 
1158         /**
1159          * Attempt to connect to the uncrypt service. Connection will be retried for up to
1160          * {@link #SOCKET_CONNECTION_MAX_RETRY} times. If the connection is unsuccessful, the
1161          * socket will be closed. If the connection is successful, the connection must be closed
1162          * by the caller.
1163          *
1164          * @return true if connection was successful, false if unsuccessful
1165          */
connectService()1166         public boolean connectService() {
1167             mLocalSocket = new LocalSocket();
1168             boolean done = false;
1169             // The uncrypt socket will be created by init upon receiving the
1170             // service request. It may not be ready by this point. So we will
1171             // keep retrying until success or reaching timeout.
1172             for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
1173                 try {
1174                     mLocalSocket.connect(new LocalSocketAddress(UNCRYPT_SOCKET,
1175                             LocalSocketAddress.Namespace.RESERVED));
1176                     done = true;
1177                     break;
1178                 } catch (IOException ignored) {
1179                     try {
1180                         Thread.sleep(1000);
1181                     } catch (InterruptedException e) {
1182                         Slog.w(TAG, "Interrupted:", e);
1183                     }
1184                 }
1185             }
1186             if (!done) {
1187                 Slog.e(TAG, "Timed out connecting to uncrypt socket");
1188                 close();
1189                 return false;
1190             }
1191 
1192             try {
1193                 mInputStream = new DataInputStream(mLocalSocket.getInputStream());
1194                 mOutputStream = new DataOutputStream(mLocalSocket.getOutputStream());
1195             } catch (IOException e) {
1196                 close();
1197                 return false;
1198             }
1199 
1200             return true;
1201         }
1202 
1203         /**
1204          * Sends a command to the uncrypt service.
1205          *
1206          * @param command command to send to the uncrypt service
1207          * @throws IOException if there was an error writing to the socket
1208          */
sendCommand(String command)1209         public void sendCommand(String command) throws IOException {
1210             byte[] cmdUtf8 = command.getBytes(StandardCharsets.UTF_8);
1211             mOutputStream.writeInt(cmdUtf8.length);
1212             mOutputStream.write(cmdUtf8, 0, cmdUtf8.length);
1213         }
1214 
1215         /**
1216          * Reads the status from the uncrypt service which is usually represented as a percentage.
1217          *
1218          * @return an integer representing the percentage completed
1219          * @throws IOException if there was an error reading the socket
1220          */
getPercentageUncrypted()1221         public int getPercentageUncrypted() throws IOException {
1222             return mInputStream.readInt();
1223         }
1224 
1225         /**
1226          * Sends a confirmation to the uncrypt service.
1227          *
1228          * @throws IOException if there was an error writing to the socket
1229          */
sendAck()1230         public void sendAck() throws IOException {
1231             mOutputStream.writeInt(0);
1232         }
1233 
1234         /**
1235          * Closes the socket and all underlying data streams.
1236          */
close()1237         public void close() {
1238             IoUtils.closeQuietly(mInputStream);
1239             IoUtils.closeQuietly(mOutputStream);
1240             IoUtils.closeQuietly(mLocalSocket);
1241         }
1242     }
1243 
isCallerShell()1244     private boolean isCallerShell() {
1245         final int callingUid = Binder.getCallingUid();
1246         return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
1247     }
1248 
enforceShell()1249     private void enforceShell() {
1250         if (!isCallerShell()) {
1251             throw new SecurityException("Caller must be shell");
1252         }
1253     }
1254 
1255     @Override
onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)1256     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
1257             String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
1258         enforceShell();
1259         final long origId = Binder.clearCallingIdentity();
1260         try {
1261             new RecoverySystemShellCommand(this).exec(
1262                     this, in, out, err, args, callback, resultReceiver);
1263         } finally {
1264             Binder.restoreCallingIdentity(origId);
1265         }
1266     }
1267 }
1268