PersistentDataBlockService.java revision bfe1b04f8ba9cd5486e26ca4cc30242b3b2f2c7f
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 enforceOemUnlockPermission() { 130 mContext.enforceCallingOrSelfPermission( 131 Manifest.permission.OEM_UNLOCK_STATE, 132 "Can't access OEM unlock state"); 133 } 134 135 private void enforceUid(int callingUid) { 136 if (callingUid != mAllowedUid) { 137 throw new SecurityException("uid " + callingUid + " not allowed to access PST"); 138 } 139 } 140 141 private void enforceIsAdmin() { 142 final int userId = UserHandle.getCallingUserId(); 143 final boolean isAdmin = UserManager.get(mContext).isUserAdmin(userId); 144 if (!isAdmin) { 145 throw new SecurityException( 146 "Only the Admin user is allowed to change OEM unlock state"); 147 } 148 } 149 private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException { 150 // skip over checksum 151 inputStream.skipBytes(DIGEST_SIZE_BYTES); 152 153 int totalDataSize; 154 int blockId = inputStream.readInt(); 155 if (blockId == PARTITION_TYPE_MARKER) { 156 totalDataSize = inputStream.readInt(); 157 } else { 158 totalDataSize = 0; 159 } 160 return totalDataSize; 161 } 162 163 private long getBlockDeviceSize() { 164 synchronized (mLock) { 165 if (mBlockDeviceSize == -1) { 166 mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile); 167 } 168 } 169 170 return mBlockDeviceSize; 171 } 172 173 private boolean enforceChecksumValidity() { 174 byte[] storedDigest = new byte[DIGEST_SIZE_BYTES]; 175 176 synchronized (mLock) { 177 byte[] digest = computeDigestLocked(storedDigest); 178 if (digest == null || !Arrays.equals(storedDigest, digest)) { 179 Slog.i(TAG, "Formatting FRP partition..."); 180 formatPartitionLocked(false); 181 return false; 182 } 183 } 184 185 return true; 186 } 187 188 private boolean computeAndWriteDigestLocked() { 189 byte[] digest = computeDigestLocked(null); 190 if (digest != null) { 191 DataOutputStream outputStream; 192 try { 193 outputStream = new DataOutputStream( 194 new FileOutputStream(new File(mDataBlockFile))); 195 } catch (FileNotFoundException e) { 196 Slog.e(TAG, "partition not available?", e); 197 return false; 198 } 199 200 try { 201 outputStream.write(digest, 0, DIGEST_SIZE_BYTES); 202 outputStream.flush(); 203 } catch (IOException e) { 204 Slog.e(TAG, "failed to write block checksum", e); 205 return false; 206 } finally { 207 IoUtils.closeQuietly(outputStream); 208 } 209 return true; 210 } else { 211 return false; 212 } 213 } 214 215 private byte[] computeDigestLocked(byte[] storedDigest) { 216 DataInputStream inputStream; 217 try { 218 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 219 } catch (FileNotFoundException e) { 220 Slog.e(TAG, "partition not available?", e); 221 return null; 222 } 223 224 MessageDigest md; 225 try { 226 md = MessageDigest.getInstance("SHA-256"); 227 } catch (NoSuchAlgorithmException e) { 228 // won't ever happen -- every implementation is required to support SHA-256 229 Slog.e(TAG, "SHA-256 not supported?", e); 230 IoUtils.closeQuietly(inputStream); 231 return null; 232 } 233 234 try { 235 if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) { 236 inputStream.read(storedDigest); 237 } else { 238 inputStream.skipBytes(DIGEST_SIZE_BYTES); 239 } 240 241 int read; 242 byte[] data = new byte[1024]; 243 md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest 244 while ((read = inputStream.read(data)) != -1) { 245 md.update(data, 0, read); 246 } 247 } catch (IOException e) { 248 Slog.e(TAG, "failed to read partition", e); 249 return null; 250 } finally { 251 IoUtils.closeQuietly(inputStream); 252 } 253 254 return md.digest(); 255 } 256 257 private void formatPartitionLocked(boolean setOemUnlockEnabled) { 258 DataOutputStream outputStream; 259 try { 260 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile))); 261 } catch (FileNotFoundException e) { 262 Slog.e(TAG, "partition not available?", e); 263 return; 264 } 265 266 byte[] data = new byte[DIGEST_SIZE_BYTES]; 267 try { 268 outputStream.write(data, 0, DIGEST_SIZE_BYTES); 269 outputStream.writeInt(PARTITION_TYPE_MARKER); 270 outputStream.writeInt(0); // data size 271 outputStream.flush(); 272 } catch (IOException e) { 273 Slog.e(TAG, "failed to format block", e); 274 return; 275 } finally { 276 IoUtils.closeQuietly(outputStream); 277 } 278 279 doSetOemUnlockEnabledLocked(setOemUnlockEnabled); 280 computeAndWriteDigestLocked(); 281 } 282 283 private void doSetOemUnlockEnabledLocked(boolean enabled) { 284 FileOutputStream outputStream; 285 try { 286 outputStream = new FileOutputStream(new File(mDataBlockFile)); 287 } catch (FileNotFoundException e) { 288 Slog.e(TAG, "partition not available", e); 289 return; 290 } 291 292 try { 293 FileChannel channel = outputStream.getChannel(); 294 295 channel.position(getBlockDeviceSize() - 1); 296 297 ByteBuffer data = ByteBuffer.allocate(1); 298 data.put(enabled ? (byte) 1 : (byte) 0); 299 data.flip(); 300 channel.write(data); 301 outputStream.flush(); 302 } catch (IOException e) { 303 Slog.e(TAG, "unable to access persistent partition", e); 304 return; 305 } finally { 306 SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0"); 307 IoUtils.closeQuietly(outputStream); 308 } 309 } 310 311 private boolean doGetOemUnlockEnabled() { 312 DataInputStream inputStream; 313 try { 314 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 315 } catch (FileNotFoundException e) { 316 Slog.e(TAG, "partition not available"); 317 return false; 318 } 319 320 try { 321 synchronized (mLock) { 322 inputStream.skip(getBlockDeviceSize() - 1); 323 return inputStream.readByte() != 0; 324 } 325 } catch (IOException e) { 326 Slog.e(TAG, "unable to access persistent partition", e); 327 return false; 328 } finally { 329 IoUtils.closeQuietly(inputStream); 330 } 331 } 332 333 private native long nativeGetBlockDeviceSize(String path); 334 private native int nativeWipe(String path); 335 336 private final IBinder mService = new IPersistentDataBlockService.Stub() { 337 @Override 338 public int write(byte[] data) throws RemoteException { 339 enforceUid(Binder.getCallingUid()); 340 341 // Need to ensure we don't write over the last byte 342 long maxBlockSize = getBlockDeviceSize() - HEADER_SIZE - 1; 343 if (data.length > maxBlockSize) { 344 // partition is ~500k so shouldn't be a problem to downcast 345 return (int) -maxBlockSize; 346 } 347 348 DataOutputStream outputStream; 349 try { 350 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile))); 351 } catch (FileNotFoundException e) { 352 Slog.e(TAG, "partition not available?", e); 353 return -1; 354 } 355 356 ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE); 357 headerAndData.putInt(PARTITION_TYPE_MARKER); 358 headerAndData.putInt(data.length); 359 headerAndData.put(data); 360 361 synchronized (mLock) { 362 try { 363 byte[] checksum = new byte[DIGEST_SIZE_BYTES]; 364 outputStream.write(checksum, 0, DIGEST_SIZE_BYTES); 365 outputStream.write(headerAndData.array()); 366 outputStream.flush(); 367 } catch (IOException e) { 368 Slog.e(TAG, "failed writing to the persistent data block", e); 369 return -1; 370 } finally { 371 IoUtils.closeQuietly(outputStream); 372 } 373 374 if (computeAndWriteDigestLocked()) { 375 return data.length; 376 } else { 377 return -1; 378 } 379 } 380 } 381 382 @Override 383 public byte[] read() { 384 enforceUid(Binder.getCallingUid()); 385 if (!enforceChecksumValidity()) { 386 return new byte[0]; 387 } 388 389 DataInputStream inputStream; 390 try { 391 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 392 } catch (FileNotFoundException e) { 393 Slog.e(TAG, "partition not available?", e); 394 return null; 395 } 396 397 try { 398 synchronized (mLock) { 399 int totalDataSize = getTotalDataSizeLocked(inputStream); 400 401 if (totalDataSize == 0) { 402 return new byte[0]; 403 } 404 405 byte[] data = new byte[totalDataSize]; 406 int read = inputStream.read(data, 0, totalDataSize); 407 if (read < totalDataSize) { 408 // something went wrong, not returning potentially corrupt data 409 Slog.e(TAG, "failed to read entire data block. bytes read: " + 410 read + "/" + totalDataSize); 411 return null; 412 } 413 return data; 414 } 415 } catch (IOException e) { 416 Slog.e(TAG, "failed to read data", e); 417 return null; 418 } finally { 419 try { 420 inputStream.close(); 421 } catch (IOException e) { 422 Slog.e(TAG, "failed to close OutputStream"); 423 } 424 } 425 } 426 427 @Override 428 public void wipe() { 429 enforceOemUnlockPermission(); 430 431 synchronized (mLock) { 432 int ret = nativeWipe(mDataBlockFile); 433 434 if (ret < 0) { 435 Slog.e(TAG, "failed to wipe persistent partition"); 436 } 437 } 438 } 439 440 @Override 441 public void setOemUnlockEnabled(boolean enabled) throws SecurityException { 442 // do not allow monkey to flip the flag 443 if (ActivityManager.isUserAMonkey()) { 444 return; 445 } 446 447 enforceOemUnlockPermission(); 448 enforceIsAdmin(); 449 450 // Do not allow oem unlock modification if it has been disallowed. 451 if (Settings.Global.getInt(getContext().getContentResolver(), 452 Settings.Global.OEM_UNLOCK_DISALLOWED, 0) == 1) { 453 throw new SecurityException("OEM unlock has been disallowed."); 454 } 455 456 synchronized (mLock) { 457 doSetOemUnlockEnabledLocked(enabled); 458 computeAndWriteDigestLocked(); 459 } 460 } 461 462 @Override 463 public boolean getOemUnlockEnabled() { 464 enforceOemUnlockPermission(); 465 return doGetOemUnlockEnabled(); 466 } 467 468 @Override 469 public int getFlashLockState() { 470 enforceOemUnlockPermission(); 471 String locked = SystemProperties.get(FLASH_LOCK_PROP); 472 switch (locked) { 473 case FLASH_LOCK_LOCKED: 474 return PersistentDataBlockManager.FLASH_LOCK_LOCKED; 475 case FLASH_LOCK_UNLOCKED: 476 return PersistentDataBlockManager.FLASH_LOCK_UNLOCKED; 477 default: 478 return PersistentDataBlockManager.FLASH_LOCK_UNKNOWN; 479 } 480 } 481 482 @Override 483 public int getDataBlockSize() { 484 enforcePersistentDataBlockAccess(); 485 486 DataInputStream inputStream; 487 try { 488 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 489 } catch (FileNotFoundException e) { 490 Slog.e(TAG, "partition not available"); 491 return 0; 492 } 493 494 try { 495 synchronized (mLock) { 496 return getTotalDataSizeLocked(inputStream); 497 } 498 } catch (IOException e) { 499 Slog.e(TAG, "error reading data block size"); 500 return 0; 501 } finally { 502 IoUtils.closeQuietly(inputStream); 503 } 504 } 505 506 private void enforcePersistentDataBlockAccess() { 507 if (mContext.checkCallingPermission(Manifest.permission.ACCESS_PDB_STATE) 508 != PackageManager.PERMISSION_GRANTED) { 509 enforceUid(Binder.getCallingUid()); 510 } 511 } 512 513 @Override 514 public long getMaximumDataBlockSize() { 515 long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1; 516 return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE; 517 } 518 }; 519} 520