blob: 56f9942cb784d0dceb76f05f31451afa61d42e1f [file] [log] [blame]
Andres Morales963295e2014-07-10 15:40:24 -07001/*
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
Andres Morales68d4acd2014-07-01 19:40:41 -070017package com.android.server;
18
19import android.Manifest;
Guang Zhu514c5802014-09-12 15:14:00 -070020import android.app.ActivityManager;
Craig Lafayette66445a62015-03-27 09:01:43 -040021import android.app.PendingIntent;
Andres Morales68d4acd2014-07-01 19:40:41 -070022import android.content.Context;
Craig Lafayette66445a62015-03-27 09:01:43 -040023import android.content.Intent;
Andres Morales68d4acd2014-07-01 19:40:41 -070024import android.content.pm.PackageManager;
25import android.os.Binder;
Craig Lafayette66445a62015-03-27 09:01:43 -040026import android.os.Bundle;
Andres Morales68d4acd2014-07-01 19:40:41 -070027import android.os.IBinder;
28import android.os.RemoteException;
29import android.os.SystemProperties;
Andres Morales6429f312014-08-04 16:35:15 -070030import android.os.UserHandle;
Andres Morales68d4acd2014-07-01 19:40:41 -070031import android.service.persistentdata.IPersistentDataBlockService;
Craig Lafayette66445a62015-03-27 09:01:43 -040032import android.service.persistentdata.PersistentDataBlockManager;
Andres Morales963295e2014-07-10 15:40:24 -070033import android.util.Slog;
Guang Zhu514c5802014-09-12 15:14:00 -070034
Andres Morales68d4acd2014-07-01 19:40:41 -070035import com.android.internal.R;
Guang Zhu514c5802014-09-12 15:14:00 -070036
Andres Morales963295e2014-07-10 15:40:24 -070037import libcore.io.IoUtils;
Andres Morales68d4acd2014-07-01 19:40:41 -070038
39import java.io.DataInputStream;
40import java.io.DataOutputStream;
41import java.io.File;
42import java.io.FileInputStream;
43import java.io.FileNotFoundException;
44import java.io.FileOutputStream;
45import java.io.IOException;
46import java.nio.ByteBuffer;
47import java.nio.channels.FileChannel;
Andres Morales28301302014-11-12 07:56:46 -080048import java.security.MessageDigest;
49import java.security.NoSuchAlgorithmException;
50import java.util.Arrays;
Andres Morales68d4acd2014-07-01 19:40:41 -070051
52/**
53 * Service for reading and writing blocks to a persistent partition.
Andres Morales963295e2014-07-10 15:40:24 -070054 * This data will live across factory resets not initiated via the Settings UI.
55 * When a device is factory reset through Settings this data is wiped.
Andres Morales68d4acd2014-07-01 19:40:41 -070056 *
57 * Allows writing one block at a time. Namely, each time
58 * {@link android.service.persistentdata.IPersistentDataBlockService}.write(byte[] data)
59 * is called, it will overwite the data that was previously written on the block.
60 *
61 * Clients can query the size of the currently written block via
62 * {@link android.service.persistentdata.IPersistentDataBlockService}.getTotalDataSize().
63 *
64 * Clients can any number of bytes from the currently written block up to its total size by invoking
65 * {@link android.service.persistentdata.IPersistentDataBlockService}.read(byte[] data)
66 */
67public class PersistentDataBlockService extends SystemService {
68 private static final String TAG = PersistentDataBlockService.class.getSimpleName();
69
70 private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
71 private static final int HEADER_SIZE = 8;
Andres Morales963295e2014-07-10 15:40:24 -070072 // Magic number to mark block device as adhering to the format consumed by this service
Andres Morales28301302014-11-12 07:56:46 -080073 private static final int PARTITION_TYPE_MARKER = 0x19901873;
Andres Morales963295e2014-07-10 15:40:24 -070074 // Limit to 100k as blocks larger than this might cause strain on Binder.
Andres Morales963295e2014-07-10 15:40:24 -070075 private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
Andres Morales28301302014-11-12 07:56:46 -080076 public static final int DIGEST_SIZE_BYTES = 32;
Andres Morales5ca4cc52015-03-19 16:37:54 -070077 private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
Andres Morales68d4acd2014-07-01 19:40:41 -070078
79 private final Context mContext;
80 private final String mDataBlockFile;
Andres Morales963295e2014-07-10 15:40:24 -070081 private final Object mLock = new Object();
Andres Morales6429f312014-08-04 16:35:15 -070082
Andres Moralesa31c23d2014-10-30 15:31:31 -070083 private int mAllowedUid = -1;
Andres Morales963295e2014-07-10 15:40:24 -070084 private long mBlockDeviceSize;
Andres Morales68d4acd2014-07-01 19:40:41 -070085
86 public PersistentDataBlockService(Context context) {
87 super(context);
88 mContext = context;
89 mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
Andres Morales963295e2014-07-10 15:40:24 -070090 mBlockDeviceSize = -1; // Load lazily
Andres Moralesa31c23d2014-10-30 15:31:31 -070091 mAllowedUid = getAllowedUid(UserHandle.USER_OWNER);
Andres Morales6429f312014-08-04 16:35:15 -070092 }
93
Andres Moralesa31c23d2014-10-30 15:31:31 -070094 private int getAllowedUid(int userHandle) {
Andres Morales6429f312014-08-04 16:35:15 -070095 String allowedPackage = mContext.getResources()
Andres Morales68d4acd2014-07-01 19:40:41 -070096 .getString(R.string.config_persistentDataPackageName);
97 PackageManager pm = mContext.getPackageManager();
98 int allowedUid = -1;
99 try {
Andres Morales6429f312014-08-04 16:35:15 -0700100 allowedUid = pm.getPackageUid(allowedPackage, userHandle);
Andres Morales68d4acd2014-07-01 19:40:41 -0700101 } catch (PackageManager.NameNotFoundException e) {
102 // not expected
Andres Morales963295e2014-07-10 15:40:24 -0700103 Slog.e(TAG, "not able to find package " + allowedPackage, e);
Andres Morales68d4acd2014-07-01 19:40:41 -0700104 }
Andres Moralesa31c23d2014-10-30 15:31:31 -0700105 return allowedUid;
Andres Morales68d4acd2014-07-01 19:40:41 -0700106 }
107
108 @Override
109 public void onStart() {
Andres Morales28301302014-11-12 07:56:46 -0800110 enforceChecksumValidity();
Andres Morales1ce7d172015-01-07 14:24:57 -0800111 formatIfOemUnlockEnabled();
Andres Morales68d4acd2014-07-01 19:40:41 -0700112 publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
113 }
114
Andres Morales1ce7d172015-01-07 14:24:57 -0800115 private void formatIfOemUnlockEnabled() {
Andres Morales5ca4cc52015-03-19 16:37:54 -0700116 boolean enabled = doGetOemUnlockEnabled();
117 if (enabled) {
Andres Morales1ce7d172015-01-07 14:24:57 -0800118 synchronized (mLock) {
Andres Moralesc8f952c2015-03-19 08:34:55 -0700119 formatPartitionLocked(true);
Andres Morales1ce7d172015-01-07 14:24:57 -0800120 }
121 }
Andres Morales5ca4cc52015-03-19 16:37:54 -0700122
123 SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
Andres Morales1ce7d172015-01-07 14:24:57 -0800124 }
125
Andres Morales68d4acd2014-07-01 19:40:41 -0700126 private void enforceOemUnlockPermission() {
127 mContext.enforceCallingOrSelfPermission(
128 Manifest.permission.OEM_UNLOCK_STATE,
129 "Can't access OEM unlock state");
130 }
131
132 private void enforceUid(int callingUid) {
Andres Moralesa31c23d2014-10-30 15:31:31 -0700133 if (callingUid != mAllowedUid) {
Andres Morales68d4acd2014-07-01 19:40:41 -0700134 throw new SecurityException("uid " + callingUid + " not allowed to access PST");
135 }
136 }
137
Andres Moralesa31c23d2014-10-30 15:31:31 -0700138 private void enforceIsOwner() {
139 if (!Binder.getCallingUserHandle().isOwner()) {
140 throw new SecurityException("Only the Owner is allowed to change OEM unlock state");
141 }
142 }
Andres Morales963295e2014-07-10 15:40:24 -0700143 private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
Andres Morales28301302014-11-12 07:56:46 -0800144 // skip over checksum
145 inputStream.skipBytes(DIGEST_SIZE_BYTES);
146
Andres Morales68d4acd2014-07-01 19:40:41 -0700147 int totalDataSize;
148 int blockId = inputStream.readInt();
Andres Morales963295e2014-07-10 15:40:24 -0700149 if (blockId == PARTITION_TYPE_MARKER) {
Andres Morales68d4acd2014-07-01 19:40:41 -0700150 totalDataSize = inputStream.readInt();
151 } else {
152 totalDataSize = 0;
153 }
154 return totalDataSize;
155 }
156
Andres Morales963295e2014-07-10 15:40:24 -0700157 private long getBlockDeviceSize() {
158 synchronized (mLock) {
159 if (mBlockDeviceSize == -1) {
160 mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
Andres Morales68d4acd2014-07-01 19:40:41 -0700161 }
162 }
163
164 return mBlockDeviceSize;
165 }
166
Andres Morales28301302014-11-12 07:56:46 -0800167 private boolean enforceChecksumValidity() {
168 byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
169
170 synchronized (mLock) {
171 byte[] digest = computeDigestLocked(storedDigest);
172 if (digest == null || !Arrays.equals(storedDigest, digest)) {
173 Slog.i(TAG, "Formatting FRP partition...");
Andres Moralesc8f952c2015-03-19 08:34:55 -0700174 formatPartitionLocked(false);
Andres Morales28301302014-11-12 07:56:46 -0800175 return false;
176 }
177 }
178
179 return true;
180 }
181
182 private boolean computeAndWriteDigestLocked() {
183 byte[] digest = computeDigestLocked(null);
184 if (digest != null) {
185 DataOutputStream outputStream;
186 try {
187 outputStream = new DataOutputStream(
188 new FileOutputStream(new File(mDataBlockFile)));
189 } catch (FileNotFoundException e) {
190 Slog.e(TAG, "partition not available?", e);
191 return false;
192 }
193
194 try {
195 outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
196 outputStream.flush();
197 } catch (IOException e) {
198 Slog.e(TAG, "failed to write block checksum", e);
199 return false;
200 } finally {
201 IoUtils.closeQuietly(outputStream);
202 }
203 return true;
204 } else {
205 return false;
206 }
207 }
208
209 private byte[] computeDigestLocked(byte[] storedDigest) {
210 DataInputStream inputStream;
211 try {
212 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
213 } catch (FileNotFoundException e) {
214 Slog.e(TAG, "partition not available?", e);
215 return null;
216 }
217
218 MessageDigest md;
219 try {
220 md = MessageDigest.getInstance("SHA-256");
221 } catch (NoSuchAlgorithmException e) {
222 // won't ever happen -- every implementation is required to support SHA-256
223 Slog.e(TAG, "SHA-256 not supported?", e);
224 IoUtils.closeQuietly(inputStream);
225 return null;
226 }
227
228 try {
229 if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) {
230 inputStream.read(storedDigest);
231 } else {
232 inputStream.skipBytes(DIGEST_SIZE_BYTES);
233 }
234
235 int read;
236 byte[] data = new byte[1024];
237 md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest
238 while ((read = inputStream.read(data)) != -1) {
239 md.update(data, 0, read);
240 }
241 } catch (IOException e) {
242 Slog.e(TAG, "failed to read partition", e);
243 return null;
244 } finally {
245 IoUtils.closeQuietly(inputStream);
246 }
247
248 return md.digest();
249 }
250
Andres Moralesc8f952c2015-03-19 08:34:55 -0700251 private void formatPartitionLocked(boolean setOemUnlockEnabled) {
Andres Morales28301302014-11-12 07:56:46 -0800252 DataOutputStream outputStream;
253 try {
254 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
255 } catch (FileNotFoundException e) {
256 Slog.e(TAG, "partition not available?", e);
257 return;
258 }
259
260 byte[] data = new byte[DIGEST_SIZE_BYTES];
261 try {
262 outputStream.write(data, 0, DIGEST_SIZE_BYTES);
263 outputStream.writeInt(PARTITION_TYPE_MARKER);
264 outputStream.writeInt(0); // data size
265 outputStream.flush();
266 } catch (IOException e) {
267 Slog.e(TAG, "failed to format block", e);
268 return;
269 } finally {
270 IoUtils.closeQuietly(outputStream);
271 }
272
Andres Moralesc8f952c2015-03-19 08:34:55 -0700273 doSetOemUnlockEnabledLocked(setOemUnlockEnabled);
Andres Morales28301302014-11-12 07:56:46 -0800274 computeAndWriteDigestLocked();
275 }
276
277 private void doSetOemUnlockEnabledLocked(boolean enabled) {
278 FileOutputStream outputStream;
279 try {
280 outputStream = new FileOutputStream(new File(mDataBlockFile));
281 } catch (FileNotFoundException e) {
282 Slog.e(TAG, "partition not available", e);
283 return;
284 }
285
286 try {
287 FileChannel channel = outputStream.getChannel();
288
289 channel.position(getBlockDeviceSize() - 1);
290
291 ByteBuffer data = ByteBuffer.allocate(1);
292 data.put(enabled ? (byte) 1 : (byte) 0);
293 data.flip();
294 channel.write(data);
295 outputStream.flush();
296 } catch (IOException e) {
297 Slog.e(TAG, "unable to access persistent partition", e);
298 return;
299 } finally {
Andres Morales5ca4cc52015-03-19 16:37:54 -0700300 SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
Andres Morales28301302014-11-12 07:56:46 -0800301 IoUtils.closeQuietly(outputStream);
302 }
303 }
304
Andres Morales1ce7d172015-01-07 14:24:57 -0800305 private boolean doGetOemUnlockEnabled() {
306 DataInputStream inputStream;
307 try {
308 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
309 } catch (FileNotFoundException e) {
310 Slog.e(TAG, "partition not available");
311 return false;
312 }
313
314 try {
315 synchronized (mLock) {
316 inputStream.skip(getBlockDeviceSize() - 1);
317 return inputStream.readByte() != 0;
318 }
319 } catch (IOException e) {
320 Slog.e(TAG, "unable to access persistent partition", e);
321 return false;
322 } finally {
323 IoUtils.closeQuietly(inputStream);
324 }
325 }
326
Andres Morales963295e2014-07-10 15:40:24 -0700327 private native long nativeGetBlockDeviceSize(String path);
328 private native int nativeWipe(String path);
Andres Morales68d4acd2014-07-01 19:40:41 -0700329
330 private final IBinder mService = new IPersistentDataBlockService.Stub() {
331 @Override
332 public int write(byte[] data) throws RemoteException {
333 enforceUid(Binder.getCallingUid());
334
335 // Need to ensure we don't write over the last byte
Andres Morales963295e2014-07-10 15:40:24 -0700336 long maxBlockSize = getBlockDeviceSize() - HEADER_SIZE - 1;
337 if (data.length > maxBlockSize) {
338 // partition is ~500k so shouldn't be a problem to downcast
339 return (int) -maxBlockSize;
Andres Morales68d4acd2014-07-01 19:40:41 -0700340 }
341
342 DataOutputStream outputStream;
343 try {
344 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
345 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700346 Slog.e(TAG, "partition not available?", e);
Andres Morales68d4acd2014-07-01 19:40:41 -0700347 return -1;
348 }
349
350 ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
Andres Morales963295e2014-07-10 15:40:24 -0700351 headerAndData.putInt(PARTITION_TYPE_MARKER);
Andres Morales68d4acd2014-07-01 19:40:41 -0700352 headerAndData.putInt(data.length);
353 headerAndData.put(data);
354
Andres Morales28301302014-11-12 07:56:46 -0800355 synchronized (mLock) {
Andres Morales68d4acd2014-07-01 19:40:41 -0700356 try {
Andres Morales28301302014-11-12 07:56:46 -0800357 byte[] checksum = new byte[DIGEST_SIZE_BYTES];
358 outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
359 outputStream.write(headerAndData.array());
360 outputStream.flush();
Andres Morales68d4acd2014-07-01 19:40:41 -0700361 } catch (IOException e) {
Andres Morales28301302014-11-12 07:56:46 -0800362 Slog.e(TAG, "failed writing to the persistent data block", e);
363 return -1;
364 } finally {
365 IoUtils.closeQuietly(outputStream);
366 }
367
368 if (computeAndWriteDigestLocked()) {
369 return data.length;
370 } else {
371 return -1;
Andres Morales68d4acd2014-07-01 19:40:41 -0700372 }
373 }
374 }
375
376 @Override
Andres Morales963295e2014-07-10 15:40:24 -0700377 public byte[] read() {
Andres Morales68d4acd2014-07-01 19:40:41 -0700378 enforceUid(Binder.getCallingUid());
Andres Morales28301302014-11-12 07:56:46 -0800379 if (!enforceChecksumValidity()) {
380 return new byte[0];
381 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700382
383 DataInputStream inputStream;
384 try {
385 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
386 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700387 Slog.e(TAG, "partition not available?", e);
388 return null;
Andres Morales68d4acd2014-07-01 19:40:41 -0700389 }
390
391 try {
Andres Morales963295e2014-07-10 15:40:24 -0700392 synchronized (mLock) {
393 int totalDataSize = getTotalDataSizeLocked(inputStream);
394
395 if (totalDataSize == 0) {
396 return new byte[0];
397 }
398
399 byte[] data = new byte[totalDataSize];
400 int read = inputStream.read(data, 0, totalDataSize);
401 if (read < totalDataSize) {
402 // something went wrong, not returning potentially corrupt data
403 Slog.e(TAG, "failed to read entire data block. bytes read: " +
404 read + "/" + totalDataSize);
405 return null;
406 }
407 return data;
408 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700409 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700410 Slog.e(TAG, "failed to read data", e);
411 return null;
Andres Morales68d4acd2014-07-01 19:40:41 -0700412 } finally {
413 try {
414 inputStream.close();
415 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700416 Slog.e(TAG, "failed to close OutputStream");
417 }
418 }
419 }
420
421 @Override
422 public void wipe() {
423 enforceOemUnlockPermission();
424
425 synchronized (mLock) {
426 int ret = nativeWipe(mDataBlockFile);
427
428 if (ret < 0) {
429 Slog.e(TAG, "failed to wipe persistent partition");
Andres Morales68d4acd2014-07-01 19:40:41 -0700430 }
431 }
432 }
433
434 @Override
Craig Lafayette66445a62015-03-27 09:01:43 -0400435 public void wipeIfAllowed(Bundle bundle, PendingIntent pi) {
436 // Should only be called by owner
437 if (UserHandle.getCallingUserId() != UserHandle.USER_OWNER) {
438 throw new SecurityException("Only the Owner is allowed to wipe");
439 }
440 // Caller must be able to query the the state of the PersistentDataBlock
441 enforcePersistentDataBlockAccess();
442 String allowedPackage = mContext.getResources()
443 .getString(R.string.config_persistentDataPackageName);
444 Intent intent = new Intent();
445 intent.setPackage(allowedPackage);
446 intent.setAction(PersistentDataBlockManager.ACTION_WIPE_IF_ALLOWED);
447 intent.putExtras(bundle);
448 intent.putExtra(PersistentDataBlockManager.EXTRA_WIPE_IF_ALLOWED_CALLBACK, pi);
449 long id = Binder.clearCallingIdentity();
450 try {
451 mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
452 } finally {
453 restoreCallingIdentity(id);
454 }
455 }
456
457 @Override
Andres Morales68d4acd2014-07-01 19:40:41 -0700458 public void setOemUnlockEnabled(boolean enabled) {
Guang Zhu514c5802014-09-12 15:14:00 -0700459 // do not allow monkey to flip the flag
460 if (ActivityManager.isUserAMonkey()) {
461 return;
462 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700463 enforceOemUnlockPermission();
Andres Moralesa31c23d2014-10-30 15:31:31 -0700464 enforceIsOwner();
Andres Morales68d4acd2014-07-01 19:40:41 -0700465
Andres Morales28301302014-11-12 07:56:46 -0800466 synchronized (mLock) {
467 doSetOemUnlockEnabledLocked(enabled);
468 computeAndWriteDigestLocked();
Andres Morales68d4acd2014-07-01 19:40:41 -0700469 }
470 }
471
472 @Override
473 public boolean getOemUnlockEnabled() {
474 enforceOemUnlockPermission();
Andres Morales1ce7d172015-01-07 14:24:57 -0800475 return doGetOemUnlockEnabled();
Andres Morales68d4acd2014-07-01 19:40:41 -0700476 }
477
478 @Override
479 public int getDataBlockSize() {
Craig Lafayette66445a62015-03-27 09:01:43 -0400480 enforcePersistentDataBlockAccess();
Andres Morales68d4acd2014-07-01 19:40:41 -0700481
482 DataInputStream inputStream;
483 try {
484 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
485 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700486 Slog.e(TAG, "partition not available");
Andres Morales68d4acd2014-07-01 19:40:41 -0700487 return 0;
488 }
489
490 try {
Andres Morales963295e2014-07-10 15:40:24 -0700491 synchronized (mLock) {
492 return getTotalDataSizeLocked(inputStream);
493 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700494 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700495 Slog.e(TAG, "error reading data block size");
Andres Morales68d4acd2014-07-01 19:40:41 -0700496 return 0;
497 } finally {
Andres Morales963295e2014-07-10 15:40:24 -0700498 IoUtils.closeQuietly(inputStream);
Andres Morales68d4acd2014-07-01 19:40:41 -0700499 }
500 }
Andres Morales963295e2014-07-10 15:40:24 -0700501
Craig Lafayette66445a62015-03-27 09:01:43 -0400502 private void enforcePersistentDataBlockAccess() {
503 if (mContext.checkCallingPermission(Manifest.permission.ACCESS_PDB_STATE)
504 != PackageManager.PERMISSION_GRANTED) {
505 enforceUid(Binder.getCallingUid());
506 }
507 }
508
Andres Morales963295e2014-07-10 15:40:24 -0700509 @Override
510 public long getMaximumDataBlockSize() {
511 long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1;
512 return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
513 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700514 };
515}