PersistentDataBlockService.java revision a54f83c99c418d561764132c9b1485bebefac629
1/* 2 * Copyright (C) 2014 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 17package com.android.server; 18 19import android.Manifest; 20import android.app.ActivityManager; 21import android.content.Context; 22import android.content.pm.PackageManager; 23import android.os.Binder; 24import android.os.IBinder; 25import android.os.RemoteException; 26import android.os.SystemProperties; 27import android.os.UserHandle; 28import android.os.UserManager; 29import android.service.persistentdata.IPersistentDataBlockService; 30import android.service.persistentdata.PersistentDataBlockManager; 31import android.util.Slog; 32 33import com.android.internal.R; 34import com.android.internal.annotations.GuardedBy; 35 36import libcore.io.IoUtils; 37 38import java.io.DataInputStream; 39import java.io.DataOutputStream; 40import java.io.File; 41import java.io.FileInputStream; 42import java.io.FileNotFoundException; 43import java.io.FileOutputStream; 44import java.io.IOException; 45import java.nio.ByteBuffer; 46import java.nio.channels.FileChannel; 47import java.security.MessageDigest; 48import java.security.NoSuchAlgorithmException; 49import java.util.Arrays; 50import java.util.concurrent.CountDownLatch; 51import java.util.concurrent.TimeUnit; 52 53/** 54 * Service for reading and writing blocks to a persistent partition. 55 * This data will live across factory resets not initiated via the Settings UI. 56 * When a device is factory reset through Settings this data is wiped. 57 * 58 * Allows writing one block at a time. Namely, each time {@link IPersistentDataBlockService#write} 59 * is called, it will overwrite the data that was previously written on the block. 60 * 61 * Clients can query the size of the currently written block via 62 * {@link IPersistentDataBlockService#getDataBlockSize} 63 * 64 * Clients can read any number of bytes from the currently written block up to its total size by 65 * invoking {@link IPersistentDataBlockService#read} 66 */ 67public class PersistentDataBlockService extends SystemService { 68 private static final String TAG = PersistentDataBlockService.class.getSimpleName(); 69 70 private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; 71 private static final int HEADER_SIZE = 8; 72 // Magic number to mark block device as adhering to the format consumed by this service 73 private static final int PARTITION_TYPE_MARKER = 0x19901873; 74 // Limit to 100k as blocks larger than this might cause strain on Binder. 75 private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100; 76 public static final int DIGEST_SIZE_BYTES = 32; 77 private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed"; 78 private static final String FLASH_LOCK_PROP = "ro.boot.flash.locked"; 79 private static final String FLASH_LOCK_LOCKED = "1"; 80 private static final String FLASH_LOCK_UNLOCKED = "0"; 81 82 private final Context mContext; 83 private final String mDataBlockFile; 84 private final Object mLock = new Object(); 85 private final CountDownLatch mInitDoneSignal = new CountDownLatch(1); 86 87 private int mAllowedUid = -1; 88 private long mBlockDeviceSize; 89 90 @GuardedBy("mLock") 91 private boolean mIsWritable = true; 92 93 @GuardedBy("mLock") 94 private boolean mIsWritable = true; 95 96 public PersistentDataBlockService(Context context) { 97 super(context); 98 mContext = context; 99 mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP); 100 mBlockDeviceSize = -1; // Load lazily 101 mAllowedUid = getAllowedUid(UserHandle.USER_SYSTEM); 102 } 103 104 private int getAllowedUid(int userHandle) { 105 String allowedPackage = mContext.getResources() 106 .getString(R.string.config_persistentDataPackageName); 107 PackageManager pm = mContext.getPackageManager(); 108 int allowedUid = -1; 109 try { 110 allowedUid = pm.getPackageUidAsUser(allowedPackage, 111 PackageManager.MATCH_SYSTEM_ONLY, userHandle); 112 } catch (PackageManager.NameNotFoundException e) { 113 // not expected 114 Slog.e(TAG, "not able to find package " + allowedPackage, e); 115 } 116 return allowedUid; 117 } 118 119 @Override 120 public void onStart() { 121 // Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY 122 FgThread.getHandler().post(() -> { 123 enforceChecksumValidity(); 124 formatIfOemUnlockEnabled(); 125 publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService); 126 mInitDoneSignal.countDown(); 127 }); 128 } 129 130 @Override 131 public void onBootPhase(int phase) { 132 // Wait for initialization in onStart to finish 133 if (phase == PHASE_SYSTEM_SERVICES_READY) { 134 try { 135 if (!mInitDoneSignal.await(10, TimeUnit.SECONDS)) { 136 throw new IllegalStateException("Service " + TAG + " init timeout"); 137 } 138 } catch (InterruptedException e) { 139 Thread.currentThread().interrupt(); 140 throw new IllegalStateException("Service " + TAG + " init interrupted", e); 141 } 142 } 143 super.onBootPhase(phase); 144 } 145 146 private void formatIfOemUnlockEnabled() { 147 boolean enabled = doGetOemUnlockEnabled(); 148 if (enabled) { 149 synchronized (mLock) { 150 formatPartitionLocked(true); 151 } 152 } 153 154 SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0"); 155 } 156 157 private void enforceOemUnlockReadPermission() { 158 if (mContext.checkCallingOrSelfPermission(Manifest.permission.READ_OEM_UNLOCK_STATE) 159 == PackageManager.PERMISSION_DENIED 160 && mContext.checkCallingOrSelfPermission(Manifest.permission.OEM_UNLOCK_STATE) 161 == PackageManager.PERMISSION_DENIED) { 162 throw new SecurityException("Can't access OEM unlock state. Requires " 163 + "READ_OEM_UNLOCK_STATE or OEM_UNLOCK_STATE permission."); 164 } 165 } 166 167 private void enforceOemUnlockWritePermission() { 168 mContext.enforceCallingOrSelfPermission( 169 Manifest.permission.OEM_UNLOCK_STATE, 170 "Can't modify OEM unlock state"); 171 } 172 173 private void enforceUid(int callingUid) { 174 if (callingUid != mAllowedUid) { 175 throw new SecurityException("uid " + callingUid + " not allowed to access PST"); 176 } 177 } 178 179 private void enforceIsAdmin() { 180 final int userId = UserHandle.getCallingUserId(); 181 final boolean isAdmin = UserManager.get(mContext).isUserAdmin(userId); 182 if (!isAdmin) { 183 throw new SecurityException( 184 "Only the Admin user is allowed to change OEM unlock state"); 185 } 186 } 187 188 private void enforceUserRestriction(String userRestriction) { 189 if (UserManager.get(mContext).hasUserRestriction(userRestriction)) { 190 throw new SecurityException( 191 "OEM unlock is disallowed by user restriction: " + userRestriction); 192 } 193 } 194 195 private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException { 196 // skip over checksum 197 inputStream.skipBytes(DIGEST_SIZE_BYTES); 198 199 int totalDataSize; 200 int blockId = inputStream.readInt(); 201 if (blockId == PARTITION_TYPE_MARKER) { 202 totalDataSize = inputStream.readInt(); 203 } else { 204 totalDataSize = 0; 205 } 206 return totalDataSize; 207 } 208 209 private long getBlockDeviceSize() { 210 synchronized (mLock) { 211 if (mBlockDeviceSize == -1) { 212 mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile); 213 } 214 } 215 216 return mBlockDeviceSize; 217 } 218 219 private boolean enforceChecksumValidity() { 220 byte[] storedDigest = new byte[DIGEST_SIZE_BYTES]; 221 222 synchronized (mLock) { 223 byte[] digest = computeDigestLocked(storedDigest); 224 if (digest == null || !Arrays.equals(storedDigest, digest)) { 225 Slog.i(TAG, "Formatting FRP partition..."); 226 formatPartitionLocked(false); 227 return false; 228 } 229 } 230 231 return true; 232 } 233 234 private boolean computeAndWriteDigestLocked() { 235 byte[] digest = computeDigestLocked(null); 236 if (digest != null) { 237 DataOutputStream outputStream; 238 try { 239 outputStream = new DataOutputStream( 240 new FileOutputStream(new File(mDataBlockFile))); 241 } catch (FileNotFoundException e) { 242 Slog.e(TAG, "partition not available?", e); 243 return false; 244 } 245 246 try { 247 outputStream.write(digest, 0, DIGEST_SIZE_BYTES); 248 outputStream.flush(); 249 } catch (IOException e) { 250 Slog.e(TAG, "failed to write block checksum", e); 251 return false; 252 } finally { 253 IoUtils.closeQuietly(outputStream); 254 } 255 return true; 256 } else { 257 return false; 258 } 259 } 260 261 private byte[] computeDigestLocked(byte[] storedDigest) { 262 DataInputStream inputStream; 263 try { 264 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 265 } catch (FileNotFoundException e) { 266 Slog.e(TAG, "partition not available?", e); 267 return null; 268 } 269 270 MessageDigest md; 271 try { 272 md = MessageDigest.getInstance("SHA-256"); 273 } catch (NoSuchAlgorithmException e) { 274 // won't ever happen -- every implementation is required to support SHA-256 275 Slog.e(TAG, "SHA-256 not supported?", e); 276 IoUtils.closeQuietly(inputStream); 277 return null; 278 } 279 280 try { 281 if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) { 282 inputStream.read(storedDigest); 283 } else { 284 inputStream.skipBytes(DIGEST_SIZE_BYTES); 285 } 286 287 int read; 288 byte[] data = new byte[1024]; 289 md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest 290 while ((read = inputStream.read(data)) != -1) { 291 md.update(data, 0, read); 292 } 293 } catch (IOException e) { 294 Slog.e(TAG, "failed to read partition", e); 295 return null; 296 } finally { 297 IoUtils.closeQuietly(inputStream); 298 } 299 300 return md.digest(); 301 } 302 303 private void formatPartitionLocked(boolean setOemUnlockEnabled) { 304 DataOutputStream outputStream; 305 try { 306 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile))); 307 } catch (FileNotFoundException e) { 308 Slog.e(TAG, "partition not available?", e); 309 return; 310 } 311 312 byte[] data = new byte[DIGEST_SIZE_BYTES]; 313 try { 314 outputStream.write(data, 0, DIGEST_SIZE_BYTES); 315 outputStream.writeInt(PARTITION_TYPE_MARKER); 316 outputStream.writeInt(0); // data size 317 outputStream.flush(); 318 } catch (IOException e) { 319 Slog.e(TAG, "failed to format block", e); 320 return; 321 } finally { 322 IoUtils.closeQuietly(outputStream); 323 } 324 325 doSetOemUnlockEnabledLocked(setOemUnlockEnabled); 326 computeAndWriteDigestLocked(); 327 } 328 329 private void doSetOemUnlockEnabledLocked(boolean enabled) { 330 FileOutputStream outputStream; 331 try { 332 outputStream = new FileOutputStream(new File(mDataBlockFile)); 333 } catch (FileNotFoundException e) { 334 Slog.e(TAG, "partition not available", e); 335 return; 336 } 337 338 try { 339 FileChannel channel = outputStream.getChannel(); 340 341 channel.position(getBlockDeviceSize() - 1); 342 343 ByteBuffer data = ByteBuffer.allocate(1); 344 data.put(enabled ? (byte) 1 : (byte) 0); 345 data.flip(); 346 channel.write(data); 347 outputStream.flush(); 348 } catch (IOException e) { 349 Slog.e(TAG, "unable to access persistent partition", e); 350 return; 351 } finally { 352 SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0"); 353 IoUtils.closeQuietly(outputStream); 354 } 355 } 356 357 private boolean doGetOemUnlockEnabled() { 358 DataInputStream inputStream; 359 try { 360 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 361 } catch (FileNotFoundException e) { 362 Slog.e(TAG, "partition not available"); 363 return false; 364 } 365 366 try { 367 synchronized (mLock) { 368 inputStream.skip(getBlockDeviceSize() - 1); 369 return inputStream.readByte() != 0; 370 } 371 } catch (IOException e) { 372 Slog.e(TAG, "unable to access persistent partition", e); 373 return false; 374 } finally { 375 IoUtils.closeQuietly(inputStream); 376 } 377 } 378 379 private native long nativeGetBlockDeviceSize(String path); 380 private native int nativeWipe(String path); 381 382 private final IBinder mService = new IPersistentDataBlockService.Stub() { 383 @Override 384 public int write(byte[] data) throws RemoteException { 385 enforceUid(Binder.getCallingUid()); 386 387 // Need to ensure we don't write over the last byte 388 long maxBlockSize = getBlockDeviceSize() - HEADER_SIZE - 1; 389 if (data.length > maxBlockSize) { 390 // partition is ~500k so shouldn't be a problem to downcast 391 return (int) -maxBlockSize; 392 } 393 394 DataOutputStream outputStream; 395 try { 396 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile))); 397 } catch (FileNotFoundException e) { 398 Slog.e(TAG, "partition not available?", e); 399 return -1; 400 } 401 402 ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE); 403 headerAndData.putInt(PARTITION_TYPE_MARKER); 404 headerAndData.putInt(data.length); 405 headerAndData.put(data); 406 407 synchronized (mLock) { 408 if (!mIsWritable) { 409 IoUtils.closeQuietly(outputStream); 410 return -1; 411 } 412 413 try { 414 byte[] checksum = new byte[DIGEST_SIZE_BYTES]; 415 outputStream.write(checksum, 0, DIGEST_SIZE_BYTES); 416 outputStream.write(headerAndData.array()); 417 outputStream.flush(); 418 } catch (IOException e) { 419 Slog.e(TAG, "failed writing to the persistent data block", e); 420 return -1; 421 } finally { 422 IoUtils.closeQuietly(outputStream); 423 } 424 425 if (computeAndWriteDigestLocked()) { 426 return data.length; 427 } else { 428 return -1; 429 } 430 } 431 } 432 433 @Override 434 public byte[] read() { 435 enforceUid(Binder.getCallingUid()); 436 if (!enforceChecksumValidity()) { 437 return new byte[0]; 438 } 439 440 DataInputStream inputStream; 441 try { 442 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 443 } catch (FileNotFoundException e) { 444 Slog.e(TAG, "partition not available?", e); 445 return null; 446 } 447 448 try { 449 synchronized (mLock) { 450 int totalDataSize = getTotalDataSizeLocked(inputStream); 451 452 if (totalDataSize == 0) { 453 return new byte[0]; 454 } 455 456 byte[] data = new byte[totalDataSize]; 457 int read = inputStream.read(data, 0, totalDataSize); 458 if (read < totalDataSize) { 459 // something went wrong, not returning potentially corrupt data 460 Slog.e(TAG, "failed to read entire data block. bytes read: " + 461 read + "/" + totalDataSize); 462 return null; 463 } 464 return data; 465 } 466 } catch (IOException e) { 467 Slog.e(TAG, "failed to read data", e); 468 return null; 469 } finally { 470 try { 471 inputStream.close(); 472 } catch (IOException e) { 473 Slog.e(TAG, "failed to close OutputStream"); 474 } 475 } 476 } 477 478 @Override 479 public void wipe() { 480 enforceOemUnlockWritePermission(); 481 482 synchronized (mLock) { 483 int ret = nativeWipe(mDataBlockFile); 484 485 if (ret < 0) { 486 Slog.e(TAG, "failed to wipe persistent partition"); 487 } else { 488 mIsWritable = false; 489 Slog.i(TAG, "persistent partition now wiped and unwritable"); 490 } 491 } 492 } 493 494 @Override 495 public void setOemUnlockEnabled(boolean enabled) throws SecurityException { 496 // do not allow monkey to flip the flag 497 if (ActivityManager.isUserAMonkey()) { 498 return; 499 } 500 501 enforceOemUnlockWritePermission(); 502 enforceIsAdmin(); 503 504 if (enabled) { 505 // Do not allow oem unlock to be enabled if it's disallowed by a user restriction. 506 enforceUserRestriction(UserManager.DISALLOW_OEM_UNLOCK); 507 enforceUserRestriction(UserManager.DISALLOW_FACTORY_RESET); 508 } 509 synchronized (mLock) { 510 doSetOemUnlockEnabledLocked(enabled); 511 computeAndWriteDigestLocked(); 512 } 513 } 514 515 @Override 516 public boolean getOemUnlockEnabled() { 517 enforceOemUnlockReadPermission(); 518 return doGetOemUnlockEnabled(); 519 } 520 521 @Override 522 public int getFlashLockState() { 523 enforceOemUnlockReadPermission(); 524 String locked = SystemProperties.get(FLASH_LOCK_PROP); 525 switch (locked) { 526 case FLASH_LOCK_LOCKED: 527 return PersistentDataBlockManager.FLASH_LOCK_LOCKED; 528 case FLASH_LOCK_UNLOCKED: 529 return PersistentDataBlockManager.FLASH_LOCK_UNLOCKED; 530 default: 531 return PersistentDataBlockManager.FLASH_LOCK_UNKNOWN; 532 } 533 } 534 535 @Override 536 public int getDataBlockSize() { 537 enforcePersistentDataBlockAccess(); 538 539 DataInputStream inputStream; 540 try { 541 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 542 } catch (FileNotFoundException e) { 543 Slog.e(TAG, "partition not available"); 544 return 0; 545 } 546 547 try { 548 synchronized (mLock) { 549 return getTotalDataSizeLocked(inputStream); 550 } 551 } catch (IOException e) { 552 Slog.e(TAG, "error reading data block size"); 553 return 0; 554 } finally { 555 IoUtils.closeQuietly(inputStream); 556 } 557 } 558 559 private void enforcePersistentDataBlockAccess() { 560 if (mContext.checkCallingPermission(Manifest.permission.ACCESS_PDB_STATE) 561 != PackageManager.PERMISSION_GRANTED) { 562 enforceUid(Binder.getCallingUid()); 563 } 564 } 565 566 @Override 567 public long getMaximumDataBlockSize() { 568 long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1; 569 return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE; 570 } 571 }; 572} 573