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 static com.android.internal.util.Preconditions.checkArgument;
20 
21 import android.Manifest;
22 import android.app.ActivityManager;
23 import android.content.Context;
24 import android.content.pm.PackageManager;
25 import android.os.Binder;
26 import android.os.IBinder;
27 import android.os.RemoteException;
28 import android.os.SystemProperties;
29 import android.os.UserHandle;
30 import android.os.UserManager;
31 import android.service.persistentdata.IPersistentDataBlockService;
32 import android.service.persistentdata.PersistentDataBlockManager;
33 import android.text.TextUtils;
34 import android.util.Slog;
35 
36 import com.android.internal.R;
37 import com.android.internal.annotations.GuardedBy;
38 import com.android.internal.util.DumpUtils;
39 import com.android.server.pm.UserManagerInternal;
40 
41 import libcore.io.IoUtils;
42 
43 import java.io.DataInputStream;
44 import java.io.File;
45 import java.io.FileDescriptor;
46 import java.io.FileInputStream;
47 import java.io.FileNotFoundException;
48 import java.io.IOException;
49 import java.io.PrintWriter;
50 import java.io.RandomAccessFile;
51 import java.nio.ByteBuffer;
52 import java.nio.channels.FileChannel;
53 import java.security.MessageDigest;
54 import java.security.NoSuchAlgorithmException;
55 import java.util.Arrays;
56 import java.util.concurrent.CountDownLatch;
57 import java.util.concurrent.TimeUnit;
58 
59 /**
60  * Service for reading and writing blocks to a persistent partition.
61  * This data will live across factory resets not initiated via the Settings UI.
62  * When a device is factory reset through Settings this data is wiped.
63  *
64  * Allows writing one block at a time. Namely, each time {@link IPersistentDataBlockService#write}
65  * is called, it will overwrite the data that was previously written on the block.
66  *
67  * Clients can query the size of the currently written block via
68  * {@link IPersistentDataBlockService#getDataBlockSize}
69  *
70  * Clients can read any number of bytes from the currently written block up to its total size by
71  * invoking {@link IPersistentDataBlockService#read}
72  *
73  * The persistent data block is currently laid out as follows:
74  * | ---------BEGINNING OF PARTITION-------------|
75  * | Partition digest (32 bytes)                 |
76  * | --------------------------------------------|
77  * | PARTITION_TYPE_MARKER (4 bytes)             |
78  * | --------------------------------------------|
79  * | FRP data block length (4 bytes)             |
80  * | --------------------------------------------|
81  * | FRP data (variable length)                  |
82  * | --------------------------------------------|
83  * | ...                                         |
84  * | --------------------------------------------|
85  * | Test mode data block (10000 bytes)          |
86  * | --------------------------------------------|
87  * |     | Test mode data length (4 bytes)       |
88  * | --------------------------------------------|
89  * |     | Test mode data (variable length)      |
90  * |     | ...                                   |
91  * | --------------------------------------------|
92  * | FRP credential handle block (1000 bytes)    |
93  * | --------------------------------------------|
94  * |     | FRP credential handle length (4 bytes)|
95  * | --------------------------------------------|
96  * |     | FRP credential handle (variable len)  |
97  * |     | ...                                   |
98  * | --------------------------------------------|
99  * | OEM Unlock bit (1 byte)                     |
100  * | ---------END OF PARTITION-------------------|
101  *
102  * TODO: now that the persistent partition contains several blocks, next time someone wants a new
103  * block, we should look at adding more generic block definitions and get rid of the various raw
104  * XXX_RESERVED_SIZE and XXX_DATA_SIZE constants. That will ensure the code is easier to maintain
105  * and less likely to introduce out-of-bounds read/write.
106  */
107 public class PersistentDataBlockService extends SystemService {
108     private static final String TAG = PersistentDataBlockService.class.getSimpleName();
109 
110     private static final String GSI_SANDBOX = "/data/gsi_persistent_data";
111     private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
112 
113     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
114     private static final int HEADER_SIZE = 8;
115     // Magic number to mark block device as adhering to the format consumed by this service
116     private static final int PARTITION_TYPE_MARKER = 0x19901873;
117     /** Size of the block reserved for FRP credential, including 4 bytes for the size header. */
118     private static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
119     /** Maximum size of the FRP credential handle that can be stored. */
120     private static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
121     /**
122      * Size of the block reserved for Test Harness Mode data, including 4 bytes for the size header.
123      */
124     private static final int TEST_MODE_RESERVED_SIZE = 10000;
125     /** Maximum size of the Test Harness Mode data that can be stored. */
126     private static final int MAX_TEST_MODE_DATA_SIZE = TEST_MODE_RESERVED_SIZE - 4;
127     // Limit to 100k as blocks larger than this might cause strain on Binder.
128     private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
129 
130     public static final int DIGEST_SIZE_BYTES = 32;
131     private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
132     private static final String FLASH_LOCK_PROP = "ro.boot.flash.locked";
133     private static final String FLASH_LOCK_LOCKED = "1";
134     private static final String FLASH_LOCK_UNLOCKED = "0";
135 
136     private final Context mContext;
137     private final String mDataBlockFile;
138     private final boolean mIsRunningDSU;
139     private final Object mLock = new Object();
140     private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
141 
142     private int mAllowedUid = -1;
143     private long mBlockDeviceSize;
144 
145     @GuardedBy("mLock")
146     private boolean mIsWritable = true;
147 
PersistentDataBlockService(Context context)148     public PersistentDataBlockService(Context context) {
149         super(context);
150         mContext = context;
151         mIsRunningDSU = SystemProperties.getBoolean(GSI_RUNNING_PROP, false);
152         if (mIsRunningDSU) {
153             mDataBlockFile = GSI_SANDBOX;
154         } else {
155             mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
156         }
157         mBlockDeviceSize = -1; // Load lazily
158     }
159 
getAllowedUid()160     private int getAllowedUid() {
161         final UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
162         int mainUserId = umInternal.getMainUserId();
163         if (mainUserId < 0) {
164             // If main user is not defined. Use the SYSTEM user instead.
165             mainUserId = UserHandle.USER_SYSTEM;
166         }
167         String allowedPackage = mContext.getResources()
168                 .getString(R.string.config_persistentDataPackageName);
169         int allowedUid = -1;
170         if (!TextUtils.isEmpty(allowedPackage)) {
171             try {
172                 allowedUid = mContext.getPackageManager().getPackageUidAsUser(
173                         allowedPackage, PackageManager.MATCH_SYSTEM_ONLY, mainUserId);
174             } catch (PackageManager.NameNotFoundException e) {
175                 // not expected
176                 Slog.e(TAG, "not able to find package " + allowedPackage, e);
177             }
178         }
179         return allowedUid;
180     }
181 
182     @Override
onStart()183     public void onStart() {
184         // Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY
185         SystemServerInitThreadPool.submit(() -> {
186             enforceChecksumValidity();
187             formatIfOemUnlockEnabled();
188             publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
189             mInitDoneSignal.countDown();
190         }, TAG + ".onStart");
191     }
192 
193     @Override
onBootPhase(int phase)194     public void onBootPhase(int phase) {
195         // Wait for initialization in onStart to finish
196         if (phase == PHASE_SYSTEM_SERVICES_READY) {
197             try {
198                 if (!mInitDoneSignal.await(10, TimeUnit.SECONDS)) {
199                     throw new IllegalStateException("Service " + TAG + " init timeout");
200                 }
201             } catch (InterruptedException e) {
202                 Thread.currentThread().interrupt();
203                 throw new IllegalStateException("Service " + TAG + " init interrupted", e);
204             }
205             // The user responsible for FRP should exist by now.
206             mAllowedUid = getAllowedUid();
207             LocalServices.addService(PersistentDataBlockManagerInternal.class, mInternalService);
208         }
209         super.onBootPhase(phase);
210     }
211 
formatIfOemUnlockEnabled()212     private void formatIfOemUnlockEnabled() {
213         boolean enabled = doGetOemUnlockEnabled();
214         if (enabled) {
215             synchronized (mLock) {
216                 formatPartitionLocked(true);
217             }
218         }
219 
220         SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
221     }
222 
enforceOemUnlockReadPermission()223     private void enforceOemUnlockReadPermission() {
224         if (mContext.checkCallingOrSelfPermission(Manifest.permission.READ_OEM_UNLOCK_STATE)
225                 == PackageManager.PERMISSION_DENIED
226                 && mContext.checkCallingOrSelfPermission(Manifest.permission.OEM_UNLOCK_STATE)
227                 == PackageManager.PERMISSION_DENIED) {
228             throw new SecurityException("Can't access OEM unlock state. Requires "
229                     + "READ_OEM_UNLOCK_STATE or OEM_UNLOCK_STATE permission.");
230         }
231     }
232 
enforceOemUnlockWritePermission()233     private void enforceOemUnlockWritePermission() {
234         mContext.enforceCallingOrSelfPermission(
235                 Manifest.permission.OEM_UNLOCK_STATE,
236                 "Can't modify OEM unlock state");
237     }
238 
enforceUid(int callingUid)239     private void enforceUid(int callingUid) {
240         if (callingUid != mAllowedUid) {
241             throw new SecurityException("uid " + callingUid + " not allowed to access PST");
242         }
243     }
244 
enforceIsAdmin()245     private void enforceIsAdmin() {
246         final int userId = UserHandle.getCallingUserId();
247         final boolean isAdmin = UserManager.get(mContext).isUserAdmin(userId);
248         if (!isAdmin) {
249             throw new SecurityException(
250                     "Only the Admin user is allowed to change OEM unlock state");
251         }
252     }
253 
enforceUserRestriction(String userRestriction)254     private void enforceUserRestriction(String userRestriction) {
255         if (UserManager.get(mContext).hasUserRestriction(userRestriction)) {
256             throw new SecurityException(
257                     "OEM unlock is disallowed by user restriction: " + userRestriction);
258         }
259     }
260 
getTotalDataSizeLocked(DataInputStream inputStream)261     private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
262         // skip over checksum
263         inputStream.skipBytes(DIGEST_SIZE_BYTES);
264 
265         int totalDataSize;
266         int blockId = inputStream.readInt();
267         if (blockId == PARTITION_TYPE_MARKER) {
268             totalDataSize = inputStream.readInt();
269         } else {
270             totalDataSize = 0;
271         }
272         return totalDataSize;
273     }
274 
getBlockDeviceSize()275     private long getBlockDeviceSize() {
276         synchronized (mLock) {
277             if (mBlockDeviceSize == -1) {
278                 if (mIsRunningDSU) {
279                     mBlockDeviceSize = MAX_DATA_BLOCK_SIZE;
280                 } else {
281                     mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
282                 }
283             }
284         }
285 
286         return mBlockDeviceSize;
287     }
288 
getFrpCredentialDataOffset()289     private long getFrpCredentialDataOffset() {
290         return getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE;
291     }
292 
getTestHarnessModeDataOffset()293     private long getTestHarnessModeDataOffset() {
294         return getFrpCredentialDataOffset() - TEST_MODE_RESERVED_SIZE;
295     }
296 
enforceChecksumValidity()297     private boolean enforceChecksumValidity() {
298         byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
299 
300         synchronized (mLock) {
301             byte[] digest = computeDigestLocked(storedDigest);
302             if (digest == null || !Arrays.equals(storedDigest, digest)) {
303                 Slog.i(TAG, "Formatting FRP partition...");
304                 formatPartitionLocked(false);
305                 return false;
306             }
307         }
308 
309         return true;
310     }
311 
getBlockOutputChannel()312     private FileChannel getBlockOutputChannel() throws IOException {
313         return new RandomAccessFile(mDataBlockFile, "rw").getChannel();
314     }
315 
computeAndWriteDigestLocked()316     private boolean computeAndWriteDigestLocked() {
317         byte[] digest = computeDigestLocked(null);
318         if (digest != null) {
319             FileChannel channel;
320             try {
321                 channel = getBlockOutputChannel();
322             } catch (IOException e) {
323                 Slog.e(TAG, "partition not available?", e);
324                 return false;
325             }
326 
327             try {
328                 ByteBuffer buf = ByteBuffer.allocate(DIGEST_SIZE_BYTES);
329                 buf.put(digest);
330                 buf.flip();
331                 channel.write(buf);
332                 channel.force(true);
333             } catch (IOException e) {
334                 Slog.e(TAG, "failed to write block checksum", e);
335                 return false;
336             }
337             return true;
338         } else {
339             return false;
340         }
341     }
342 
computeDigestLocked(byte[] storedDigest)343     private byte[] computeDigestLocked(byte[] storedDigest) {
344         DataInputStream inputStream;
345         try {
346             inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
347         } catch (FileNotFoundException e) {
348             Slog.e(TAG, "partition not available?", e);
349             return null;
350         }
351 
352         MessageDigest md;
353         try {
354             md = MessageDigest.getInstance("SHA-256");
355         } catch (NoSuchAlgorithmException e) {
356             // won't ever happen -- every implementation is required to support SHA-256
357             Slog.e(TAG, "SHA-256 not supported?", e);
358             IoUtils.closeQuietly(inputStream);
359             return null;
360         }
361 
362         try {
363             if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) {
364                 inputStream.read(storedDigest);
365             } else {
366                 inputStream.skipBytes(DIGEST_SIZE_BYTES);
367             }
368 
369             int read;
370             byte[] data = new byte[1024];
371             md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest
372             while ((read = inputStream.read(data)) != -1) {
373                 md.update(data, 0, read);
374             }
375         } catch (IOException e) {
376             Slog.e(TAG, "failed to read partition", e);
377             return null;
378         } finally {
379             IoUtils.closeQuietly(inputStream);
380         }
381 
382         return md.digest();
383     }
384 
formatPartitionLocked(boolean setOemUnlockEnabled)385     private void formatPartitionLocked(boolean setOemUnlockEnabled) {
386 
387         try {
388             FileChannel channel = getBlockOutputChannel();
389             // Format the data selectively.
390             //
391             // 1. write header, set length = 0
392             int header_size = DIGEST_SIZE_BYTES + HEADER_SIZE;
393             ByteBuffer buf = ByteBuffer.allocate(header_size);
394             buf.put(new byte[DIGEST_SIZE_BYTES]);
395             buf.putInt(PARTITION_TYPE_MARKER);
396             buf.putInt(0);
397             buf.flip();
398             channel.write(buf);
399             channel.force(true);
400 
401             // 2. corrupt the legacy FRP data explicitly
402             int payload_size = (int) getBlockDeviceSize() - header_size;
403             buf = ByteBuffer.allocate(payload_size
404                           - TEST_MODE_RESERVED_SIZE - FRP_CREDENTIAL_RESERVED_SIZE - 1);
405             channel.write(buf);
406             channel.force(true);
407 
408             // 3. skip the test mode data and leave it unformat
409             //    This is for a feature that enables testing.
410             channel.position(channel.position() + TEST_MODE_RESERVED_SIZE);
411 
412             // 4. wipe the FRP_CREDENTIAL explicitly
413             buf = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
414             channel.write(buf);
415             channel.force(true);
416 
417             // 5. set unlock = 0 because it's a formatPartitionLocked
418             buf = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
419             buf.put((byte)0);
420             buf.flip();
421             channel.write(buf);
422             channel.force(true);
423         } catch (IOException e) {
424             Slog.e(TAG, "failed to format block", e);
425             return;
426         }
427 
428         doSetOemUnlockEnabledLocked(setOemUnlockEnabled);
429         computeAndWriteDigestLocked();
430     }
431 
doSetOemUnlockEnabledLocked(boolean enabled)432     private void doSetOemUnlockEnabledLocked(boolean enabled) {
433 
434         try {
435             FileChannel channel = getBlockOutputChannel();
436 
437             channel.position(getBlockDeviceSize() - 1);
438 
439             ByteBuffer data = ByteBuffer.allocate(1);
440             data.put(enabled ? (byte) 1 : (byte) 0);
441             data.flip();
442             channel.write(data);
443             channel.force(true);
444         } catch (IOException e) {
445             Slog.e(TAG, "unable to access persistent partition", e);
446             return;
447         } finally {
448             SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
449         }
450     }
451 
doGetOemUnlockEnabled()452     private boolean doGetOemUnlockEnabled() {
453         DataInputStream inputStream;
454         try {
455             inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
456         } catch (FileNotFoundException e) {
457             Slog.e(TAG, "partition not available");
458             return false;
459         }
460 
461         try {
462             synchronized (mLock) {
463                 inputStream.skip(getBlockDeviceSize() - 1);
464                 return inputStream.readByte() != 0;
465             }
466         } catch (IOException e) {
467             Slog.e(TAG, "unable to access persistent partition", e);
468             return false;
469         } finally {
470             IoUtils.closeQuietly(inputStream);
471         }
472     }
473 
doGetMaximumDataBlockSize()474     private long doGetMaximumDataBlockSize() {
475         long actualSize = getBlockDeviceSize() - HEADER_SIZE - DIGEST_SIZE_BYTES
476                 - TEST_MODE_RESERVED_SIZE - FRP_CREDENTIAL_RESERVED_SIZE - 1;
477         return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
478     }
479 
nativeGetBlockDeviceSize(String path)480     private native long nativeGetBlockDeviceSize(String path);
nativeWipe(String path)481     private native int nativeWipe(String path);
482 
483     private final IBinder mService = new IPersistentDataBlockService.Stub() {
484 
485         /**
486          * Write the data to the persistent data block.
487          *
488          * @return a positive integer of the number of bytes that were written if successful,
489          * otherwise a negative integer indicating there was a problem
490          */
491         @Override
492         public int write(byte[] data) throws RemoteException {
493             enforceUid(Binder.getCallingUid());
494 
495             // Need to ensure we don't write over the last byte
496             long maxBlockSize = doGetMaximumDataBlockSize();
497             if (data.length > maxBlockSize) {
498                 // partition is ~500k so shouldn't be a problem to downcast
499                 return (int) -maxBlockSize;
500             }
501 
502             FileChannel channel;
503             try {
504                 channel = getBlockOutputChannel();
505             } catch (IOException e) {
506                 Slog.e(TAG, "partition not available?", e);
507                return -1;
508             }
509 
510             ByteBuffer headerAndData = ByteBuffer.allocate(
511                                            data.length + HEADER_SIZE + DIGEST_SIZE_BYTES);
512             headerAndData.put(new byte[DIGEST_SIZE_BYTES]);
513             headerAndData.putInt(PARTITION_TYPE_MARKER);
514             headerAndData.putInt(data.length);
515             headerAndData.put(data);
516             headerAndData.flip();
517             synchronized (mLock) {
518                 if (!mIsWritable) {
519                     return -1;
520                 }
521 
522                 try {
523                     channel.write(headerAndData);
524                     channel.force(true);
525                 } catch (IOException e) {
526                     Slog.e(TAG, "failed writing to the persistent data block", e);
527                     return -1;
528                 }
529 
530                 if (computeAndWriteDigestLocked()) {
531                     return data.length;
532                 } else {
533                     return -1;
534                 }
535             }
536         }
537 
538         @Override
539         public byte[] read() {
540             enforceUid(Binder.getCallingUid());
541             if (!enforceChecksumValidity()) {
542                 return new byte[0];
543             }
544 
545             DataInputStream inputStream;
546             try {
547                 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
548             } catch (FileNotFoundException e) {
549                 Slog.e(TAG, "partition not available?", e);
550                 return null;
551             }
552 
553             try {
554                 synchronized (mLock) {
555                     int totalDataSize = getTotalDataSizeLocked(inputStream);
556 
557                     if (totalDataSize == 0) {
558                         return new byte[0];
559                     }
560 
561                     byte[] data = new byte[totalDataSize];
562                     int read = inputStream.read(data, 0, totalDataSize);
563                     if (read < totalDataSize) {
564                         // something went wrong, not returning potentially corrupt data
565                         Slog.e(TAG, "failed to read entire data block. bytes read: " +
566                                 read + "/" + totalDataSize);
567                         return null;
568                     }
569                     return data;
570                 }
571             } catch (IOException e) {
572                 Slog.e(TAG, "failed to read data", e);
573                 return null;
574             } finally {
575                 try {
576                     inputStream.close();
577                 } catch (IOException e) {
578                     Slog.e(TAG, "failed to close OutputStream");
579                 }
580             }
581         }
582 
583         @Override
584         public void wipe() {
585             enforceOemUnlockWritePermission();
586 
587             synchronized (mLock) {
588                 int ret = nativeWipe(mDataBlockFile);
589 
590                 if (ret < 0) {
591                     Slog.e(TAG, "failed to wipe persistent partition");
592                 } else {
593                     mIsWritable = false;
594                     Slog.i(TAG, "persistent partition now wiped and unwritable");
595                 }
596             }
597         }
598 
599         @Override
600         public void setOemUnlockEnabled(boolean enabled) throws SecurityException {
601             // do not allow monkey to flip the flag
602             if (ActivityManager.isUserAMonkey()) {
603                 return;
604             }
605 
606             enforceOemUnlockWritePermission();
607             enforceIsAdmin();
608 
609             if (enabled) {
610                 // Do not allow oem unlock to be enabled if it's disallowed by a user restriction.
611                 enforceUserRestriction(UserManager.DISALLOW_OEM_UNLOCK);
612                 enforceUserRestriction(UserManager.DISALLOW_FACTORY_RESET);
613             }
614             synchronized (mLock) {
615                 doSetOemUnlockEnabledLocked(enabled);
616                 computeAndWriteDigestLocked();
617             }
618         }
619 
620         @Override
621         public boolean getOemUnlockEnabled() {
622             enforceOemUnlockReadPermission();
623             return doGetOemUnlockEnabled();
624         }
625 
626         @Override
627         public int getFlashLockState() {
628             enforceOemUnlockReadPermission();
629             String locked = SystemProperties.get(FLASH_LOCK_PROP);
630             switch (locked) {
631                 case FLASH_LOCK_LOCKED:
632                     return PersistentDataBlockManager.FLASH_LOCK_LOCKED;
633                 case FLASH_LOCK_UNLOCKED:
634                     return PersistentDataBlockManager.FLASH_LOCK_UNLOCKED;
635                 default:
636                     return PersistentDataBlockManager.FLASH_LOCK_UNKNOWN;
637             }
638         }
639 
640         @Override
641         public int getDataBlockSize() {
642             enforcePersistentDataBlockAccess();
643 
644             DataInputStream inputStream;
645             try {
646                 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
647             } catch (FileNotFoundException e) {
648                 Slog.e(TAG, "partition not available");
649                 return 0;
650             }
651 
652             try {
653                 synchronized (mLock) {
654                     return getTotalDataSizeLocked(inputStream);
655                 }
656             } catch (IOException e) {
657                 Slog.e(TAG, "error reading data block size");
658                 return 0;
659             } finally {
660                 IoUtils.closeQuietly(inputStream);
661             }
662         }
663 
664         private void enforcePersistentDataBlockAccess() {
665             if (mContext.checkCallingPermission(Manifest.permission.ACCESS_PDB_STATE)
666                     != PackageManager.PERMISSION_GRANTED) {
667                 enforceUid(Binder.getCallingUid());
668             }
669         }
670 
671         @Override
672         public long getMaximumDataBlockSize() {
673             enforceUid(Binder.getCallingUid());
674             return doGetMaximumDataBlockSize();
675         }
676 
677         @Override
678         public boolean hasFrpCredentialHandle() {
679             enforcePersistentDataBlockAccess();
680             try {
681                 return mInternalService.getFrpCredentialHandle() != null;
682             } catch (IllegalStateException e) {
683                 Slog.e(TAG, "error reading frp handle", e);
684                 throw new UnsupportedOperationException("cannot read frp credential");
685             }
686         }
687 
688         @Override
689         public String getPersistentDataPackageName() {
690             enforcePersistentDataBlockAccess();
691             return mContext.getString(R.string.config_persistentDataPackageName);
692         }
693 
694         @Override
695         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
696             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
697 
698             pw.println("mDataBlockFile: " + mDataBlockFile);
699             pw.println("mIsRunningDSU: " + mIsRunningDSU);
700             pw.println("mInitDoneSignal: " + mInitDoneSignal);
701             pw.println("mAllowedUid: " + mAllowedUid);
702             pw.println("mBlockDeviceSize: " + mBlockDeviceSize);
703             synchronized (mLock) {
704                 pw.println("mIsWritable: " + mIsWritable);
705             }
706         }
707     };
708 
709     private PersistentDataBlockManagerInternal mInternalService =
710             new PersistentDataBlockManagerInternal() {
711 
712         @Override
713         public void setFrpCredentialHandle(byte[] handle) {
714             writeInternal(handle, getFrpCredentialDataOffset(), MAX_FRP_CREDENTIAL_HANDLE_SIZE);
715         }
716 
717         @Override
718         public byte[] getFrpCredentialHandle() {
719             return readInternal(getFrpCredentialDataOffset(), MAX_FRP_CREDENTIAL_HANDLE_SIZE);
720         }
721 
722         @Override
723         public void setTestHarnessModeData(byte[] data) {
724             writeInternal(data, getTestHarnessModeDataOffset(), MAX_TEST_MODE_DATA_SIZE);
725         }
726 
727         @Override
728         public byte[] getTestHarnessModeData() {
729             byte[] data = readInternal(getTestHarnessModeDataOffset(), MAX_TEST_MODE_DATA_SIZE);
730             if (data == null) {
731                 return new byte[0];
732             }
733             return data;
734         }
735 
736         @Override
737         public void clearTestHarnessModeData() {
738             int size = Math.min(MAX_TEST_MODE_DATA_SIZE, getTestHarnessModeData().length) + 4;
739             writeDataBuffer(getTestHarnessModeDataOffset(), ByteBuffer.allocate(size));
740         }
741 
742         @Override
743         public int getAllowedUid() {
744             return mAllowedUid;
745         }
746 
747         private void writeInternal(byte[] data, long offset, int dataLength) {
748             checkArgument(data == null || data.length > 0, "data must be null or non-empty");
749             checkArgument(
750                     data == null || data.length <= dataLength,
751                     "data must not be longer than " + dataLength);
752 
753             ByteBuffer dataBuffer = ByteBuffer.allocate(dataLength + 4);
754             dataBuffer.putInt(data == null ? 0 : data.length);
755             if (data != null) {
756                 dataBuffer.put(data);
757             }
758             dataBuffer.flip();
759 
760             writeDataBuffer(offset, dataBuffer);
761         }
762 
763         private void writeDataBuffer(long offset, ByteBuffer dataBuffer) {
764             synchronized (mLock) {
765                 if (!mIsWritable) {
766                     return;
767                 }
768                 try {
769                     FileChannel channel = getBlockOutputChannel();
770                     channel.position(offset);
771                     channel.write(dataBuffer);
772                     channel.force(true);
773                 } catch (IOException e) {
774                     Slog.e(TAG, "unable to access persistent partition", e);
775                     return;
776                 }
777 
778                 computeAndWriteDigestLocked();
779             }
780         }
781 
782         private byte[] readInternal(long offset, int maxLength) {
783             if (!enforceChecksumValidity()) {
784                 throw new IllegalStateException("invalid checksum");
785             }
786 
787             DataInputStream inputStream;
788             try {
789                 inputStream = new DataInputStream(
790                         new FileInputStream(new File(mDataBlockFile)));
791             } catch (FileNotFoundException e) {
792                 throw new IllegalStateException("persistent partition not available");
793             }
794 
795             try {
796                 synchronized (mLock) {
797                     inputStream.skip(offset);
798                     int length = inputStream.readInt();
799                     if (length <= 0 || length > maxLength) {
800                         return null;
801                     }
802                     byte[] bytes = new byte[length];
803                     inputStream.readFully(bytes);
804                     return bytes;
805                 }
806             } catch (IOException e) {
807                 throw new IllegalStateException("persistent partition not readable", e);
808             } finally {
809                 IoUtils.closeQuietly(inputStream);
810             }
811         }
812 
813         @Override
814         public void forceOemUnlockEnabled(boolean enabled) {
815             synchronized (mLock) {
816                 doSetOemUnlockEnabledLocked(enabled);
817                 computeAndWriteDigestLocked();
818             }
819         }
820     };
821 }
822