blob: 417d375f302a871492e1388162b7fda2129726a2 [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;
Guang Zhu514c5802014-09-12 15:14:00 -070035
Andres Morales963295e2014-07-10 15:40:24 -070036import libcore.io.IoUtils;
Andres Morales68d4acd2014-07-01 19:40:41 -070037
38import java.io.DataInputStream;
39import java.io.DataOutputStream;
40import java.io.File;
41import java.io.FileInputStream;
42import java.io.FileNotFoundException;
43import java.io.FileOutputStream;
44import java.io.IOException;
45import java.nio.ByteBuffer;
46import java.nio.channels.FileChannel;
Andres Morales28301302014-11-12 07:56:46 -080047import java.security.MessageDigest;
48import java.security.NoSuchAlgorithmException;
49import java.util.Arrays;
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -080050import java.util.concurrent.CountDownLatch;
51import java.util.concurrent.TimeUnit;
Andres Morales68d4acd2014-07-01 19:40:41 -070052
53/**
54 * Service for reading and writing blocks to a persistent partition.
Andres Morales963295e2014-07-10 15:40:24 -070055 * This data will live across factory resets not initiated via the Settings UI.
56 * When a device is factory reset through Settings this data is wiped.
Andres Morales68d4acd2014-07-01 19:40:41 -070057 *
Charles Hea629c772016-11-24 14:05:00 +000058 * Allows writing one block at a time. Namely, each time {@link IPersistentDataBlockService#write}
59 * is called, it will overwrite the data that was previously written on the block.
Andres Morales68d4acd2014-07-01 19:40:41 -070060 *
61 * Clients can query the size of the currently written block via
Charles Hea629c772016-11-24 14:05:00 +000062 * {@link IPersistentDataBlockService#getDataBlockSize}
Andres Morales68d4acd2014-07-01 19:40:41 -070063 *
Charles Hea629c772016-11-24 14:05:00 +000064 * Clients can read any number of bytes from the currently written block up to its total size by
65 * invoking {@link IPersistentDataBlockService#read}
Andres Morales68d4acd2014-07-01 19:40:41 -070066 */
67public class PersistentDataBlockService extends SystemService {
68 private static final String TAG = PersistentDataBlockService.class.getSimpleName();
69
70 private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
71 private static final int HEADER_SIZE = 8;
Andres Morales963295e2014-07-10 15:40:24 -070072 // Magic number to mark block device as adhering to the format consumed by this service
Andres Morales28301302014-11-12 07:56:46 -080073 private static final int PARTITION_TYPE_MARKER = 0x19901873;
Andres Morales963295e2014-07-10 15:40:24 -070074 // Limit to 100k as blocks larger than this might cause strain on Binder.
Andres Morales963295e2014-07-10 15:40:24 -070075 private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
Andres Morales28301302014-11-12 07:56:46 -080076 public static final int DIGEST_SIZE_BYTES = 32;
Andres Morales5ca4cc52015-03-19 16:37:54 -070077 private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
Andres Morales74e9b182016-02-22 12:33:33 -080078 private static final String FLASH_LOCK_PROP = "ro.boot.flash.locked";
79 private static final String FLASH_LOCK_LOCKED = "1";
80 private static final String FLASH_LOCK_UNLOCKED = "0";
Andres Morales68d4acd2014-07-01 19:40:41 -070081
82 private final Context mContext;
83 private final String mDataBlockFile;
Andres Morales963295e2014-07-10 15:40:24 -070084 private final Object mLock = new Object();
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -080085 private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
Andres Morales6429f312014-08-04 16:35:15 -070086
Andres Moralesa31c23d2014-10-30 15:31:31 -070087 private int mAllowedUid = -1;
Andres Morales963295e2014-07-10 15:40:24 -070088 private long mBlockDeviceSize;
Charles He71d2a412016-12-01 19:22:33 +000089
90 @GuardedBy("mLock")
Charles Hea9437bd2016-11-24 14:05:00 +000091 private boolean mIsWritable = true;
Charles Hef6f1d622016-12-01 19:22:33 +000092
Andres Morales68d4acd2014-07-01 19:40:41 -070093 public PersistentDataBlockService(Context context) {
94 super(context);
95 mContext = context;
96 mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
Andres Morales963295e2014-07-10 15:40:24 -070097 mBlockDeviceSize = -1; // Load lazily
Xiaohui Chenf0660782015-09-02 14:25:19 -070098 mAllowedUid = getAllowedUid(UserHandle.USER_SYSTEM);
Andres Morales6429f312014-08-04 16:35:15 -070099 }
100
Andres Moralesa31c23d2014-10-30 15:31:31 -0700101 private int getAllowedUid(int userHandle) {
Andres Morales6429f312014-08-04 16:35:15 -0700102 String allowedPackage = mContext.getResources()
Andres Morales68d4acd2014-07-01 19:40:41 -0700103 .getString(R.string.config_persistentDataPackageName);
104 PackageManager pm = mContext.getPackageManager();
105 int allowedUid = -1;
106 try {
Jeff Sharkeyc5967e92016-01-07 18:50:29 -0700107 allowedUid = pm.getPackageUidAsUser(allowedPackage,
108 PackageManager.MATCH_SYSTEM_ONLY, userHandle);
Andres Morales68d4acd2014-07-01 19:40:41 -0700109 } catch (PackageManager.NameNotFoundException e) {
110 // not expected
Andres Morales963295e2014-07-10 15:40:24 -0700111 Slog.e(TAG, "not able to find package " + allowedPackage, e);
Andres Morales68d4acd2014-07-01 19:40:41 -0700112 }
Andres Moralesa31c23d2014-10-30 15:31:31 -0700113 return allowedUid;
Andres Morales68d4acd2014-07-01 19:40:41 -0700114 }
115
116 @Override
117 public void onStart() {
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800118 // Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800119 SystemServerInitThreadPool.get().submit(() -> {
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800120 enforceChecksumValidity();
121 formatIfOemUnlockEnabled();
122 publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
123 mInitDoneSignal.countDown();
Fyodor Kupolove29a5a12016-12-16 16:14:17 -0800124 }, TAG + ".onStart");
Fyodor Kupolov68f49ae2016-12-02 17:33:21 -0800125 }
126
127 @Override
128 public void onBootPhase(int phase) {
129 // Wait for initialization in onStart to finish
130 if (phase == PHASE_SYSTEM_SERVICES_READY) {
131 try {
132 if (!mInitDoneSignal.await(10, TimeUnit.SECONDS)) {
133 throw new IllegalStateException("Service " + TAG + " init timeout");
134 }
135 } catch (InterruptedException e) {
136 Thread.currentThread().interrupt();
137 throw new IllegalStateException("Service " + TAG + " init interrupted", e);
138 }
139 }
140 super.onBootPhase(phase);
Andres Morales68d4acd2014-07-01 19:40:41 -0700141 }
142
Andres Morales1ce7d172015-01-07 14:24:57 -0800143 private void formatIfOemUnlockEnabled() {
Andres Morales5ca4cc52015-03-19 16:37:54 -0700144 boolean enabled = doGetOemUnlockEnabled();
145 if (enabled) {
Andres Morales1ce7d172015-01-07 14:24:57 -0800146 synchronized (mLock) {
Andres Moralesc8f952c2015-03-19 08:34:55 -0700147 formatPartitionLocked(true);
Andres Morales1ce7d172015-01-07 14:24:57 -0800148 }
149 }
Andres Morales5ca4cc52015-03-19 16:37:54 -0700150
151 SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
Andres Morales1ce7d172015-01-07 14:24:57 -0800152 }
153
Amith Yamasanid2b21042016-06-03 10:12:47 -0700154 private void enforceOemUnlockReadPermission() {
155 if (mContext.checkCallingOrSelfPermission(Manifest.permission.READ_OEM_UNLOCK_STATE)
156 == PackageManager.PERMISSION_DENIED
157 && mContext.checkCallingOrSelfPermission(Manifest.permission.OEM_UNLOCK_STATE)
158 == PackageManager.PERMISSION_DENIED) {
159 throw new SecurityException("Can't access OEM unlock state. Requires "
160 + "READ_OEM_UNLOCK_STATE or OEM_UNLOCK_STATE permission.");
161 }
162 }
163
164 private void enforceOemUnlockWritePermission() {
Andres Morales68d4acd2014-07-01 19:40:41 -0700165 mContext.enforceCallingOrSelfPermission(
166 Manifest.permission.OEM_UNLOCK_STATE,
Amith Yamasanid2b21042016-06-03 10:12:47 -0700167 "Can't modify OEM unlock state");
Andres Morales68d4acd2014-07-01 19:40:41 -0700168 }
169
170 private void enforceUid(int callingUid) {
Andres Moralesa31c23d2014-10-30 15:31:31 -0700171 if (callingUid != mAllowedUid) {
Andres Morales68d4acd2014-07-01 19:40:41 -0700172 throw new SecurityException("uid " + callingUid + " not allowed to access PST");
173 }
174 }
175
Xiaohui Chenf0660782015-09-02 14:25:19 -0700176 private void enforceIsAdmin() {
177 final int userId = UserHandle.getCallingUserId();
178 final boolean isAdmin = UserManager.get(mContext).isUserAdmin(userId);
179 if (!isAdmin) {
180 throw new SecurityException(
181 "Only the Admin user is allowed to change OEM unlock state");
Andres Moralesa31c23d2014-10-30 15:31:31 -0700182 }
183 }
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100184
Mahaver Chopra3d9805d2016-07-07 16:25:05 +0100185 private void enforceUserRestriction(String userRestriction) {
186 if (UserManager.get(mContext).hasUserRestriction(userRestriction)) {
187 throw new SecurityException(
188 "OEM unlock is disallowed by user restriction: " + userRestriction);
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100189 }
190 }
191
Andres Morales963295e2014-07-10 15:40:24 -0700192 private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
Andres Morales28301302014-11-12 07:56:46 -0800193 // skip over checksum
194 inputStream.skipBytes(DIGEST_SIZE_BYTES);
195
Andres Morales68d4acd2014-07-01 19:40:41 -0700196 int totalDataSize;
197 int blockId = inputStream.readInt();
Andres Morales963295e2014-07-10 15:40:24 -0700198 if (blockId == PARTITION_TYPE_MARKER) {
Andres Morales68d4acd2014-07-01 19:40:41 -0700199 totalDataSize = inputStream.readInt();
200 } else {
201 totalDataSize = 0;
202 }
203 return totalDataSize;
204 }
205
Andres Morales963295e2014-07-10 15:40:24 -0700206 private long getBlockDeviceSize() {
207 synchronized (mLock) {
208 if (mBlockDeviceSize == -1) {
209 mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
Andres Morales68d4acd2014-07-01 19:40:41 -0700210 }
211 }
212
213 return mBlockDeviceSize;
214 }
215
Andres Morales28301302014-11-12 07:56:46 -0800216 private boolean enforceChecksumValidity() {
217 byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
218
219 synchronized (mLock) {
220 byte[] digest = computeDigestLocked(storedDigest);
221 if (digest == null || !Arrays.equals(storedDigest, digest)) {
222 Slog.i(TAG, "Formatting FRP partition...");
Andres Moralesc8f952c2015-03-19 08:34:55 -0700223 formatPartitionLocked(false);
Andres Morales28301302014-11-12 07:56:46 -0800224 return false;
225 }
226 }
227
228 return true;
229 }
230
231 private boolean computeAndWriteDigestLocked() {
232 byte[] digest = computeDigestLocked(null);
233 if (digest != null) {
234 DataOutputStream outputStream;
235 try {
236 outputStream = new DataOutputStream(
237 new FileOutputStream(new File(mDataBlockFile)));
238 } catch (FileNotFoundException e) {
239 Slog.e(TAG, "partition not available?", e);
240 return false;
241 }
242
243 try {
244 outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
245 outputStream.flush();
246 } catch (IOException e) {
247 Slog.e(TAG, "failed to write block checksum", e);
248 return false;
249 } finally {
250 IoUtils.closeQuietly(outputStream);
251 }
252 return true;
253 } else {
254 return false;
255 }
256 }
257
258 private byte[] computeDigestLocked(byte[] storedDigest) {
259 DataInputStream inputStream;
260 try {
261 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
262 } catch (FileNotFoundException e) {
263 Slog.e(TAG, "partition not available?", e);
264 return null;
265 }
266
267 MessageDigest md;
268 try {
269 md = MessageDigest.getInstance("SHA-256");
270 } catch (NoSuchAlgorithmException e) {
271 // won't ever happen -- every implementation is required to support SHA-256
272 Slog.e(TAG, "SHA-256 not supported?", e);
273 IoUtils.closeQuietly(inputStream);
274 return null;
275 }
276
277 try {
278 if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) {
279 inputStream.read(storedDigest);
280 } else {
281 inputStream.skipBytes(DIGEST_SIZE_BYTES);
282 }
283
284 int read;
285 byte[] data = new byte[1024];
286 md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest
287 while ((read = inputStream.read(data)) != -1) {
288 md.update(data, 0, read);
289 }
290 } catch (IOException e) {
291 Slog.e(TAG, "failed to read partition", e);
292 return null;
293 } finally {
294 IoUtils.closeQuietly(inputStream);
295 }
296
297 return md.digest();
298 }
299
Andres Moralesc8f952c2015-03-19 08:34:55 -0700300 private void formatPartitionLocked(boolean setOemUnlockEnabled) {
Andres Morales28301302014-11-12 07:56:46 -0800301 DataOutputStream outputStream;
302 try {
303 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
304 } catch (FileNotFoundException e) {
305 Slog.e(TAG, "partition not available?", e);
306 return;
307 }
308
309 byte[] data = new byte[DIGEST_SIZE_BYTES];
310 try {
311 outputStream.write(data, 0, DIGEST_SIZE_BYTES);
312 outputStream.writeInt(PARTITION_TYPE_MARKER);
313 outputStream.writeInt(0); // data size
314 outputStream.flush();
315 } catch (IOException e) {
316 Slog.e(TAG, "failed to format block", e);
317 return;
318 } finally {
319 IoUtils.closeQuietly(outputStream);
320 }
321
Andres Moralesc8f952c2015-03-19 08:34:55 -0700322 doSetOemUnlockEnabledLocked(setOemUnlockEnabled);
Andres Morales28301302014-11-12 07:56:46 -0800323 computeAndWriteDigestLocked();
324 }
325
326 private void doSetOemUnlockEnabledLocked(boolean enabled) {
327 FileOutputStream outputStream;
328 try {
329 outputStream = new FileOutputStream(new File(mDataBlockFile));
330 } catch (FileNotFoundException e) {
331 Slog.e(TAG, "partition not available", e);
332 return;
333 }
334
335 try {
336 FileChannel channel = outputStream.getChannel();
337
338 channel.position(getBlockDeviceSize() - 1);
339
340 ByteBuffer data = ByteBuffer.allocate(1);
341 data.put(enabled ? (byte) 1 : (byte) 0);
342 data.flip();
343 channel.write(data);
344 outputStream.flush();
345 } catch (IOException e) {
346 Slog.e(TAG, "unable to access persistent partition", e);
347 return;
348 } finally {
Andres Morales5ca4cc52015-03-19 16:37:54 -0700349 SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
Andres Morales28301302014-11-12 07:56:46 -0800350 IoUtils.closeQuietly(outputStream);
351 }
352 }
353
Andres Morales1ce7d172015-01-07 14:24:57 -0800354 private boolean doGetOemUnlockEnabled() {
355 DataInputStream inputStream;
356 try {
357 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
358 } catch (FileNotFoundException e) {
359 Slog.e(TAG, "partition not available");
360 return false;
361 }
362
363 try {
364 synchronized (mLock) {
365 inputStream.skip(getBlockDeviceSize() - 1);
366 return inputStream.readByte() != 0;
367 }
368 } catch (IOException e) {
369 Slog.e(TAG, "unable to access persistent partition", e);
370 return false;
371 } finally {
372 IoUtils.closeQuietly(inputStream);
373 }
374 }
375
Andres Morales963295e2014-07-10 15:40:24 -0700376 private native long nativeGetBlockDeviceSize(String path);
377 private native int nativeWipe(String path);
Andres Morales68d4acd2014-07-01 19:40:41 -0700378
379 private final IBinder mService = new IPersistentDataBlockService.Stub() {
380 @Override
381 public int write(byte[] data) throws RemoteException {
382 enforceUid(Binder.getCallingUid());
383
384 // Need to ensure we don't write over the last byte
Andres Morales963295e2014-07-10 15:40:24 -0700385 long maxBlockSize = getBlockDeviceSize() - HEADER_SIZE - 1;
386 if (data.length > maxBlockSize) {
387 // partition is ~500k so shouldn't be a problem to downcast
388 return (int) -maxBlockSize;
Andres Morales68d4acd2014-07-01 19:40:41 -0700389 }
390
391 DataOutputStream outputStream;
392 try {
393 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
394 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700395 Slog.e(TAG, "partition not available?", e);
Andres Morales68d4acd2014-07-01 19:40:41 -0700396 return -1;
397 }
398
399 ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
Andres Morales963295e2014-07-10 15:40:24 -0700400 headerAndData.putInt(PARTITION_TYPE_MARKER);
Andres Morales68d4acd2014-07-01 19:40:41 -0700401 headerAndData.putInt(data.length);
402 headerAndData.put(data);
403
Andres Morales28301302014-11-12 07:56:46 -0800404 synchronized (mLock) {
Charles Hea629c772016-11-24 14:05:00 +0000405 if (!mIsWritable) {
406 IoUtils.closeQuietly(outputStream);
407 return -1;
408 }
409
Andres Morales68d4acd2014-07-01 19:40:41 -0700410 try {
Andres Morales28301302014-11-12 07:56:46 -0800411 byte[] checksum = new byte[DIGEST_SIZE_BYTES];
412 outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
413 outputStream.write(headerAndData.array());
414 outputStream.flush();
Andres Morales68d4acd2014-07-01 19:40:41 -0700415 } catch (IOException e) {
Andres Morales28301302014-11-12 07:56:46 -0800416 Slog.e(TAG, "failed writing to the persistent data block", e);
417 return -1;
418 } finally {
419 IoUtils.closeQuietly(outputStream);
420 }
421
422 if (computeAndWriteDigestLocked()) {
423 return data.length;
424 } else {
425 return -1;
Andres Morales68d4acd2014-07-01 19:40:41 -0700426 }
427 }
428 }
429
430 @Override
Andres Morales963295e2014-07-10 15:40:24 -0700431 public byte[] read() {
Andres Morales68d4acd2014-07-01 19:40:41 -0700432 enforceUid(Binder.getCallingUid());
Andres Morales28301302014-11-12 07:56:46 -0800433 if (!enforceChecksumValidity()) {
434 return new byte[0];
435 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700436
437 DataInputStream inputStream;
438 try {
439 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
440 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700441 Slog.e(TAG, "partition not available?", e);
442 return null;
Andres Morales68d4acd2014-07-01 19:40:41 -0700443 }
444
445 try {
Andres Morales963295e2014-07-10 15:40:24 -0700446 synchronized (mLock) {
447 int totalDataSize = getTotalDataSizeLocked(inputStream);
448
449 if (totalDataSize == 0) {
450 return new byte[0];
451 }
452
453 byte[] data = new byte[totalDataSize];
454 int read = inputStream.read(data, 0, totalDataSize);
455 if (read < totalDataSize) {
456 // something went wrong, not returning potentially corrupt data
457 Slog.e(TAG, "failed to read entire data block. bytes read: " +
458 read + "/" + totalDataSize);
459 return null;
460 }
461 return data;
462 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700463 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700464 Slog.e(TAG, "failed to read data", e);
465 return null;
Andres Morales68d4acd2014-07-01 19:40:41 -0700466 } finally {
467 try {
468 inputStream.close();
469 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700470 Slog.e(TAG, "failed to close OutputStream");
471 }
472 }
473 }
474
475 @Override
476 public void wipe() {
Amith Yamasanid2b21042016-06-03 10:12:47 -0700477 enforceOemUnlockWritePermission();
Andres Morales963295e2014-07-10 15:40:24 -0700478
479 synchronized (mLock) {
480 int ret = nativeWipe(mDataBlockFile);
481
482 if (ret < 0) {
483 Slog.e(TAG, "failed to wipe persistent partition");
Charles Hea629c772016-11-24 14:05:00 +0000484 } else {
485 mIsWritable = false;
486 Slog.i(TAG, "persistent partition now wiped and unwritable");
Andres Morales68d4acd2014-07-01 19:40:41 -0700487 }
488 }
489 }
490
491 @Override
Steven Ngdc20ba62016-04-26 18:19:04 +0100492 public void setOemUnlockEnabled(boolean enabled) throws SecurityException {
Guang Zhu514c5802014-09-12 15:14:00 -0700493 // do not allow monkey to flip the flag
494 if (ActivityManager.isUserAMonkey()) {
495 return;
496 }
Steven Ngbfe1b042016-05-16 11:20:57 +0100497
Amith Yamasanid2b21042016-06-03 10:12:47 -0700498 enforceOemUnlockWritePermission();
Steven Ngbfe1b042016-05-16 11:20:57 +0100499 enforceIsAdmin();
500
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100501 if (enabled) {
Mahaver Chopra3d9805d2016-07-07 16:25:05 +0100502 // Do not allow oem unlock to be enabled if it's disallowed by a user restriction.
503 enforceUserRestriction(UserManager.DISALLOW_OEM_UNLOCK);
504 enforceUserRestriction(UserManager.DISALLOW_FACTORY_RESET);
Mahaver Chopra830e32c2016-05-17 18:53:09 +0100505 }
Andres Morales28301302014-11-12 07:56:46 -0800506 synchronized (mLock) {
507 doSetOemUnlockEnabledLocked(enabled);
508 computeAndWriteDigestLocked();
Andres Morales68d4acd2014-07-01 19:40:41 -0700509 }
510 }
511
512 @Override
513 public boolean getOemUnlockEnabled() {
Amith Yamasanid2b21042016-06-03 10:12:47 -0700514 enforceOemUnlockReadPermission();
Andres Morales1ce7d172015-01-07 14:24:57 -0800515 return doGetOemUnlockEnabled();
Andres Morales68d4acd2014-07-01 19:40:41 -0700516 }
517
518 @Override
Andres Morales74e9b182016-02-22 12:33:33 -0800519 public int getFlashLockState() {
Amith Yamasanid2b21042016-06-03 10:12:47 -0700520 enforceOemUnlockReadPermission();
Andres Morales74e9b182016-02-22 12:33:33 -0800521 String locked = SystemProperties.get(FLASH_LOCK_PROP);
522 switch (locked) {
523 case FLASH_LOCK_LOCKED:
524 return PersistentDataBlockManager.FLASH_LOCK_LOCKED;
525 case FLASH_LOCK_UNLOCKED:
526 return PersistentDataBlockManager.FLASH_LOCK_UNLOCKED;
527 default:
528 return PersistentDataBlockManager.FLASH_LOCK_UNKNOWN;
529 }
530 }
531
532 @Override
Andres Morales68d4acd2014-07-01 19:40:41 -0700533 public int getDataBlockSize() {
Craig Lafayette66445a62015-03-27 09:01:43 -0400534 enforcePersistentDataBlockAccess();
Andres Morales68d4acd2014-07-01 19:40:41 -0700535
536 DataInputStream inputStream;
537 try {
538 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
539 } catch (FileNotFoundException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700540 Slog.e(TAG, "partition not available");
Andres Morales68d4acd2014-07-01 19:40:41 -0700541 return 0;
542 }
543
544 try {
Andres Morales963295e2014-07-10 15:40:24 -0700545 synchronized (mLock) {
546 return getTotalDataSizeLocked(inputStream);
547 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700548 } catch (IOException e) {
Andres Morales963295e2014-07-10 15:40:24 -0700549 Slog.e(TAG, "error reading data block size");
Andres Morales68d4acd2014-07-01 19:40:41 -0700550 return 0;
551 } finally {
Andres Morales963295e2014-07-10 15:40:24 -0700552 IoUtils.closeQuietly(inputStream);
Andres Morales68d4acd2014-07-01 19:40:41 -0700553 }
554 }
Andres Morales963295e2014-07-10 15:40:24 -0700555
Craig Lafayette66445a62015-03-27 09:01:43 -0400556 private void enforcePersistentDataBlockAccess() {
557 if (mContext.checkCallingPermission(Manifest.permission.ACCESS_PDB_STATE)
558 != PackageManager.PERMISSION_GRANTED) {
559 enforceUid(Binder.getCallingUid());
560 }
561 }
562
Andres Morales963295e2014-07-10 15:40:24 -0700563 @Override
564 public long getMaximumDataBlockSize() {
565 long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1;
566 return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
567 }
Andres Morales68d4acd2014-07-01 19:40:41 -0700568 };
569}