FileDocCategorySizeDatePackage
PersistentDataBlockService.javaAPI DocAndroid 5.1 API16643Thu Mar 12 22:22:42 GMT 2015com.android.server

PersistentDataBlockService

public class PersistentDataBlockService extends SystemService
Service for reading and writing blocks to a persistent partition. This data will live across factory resets not initiated via the Settings UI. When a device is factory reset through Settings this data is wiped. Allows writing one block at a time. Namely, each time {@link android.service.persistentdata.IPersistentDataBlockService}.write(byte[] data) is called, it will overwite the data that was previously written on the block. Clients can query the size of the currently written block via {@link android.service.persistentdata.IPersistentDataBlockService}.getTotalDataSize(). Clients can any number of bytes from the currently written block up to its total size by invoking {@link android.service.persistentdata.IPersistentDataBlockService}.read(byte[] data)

Fields Summary
private static final String
TAG
private static final String
PERSISTENT_DATA_BLOCK_PROP
private static final int
HEADER_SIZE
private static final int
PARTITION_TYPE_MARKER
private static final int
MAX_DATA_BLOCK_SIZE
public static final int
DIGEST_SIZE_BYTES
private final android.content.Context
mContext
private final String
mDataBlockFile
private final Object
mLock
private int
mAllowedUid
private long
mBlockDeviceSize
private final android.os.IBinder
mService
Constructors Summary
public PersistentDataBlockService(android.content.Context context)


       
        super(context);
        mContext = context;
        mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
        mBlockDeviceSize = -1; // Load lazily
        mAllowedUid = getAllowedUid(UserHandle.USER_OWNER);
    
Methods Summary
private booleancomputeAndWriteDigestLocked()

        byte[] digest = computeDigestLocked(null);
        if (digest != null) {
            DataOutputStream outputStream;
            try {
                outputStream = new DataOutputStream(
                        new FileOutputStream(new File(mDataBlockFile)));
            } catch (FileNotFoundException e) {
                Slog.e(TAG, "partition not available?", e);
                return false;
            }

            try {
                outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
                outputStream.flush();
            } catch (IOException e) {
                Slog.e(TAG, "failed to write block checksum", e);
                return false;
            } finally {
                IoUtils.closeQuietly(outputStream);
            }
            return true;
        } else {
            return false;
        }
    
private byte[]computeDigestLocked(byte[] storedDigest)

        DataInputStream inputStream;
        try {
            inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
        } catch (FileNotFoundException e) {
            Slog.e(TAG, "partition not available?", e);
            return null;
        }

        MessageDigest md;
        try {
            md = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
            // won't ever happen -- every implementation is required to support SHA-256
            Slog.e(TAG, "SHA-256 not supported?", e);
            IoUtils.closeQuietly(inputStream);
            return null;
        }

        try {
            if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) {
                inputStream.read(storedDigest);
            } else {
                inputStream.skipBytes(DIGEST_SIZE_BYTES);
            }

            int read;
            byte[] data = new byte[1024];
            md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest
            while ((read = inputStream.read(data)) != -1) {
                md.update(data, 0, read);
            }
        } catch (IOException e) {
            Slog.e(TAG, "failed to read partition", e);
            return null;
        } finally {
            IoUtils.closeQuietly(inputStream);
        }

        return md.digest();
    
private booleandoGetOemUnlockEnabled()

        DataInputStream inputStream;
        try {
            inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
        } catch (FileNotFoundException e) {
            Slog.e(TAG, "partition not available");
            return false;
        }

        try {
            synchronized (mLock) {
                inputStream.skip(getBlockDeviceSize() - 1);
                return inputStream.readByte() != 0;
            }
        } catch (IOException e) {
            Slog.e(TAG, "unable to access persistent partition", e);
            return false;
        } finally {
            IoUtils.closeQuietly(inputStream);
        }
    
private voiddoSetOemUnlockEnabledLocked(boolean enabled)

        FileOutputStream outputStream;
        try {
            outputStream = new FileOutputStream(new File(mDataBlockFile));
        } catch (FileNotFoundException e) {
            Slog.e(TAG, "partition not available", e);
            return;
        }

        try {
            FileChannel channel = outputStream.getChannel();

            channel.position(getBlockDeviceSize() - 1);

            ByteBuffer data = ByteBuffer.allocate(1);
            data.put(enabled ? (byte) 1 : (byte) 0);
            data.flip();
            channel.write(data);
            outputStream.flush();
        } catch (IOException e) {
            Slog.e(TAG, "unable to access persistent partition", e);
            return;
        } finally {
            IoUtils.closeQuietly(outputStream);
        }
    
private booleanenforceChecksumValidity()

        byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];

        synchronized (mLock) {
            byte[] digest = computeDigestLocked(storedDigest);
            if (digest == null || !Arrays.equals(storedDigest, digest)) {
                Slog.i(TAG, "Formatting FRP partition...");
                formatPartitionLocked();
                return false;
            }
        }

        return true;
    
private voidenforceIsOwner()

        if (!Binder.getCallingUserHandle().isOwner()) {
            throw new SecurityException("Only the Owner is allowed to change OEM unlock state");
        }
    
private voidenforceOemUnlockPermission()

        mContext.enforceCallingOrSelfPermission(
                Manifest.permission.OEM_UNLOCK_STATE,
                "Can't access OEM unlock state");
    
private voidenforceUid(int callingUid)

        if (callingUid != mAllowedUid) {
            throw new SecurityException("uid " + callingUid + " not allowed to access PST");
        }
    
private voidformatIfOemUnlockEnabled()

        if (doGetOemUnlockEnabled()) {
            synchronized (mLock) {
                formatPartitionLocked();
                doSetOemUnlockEnabledLocked(true);
            }
        }
    
private voidformatPartitionLocked()

        DataOutputStream outputStream;
        try {
            outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
        } catch (FileNotFoundException e) {
            Slog.e(TAG, "partition not available?", e);
            return;
        }

        byte[] data = new byte[DIGEST_SIZE_BYTES];
        try {
            outputStream.write(data, 0, DIGEST_SIZE_BYTES);
            outputStream.writeInt(PARTITION_TYPE_MARKER);
            outputStream.writeInt(0); // data size
            outputStream.flush();
        } catch (IOException e) {
            Slog.e(TAG, "failed to format block", e);
            return;
        } finally {
            IoUtils.closeQuietly(outputStream);
        }

        doSetOemUnlockEnabledLocked(false);
        computeAndWriteDigestLocked();
    
private intgetAllowedUid(int userHandle)

        String allowedPackage = mContext.getResources()
                .getString(R.string.config_persistentDataPackageName);
        PackageManager pm = mContext.getPackageManager();
        int allowedUid = -1;
        try {
            allowedUid = pm.getPackageUid(allowedPackage, userHandle);
        } catch (PackageManager.NameNotFoundException e) {
            // not expected
            Slog.e(TAG, "not able to find package " + allowedPackage, e);
        }
        return allowedUid;
    
private longgetBlockDeviceSize()

        synchronized (mLock) {
            if (mBlockDeviceSize == -1) {
                mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
            }
        }

        return mBlockDeviceSize;
    
private intgetTotalDataSizeLocked(java.io.DataInputStream inputStream)

        // skip over checksum
        inputStream.skipBytes(DIGEST_SIZE_BYTES);

        int totalDataSize;
        int blockId = inputStream.readInt();
        if (blockId == PARTITION_TYPE_MARKER) {
            totalDataSize = inputStream.readInt();
        } else {
            totalDataSize = 0;
        }
        return totalDataSize;
    
private native longnativeGetBlockDeviceSize(java.lang.String path)

private native intnativeWipe(java.lang.String path)

public voidonStart()

        enforceChecksumValidity();
        formatIfOemUnlockEnabled();
        publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);