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