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