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