blob: bd5ad960a8860fd457133886b116ef78506b3fa1 [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
William Hestera96d3d32018-12-19 13:14:51 -080019import static com.android.internal.util.Preconditions.checkArgument;
20
Andres Morales68d4acd2014-07-01 19:40:41 -070021import android.Manifest;
Guang Zhu514c5802014-09-12 15:14:00 -070022import android.app.ActivityManager;
Andres Morales68d4acd2014-07-01 19:40:41 -070023import android.content.Context;
24import android.content.pm.PackageManager;
25import android.os.Binder;
26import android.os.IBinder;
27import android.os.RemoteException;
28import android.os.SystemProperties;
Andres Morales6429f312014-08-04 16:35:15 -070029import android.os.UserHandle;
Xiaohui Chenf0660782015-09-02 14:25:19 -070030import android.os.UserManager;
Andres Morales68d4acd2014-07-01 19:40:41 -070031import android.service.persistentdata.IPersistentDataBlockService;
Andres Morales74e9b182016-02-22 12:33:33 -080032import 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;
Charles Hef6f1d622016-12-01 19:22:33 +000036import com.android.internal.annotations.GuardedBy;
Guang Zhu514c5802014-09-12 15:14:00 -070037
Andres Morales963295e2014-07-10 15:40:24 -070038import libcore.io.IoUtils;
Andres Morales68d4acd2014-07-01 19:40:41 -070039
40import java.io.DataInputStream;
41import java.io.DataOutputStream;
42import java.io.File;
43import java.io.FileInputStream;
44import java.io.FileNotFoundException;
45import java.io.FileOutputStream;
46import java.io.IOException;
47import java.nio.ByteBuffer;
48import java.nio.channels.FileChannel;
Andres Morales28301302014-11-12 07:56:46 -080049import java.security.MessageDigest;
50import java.security.NoSuchAlgorithmException;
51import java.util.Arrays;
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -080052import java.util.concurrent.CountDownLatch;
53import java.util.concurrent.TimeUnit;
Andres Morales68d4acd2014-07-01 19:40:41 -070054
55/**
56 * Service for reading and writing blocks to a persistent partition.
Andres Morales963295e2014-07-10 15:40:24 -070057 * 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.
Andres Morales68d4acd2014-07-01 19:40:41 -070059 *
Charles Hea629c772016-11-24 14:05:00 +000060 * 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.
Andres Morales68d4acd2014-07-01 19:40:41 -070062 *
63 * Clients can query the size of the currently written block via
Charles Hea629c772016-11-24 14:05:00 +000064 * {@link IPersistentDataBlockService#getDataBlockSize}
Andres Morales68d4acd2014-07-01 19:40:41 -070065 *
Charles Hea629c772016-11-24 14:05:00 +000066 * Clients can read any number of bytes from the currently written block up to its total size by
67 * invoking {@link IPersistentDataBlockService#read}
William Hestera96d3d32018-12-19 13:14:51 -080068 *
69 * The persistent data block is currently laid out as follows:
70 * | ---------BEGINNING OF PARTITION-------------|
71 * | Partition digest (32 bytes) |
72 * | --------------------------------------------|
73 * | PARTITION_TYPE_MARKER (4 bytes) |
74 * | --------------------------------------------|
75 * | FRP data block length (4 bytes) |
76 * | --------------------------------------------|
77 * | FRP data (variable length) |
78 * | --------------------------------------------|
79 * | ... |
80 * | --------------------------------------------|
81 * | Test mode data block (10000 bytes) |
82 * | --------------------------------------------|
83 * | | Test mode data length (4 bytes) |
84 * | --------------------------------------------|
85 * | | Test mode data (variable length) |
86 * | | ... |
87 * | --------------------------------------------|
88 * | FRP credential handle block (1000 bytes) |
89 * | --------------------------------------------|
90 * | | FRP credential handle length (4 bytes)|
91 * | --------------------------------------------|
92 * | | FRP credential handle (variable len) |
93 * | | ... |
94 * | --------------------------------------------|
95 * | OEM Unlock bit (1 byte) |
96 * | ---------END OF PARTITION-------------------|
97 *
98 * TODO: now that the persistent partition contains several blocks, next time someone wants a new
99 * block, we should look at adding more generic block definitions and get rid of the various raw
100 * XXX_RESERVED_SIZE and XXX_DATA_SIZE constants. That will ensure the code is easier to maintain
101 * and less likely to introduce out-of-bounds read/write.
Andres Morales68d4acd2014-07-01 19:40:41 -0700102 */
103public class PersistentDataBlockService extends SystemService {
104 private static final String TAG = PersistentDataBlockService.class.getSimpleName();
105
106 private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
107 private static final int HEADER_SIZE = 8;
Andres Morales963295e2014-07-10 15:40:24 -0700108 // Magic number to mark block device as adhering to the format consumed by this service
Andres Morales28301302014-11-12 07:56:46 -0800109 private static final int PARTITION_TYPE_MARKER = 0x19901873;
William Hestera96d3d32018-12-19 13:14:51 -0800110 /** Size of the block reserved for FRP credential, including 4 bytes for the size header. */
Adrian Roos7374d3a2017-03-31 14:14:53 -0700111 private static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
112 /** Maximum size of the FRP credential handle that can be stored. */
113 private static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
William Hestera96d3d32018-12-19 13:14:51 -0800114 /**
115 * Size of the block reserved for Test Harness Mode data, including 4 bytes for the size header.
116 */
117 private static final int TEST_MODE_RESERVED_SIZE = 10000;
118 /** Maximum size of the Test Harness Mode data that can be stored. */
119 private static final int MAX_TEST_MODE_DATA_SIZE = TEST_MODE_RESERVED_SIZE - 4;
Andres Morales963295e2014-07-10 15:40:24 -0700120 // Limit to 100k as blocks larger than this might cause strain on Binder.
Andres Morales963295e2014-07-10 15:40:24 -0700121 private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
Adrian Roos7374d3a2017-03-31 14:14:53 -0700122
Andres Morales28301302014-11-12 07:56:46 -0800123 public static final int DIGEST_SIZE_BYTES = 32;
Andres Morales5ca4cc52015-03-19 16:37:54 -0700124 private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
Andres Morales74e9b182016-02-22 12:33:33 -0800125 private static final String FLASH_LOCK_PROP = "ro.boot.flash.locked";
126 private static final String FLASH_LOCK_LOCKED = "1";
127 private static final String FLASH_LOCK_UNLOCKED = "0";
Andres Morales68d4acd2014-07-01 19:40:41 -0700128
129 private final Context mContext;
130 private final String mDataBlockFile;
Andres Morales963295e2014-07-10 15:40:24 -0700131 private final Object mLock = new Object();
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800132 private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
Andres Morales6429f312014-08-04 16:35:15 -0700133
Andres Moralesa31c23d2014-10-30 15:31:31 -0700134 private int mAllowedUid = -1;
Andres Morales963295e2014-07-10 15:40:24 -0700135 private long mBlockDeviceSize;
Charles He71d2a412016-12-01 19:22:33 +0000136
137 @GuardedBy("mLock")
Charles Hea9437bd2016-11-24 14:05:00 +0000138 private boolean mIsWritable = true;
Charles Hef6f1d622016-12-01 19:22:33 +0000139
Andres Morales68d4acd2014-07-01 19:40:41 -0700140 public PersistentDataBlockService(Context context) {
141 super(context);
142 mContext = context;
143 mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
Andres Morales963295e2014-07-10 15:40:24 -0700144 mBlockDeviceSize = -1; // Load lazily
Andres Morales6429f312014-08-04 16:35:15 -0700145 }
146
Andres Moralesa31c23d2014-10-30 15:31:31 -0700147 private int getAllowedUid(int userHandle) {
Andres Morales6429f312014-08-04 16:35:15 -0700148 String allowedPackage = mContext.getResources()
Andres Morales68d4acd2014-07-01 19:40:41 -0700149 .getString(R.string.config_persistentDataPackageName);
150 PackageManager pm = mContext.getPackageManager();
151 int allowedUid = -1;
152 try {
Jeff Sharkeyc5967e92016-01-07 18:50:29 -0700153 allowedUid = pm.getPackageUidAsUser(allowedPackage,
154 PackageManager.MATCH_SYSTEM_ONLY, userHandle);
Andres Morales68d4acd2014-07-01 19:40:41 -0700155 } catch (PackageManager.NameNotFoundException e) {
156 // not expected
Andres Morales963295e2014-07-10 15:40:24 -0700157 Slog.e(TAG, "not able to find package " + allowedPackage, e);
Andres Morales68d4acd2014-07-01 19:40:41 -0700158 }
Andres Moralesa31c23d2014-10-30 15:31:31 -0700159 return allowedUid;
Andres Morales68d4acd2014-07-01 19:40:41 -0700160 }
161
162 @Override
163 public void onStart() {
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800164 // Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800165 SystemServerInitThreadPool.get().submit(() -> {
Fyodor Kupolovec00b512017-04-25 14:19:31 -0700166 mAllowedUid = getAllowedUid(UserHandle.USER_SYSTEM);
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800167 enforceChecksumValidity();
168 formatIfOemUnlockEnabled();
169 publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
170 mInitDoneSignal.countDown();
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800171 }, TAG + ".onStart");
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800172 }
173
174 @Override
175 public void onBootPhase(int phase) {
176 // Wait for initialization in onStart to finish
177 if (phase == PHASE_SYSTEM_SERVICES_READY) {
178 try {
179 if (!mInitDoneSignal.await(10, TimeUnit.SECONDS)) {
180 throw new IllegalStateException("Service " + TAG + " init timeout");
181 }
182 } catch (InterruptedException e) {
183 Thread.currentThread().interrupt();
184 throw new IllegalStateException("Service " + TAG + " init interrupted", e);
185 }
Adrian Roos7374d3a2017-03-31 14:14:53 -0700186 LocalServices.addService(PersistentDataBlockManagerInternal.class, mInternalService);
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800187 }
188 super.onBootPhase(phase);
Andres Morales68d4acd2014-07-01 19:40:41 -0700189 }
190
Andres Morales1ce7d172015-01-07 14:24:57 -0800191 private void formatIfOemUnlockEnabled() {
Andres Morales5ca4cc52015-03-19 16:37:54 -0700192 boolean enabled = doGetOemUnlockEnabled();
193 if (enabled) {
Andres Morales1ce7d172015-01-07 14:24:57 -0800194 synchronized (mLock) {
Andres Moralesc8f952c2015-03-19 08:34:55 -0700195 formatPartitionLocked(true);
Andres Morales1ce7d172015-01-07 14:24:57 -0800196 }
197 }
Andres Morales5ca4cc52015-03-19 16:37:54 -0700198
199 SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
Andres Morales1ce7d172015-01-07 14:24:57 -0800200 }
201
Amith Yamasanid2b21042016-06-03 10:12:47 -0700202 private void enforceOemUnlockReadPermission() {
203 if (mContext.checkCallingOrSelfPermission(Manifest.permission.READ_OEM_UNLOCK_STATE)
204 == PackageManager.PERMISSION_DENIED
205 && mContext.checkCallingOrSelfPermission(Manifest.permission.OEM_UNLOCK_STATE)
206 == PackageManager.PERMISSION_DENIED) {
207 throw new SecurityException("Can't access OEM unlock state. Requires "
208 + "READ_OEM_UNLOCK_STATE or OEM_UNLOCK_STATE permission.");
209 }
210 }
211
212 private void enforceOemUnlockWritePermission() {
Andres Morales68d4acd2014-07-01 19:40:41 -0700213 mContext.enforceCallingOrSelfPermission(
214 Manifest.permission.OEM_UNLOCK_STATE,
Amith Yamasanid2b21042016-06-03 10:12:47 -0700215 "Can't modify OEM unlock state");
Andres Morales68d4acd2014-07-01 19:40:41 -0700216 }
217
218 private void enforceUid(int callingUid) {
Andres Moralesa31c23d2014-10-30 15:31:31 -0700219 if (callingUid != mAllowedUid) {
Andres Morales68d4acd2014-07-01 19:40:41 -0700220 throw new SecurityException("uid " + callingUid + " not allowed to access PST");
221 }
222 }
223
Xiaohui Chenf0660782015-09-02 14:25:19 -0700224 private void enforceIsAdmin() {
225 final int userId = UserHandle.getCallingUserId();
226 final boolean isAdmin = UserManager.get(mContext).isUserAdmin(userId);
227 if (!isAdmin) {
228 throw new SecurityException(
229 "Only the Admin user is allowed to change OEM unlock state");
Andres Moralesa31c23d2014-10-30 15:31:31 -0700230 }
231 }
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100232
Mahaver Chopra3d9805d2016-07-07 16:25:05 +0100233 private void enforceUserRestriction(String userRestriction) {
234 if (UserManager.get(mContext).hasUserRestriction(userRestriction)) {
235 throw new SecurityException(
236 "OEM unlock is disallowed by user restriction: " + userRestriction);
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100237 }
238 }
239
Andres Morales963295e2014-07-10 15:40:24 -0700240 private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
Andres Morales28301302014-11-12 07:56:46 -0800241 // skip over checksum
242 inputStream.skipBytes(DIGEST_SIZE_BYTES);
243
Andres Morales68d4acd2014-07-01 19:40:41 -0700244 int totalDataSize;
245 int blockId = inputStream.readInt();
Andres Morales963295e2014-07-10 15:40:24 -0700246 if (blockId == PARTITION_TYPE_MARKER) {
Andres Morales68d4acd2014-07-01 19:40:41 -0700247 totalDataSize = inputStream.readInt();
248 } else {
249 totalDataSize = 0;
250 }
251 return totalDataSize;
252 }
253
Andres Morales963295e2014-07-10 15:40:24 -0700254 private long getBlockDeviceSize() {
255 synchronized (mLock) {
256 if (mBlockDeviceSize == -1) {
257 mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
Andres Morales68d4acd2014-07-01 19:40:41 -0700258 }
259 }
260
261 return mBlockDeviceSize;
262 }
263
William Hestera96d3d32018-12-19 13:14:51 -0800264 private long getFrpCredentialDataOffset() {
265 return getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE;
266 }
267
268 private long getTestHarnessModeDataOffset() {
269 return getFrpCredentialDataOffset() - TEST_MODE_RESERVED_SIZE;
270 }
271
Andres Morales28301302014-11-12 07:56:46 -0800272 private boolean enforceChecksumValidity() {
273 byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
274
275 synchronized (mLock) {
276 byte[] digest = computeDigestLocked(storedDigest);
277 if (digest == null || !Arrays.equals(storedDigest, digest)) {
278 Slog.i(TAG, "Formatting FRP partition...");
Andres Moralesc8f952c2015-03-19 08:34:55 -0700279 formatPartitionLocked(false);
Andres Morales28301302014-11-12 07:56:46 -0800280 return false;
281 }
282 }
283
284 return true;
285 }
286
287 private boolean computeAndWriteDigestLocked() {
288 byte[] digest = computeDigestLocked(null);
289 if (digest != null) {
290 DataOutputStream outputStream;
291 try {
292 outputStream = new DataOutputStream(
293 new FileOutputStream(new File(mDataBlockFile)));
294 } catch (FileNotFoundException e) {
295 Slog.e(TAG, "partition not available?", e);
296 return false;
297 }
298
299 try {
300 outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
301 outputStream.flush();
302 } catch (IOException e) {
303 Slog.e(TAG, "failed to write block checksum", e);
304 return false;
305 } finally {
306 IoUtils.closeQuietly(outputStream);
307 }
308 return true;
309 } else {
310 return false;
311 }
312 }
313
314 private byte[] computeDigestLocked(byte[] storedDigest) {
315 DataInputStream inputStream;
316 try {
317 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
318 } catch (FileNotFoundException e) {
319 Slog.e(TAG, "partition not available?", e);
320 return null;
321 }
322
323 MessageDigest md;
324 try {
325 md = MessageDigest.getInstance("SHA-256");
326 } catch (NoSuchAlgorithmException e) {
327 // won't ever happen -- every implementation is required to support SHA-256
328 Slog.e(TAG, "SHA-256 not supported?", e);
329 IoUtils.closeQuietly(inputStream);
330 return null;
331 }
332
333 try {
334 if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) {
335 inputStream.read(storedDigest);
336 } else {
337 inputStream.skipBytes(DIGEST_SIZE_BYTES);
338 }
339
340 int read;
341 byte[] data = new byte[1024];
342 md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest
343 while ((read = inputStream.read(data)) != -1) {
344 md.update(data, 0, read);
345 }
346 } catch (IOException e) {
347 Slog.e(TAG, "failed to read partition", e);
348 return null;
349 } finally {
350 IoUtils.closeQuietly(inputStream);
351 }
352
353 return md.digest();
354 }
355
Andres Moralesc8f952c2015-03-19 08:34:55 -0700356 private void formatPartitionLocked(boolean setOemUnlockEnabled) {
Andres Morales28301302014-11-12 07:56:46 -0800357 DataOutputStream outputStream;
358 try {
359 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
360 } catch (FileNotFoundException e) {
361 Slog.e(TAG, "partition not available?", e);
362 return;
363 }
364
365 byte[] data = new byte[DIGEST_SIZE_BYTES];
366 try {
367 outputStream.write(data, 0, DIGEST_SIZE_BYTES);
368 outputStream.writeInt(PARTITION_TYPE_MARKER);
369 outputStream.writeInt(0); // data size
370 outputStream.flush();
371 } catch (IOException e) {
372 Slog.e(TAG, "failed to format block", e);
373 return;
374 } finally {
375 IoUtils.closeQuietly(outputStream);
376 }
377
Andres Moralesc8f952c2015-03-19 08:34:55 -0700378 doSetOemUnlockEnabledLocked(setOemUnlockEnabled);
Andres Morales28301302014-11-12 07:56:46 -0800379 computeAndWriteDigestLocked();
380 }
381
382 private void doSetOemUnlockEnabledLocked(boolean enabled) {
383 FileOutputStream outputStream;
384 try {
385 outputStream = new FileOutputStream(new File(mDataBlockFile));
386 } catch (FileNotFoundException e) {
387 Slog.e(TAG, "partition not available", e);
388 return;
389 }
390
391 try {
392 FileChannel channel = outputStream.getChannel();
393
394 channel.position(getBlockDeviceSize() - 1);
395
396 ByteBuffer data = ByteBuffer.allocate(1);
397 data.put(enabled ? (byte) 1 : (byte) 0);
398 data.flip();
399 channel.write(data);
400 outputStream.flush();
401 } catch (IOException e) {
402 Slog.e(TAG, "unable to access persistent partition", e);
403 return;
404 } finally {
Andres Morales5ca4cc52015-03-19 16:37:54 -0700405 SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
Andres Morales28301302014-11-12 07:56:46 -0800406 IoUtils.closeQuietly(outputStream);
407 }
408 }
409
Andres Morales1ce7d172015-01-07 14:24:57 -0800410 private boolean doGetOemUnlockEnabled() {
411 DataInputStream inputStream;
412 try {
413 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
414 } catch (FileNotFoundException e) {
415 Slog.e(TAG, "partition not available");
416 return false;
417 }
418
419 try {
420 synchronized (mLock) {
421 inputStream.skip(getBlockDeviceSize() - 1);
422 return inputStream.readByte() != 0;
423 }
424 } catch (IOException e) {
425 Slog.e(TAG, "unable to access persistent partition", e);
426 return false;
427 } finally {
428 IoUtils.closeQuietly(inputStream);
429 }
430 }
431
Andrew Scull0e5d5b12017-06-14 16:27:04 +0100432 private long doGetMaximumDataBlockSize() {
433 long actualSize = getBlockDeviceSize() - HEADER_SIZE - DIGEST_SIZE_BYTES
William Hestera96d3d32018-12-19 13:14:51 -0800434 - TEST_MODE_RESERVED_SIZE - FRP_CREDENTIAL_RESERVED_SIZE - 1;
Andrew Scull0e5d5b12017-06-14 16:27:04 +0100435 return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
436 }
437
Andres Morales963295e2014-07-10 15:40:24 -0700438 private native long nativeGetBlockDeviceSize(String path);
439 private native int nativeWipe(String path);
Andres Morales68d4acd2014-07-01 19:40:41 -0700440
441 private final IBinder mService = new IPersistentDataBlockService.Stub() {
William Hestera96d3d32018-12-19 13:14:51 -0800442
443 /**
444 * Write the data to the persistent data block.
445 *
446 * @return a positive integer of the number of bytes that were written if successful,
447 * otherwise a negative integer indicating there was a problem
448 */
Andres Morales68d4acd2014-07-01 19:40:41 -0700449 @Override
450 public int write(byte[] data) throws RemoteException {
451 enforceUid(Binder.getCallingUid());
452
453 // Need to ensure we don't write over the last byte
Andrew Scull0e5d5b12017-06-14 16:27:04 +0100454 long maxBlockSize = doGetMaximumDataBlockSize();
Andres Morales963295e2014-07-10 15:40:24 -0700455 if (data.length > maxBlockSize) {
456 // partition is ~500k so shouldn't be a problem to downcast
457 return (int) -maxBlockSize;
Andres Morales68d4acd2014-07-01 19:40:41 -0700458 }
459
460 DataOutputStream outputStream;
461 try {
462 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
463 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700464 Slog.e(TAG, "partition not available?", e);
Andres Morales68d4acd2014-07-01 19:40:41 -0700465 return -1;
466 }
467
468 ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
Andres Morales963295e2014-07-10 15:40:24 -0700469 headerAndData.putInt(PARTITION_TYPE_MARKER);
Andres Morales68d4acd2014-07-01 19:40:41 -0700470 headerAndData.putInt(data.length);
471 headerAndData.put(data);
472
Andres Morales28301302014-11-12 07:56:46 -0800473 synchronized (mLock) {
Charles Hea629c772016-11-24 14:05:00 +0000474 if (!mIsWritable) {
475 IoUtils.closeQuietly(outputStream);
476 return -1;
477 }
478
Andres Morales68d4acd2014-07-01 19:40:41 -0700479 try {
Andres Morales28301302014-11-12 07:56:46 -0800480 byte[] checksum = new byte[DIGEST_SIZE_BYTES];
481 outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
482 outputStream.write(headerAndData.array());
483 outputStream.flush();
Andres Morales68d4acd2014-07-01 19:40:41 -0700484 } catch (IOException e) {
Andres Morales28301302014-11-12 07:56:46 -0800485 Slog.e(TAG, "failed writing to the persistent data block", e);
486 return -1;
487 } finally {
488 IoUtils.closeQuietly(outputStream);
489 }
490
491 if (computeAndWriteDigestLocked()) {
492 return data.length;
493 } else {
494 return -1;
Andres Morales68d4acd2014-07-01 19:40:41 -0700495 }
496 }
497 }
498
499 @Override
Andres Morales963295e2014-07-10 15:40:24 -0700500 public byte[] read() {
Andres Morales68d4acd2014-07-01 19:40:41 -0700501 enforceUid(Binder.getCallingUid());
Andres Morales28301302014-11-12 07:56:46 -0800502 if (!enforceChecksumValidity()) {
503 return new byte[0];
504 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700505
506 DataInputStream inputStream;
507 try {
508 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
509 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700510 Slog.e(TAG, "partition not available?", e);
511 return null;
Andres Morales68d4acd2014-07-01 19:40:41 -0700512 }
513
514 try {
Andres Morales963295e2014-07-10 15:40:24 -0700515 synchronized (mLock) {
516 int totalDataSize = getTotalDataSizeLocked(inputStream);
517
518 if (totalDataSize == 0) {
519 return new byte[0];
520 }
521
522 byte[] data = new byte[totalDataSize];
523 int read = inputStream.read(data, 0, totalDataSize);
524 if (read < totalDataSize) {
525 // something went wrong, not returning potentially corrupt data
526 Slog.e(TAG, "failed to read entire data block. bytes read: " +
527 read + "/" + totalDataSize);
528 return null;
529 }
530 return data;
531 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700532 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700533 Slog.e(TAG, "failed to read data", e);
534 return null;
Andres Morales68d4acd2014-07-01 19:40:41 -0700535 } finally {
536 try {
537 inputStream.close();
538 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700539 Slog.e(TAG, "failed to close OutputStream");
540 }
541 }
542 }
543
544 @Override
545 public void wipe() {
Amith Yamasanid2b21042016-06-03 10:12:47 -0700546 enforceOemUnlockWritePermission();
Andres Morales963295e2014-07-10 15:40:24 -0700547
548 synchronized (mLock) {
549 int ret = nativeWipe(mDataBlockFile);
550
551 if (ret < 0) {
552 Slog.e(TAG, "failed to wipe persistent partition");
Charles Hea629c772016-11-24 14:05:00 +0000553 } else {
554 mIsWritable = false;
555 Slog.i(TAG, "persistent partition now wiped and unwritable");
Andres Morales68d4acd2014-07-01 19:40:41 -0700556 }
557 }
558 }
559
560 @Override
Steven Ngdc20ba62016-04-26 18:19:04 +0100561 public void setOemUnlockEnabled(boolean enabled) throws SecurityException {
Guang Zhu514c5802014-09-12 15:14:00 -0700562 // do not allow monkey to flip the flag
563 if (ActivityManager.isUserAMonkey()) {
564 return;
565 }
Steven Ngbfe1b042016-05-16 11:20:57 +0100566
Amith Yamasanid2b21042016-06-03 10:12:47 -0700567 enforceOemUnlockWritePermission();
Steven Ngbfe1b042016-05-16 11:20:57 +0100568 enforceIsAdmin();
569
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100570 if (enabled) {
Mahaver Chopra3d9805d2016-07-07 16:25:05 +0100571 // Do not allow oem unlock to be enabled if it's disallowed by a user restriction.
572 enforceUserRestriction(UserManager.DISALLOW_OEM_UNLOCK);
573 enforceUserRestriction(UserManager.DISALLOW_FACTORY_RESET);
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100574 }
Andres Morales28301302014-11-12 07:56:46 -0800575 synchronized (mLock) {
576 doSetOemUnlockEnabledLocked(enabled);
577 computeAndWriteDigestLocked();
Andres Morales68d4acd2014-07-01 19:40:41 -0700578 }
579 }
580
581 @Override
582 public boolean getOemUnlockEnabled() {
Amith Yamasanid2b21042016-06-03 10:12:47 -0700583 enforceOemUnlockReadPermission();
Andres Morales1ce7d172015-01-07 14:24:57 -0800584 return doGetOemUnlockEnabled();
Andres Morales68d4acd2014-07-01 19:40:41 -0700585 }
586
587 @Override
Andres Morales74e9b182016-02-22 12:33:33 -0800588 public int getFlashLockState() {
Amith Yamasanid2b21042016-06-03 10:12:47 -0700589 enforceOemUnlockReadPermission();
Andres Morales74e9b182016-02-22 12:33:33 -0800590 String locked = SystemProperties.get(FLASH_LOCK_PROP);
591 switch (locked) {
592 case FLASH_LOCK_LOCKED:
593 return PersistentDataBlockManager.FLASH_LOCK_LOCKED;
594 case FLASH_LOCK_UNLOCKED:
595 return PersistentDataBlockManager.FLASH_LOCK_UNLOCKED;
596 default:
597 return PersistentDataBlockManager.FLASH_LOCK_UNKNOWN;
598 }
599 }
600
601 @Override
Andres Morales68d4acd2014-07-01 19:40:41 -0700602 public int getDataBlockSize() {
Craig Lafayette66445a62015-03-27 09:01:43 -0400603 enforcePersistentDataBlockAccess();
Andres Morales68d4acd2014-07-01 19:40:41 -0700604
605 DataInputStream inputStream;
606 try {
607 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
608 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700609 Slog.e(TAG, "partition not available");
Andres Morales68d4acd2014-07-01 19:40:41 -0700610 return 0;
611 }
612
613 try {
Andres Morales963295e2014-07-10 15:40:24 -0700614 synchronized (mLock) {
615 return getTotalDataSizeLocked(inputStream);
616 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700617 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700618 Slog.e(TAG, "error reading data block size");
Andres Morales68d4acd2014-07-01 19:40:41 -0700619 return 0;
620 } finally {
Andres Morales963295e2014-07-10 15:40:24 -0700621 IoUtils.closeQuietly(inputStream);
Andres Morales68d4acd2014-07-01 19:40:41 -0700622 }
623 }
Andres Morales963295e2014-07-10 15:40:24 -0700624
Craig Lafayette66445a62015-03-27 09:01:43 -0400625 private void enforcePersistentDataBlockAccess() {
626 if (mContext.checkCallingPermission(Manifest.permission.ACCESS_PDB_STATE)
627 != PackageManager.PERMISSION_GRANTED) {
628 enforceUid(Binder.getCallingUid());
629 }
630 }
631
Andres Morales963295e2014-07-10 15:40:24 -0700632 @Override
633 public long getMaximumDataBlockSize() {
Andrew Scull0e5d5b12017-06-14 16:27:04 +0100634 enforceUid(Binder.getCallingUid());
635 return doGetMaximumDataBlockSize();
Andres Morales963295e2014-07-10 15:40:24 -0700636 }
Adrian Roos7374d3a2017-03-31 14:14:53 -0700637
638 @Override
639 public boolean hasFrpCredentialHandle() {
640 enforcePersistentDataBlockAccess();
Adrian Roosb2375942018-01-19 22:31:28 +0100641 try {
642 return mInternalService.getFrpCredentialHandle() != null;
643 } catch (IllegalStateException e) {
644 Slog.e(TAG, "error reading frp handle", e);
645 throw new UnsupportedOperationException("cannot read frp credential");
646 }
Adrian Roos7374d3a2017-03-31 14:14:53 -0700647 }
648 };
649
650 private PersistentDataBlockManagerInternal mInternalService =
651 new PersistentDataBlockManagerInternal() {
652
653 @Override
654 public void setFrpCredentialHandle(byte[] handle) {
William Hestera96d3d32018-12-19 13:14:51 -0800655 writeInternal(handle, getFrpCredentialDataOffset(), MAX_FRP_CREDENTIAL_HANDLE_SIZE);
656 }
Adrian Roos7374d3a2017-03-31 14:14:53 -0700657
William Hestera96d3d32018-12-19 13:14:51 -0800658 @Override
659 public byte[] getFrpCredentialHandle() {
660 return readInternal(getFrpCredentialDataOffset(), MAX_FRP_CREDENTIAL_HANDLE_SIZE);
661 }
662
663 @Override
664 public void setTestHarnessModeData(byte[] data) {
665 writeInternal(data, getTestHarnessModeDataOffset(), MAX_TEST_MODE_DATA_SIZE);
666 }
667
668 @Override
669 public byte[] getTestHarnessModeData() {
670 byte[] data = readInternal(getTestHarnessModeDataOffset(), MAX_TEST_MODE_DATA_SIZE);
671 if (data == null) {
672 return new byte[0];
673 }
674 return data;
675 }
676
677 @Override
678 public void clearTestHarnessModeData() {
679 int size = Math.min(MAX_TEST_MODE_DATA_SIZE, getTestHarnessModeData().length) + 4;
680 writeDataBuffer(getTestHarnessModeDataOffset(), ByteBuffer.allocate(size));
681 }
682
683 private void writeInternal(byte[] data, long offset, int dataLength) {
684 checkArgument(data == null || data.length > 0, "data must be null or non-empty");
685 checkArgument(
686 data == null || data.length <= dataLength,
687 "data must not be longer than " + dataLength);
688
689 ByteBuffer dataBuffer = ByteBuffer.allocate(dataLength + 4);
690 dataBuffer.putInt(data == null ? 0 : data.length);
691 if (data != null) {
692 dataBuffer.put(data);
693 }
694 dataBuffer.flip();
695
696 writeDataBuffer(offset, dataBuffer);
697 }
698
699 private void writeDataBuffer(long offset, ByteBuffer dataBuffer) {
Adrian Roos7374d3a2017-03-31 14:14:53 -0700700 FileOutputStream outputStream;
701 try {
702 outputStream = new FileOutputStream(new File(mDataBlockFile));
703 } catch (FileNotFoundException e) {
704 Slog.e(TAG, "partition not available", e);
705 return;
706 }
Adrian Roos7374d3a2017-03-31 14:14:53 -0700707 synchronized (mLock) {
708 if (!mIsWritable) {
709 IoUtils.closeQuietly(outputStream);
710 return;
711 }
Adrian Roos7374d3a2017-03-31 14:14:53 -0700712 try {
713 FileChannel channel = outputStream.getChannel();
William Hestera96d3d32018-12-19 13:14:51 -0800714 channel.position(offset);
715 channel.write(dataBuffer);
Adrian Roos7374d3a2017-03-31 14:14:53 -0700716 outputStream.flush();
717 } catch (IOException e) {
718 Slog.e(TAG, "unable to access persistent partition", e);
719 return;
720 } finally {
721 IoUtils.closeQuietly(outputStream);
722 }
723
724 computeAndWriteDigestLocked();
725 }
726 }
727
William Hestera96d3d32018-12-19 13:14:51 -0800728 private byte[] readInternal(long offset, int maxLength) {
Adrian Roos7374d3a2017-03-31 14:14:53 -0700729 if (!enforceChecksumValidity()) {
Adrian Roosb2375942018-01-19 22:31:28 +0100730 throw new IllegalStateException("invalid checksum");
Adrian Roos7374d3a2017-03-31 14:14:53 -0700731 }
732
733 DataInputStream inputStream;
734 try {
735 inputStream = new DataInputStream(
736 new FileInputStream(new File(mDataBlockFile)));
737 } catch (FileNotFoundException e) {
William Hestera96d3d32018-12-19 13:14:51 -0800738 throw new IllegalStateException("persistent partition not available");
Adrian Roos7374d3a2017-03-31 14:14:53 -0700739 }
740
741 try {
742 synchronized (mLock) {
William Hestera96d3d32018-12-19 13:14:51 -0800743 inputStream.skip(offset);
Adrian Roos7374d3a2017-03-31 14:14:53 -0700744 int length = inputStream.readInt();
William Hestera96d3d32018-12-19 13:14:51 -0800745 if (length <= 0 || length > maxLength) {
Adrian Roos7374d3a2017-03-31 14:14:53 -0700746 return null;
747 }
748 byte[] bytes = new byte[length];
749 inputStream.readFully(bytes);
750 return bytes;
751 }
752 } catch (IOException e) {
William Hestera96d3d32018-12-19 13:14:51 -0800753 throw new IllegalStateException("persistent partition not readable", e);
Adrian Roos7374d3a2017-03-31 14:14:53 -0700754 } finally {
755 IoUtils.closeQuietly(inputStream);
756 }
757 }
Andrew Scull1a5b76c2018-01-19 16:56:22 +0000758
759 @Override
760 public void forceOemUnlockEnabled(boolean enabled) {
761 synchronized (mLock) {
762 doSetOemUnlockEnabledLocked(enabled);
763 computeAndWriteDigestLocked();
764 }
765 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700766 };
767}