blob: 21093b9f6f88c96b5211094f73f7f650b15b3a2c [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;
Andres Morales68d4acd2014-07-01 19:40:41 -070021import android.content.Context;
22import android.content.pm.PackageManager;
23import android.os.Binder;
24import android.os.IBinder;
25import android.os.RemoteException;
26import android.os.SystemProperties;
Andres Morales6429f312014-08-04 16:35:15 -070027import android.os.UserHandle;
Xiaohui Chenf0660782015-09-02 14:25:19 -070028import android.os.UserManager;
Andres Morales68d4acd2014-07-01 19:40:41 -070029import android.service.persistentdata.IPersistentDataBlockService;
Andres Morales74e9b182016-02-22 12:33:33 -080030import android.service.persistentdata.PersistentDataBlockManager;
Adrian Roosb2375942018-01-19 22:31:28 +010031import android.util.Log;
Andres Morales963295e2014-07-10 15:40:24 -070032import android.util.Slog;
Guang Zhu514c5802014-09-12 15:14:00 -070033
Andres Morales68d4acd2014-07-01 19:40:41 -070034import com.android.internal.R;
Charles Hef6f1d622016-12-01 19:22:33 +000035import com.android.internal.annotations.GuardedBy;
Adrian Roos7374d3a2017-03-31 14:14:53 -070036import com.android.internal.util.Preconditions;
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}
Andres Morales68d4acd2014-07-01 19:40:41 -070068 */
69public class PersistentDataBlockService extends SystemService {
70 private static final String TAG = PersistentDataBlockService.class.getSimpleName();
71
72 private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
73 private static final int HEADER_SIZE = 8;
Andres Morales963295e2014-07-10 15:40:24 -070074 // Magic number to mark block device as adhering to the format consumed by this service
Andres Morales28301302014-11-12 07:56:46 -080075 private static final int PARTITION_TYPE_MARKER = 0x19901873;
Adrian Roos7374d3a2017-03-31 14:14:53 -070076 /** Size of the block reserved for FPR credential, including 4 bytes for the size header. */
77 private static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
78 /** Maximum size of the FRP credential handle that can be stored. */
79 private static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
Andres Morales963295e2014-07-10 15:40:24 -070080 // Limit to 100k as blocks larger than this might cause strain on Binder.
Andres Morales963295e2014-07-10 15:40:24 -070081 private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
Adrian Roos7374d3a2017-03-31 14:14:53 -070082
Andres Morales28301302014-11-12 07:56:46 -080083 public static final int DIGEST_SIZE_BYTES = 32;
Andres Morales5ca4cc52015-03-19 16:37:54 -070084 private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
Andres Morales74e9b182016-02-22 12:33:33 -080085 private static final String FLASH_LOCK_PROP = "ro.boot.flash.locked";
86 private static final String FLASH_LOCK_LOCKED = "1";
87 private static final String FLASH_LOCK_UNLOCKED = "0";
Andres Morales68d4acd2014-07-01 19:40:41 -070088
89 private final Context mContext;
90 private final String mDataBlockFile;
Andres Morales963295e2014-07-10 15:40:24 -070091 private final Object mLock = new Object();
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -080092 private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
Andres Morales6429f312014-08-04 16:35:15 -070093
Andres Moralesa31c23d2014-10-30 15:31:31 -070094 private int mAllowedUid = -1;
Andres Morales963295e2014-07-10 15:40:24 -070095 private long mBlockDeviceSize;
Charles He71d2a412016-12-01 19:22:33 +000096
97 @GuardedBy("mLock")
Charles Hea9437bd2016-11-24 14:05:00 +000098 private boolean mIsWritable = true;
Charles Hef6f1d622016-12-01 19:22:33 +000099
Andres Morales68d4acd2014-07-01 19:40:41 -0700100 public PersistentDataBlockService(Context context) {
101 super(context);
102 mContext = context;
103 mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
Andres Morales963295e2014-07-10 15:40:24 -0700104 mBlockDeviceSize = -1; // Load lazily
Andres Morales6429f312014-08-04 16:35:15 -0700105 }
106
Andres Moralesa31c23d2014-10-30 15:31:31 -0700107 private int getAllowedUid(int userHandle) {
Andres Morales6429f312014-08-04 16:35:15 -0700108 String allowedPackage = mContext.getResources()
Andres Morales68d4acd2014-07-01 19:40:41 -0700109 .getString(R.string.config_persistentDataPackageName);
110 PackageManager pm = mContext.getPackageManager();
111 int allowedUid = -1;
112 try {
Jeff Sharkeyc5967e92016-01-07 18:50:29 -0700113 allowedUid = pm.getPackageUidAsUser(allowedPackage,
114 PackageManager.MATCH_SYSTEM_ONLY, userHandle);
Andres Morales68d4acd2014-07-01 19:40:41 -0700115 } catch (PackageManager.NameNotFoundException e) {
116 // not expected
Andres Morales963295e2014-07-10 15:40:24 -0700117 Slog.e(TAG, "not able to find package " + allowedPackage, e);
Andres Morales68d4acd2014-07-01 19:40:41 -0700118 }
Andres Moralesa31c23d2014-10-30 15:31:31 -0700119 return allowedUid;
Andres Morales68d4acd2014-07-01 19:40:41 -0700120 }
121
122 @Override
123 public void onStart() {
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800124 // Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800125 SystemServerInitThreadPool.get().submit(() -> {
Fyodor Kupolovec00b512017-04-25 14:19:31 -0700126 mAllowedUid = getAllowedUid(UserHandle.USER_SYSTEM);
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800127 enforceChecksumValidity();
128 formatIfOemUnlockEnabled();
129 publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
130 mInitDoneSignal.countDown();
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800131 }, TAG + ".onStart");
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800132 }
133
134 @Override
135 public void onBootPhase(int phase) {
136 // Wait for initialization in onStart to finish
137 if (phase == PHASE_SYSTEM_SERVICES_READY) {
138 try {
139 if (!mInitDoneSignal.await(10, TimeUnit.SECONDS)) {
140 throw new IllegalStateException("Service " + TAG + " init timeout");
141 }
142 } catch (InterruptedException e) {
143 Thread.currentThread().interrupt();
144 throw new IllegalStateException("Service " + TAG + " init interrupted", e);
145 }
Adrian Roos7374d3a2017-03-31 14:14:53 -0700146 LocalServices.addService(PersistentDataBlockManagerInternal.class, mInternalService);
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800147 }
148 super.onBootPhase(phase);
Andres Morales68d4acd2014-07-01 19:40:41 -0700149 }
150
Andres Morales1ce7d172015-01-07 14:24:57 -0800151 private void formatIfOemUnlockEnabled() {
Andres Morales5ca4cc52015-03-19 16:37:54 -0700152 boolean enabled = doGetOemUnlockEnabled();
153 if (enabled) {
Andres Morales1ce7d172015-01-07 14:24:57 -0800154 synchronized (mLock) {
Andres Moralesc8f952c2015-03-19 08:34:55 -0700155 formatPartitionLocked(true);
Andres Morales1ce7d172015-01-07 14:24:57 -0800156 }
157 }
Andres Morales5ca4cc52015-03-19 16:37:54 -0700158
159 SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
Andres Morales1ce7d172015-01-07 14:24:57 -0800160 }
161
Amith Yamasanid2b21042016-06-03 10:12:47 -0700162 private void enforceOemUnlockReadPermission() {
163 if (mContext.checkCallingOrSelfPermission(Manifest.permission.READ_OEM_UNLOCK_STATE)
164 == PackageManager.PERMISSION_DENIED
165 && mContext.checkCallingOrSelfPermission(Manifest.permission.OEM_UNLOCK_STATE)
166 == PackageManager.PERMISSION_DENIED) {
167 throw new SecurityException("Can't access OEM unlock state. Requires "
168 + "READ_OEM_UNLOCK_STATE or OEM_UNLOCK_STATE permission.");
169 }
170 }
171
172 private void enforceOemUnlockWritePermission() {
Andres Morales68d4acd2014-07-01 19:40:41 -0700173 mContext.enforceCallingOrSelfPermission(
174 Manifest.permission.OEM_UNLOCK_STATE,
Amith Yamasanid2b21042016-06-03 10:12:47 -0700175 "Can't modify OEM unlock state");
Andres Morales68d4acd2014-07-01 19:40:41 -0700176 }
177
178 private void enforceUid(int callingUid) {
Andres Moralesa31c23d2014-10-30 15:31:31 -0700179 if (callingUid != mAllowedUid) {
Andres Morales68d4acd2014-07-01 19:40:41 -0700180 throw new SecurityException("uid " + callingUid + " not allowed to access PST");
181 }
182 }
183
Xiaohui Chenf0660782015-09-02 14:25:19 -0700184 private void enforceIsAdmin() {
185 final int userId = UserHandle.getCallingUserId();
186 final boolean isAdmin = UserManager.get(mContext).isUserAdmin(userId);
187 if (!isAdmin) {
188 throw new SecurityException(
189 "Only the Admin user is allowed to change OEM unlock state");
Andres Moralesa31c23d2014-10-30 15:31:31 -0700190 }
191 }
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100192
Mahaver Chopra3d9805d2016-07-07 16:25:05 +0100193 private void enforceUserRestriction(String userRestriction) {
194 if (UserManager.get(mContext).hasUserRestriction(userRestriction)) {
195 throw new SecurityException(
196 "OEM unlock is disallowed by user restriction: " + userRestriction);
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100197 }
198 }
199
Andres Morales963295e2014-07-10 15:40:24 -0700200 private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
Andres Morales28301302014-11-12 07:56:46 -0800201 // skip over checksum
202 inputStream.skipBytes(DIGEST_SIZE_BYTES);
203
Andres Morales68d4acd2014-07-01 19:40:41 -0700204 int totalDataSize;
205 int blockId = inputStream.readInt();
Andres Morales963295e2014-07-10 15:40:24 -0700206 if (blockId == PARTITION_TYPE_MARKER) {
Andres Morales68d4acd2014-07-01 19:40:41 -0700207 totalDataSize = inputStream.readInt();
208 } else {
209 totalDataSize = 0;
210 }
211 return totalDataSize;
212 }
213
Andres Morales963295e2014-07-10 15:40:24 -0700214 private long getBlockDeviceSize() {
215 synchronized (mLock) {
216 if (mBlockDeviceSize == -1) {
217 mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
Andres Morales68d4acd2014-07-01 19:40:41 -0700218 }
219 }
220
221 return mBlockDeviceSize;
222 }
223
Andres Morales28301302014-11-12 07:56:46 -0800224 private boolean enforceChecksumValidity() {
225 byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
226
227 synchronized (mLock) {
228 byte[] digest = computeDigestLocked(storedDigest);
229 if (digest == null || !Arrays.equals(storedDigest, digest)) {
230 Slog.i(TAG, "Formatting FRP partition...");
Andres Moralesc8f952c2015-03-19 08:34:55 -0700231 formatPartitionLocked(false);
Andres Morales28301302014-11-12 07:56:46 -0800232 return false;
233 }
234 }
235
236 return true;
237 }
238
239 private boolean computeAndWriteDigestLocked() {
240 byte[] digest = computeDigestLocked(null);
241 if (digest != null) {
242 DataOutputStream outputStream;
243 try {
244 outputStream = new DataOutputStream(
245 new FileOutputStream(new File(mDataBlockFile)));
246 } catch (FileNotFoundException e) {
247 Slog.e(TAG, "partition not available?", e);
248 return false;
249 }
250
251 try {
252 outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
253 outputStream.flush();
254 } catch (IOException e) {
255 Slog.e(TAG, "failed to write block checksum", e);
256 return false;
257 } finally {
258 IoUtils.closeQuietly(outputStream);
259 }
260 return true;
261 } else {
262 return false;
263 }
264 }
265
266 private byte[] computeDigestLocked(byte[] storedDigest) {
267 DataInputStream inputStream;
268 try {
269 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
270 } catch (FileNotFoundException e) {
271 Slog.e(TAG, "partition not available?", e);
272 return null;
273 }
274
275 MessageDigest md;
276 try {
277 md = MessageDigest.getInstance("SHA-256");
278 } catch (NoSuchAlgorithmException e) {
279 // won't ever happen -- every implementation is required to support SHA-256
280 Slog.e(TAG, "SHA-256 not supported?", e);
281 IoUtils.closeQuietly(inputStream);
282 return null;
283 }
284
285 try {
286 if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) {
287 inputStream.read(storedDigest);
288 } else {
289 inputStream.skipBytes(DIGEST_SIZE_BYTES);
290 }
291
292 int read;
293 byte[] data = new byte[1024];
294 md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest
295 while ((read = inputStream.read(data)) != -1) {
296 md.update(data, 0, read);
297 }
298 } catch (IOException e) {
299 Slog.e(TAG, "failed to read partition", e);
300 return null;
301 } finally {
302 IoUtils.closeQuietly(inputStream);
303 }
304
305 return md.digest();
306 }
307
Andres Moralesc8f952c2015-03-19 08:34:55 -0700308 private void formatPartitionLocked(boolean setOemUnlockEnabled) {
Andres Morales28301302014-11-12 07:56:46 -0800309 DataOutputStream outputStream;
310 try {
311 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
312 } catch (FileNotFoundException e) {
313 Slog.e(TAG, "partition not available?", e);
314 return;
315 }
316
317 byte[] data = new byte[DIGEST_SIZE_BYTES];
318 try {
319 outputStream.write(data, 0, DIGEST_SIZE_BYTES);
320 outputStream.writeInt(PARTITION_TYPE_MARKER);
321 outputStream.writeInt(0); // data size
322 outputStream.flush();
323 } catch (IOException e) {
324 Slog.e(TAG, "failed to format block", e);
325 return;
326 } finally {
327 IoUtils.closeQuietly(outputStream);
328 }
329
Andres Moralesc8f952c2015-03-19 08:34:55 -0700330 doSetOemUnlockEnabledLocked(setOemUnlockEnabled);
Andres Morales28301302014-11-12 07:56:46 -0800331 computeAndWriteDigestLocked();
332 }
333
334 private void doSetOemUnlockEnabledLocked(boolean enabled) {
335 FileOutputStream outputStream;
336 try {
337 outputStream = new FileOutputStream(new File(mDataBlockFile));
338 } catch (FileNotFoundException e) {
339 Slog.e(TAG, "partition not available", e);
340 return;
341 }
342
343 try {
344 FileChannel channel = outputStream.getChannel();
345
346 channel.position(getBlockDeviceSize() - 1);
347
348 ByteBuffer data = ByteBuffer.allocate(1);
349 data.put(enabled ? (byte) 1 : (byte) 0);
350 data.flip();
351 channel.write(data);
352 outputStream.flush();
353 } catch (IOException e) {
354 Slog.e(TAG, "unable to access persistent partition", e);
355 return;
356 } finally {
Andres Morales5ca4cc52015-03-19 16:37:54 -0700357 SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
Andres Morales28301302014-11-12 07:56:46 -0800358 IoUtils.closeQuietly(outputStream);
359 }
360 }
361
Andres Morales1ce7d172015-01-07 14:24:57 -0800362 private boolean doGetOemUnlockEnabled() {
363 DataInputStream inputStream;
364 try {
365 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
366 } catch (FileNotFoundException e) {
367 Slog.e(TAG, "partition not available");
368 return false;
369 }
370
371 try {
372 synchronized (mLock) {
373 inputStream.skip(getBlockDeviceSize() - 1);
374 return inputStream.readByte() != 0;
375 }
376 } catch (IOException e) {
377 Slog.e(TAG, "unable to access persistent partition", e);
378 return false;
379 } finally {
380 IoUtils.closeQuietly(inputStream);
381 }
382 }
383
Andrew Scull0e5d5b12017-06-14 16:27:04 +0100384 private long doGetMaximumDataBlockSize() {
385 long actualSize = getBlockDeviceSize() - HEADER_SIZE - DIGEST_SIZE_BYTES
386 - FRP_CREDENTIAL_RESERVED_SIZE - 1;
387 return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
388 }
389
Andres Morales963295e2014-07-10 15:40:24 -0700390 private native long nativeGetBlockDeviceSize(String path);
391 private native int nativeWipe(String path);
Andres Morales68d4acd2014-07-01 19:40:41 -0700392
393 private final IBinder mService = new IPersistentDataBlockService.Stub() {
394 @Override
395 public int write(byte[] data) throws RemoteException {
396 enforceUid(Binder.getCallingUid());
397
398 // Need to ensure we don't write over the last byte
Andrew Scull0e5d5b12017-06-14 16:27:04 +0100399 long maxBlockSize = doGetMaximumDataBlockSize();
Andres Morales963295e2014-07-10 15:40:24 -0700400 if (data.length > maxBlockSize) {
401 // partition is ~500k so shouldn't be a problem to downcast
402 return (int) -maxBlockSize;
Andres Morales68d4acd2014-07-01 19:40:41 -0700403 }
404
405 DataOutputStream outputStream;
406 try {
407 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
408 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700409 Slog.e(TAG, "partition not available?", e);
Andres Morales68d4acd2014-07-01 19:40:41 -0700410 return -1;
411 }
412
413 ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
Andres Morales963295e2014-07-10 15:40:24 -0700414 headerAndData.putInt(PARTITION_TYPE_MARKER);
Andres Morales68d4acd2014-07-01 19:40:41 -0700415 headerAndData.putInt(data.length);
416 headerAndData.put(data);
417
Andres Morales28301302014-11-12 07:56:46 -0800418 synchronized (mLock) {
Charles Hea629c772016-11-24 14:05:00 +0000419 if (!mIsWritable) {
420 IoUtils.closeQuietly(outputStream);
421 return -1;
422 }
423
Andres Morales68d4acd2014-07-01 19:40:41 -0700424 try {
Andres Morales28301302014-11-12 07:56:46 -0800425 byte[] checksum = new byte[DIGEST_SIZE_BYTES];
426 outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
427 outputStream.write(headerAndData.array());
428 outputStream.flush();
Andres Morales68d4acd2014-07-01 19:40:41 -0700429 } catch (IOException e) {
Andres Morales28301302014-11-12 07:56:46 -0800430 Slog.e(TAG, "failed writing to the persistent data block", e);
431 return -1;
432 } finally {
433 IoUtils.closeQuietly(outputStream);
434 }
435
436 if (computeAndWriteDigestLocked()) {
437 return data.length;
438 } else {
439 return -1;
Andres Morales68d4acd2014-07-01 19:40:41 -0700440 }
441 }
442 }
443
444 @Override
Andres Morales963295e2014-07-10 15:40:24 -0700445 public byte[] read() {
Andres Morales68d4acd2014-07-01 19:40:41 -0700446 enforceUid(Binder.getCallingUid());
Andres Morales28301302014-11-12 07:56:46 -0800447 if (!enforceChecksumValidity()) {
448 return new byte[0];
449 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700450
451 DataInputStream inputStream;
452 try {
453 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
454 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700455 Slog.e(TAG, "partition not available?", e);
456 return null;
Andres Morales68d4acd2014-07-01 19:40:41 -0700457 }
458
459 try {
Andres Morales963295e2014-07-10 15:40:24 -0700460 synchronized (mLock) {
461 int totalDataSize = getTotalDataSizeLocked(inputStream);
462
463 if (totalDataSize == 0) {
464 return new byte[0];
465 }
466
467 byte[] data = new byte[totalDataSize];
468 int read = inputStream.read(data, 0, totalDataSize);
469 if (read < totalDataSize) {
470 // something went wrong, not returning potentially corrupt data
471 Slog.e(TAG, "failed to read entire data block. bytes read: " +
472 read + "/" + totalDataSize);
473 return null;
474 }
475 return data;
476 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700477 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700478 Slog.e(TAG, "failed to read data", e);
479 return null;
Andres Morales68d4acd2014-07-01 19:40:41 -0700480 } finally {
481 try {
482 inputStream.close();
483 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700484 Slog.e(TAG, "failed to close OutputStream");
485 }
486 }
487 }
488
489 @Override
490 public void wipe() {
Amith Yamasanid2b21042016-06-03 10:12:47 -0700491 enforceOemUnlockWritePermission();
Andres Morales963295e2014-07-10 15:40:24 -0700492
493 synchronized (mLock) {
494 int ret = nativeWipe(mDataBlockFile);
495
496 if (ret < 0) {
497 Slog.e(TAG, "failed to wipe persistent partition");
Charles Hea629c772016-11-24 14:05:00 +0000498 } else {
499 mIsWritable = false;
500 Slog.i(TAG, "persistent partition now wiped and unwritable");
Andres Morales68d4acd2014-07-01 19:40:41 -0700501 }
502 }
503 }
504
505 @Override
Steven Ngdc20ba62016-04-26 18:19:04 +0100506 public void setOemUnlockEnabled(boolean enabled) throws SecurityException {
Guang Zhu514c5802014-09-12 15:14:00 -0700507 // do not allow monkey to flip the flag
508 if (ActivityManager.isUserAMonkey()) {
509 return;
510 }
Steven Ngbfe1b042016-05-16 11:20:57 +0100511
Amith Yamasanid2b21042016-06-03 10:12:47 -0700512 enforceOemUnlockWritePermission();
Steven Ngbfe1b042016-05-16 11:20:57 +0100513 enforceIsAdmin();
514
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100515 if (enabled) {
Mahaver Chopra3d9805d2016-07-07 16:25:05 +0100516 // Do not allow oem unlock to be enabled if it's disallowed by a user restriction.
517 enforceUserRestriction(UserManager.DISALLOW_OEM_UNLOCK);
518 enforceUserRestriction(UserManager.DISALLOW_FACTORY_RESET);
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100519 }
Andres Morales28301302014-11-12 07:56:46 -0800520 synchronized (mLock) {
521 doSetOemUnlockEnabledLocked(enabled);
522 computeAndWriteDigestLocked();
Andres Morales68d4acd2014-07-01 19:40:41 -0700523 }
524 }
525
526 @Override
527 public boolean getOemUnlockEnabled() {
Amith Yamasanid2b21042016-06-03 10:12:47 -0700528 enforceOemUnlockReadPermission();
Andres Morales1ce7d172015-01-07 14:24:57 -0800529 return doGetOemUnlockEnabled();
Andres Morales68d4acd2014-07-01 19:40:41 -0700530 }
531
532 @Override
Andres Morales74e9b182016-02-22 12:33:33 -0800533 public int getFlashLockState() {
Amith Yamasanid2b21042016-06-03 10:12:47 -0700534 enforceOemUnlockReadPermission();
Andres Morales74e9b182016-02-22 12:33:33 -0800535 String locked = SystemProperties.get(FLASH_LOCK_PROP);
536 switch (locked) {
537 case FLASH_LOCK_LOCKED:
538 return PersistentDataBlockManager.FLASH_LOCK_LOCKED;
539 case FLASH_LOCK_UNLOCKED:
540 return PersistentDataBlockManager.FLASH_LOCK_UNLOCKED;
541 default:
542 return PersistentDataBlockManager.FLASH_LOCK_UNKNOWN;
543 }
544 }
545
546 @Override
Andres Morales68d4acd2014-07-01 19:40:41 -0700547 public int getDataBlockSize() {
Craig Lafayette66445a62015-03-27 09:01:43 -0400548 enforcePersistentDataBlockAccess();
Andres Morales68d4acd2014-07-01 19:40:41 -0700549
550 DataInputStream inputStream;
551 try {
552 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
553 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700554 Slog.e(TAG, "partition not available");
Andres Morales68d4acd2014-07-01 19:40:41 -0700555 return 0;
556 }
557
558 try {
Andres Morales963295e2014-07-10 15:40:24 -0700559 synchronized (mLock) {
560 return getTotalDataSizeLocked(inputStream);
561 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700562 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700563 Slog.e(TAG, "error reading data block size");
Andres Morales68d4acd2014-07-01 19:40:41 -0700564 return 0;
565 } finally {
Andres Morales963295e2014-07-10 15:40:24 -0700566 IoUtils.closeQuietly(inputStream);
Andres Morales68d4acd2014-07-01 19:40:41 -0700567 }
568 }
Andres Morales963295e2014-07-10 15:40:24 -0700569
Craig Lafayette66445a62015-03-27 09:01:43 -0400570 private void enforcePersistentDataBlockAccess() {
571 if (mContext.checkCallingPermission(Manifest.permission.ACCESS_PDB_STATE)
572 != PackageManager.PERMISSION_GRANTED) {
573 enforceUid(Binder.getCallingUid());
574 }
575 }
576
Andres Morales963295e2014-07-10 15:40:24 -0700577 @Override
578 public long getMaximumDataBlockSize() {
Andrew Scull0e5d5b12017-06-14 16:27:04 +0100579 enforceUid(Binder.getCallingUid());
580 return doGetMaximumDataBlockSize();
Andres Morales963295e2014-07-10 15:40:24 -0700581 }
Adrian Roos7374d3a2017-03-31 14:14:53 -0700582
583 @Override
584 public boolean hasFrpCredentialHandle() {
585 enforcePersistentDataBlockAccess();
Adrian Roosb2375942018-01-19 22:31:28 +0100586 try {
587 return mInternalService.getFrpCredentialHandle() != null;
588 } catch (IllegalStateException e) {
589 Slog.e(TAG, "error reading frp handle", e);
590 throw new UnsupportedOperationException("cannot read frp credential");
591 }
Adrian Roos7374d3a2017-03-31 14:14:53 -0700592 }
593 };
594
595 private PersistentDataBlockManagerInternal mInternalService =
596 new PersistentDataBlockManagerInternal() {
597
598 @Override
599 public void setFrpCredentialHandle(byte[] handle) {
600 Preconditions.checkArgument(handle == null || handle.length > 0,
601 "handle must be null or non-empty");
602 Preconditions.checkArgument(handle == null
603 || handle.length <= MAX_FRP_CREDENTIAL_HANDLE_SIZE,
604 "handle must not be longer than " + MAX_FRP_CREDENTIAL_HANDLE_SIZE);
605
606 FileOutputStream outputStream;
607 try {
608 outputStream = new FileOutputStream(new File(mDataBlockFile));
609 } catch (FileNotFoundException e) {
610 Slog.e(TAG, "partition not available", e);
611 return;
612 }
613
614 ByteBuffer data = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
615 data.putInt(handle == null ? 0 : handle.length);
616 if (handle != null) {
617 data.put(handle);
618 }
619 data.flip();
620
621 synchronized (mLock) {
622 if (!mIsWritable) {
623 IoUtils.closeQuietly(outputStream);
624 return;
625 }
626
627 try {
628 FileChannel channel = outputStream.getChannel();
629
630 channel.position(getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE);
631 channel.write(data);
632 outputStream.flush();
633 } catch (IOException e) {
634 Slog.e(TAG, "unable to access persistent partition", e);
635 return;
636 } finally {
637 IoUtils.closeQuietly(outputStream);
638 }
639
640 computeAndWriteDigestLocked();
641 }
642 }
643
644 @Override
645 public byte[] getFrpCredentialHandle() {
646 if (!enforceChecksumValidity()) {
Adrian Roosb2375942018-01-19 22:31:28 +0100647 throw new IllegalStateException("invalid checksum");
Adrian Roos7374d3a2017-03-31 14:14:53 -0700648 }
649
650 DataInputStream inputStream;
651 try {
652 inputStream = new DataInputStream(
653 new FileInputStream(new File(mDataBlockFile)));
654 } catch (FileNotFoundException e) {
Adrian Roosb2375942018-01-19 22:31:28 +0100655 throw new IllegalStateException("frp partition not available");
Adrian Roos7374d3a2017-03-31 14:14:53 -0700656 }
657
658 try {
659 synchronized (mLock) {
660 inputStream.skip(getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE);
661 int length = inputStream.readInt();
662 if (length <= 0 || length > MAX_FRP_CREDENTIAL_HANDLE_SIZE) {
663 return null;
664 }
665 byte[] bytes = new byte[length];
666 inputStream.readFully(bytes);
667 return bytes;
668 }
669 } catch (IOException e) {
Adrian Roosb2375942018-01-19 22:31:28 +0100670 throw new IllegalStateException("frp handle not readable", e);
Adrian Roos7374d3a2017-03-31 14:14:53 -0700671 } finally {
672 IoUtils.closeQuietly(inputStream);
673 }
674 }
Andrew Scull1a5b76c2018-01-19 16:56:22 +0000675
676 @Override
677 public void forceOemUnlockEnabled(boolean enabled) {
678 synchronized (mLock) {
679 doSetOemUnlockEnabledLocked(enabled);
680 computeAndWriteDigestLocked();
681 }
682 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700683 };
684}