blob: c32a2d10b0bc128ea1e1318f31af7c38f080fa4a [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;
Andres Morales963295e2014-07-10 15:40:24 -070031import android.util.Slog;
Guang Zhu514c5802014-09-12 15:14:00 -070032
Andres Morales68d4acd2014-07-01 19:40:41 -070033import com.android.internal.R;
Charles Hef6f1d622016-12-01 19:22:33 +000034import com.android.internal.annotations.GuardedBy;
Adrian Roos7374d3a2017-03-31 14:14:53 -070035import com.android.internal.util.Preconditions;
Guang Zhu514c5802014-09-12 15:14:00 -070036
Andres Morales963295e2014-07-10 15:40:24 -070037import libcore.io.IoUtils;
Andres Morales68d4acd2014-07-01 19:40:41 -070038
39import java.io.DataInputStream;
40import java.io.DataOutputStream;
41import java.io.File;
42import java.io.FileInputStream;
43import java.io.FileNotFoundException;
44import java.io.FileOutputStream;
45import java.io.IOException;
46import java.nio.ByteBuffer;
47import java.nio.channels.FileChannel;
Andres Morales28301302014-11-12 07:56:46 -080048import java.security.MessageDigest;
49import java.security.NoSuchAlgorithmException;
50import java.util.Arrays;
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -080051import java.util.concurrent.CountDownLatch;
52import java.util.concurrent.TimeUnit;
Andres Morales68d4acd2014-07-01 19:40:41 -070053
54/**
55 * Service for reading and writing blocks to a persistent partition.
Andres Morales963295e2014-07-10 15:40:24 -070056 * This data will live across factory resets not initiated via the Settings UI.
57 * When a device is factory reset through Settings this data is wiped.
Andres Morales68d4acd2014-07-01 19:40:41 -070058 *
Charles Hea629c772016-11-24 14:05:00 +000059 * Allows writing one block at a time. Namely, each time {@link IPersistentDataBlockService#write}
60 * is called, it will overwrite the data that was previously written on the block.
Andres Morales68d4acd2014-07-01 19:40:41 -070061 *
62 * Clients can query the size of the currently written block via
Charles Hea629c772016-11-24 14:05:00 +000063 * {@link IPersistentDataBlockService#getDataBlockSize}
Andres Morales68d4acd2014-07-01 19:40:41 -070064 *
Charles Hea629c772016-11-24 14:05:00 +000065 * Clients can read any number of bytes from the currently written block up to its total size by
66 * invoking {@link IPersistentDataBlockService#read}
Andres Morales68d4acd2014-07-01 19:40:41 -070067 */
68public class PersistentDataBlockService extends SystemService {
69 private static final String TAG = PersistentDataBlockService.class.getSimpleName();
70
71 private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
72 private static final int HEADER_SIZE = 8;
Andres Morales963295e2014-07-10 15:40:24 -070073 // Magic number to mark block device as adhering to the format consumed by this service
Andres Morales28301302014-11-12 07:56:46 -080074 private static final int PARTITION_TYPE_MARKER = 0x19901873;
Adrian Roos7374d3a2017-03-31 14:14:53 -070075 /** Size of the block reserved for FPR credential, including 4 bytes for the size header. */
76 private static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
77 /** Maximum size of the FRP credential handle that can be stored. */
78 private static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
Andres Morales963295e2014-07-10 15:40:24 -070079 // Limit to 100k as blocks larger than this might cause strain on Binder.
Andres Morales963295e2014-07-10 15:40:24 -070080 private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
Adrian Roos7374d3a2017-03-31 14:14:53 -070081
Andres Morales28301302014-11-12 07:56:46 -080082 public static final int DIGEST_SIZE_BYTES = 32;
Andres Morales5ca4cc52015-03-19 16:37:54 -070083 private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
Andres Morales74e9b182016-02-22 12:33:33 -080084 private static final String FLASH_LOCK_PROP = "ro.boot.flash.locked";
85 private static final String FLASH_LOCK_LOCKED = "1";
86 private static final String FLASH_LOCK_UNLOCKED = "0";
Andres Morales68d4acd2014-07-01 19:40:41 -070087
88 private final Context mContext;
89 private final String mDataBlockFile;
Andres Morales963295e2014-07-10 15:40:24 -070090 private final Object mLock = new Object();
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -080091 private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
Andres Morales6429f312014-08-04 16:35:15 -070092
Andres Moralesa31c23d2014-10-30 15:31:31 -070093 private int mAllowedUid = -1;
Andres Morales963295e2014-07-10 15:40:24 -070094 private long mBlockDeviceSize;
Charles He71d2a412016-12-01 19:22:33 +000095
96 @GuardedBy("mLock")
Charles Hea9437bd2016-11-24 14:05:00 +000097 private boolean mIsWritable = true;
Charles Hef6f1d622016-12-01 19:22:33 +000098
Andres Morales68d4acd2014-07-01 19:40:41 -070099 public PersistentDataBlockService(Context context) {
100 super(context);
101 mContext = context;
102 mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
Andres Morales963295e2014-07-10 15:40:24 -0700103 mBlockDeviceSize = -1; // Load lazily
Andres Morales6429f312014-08-04 16:35:15 -0700104 }
105
Andres Moralesa31c23d2014-10-30 15:31:31 -0700106 private int getAllowedUid(int userHandle) {
Andres Morales6429f312014-08-04 16:35:15 -0700107 String allowedPackage = mContext.getResources()
Andres Morales68d4acd2014-07-01 19:40:41 -0700108 .getString(R.string.config_persistentDataPackageName);
109 PackageManager pm = mContext.getPackageManager();
110 int allowedUid = -1;
111 try {
Jeff Sharkeyc5967e92016-01-07 18:50:29 -0700112 allowedUid = pm.getPackageUidAsUser(allowedPackage,
113 PackageManager.MATCH_SYSTEM_ONLY, userHandle);
Andres Morales68d4acd2014-07-01 19:40:41 -0700114 } catch (PackageManager.NameNotFoundException e) {
115 // not expected
Andres Morales963295e2014-07-10 15:40:24 -0700116 Slog.e(TAG, "not able to find package " + allowedPackage, e);
Andres Morales68d4acd2014-07-01 19:40:41 -0700117 }
Andres Moralesa31c23d2014-10-30 15:31:31 -0700118 return allowedUid;
Andres Morales68d4acd2014-07-01 19:40:41 -0700119 }
120
121 @Override
122 public void onStart() {
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800123 // Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800124 SystemServerInitThreadPool.get().submit(() -> {
Fyodor Kupolovec00b512017-04-25 14:19:31 -0700125 mAllowedUid = getAllowedUid(UserHandle.USER_SYSTEM);
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800126 enforceChecksumValidity();
127 formatIfOemUnlockEnabled();
128 publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
129 mInitDoneSignal.countDown();
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800130 }, TAG + ".onStart");
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800131 }
132
133 @Override
134 public void onBootPhase(int phase) {
135 // Wait for initialization in onStart to finish
136 if (phase == PHASE_SYSTEM_SERVICES_READY) {
137 try {
138 if (!mInitDoneSignal.await(10, TimeUnit.SECONDS)) {
139 throw new IllegalStateException("Service " + TAG + " init timeout");
140 }
141 } catch (InterruptedException e) {
142 Thread.currentThread().interrupt();
143 throw new IllegalStateException("Service " + TAG + " init interrupted", e);
144 }
Adrian Roos7374d3a2017-03-31 14:14:53 -0700145 LocalServices.addService(PersistentDataBlockManagerInternal.class, mInternalService);
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800146 }
147 super.onBootPhase(phase);
Andres Morales68d4acd2014-07-01 19:40:41 -0700148 }
149
Andres Morales1ce7d172015-01-07 14:24:57 -0800150 private void formatIfOemUnlockEnabled() {
Andres Morales5ca4cc52015-03-19 16:37:54 -0700151 boolean enabled = doGetOemUnlockEnabled();
152 if (enabled) {
Andres Morales1ce7d172015-01-07 14:24:57 -0800153 synchronized (mLock) {
Andres Moralesc8f952c2015-03-19 08:34:55 -0700154 formatPartitionLocked(true);
Andres Morales1ce7d172015-01-07 14:24:57 -0800155 }
156 }
Andres Morales5ca4cc52015-03-19 16:37:54 -0700157
158 SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
Andres Morales1ce7d172015-01-07 14:24:57 -0800159 }
160
Amith Yamasanid2b21042016-06-03 10:12:47 -0700161 private void enforceOemUnlockReadPermission() {
162 if (mContext.checkCallingOrSelfPermission(Manifest.permission.READ_OEM_UNLOCK_STATE)
163 == PackageManager.PERMISSION_DENIED
164 && mContext.checkCallingOrSelfPermission(Manifest.permission.OEM_UNLOCK_STATE)
165 == PackageManager.PERMISSION_DENIED) {
166 throw new SecurityException("Can't access OEM unlock state. Requires "
167 + "READ_OEM_UNLOCK_STATE or OEM_UNLOCK_STATE permission.");
168 }
169 }
170
171 private void enforceOemUnlockWritePermission() {
Andres Morales68d4acd2014-07-01 19:40:41 -0700172 mContext.enforceCallingOrSelfPermission(
173 Manifest.permission.OEM_UNLOCK_STATE,
Amith Yamasanid2b21042016-06-03 10:12:47 -0700174 "Can't modify OEM unlock state");
Andres Morales68d4acd2014-07-01 19:40:41 -0700175 }
176
177 private void enforceUid(int callingUid) {
Andres Moralesa31c23d2014-10-30 15:31:31 -0700178 if (callingUid != mAllowedUid) {
Andres Morales68d4acd2014-07-01 19:40:41 -0700179 throw new SecurityException("uid " + callingUid + " not allowed to access PST");
180 }
181 }
182
Xiaohui Chenf0660782015-09-02 14:25:19 -0700183 private void enforceIsAdmin() {
184 final int userId = UserHandle.getCallingUserId();
185 final boolean isAdmin = UserManager.get(mContext).isUserAdmin(userId);
186 if (!isAdmin) {
187 throw new SecurityException(
188 "Only the Admin user is allowed to change OEM unlock state");
Andres Moralesa31c23d2014-10-30 15:31:31 -0700189 }
190 }
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100191
Mahaver Chopra3d9805d2016-07-07 16:25:05 +0100192 private void enforceUserRestriction(String userRestriction) {
193 if (UserManager.get(mContext).hasUserRestriction(userRestriction)) {
194 throw new SecurityException(
195 "OEM unlock is disallowed by user restriction: " + userRestriction);
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100196 }
197 }
198
Andres Morales963295e2014-07-10 15:40:24 -0700199 private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
Andres Morales28301302014-11-12 07:56:46 -0800200 // skip over checksum
201 inputStream.skipBytes(DIGEST_SIZE_BYTES);
202
Andres Morales68d4acd2014-07-01 19:40:41 -0700203 int totalDataSize;
204 int blockId = inputStream.readInt();
Andres Morales963295e2014-07-10 15:40:24 -0700205 if (blockId == PARTITION_TYPE_MARKER) {
Andres Morales68d4acd2014-07-01 19:40:41 -0700206 totalDataSize = inputStream.readInt();
207 } else {
208 totalDataSize = 0;
209 }
210 return totalDataSize;
211 }
212
Andres Morales963295e2014-07-10 15:40:24 -0700213 private long getBlockDeviceSize() {
214 synchronized (mLock) {
215 if (mBlockDeviceSize == -1) {
216 mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
Andres Morales68d4acd2014-07-01 19:40:41 -0700217 }
218 }
219
220 return mBlockDeviceSize;
221 }
222
Andres Morales28301302014-11-12 07:56:46 -0800223 private boolean enforceChecksumValidity() {
224 byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
225
226 synchronized (mLock) {
227 byte[] digest = computeDigestLocked(storedDigest);
228 if (digest == null || !Arrays.equals(storedDigest, digest)) {
229 Slog.i(TAG, "Formatting FRP partition...");
Andres Moralesc8f952c2015-03-19 08:34:55 -0700230 formatPartitionLocked(false);
Andres Morales28301302014-11-12 07:56:46 -0800231 return false;
232 }
233 }
234
235 return true;
236 }
237
238 private boolean computeAndWriteDigestLocked() {
239 byte[] digest = computeDigestLocked(null);
240 if (digest != null) {
241 DataOutputStream outputStream;
242 try {
243 outputStream = new DataOutputStream(
244 new FileOutputStream(new File(mDataBlockFile)));
245 } catch (FileNotFoundException e) {
246 Slog.e(TAG, "partition not available?", e);
247 return false;
248 }
249
250 try {
251 outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
252 outputStream.flush();
253 } catch (IOException e) {
254 Slog.e(TAG, "failed to write block checksum", e);
255 return false;
256 } finally {
257 IoUtils.closeQuietly(outputStream);
258 }
259 return true;
260 } else {
261 return false;
262 }
263 }
264
265 private byte[] computeDigestLocked(byte[] storedDigest) {
266 DataInputStream inputStream;
267 try {
268 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
269 } catch (FileNotFoundException e) {
270 Slog.e(TAG, "partition not available?", e);
271 return null;
272 }
273
274 MessageDigest md;
275 try {
276 md = MessageDigest.getInstance("SHA-256");
277 } catch (NoSuchAlgorithmException e) {
278 // won't ever happen -- every implementation is required to support SHA-256
279 Slog.e(TAG, "SHA-256 not supported?", e);
280 IoUtils.closeQuietly(inputStream);
281 return null;
282 }
283
284 try {
285 if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) {
286 inputStream.read(storedDigest);
287 } else {
288 inputStream.skipBytes(DIGEST_SIZE_BYTES);
289 }
290
291 int read;
292 byte[] data = new byte[1024];
293 md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest
294 while ((read = inputStream.read(data)) != -1) {
295 md.update(data, 0, read);
296 }
297 } catch (IOException e) {
298 Slog.e(TAG, "failed to read partition", e);
299 return null;
300 } finally {
301 IoUtils.closeQuietly(inputStream);
302 }
303
304 return md.digest();
305 }
306
Andres Moralesc8f952c2015-03-19 08:34:55 -0700307 private void formatPartitionLocked(boolean setOemUnlockEnabled) {
Andres Morales28301302014-11-12 07:56:46 -0800308 DataOutputStream outputStream;
309 try {
310 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
311 } catch (FileNotFoundException e) {
312 Slog.e(TAG, "partition not available?", e);
313 return;
314 }
315
316 byte[] data = new byte[DIGEST_SIZE_BYTES];
317 try {
318 outputStream.write(data, 0, DIGEST_SIZE_BYTES);
319 outputStream.writeInt(PARTITION_TYPE_MARKER);
320 outputStream.writeInt(0); // data size
321 outputStream.flush();
322 } catch (IOException e) {
323 Slog.e(TAG, "failed to format block", e);
324 return;
325 } finally {
326 IoUtils.closeQuietly(outputStream);
327 }
328
Andres Moralesc8f952c2015-03-19 08:34:55 -0700329 doSetOemUnlockEnabledLocked(setOemUnlockEnabled);
Andres Morales28301302014-11-12 07:56:46 -0800330 computeAndWriteDigestLocked();
331 }
332
333 private void doSetOemUnlockEnabledLocked(boolean enabled) {
334 FileOutputStream outputStream;
335 try {
336 outputStream = new FileOutputStream(new File(mDataBlockFile));
337 } catch (FileNotFoundException e) {
338 Slog.e(TAG, "partition not available", e);
339 return;
340 }
341
342 try {
343 FileChannel channel = outputStream.getChannel();
344
345 channel.position(getBlockDeviceSize() - 1);
346
347 ByteBuffer data = ByteBuffer.allocate(1);
348 data.put(enabled ? (byte) 1 : (byte) 0);
349 data.flip();
350 channel.write(data);
351 outputStream.flush();
352 } catch (IOException e) {
353 Slog.e(TAG, "unable to access persistent partition", e);
354 return;
355 } finally {
Andres Morales5ca4cc52015-03-19 16:37:54 -0700356 SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
Andres Morales28301302014-11-12 07:56:46 -0800357 IoUtils.closeQuietly(outputStream);
358 }
359 }
360
Andres Morales1ce7d172015-01-07 14:24:57 -0800361 private boolean doGetOemUnlockEnabled() {
362 DataInputStream inputStream;
363 try {
364 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
365 } catch (FileNotFoundException e) {
366 Slog.e(TAG, "partition not available");
367 return false;
368 }
369
370 try {
371 synchronized (mLock) {
372 inputStream.skip(getBlockDeviceSize() - 1);
373 return inputStream.readByte() != 0;
374 }
375 } catch (IOException e) {
376 Slog.e(TAG, "unable to access persistent partition", e);
377 return false;
378 } finally {
379 IoUtils.closeQuietly(inputStream);
380 }
381 }
382
Andrew Scull0e5d5b12017-06-14 16:27:04 +0100383 private long doGetMaximumDataBlockSize() {
384 long actualSize = getBlockDeviceSize() - HEADER_SIZE - DIGEST_SIZE_BYTES
385 - FRP_CREDENTIAL_RESERVED_SIZE - 1;
386 return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
387 }
388
Andres Morales963295e2014-07-10 15:40:24 -0700389 private native long nativeGetBlockDeviceSize(String path);
390 private native int nativeWipe(String path);
Andres Morales68d4acd2014-07-01 19:40:41 -0700391
392 private final IBinder mService = new IPersistentDataBlockService.Stub() {
393 @Override
394 public int write(byte[] data) throws RemoteException {
395 enforceUid(Binder.getCallingUid());
396
397 // Need to ensure we don't write over the last byte
Andrew Scull0e5d5b12017-06-14 16:27:04 +0100398 long maxBlockSize = doGetMaximumDataBlockSize();
Andres Morales963295e2014-07-10 15:40:24 -0700399 if (data.length > maxBlockSize) {
400 // partition is ~500k so shouldn't be a problem to downcast
401 return (int) -maxBlockSize;
Andres Morales68d4acd2014-07-01 19:40:41 -0700402 }
403
404 DataOutputStream outputStream;
405 try {
406 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
407 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700408 Slog.e(TAG, "partition not available?", e);
Andres Morales68d4acd2014-07-01 19:40:41 -0700409 return -1;
410 }
411
412 ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
Andres Morales963295e2014-07-10 15:40:24 -0700413 headerAndData.putInt(PARTITION_TYPE_MARKER);
Andres Morales68d4acd2014-07-01 19:40:41 -0700414 headerAndData.putInt(data.length);
415 headerAndData.put(data);
416
Andres Morales28301302014-11-12 07:56:46 -0800417 synchronized (mLock) {
Charles Hea629c772016-11-24 14:05:00 +0000418 if (!mIsWritable) {
419 IoUtils.closeQuietly(outputStream);
420 return -1;
421 }
422
Andres Morales68d4acd2014-07-01 19:40:41 -0700423 try {
Andres Morales28301302014-11-12 07:56:46 -0800424 byte[] checksum = new byte[DIGEST_SIZE_BYTES];
425 outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
426 outputStream.write(headerAndData.array());
427 outputStream.flush();
Andres Morales68d4acd2014-07-01 19:40:41 -0700428 } catch (IOException e) {
Andres Morales28301302014-11-12 07:56:46 -0800429 Slog.e(TAG, "failed writing to the persistent data block", e);
430 return -1;
431 } finally {
432 IoUtils.closeQuietly(outputStream);
433 }
434
435 if (computeAndWriteDigestLocked()) {
436 return data.length;
437 } else {
438 return -1;
Andres Morales68d4acd2014-07-01 19:40:41 -0700439 }
440 }
441 }
442
443 @Override
Andres Morales963295e2014-07-10 15:40:24 -0700444 public byte[] read() {
Andres Morales68d4acd2014-07-01 19:40:41 -0700445 enforceUid(Binder.getCallingUid());
Andres Morales28301302014-11-12 07:56:46 -0800446 if (!enforceChecksumValidity()) {
447 return new byte[0];
448 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700449
450 DataInputStream inputStream;
451 try {
452 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
453 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700454 Slog.e(TAG, "partition not available?", e);
455 return null;
Andres Morales68d4acd2014-07-01 19:40:41 -0700456 }
457
458 try {
Andres Morales963295e2014-07-10 15:40:24 -0700459 synchronized (mLock) {
460 int totalDataSize = getTotalDataSizeLocked(inputStream);
461
462 if (totalDataSize == 0) {
463 return new byte[0];
464 }
465
466 byte[] data = new byte[totalDataSize];
467 int read = inputStream.read(data, 0, totalDataSize);
468 if (read < totalDataSize) {
469 // something went wrong, not returning potentially corrupt data
470 Slog.e(TAG, "failed to read entire data block. bytes read: " +
471 read + "/" + totalDataSize);
472 return null;
473 }
474 return data;
475 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700476 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700477 Slog.e(TAG, "failed to read data", e);
478 return null;
Andres Morales68d4acd2014-07-01 19:40:41 -0700479 } finally {
480 try {
481 inputStream.close();
482 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700483 Slog.e(TAG, "failed to close OutputStream");
484 }
485 }
486 }
487
488 @Override
489 public void wipe() {
Amith Yamasanid2b21042016-06-03 10:12:47 -0700490 enforceOemUnlockWritePermission();
Andres Morales963295e2014-07-10 15:40:24 -0700491
492 synchronized (mLock) {
493 int ret = nativeWipe(mDataBlockFile);
494
495 if (ret < 0) {
496 Slog.e(TAG, "failed to wipe persistent partition");
Charles Hea629c772016-11-24 14:05:00 +0000497 } else {
498 mIsWritable = false;
499 Slog.i(TAG, "persistent partition now wiped and unwritable");
Andres Morales68d4acd2014-07-01 19:40:41 -0700500 }
501 }
502 }
503
504 @Override
Steven Ngdc20ba62016-04-26 18:19:04 +0100505 public void setOemUnlockEnabled(boolean enabled) throws SecurityException {
Guang Zhu514c5802014-09-12 15:14:00 -0700506 // do not allow monkey to flip the flag
507 if (ActivityManager.isUserAMonkey()) {
508 return;
509 }
Steven Ngbfe1b042016-05-16 11:20:57 +0100510
Amith Yamasanid2b21042016-06-03 10:12:47 -0700511 enforceOemUnlockWritePermission();
Steven Ngbfe1b042016-05-16 11:20:57 +0100512 enforceIsAdmin();
513
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100514 if (enabled) {
Mahaver Chopra3d9805d2016-07-07 16:25:05 +0100515 // Do not allow oem unlock to be enabled if it's disallowed by a user restriction.
516 enforceUserRestriction(UserManager.DISALLOW_OEM_UNLOCK);
517 enforceUserRestriction(UserManager.DISALLOW_FACTORY_RESET);
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100518 }
Andres Morales28301302014-11-12 07:56:46 -0800519 synchronized (mLock) {
520 doSetOemUnlockEnabledLocked(enabled);
521 computeAndWriteDigestLocked();
Andres Morales68d4acd2014-07-01 19:40:41 -0700522 }
523 }
524
525 @Override
526 public boolean getOemUnlockEnabled() {
Amith Yamasanid2b21042016-06-03 10:12:47 -0700527 enforceOemUnlockReadPermission();
Andres Morales1ce7d172015-01-07 14:24:57 -0800528 return doGetOemUnlockEnabled();
Andres Morales68d4acd2014-07-01 19:40:41 -0700529 }
530
531 @Override
Andres Morales74e9b182016-02-22 12:33:33 -0800532 public int getFlashLockState() {
Amith Yamasanid2b21042016-06-03 10:12:47 -0700533 enforceOemUnlockReadPermission();
Andres Morales74e9b182016-02-22 12:33:33 -0800534 String locked = SystemProperties.get(FLASH_LOCK_PROP);
535 switch (locked) {
536 case FLASH_LOCK_LOCKED:
537 return PersistentDataBlockManager.FLASH_LOCK_LOCKED;
538 case FLASH_LOCK_UNLOCKED:
539 return PersistentDataBlockManager.FLASH_LOCK_UNLOCKED;
540 default:
541 return PersistentDataBlockManager.FLASH_LOCK_UNKNOWN;
542 }
543 }
544
545 @Override
Andres Morales68d4acd2014-07-01 19:40:41 -0700546 public int getDataBlockSize() {
Craig Lafayette66445a62015-03-27 09:01:43 -0400547 enforcePersistentDataBlockAccess();
Andres Morales68d4acd2014-07-01 19:40:41 -0700548
549 DataInputStream inputStream;
550 try {
551 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
552 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700553 Slog.e(TAG, "partition not available");
Andres Morales68d4acd2014-07-01 19:40:41 -0700554 return 0;
555 }
556
557 try {
Andres Morales963295e2014-07-10 15:40:24 -0700558 synchronized (mLock) {
559 return getTotalDataSizeLocked(inputStream);
560 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700561 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700562 Slog.e(TAG, "error reading data block size");
Andres Morales68d4acd2014-07-01 19:40:41 -0700563 return 0;
564 } finally {
Andres Morales963295e2014-07-10 15:40:24 -0700565 IoUtils.closeQuietly(inputStream);
Andres Morales68d4acd2014-07-01 19:40:41 -0700566 }
567 }
Andres Morales963295e2014-07-10 15:40:24 -0700568
Craig Lafayette66445a62015-03-27 09:01:43 -0400569 private void enforcePersistentDataBlockAccess() {
570 if (mContext.checkCallingPermission(Manifest.permission.ACCESS_PDB_STATE)
571 != PackageManager.PERMISSION_GRANTED) {
572 enforceUid(Binder.getCallingUid());
573 }
574 }
575
Andres Morales963295e2014-07-10 15:40:24 -0700576 @Override
577 public long getMaximumDataBlockSize() {
Andrew Scull0e5d5b12017-06-14 16:27:04 +0100578 enforceUid(Binder.getCallingUid());
579 return doGetMaximumDataBlockSize();
Andres Morales963295e2014-07-10 15:40:24 -0700580 }
Adrian Roos7374d3a2017-03-31 14:14:53 -0700581
582 @Override
583 public boolean hasFrpCredentialHandle() {
584 enforcePersistentDataBlockAccess();
585 return mInternalService.getFrpCredentialHandle() != null;
586 }
587 };
588
589 private PersistentDataBlockManagerInternal mInternalService =
590 new PersistentDataBlockManagerInternal() {
591
592 @Override
593 public void setFrpCredentialHandle(byte[] handle) {
594 Preconditions.checkArgument(handle == null || handle.length > 0,
595 "handle must be null or non-empty");
596 Preconditions.checkArgument(handle == null
597 || handle.length <= MAX_FRP_CREDENTIAL_HANDLE_SIZE,
598 "handle must not be longer than " + MAX_FRP_CREDENTIAL_HANDLE_SIZE);
599
600 FileOutputStream outputStream;
601 try {
602 outputStream = new FileOutputStream(new File(mDataBlockFile));
603 } catch (FileNotFoundException e) {
604 Slog.e(TAG, "partition not available", e);
605 return;
606 }
607
608 ByteBuffer data = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
609 data.putInt(handle == null ? 0 : handle.length);
610 if (handle != null) {
611 data.put(handle);
612 }
613 data.flip();
614
615 synchronized (mLock) {
616 if (!mIsWritable) {
617 IoUtils.closeQuietly(outputStream);
618 return;
619 }
620
621 try {
622 FileChannel channel = outputStream.getChannel();
623
624 channel.position(getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE);
625 channel.write(data);
626 outputStream.flush();
627 } catch (IOException e) {
628 Slog.e(TAG, "unable to access persistent partition", e);
629 return;
630 } finally {
631 IoUtils.closeQuietly(outputStream);
632 }
633
634 computeAndWriteDigestLocked();
635 }
636 }
637
638 @Override
639 public byte[] getFrpCredentialHandle() {
640 if (!enforceChecksumValidity()) {
641 return null;
642 }
643
644 DataInputStream inputStream;
645 try {
646 inputStream = new DataInputStream(
647 new FileInputStream(new File(mDataBlockFile)));
648 } catch (FileNotFoundException e) {
649 Slog.e(TAG, "partition not available");
650 return null;
651 }
652
653 try {
654 synchronized (mLock) {
655 inputStream.skip(getBlockDeviceSize() - 1 - FRP_CREDENTIAL_RESERVED_SIZE);
656 int length = inputStream.readInt();
657 if (length <= 0 || length > MAX_FRP_CREDENTIAL_HANDLE_SIZE) {
658 return null;
659 }
660 byte[] bytes = new byte[length];
661 inputStream.readFully(bytes);
662 return bytes;
663 }
664 } catch (IOException e) {
665 Slog.e(TAG, "unable to access persistent partition", e);
666 return null;
667 } finally {
668 IoUtils.closeQuietly(inputStream);
669 }
670 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700671 };
672}