summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android/server/PersistentDataBlockService.java
diff options
context:
space:
mode:
authorAndres Morales <anmorales@google.com>2014-07-10 15:40:24 -0700
committerAndres Morales <anmorales@google.com>2014-07-17 20:42:04 +0000
commit963295ea105314e28e4ca9563aa09cb7440de4c3 (patch)
tree1428bba4ecdab8cb7c0f7e143b8a8bf096402197 /services/core/java/com/android/server/PersistentDataBlockService.java
parentdcb743e276926e31e219000be5f6c8ad801b5367 (diff)
downloadframeworks_base-963295ea105314e28e4ca9563aa09cb7440de4c3.zip
frameworks_base-963295ea105314e28e4ca9563aa09cb7440de4c3.tar.gz
frameworks_base-963295ea105314e28e4ca9563aa09cb7440de4c3.tar.bz2
Permit settings to "wipe" the persistent partition
One of the requirements is that when the user does a factory reset through settings, all data on the persistent partition should be cleared. This adds one last API method that allows settings to wipe the partition. Bug: 14288780 Change-Id: Ib87ee741d1e5195814516ae1d66eb7c4cf754dcf
Diffstat (limited to 'services/core/java/com/android/server/PersistentDataBlockService.java')
-rw-r--r--services/core/java/com/android/server/PersistentDataBlockService.java175
1 files changed, 118 insertions, 57 deletions
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 1eded6f..f71a18a 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.server;
import android.Manifest;
@@ -8,8 +24,9 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.service.persistentdata.IPersistentDataBlockService;
-import android.util.Log;
+import android.util.Slog;
import com.android.internal.R;
+import libcore.io.IoUtils;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -23,7 +40,8 @@ import java.nio.channels.FileChannel;
/**
* Service for reading and writing blocks to a persistent partition.
- * This data will live across factory resets.
+ * 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)
@@ -40,19 +58,29 @@ public class PersistentDataBlockService extends SystemService {
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
private static final int HEADER_SIZE = 8;
- private static final int BLOCK_ID = 0x1990;
+ // Magic number to mark block device as adhering to the format consumed by this service
+ private static final int PARTITION_TYPE_MARKER = 0x1990;
+ // Limit to 100k as blocks larger than this might cause strain on Binder.
+ // TODO(anmorales): Consider splitting up too-large blocks in PersistentDataBlockManager
+ private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
private final Context mContext;
private final String mDataBlockFile;
- private long mBlockDeviceSize;
-
private final int mAllowedUid;
+ private final Object mLock = new Object();
+ /*
+ * Separate lock for OEM unlock related operations as they can happen in parallel with regular
+ * block operations.
+ */
+ private final Object mOemLock = new Object();
+
+ private long mBlockDeviceSize;
public PersistentDataBlockService(Context context) {
super(context);
mContext = context;
mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
- mBlockDeviceSize = 0; // Load lazily
+ mBlockDeviceSize = -1; // Load lazily
String allowedPackage = context.getResources()
.getString(R.string.config_persistentDataPackageName);
PackageManager pm = mContext.getPackageManager();
@@ -62,7 +90,7 @@ public class PersistentDataBlockService extends SystemService {
Binder.getCallingUserHandle().getIdentifier());
} catch (PackageManager.NameNotFoundException e) {
// not expected
- Log.e(TAG, "not able to find package " + allowedPackage, e);
+ Slog.e(TAG, "not able to find package " + allowedPackage, e);
}
mAllowedUid = allowedUid;
@@ -85,10 +113,10 @@ public class PersistentDataBlockService extends SystemService {
}
}
- private int getTotalDataSize(DataInputStream inputStream) throws IOException {
+ private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
int totalDataSize;
int blockId = inputStream.readInt();
- if (blockId == BLOCK_ID) {
+ if (blockId == PARTITION_TYPE_MARKER) {
totalDataSize = inputStream.readInt();
} else {
totalDataSize = 0;
@@ -96,17 +124,18 @@ public class PersistentDataBlockService extends SystemService {
return totalDataSize;
}
- private long maybeReadBlockDeviceSize() {
- synchronized (this) {
- if (mBlockDeviceSize == 0) {
- mBlockDeviceSize = getBlockDeviceSize(mDataBlockFile);
+ private long getBlockDeviceSize() {
+ synchronized (mLock) {
+ if (mBlockDeviceSize == -1) {
+ mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
}
}
return mBlockDeviceSize;
}
- private native long getBlockDeviceSize(String path);
+ private native long nativeGetBlockDeviceSize(String path);
+ private native int nativeWipe(String path);
private final IBinder mService = new IPersistentDataBlockService.Stub() {
@Override
@@ -114,62 +143,93 @@ public class PersistentDataBlockService extends SystemService {
enforceUid(Binder.getCallingUid());
// Need to ensure we don't write over the last byte
- if (data.length > maybeReadBlockDeviceSize() - HEADER_SIZE - 1) {
- return -1;
+ long maxBlockSize = getBlockDeviceSize() - HEADER_SIZE - 1;
+ if (data.length > maxBlockSize) {
+ // partition is ~500k so shouldn't be a problem to downcast
+ return (int) -maxBlockSize;
}
DataOutputStream outputStream;
try {
outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
} catch (FileNotFoundException e) {
- Log.e(TAG, "partition not available?", e);
+ Slog.e(TAG, "partition not available?", e);
return -1;
}
ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
- headerAndData.putInt(BLOCK_ID);
+ headerAndData.putInt(PARTITION_TYPE_MARKER);
headerAndData.putInt(data.length);
headerAndData.put(data);
try {
- outputStream.write(headerAndData.array());
- return data.length;
+ synchronized (mLock) {
+ outputStream.write(headerAndData.array());
+ return data.length;
+ }
} catch (IOException e) {
- Log.e(TAG, "failed writing to the persistent data block", e);
+ Slog.e(TAG, "failed writing to the persistent data block", e);
return -1;
} finally {
try {
outputStream.close();
} catch (IOException e) {
- Log.e(TAG, "failed closing output stream", e);
+ Slog.e(TAG, "failed closing output stream", e);
}
}
}
@Override
- public int read(byte[] data) {
+ public byte[] read() {
enforceUid(Binder.getCallingUid());
DataInputStream inputStream;
try {
inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
} catch (FileNotFoundException e) {
- Log.e(TAG, "partition not available?", e);
- return -1;
+ Slog.e(TAG, "partition not available?", e);
+ return null;
}
try {
- int totalDataSize = getTotalDataSize(inputStream);
- return inputStream.read(data, 0,
- (data.length > totalDataSize) ? totalDataSize : data.length);
+ synchronized (mLock) {
+ int totalDataSize = getTotalDataSizeLocked(inputStream);
+
+ if (totalDataSize == 0) {
+ return new byte[0];
+ }
+
+ byte[] data = new byte[totalDataSize];
+ int read = inputStream.read(data, 0, totalDataSize);
+ if (read < totalDataSize) {
+ // something went wrong, not returning potentially corrupt data
+ Slog.e(TAG, "failed to read entire data block. bytes read: " +
+ read + "/" + totalDataSize);
+ return null;
+ }
+ return data;
+ }
} catch (IOException e) {
- Log.e(TAG, "failed to read data", e);
- return -1;
+ Slog.e(TAG, "failed to read data", e);
+ return null;
} finally {
try {
inputStream.close();
} catch (IOException e) {
- Log.e(TAG, "failed to close OutputStream");
+ Slog.e(TAG, "failed to close OutputStream");
+ }
+ }
+ }
+
+ @Override
+ public void wipe() {
+ enforceOemUnlockPermission();
+
+ synchronized (mLock) {
+ int ret = nativeWipe(mDataBlockFile);
+
+ if (ret < 0) {
+ Slog.e(TAG, "failed to wipe persistent partition");
}
}
}
@@ -181,28 +241,26 @@ public class PersistentDataBlockService extends SystemService {
try {
outputStream = new FileOutputStream(new File(mDataBlockFile));
} catch (FileNotFoundException e) {
- Log.e(TAG, "parition not available", e);
+ Slog.e(TAG, "parition not available", e);
return;
}
try {
FileChannel channel = outputStream.getChannel();
- channel.position(maybeReadBlockDeviceSize() - 1);
+ channel.position(getBlockDeviceSize() - 1);
ByteBuffer data = ByteBuffer.allocate(1);
data.put(enabled ? (byte) 1 : (byte) 0);
data.flip();
- channel.write(data);
+ synchronized (mOemLock) {
+ channel.write(data);
+ }
} catch (IOException e) {
- Log.e(TAG, "unable to access persistent partition", e);
+ Slog.e(TAG, "unable to access persistent partition", e);
} finally {
- try {
- outputStream.close();
- } catch (IOException e) {
- Log.e(TAG, "failed to close OutputStream");
- }
+ IoUtils.closeQuietly(outputStream);
}
}
@@ -213,22 +271,20 @@ public class PersistentDataBlockService extends SystemService {
try {
inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
} catch (FileNotFoundException e) {
- Log.e(TAG, "partition not available");
+ Slog.e(TAG, "partition not available");
return false;
}
try {
- inputStream.skip(maybeReadBlockDeviceSize() - 1);
- return inputStream.readByte() != 0;
+ inputStream.skip(getBlockDeviceSize() - 1);
+ synchronized (mOemLock) {
+ return inputStream.readByte() != 0;
+ }
} catch (IOException e) {
- Log.e(TAG, "unable to access persistent partition", e);
+ Slog.e(TAG, "unable to access persistent partition", e);
return false;
} finally {
- try {
- inputStream.close();
- } catch (IOException e) {
- Log.e(TAG, "failed to close OutputStream");
- }
+ IoUtils.closeQuietly(inputStream);
}
}
@@ -240,22 +296,27 @@ public class PersistentDataBlockService extends SystemService {
try {
inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
} catch (FileNotFoundException e) {
- Log.e(TAG, "partition not available");
+ Slog.e(TAG, "partition not available");
return 0;
}
try {
- return getTotalDataSize(inputStream);
+ synchronized (mLock) {
+ return getTotalDataSizeLocked(inputStream);
+ }
} catch (IOException e) {
- Log.e(TAG, "error reading data block size");
+ Slog.e(TAG, "error reading data block size");
return 0;
} finally {
- try {
- inputStream.close();
- } catch (IOException e) {
- Log.e(TAG, "failed to close OutputStream");
- }
+ IoUtils.closeQuietly(inputStream);
}
}
+
+ @Override
+ public long getMaximumDataBlockSize() {
+ long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1;
+ return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
+ }
+
};
}