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