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