PersistentDataBlockService.java revision ec00b5199ce788208ea80fee1ee30a1653e0a143
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import android.Manifest;
20import android.app.ActivityManager;
21import android.content.Context;
22import android.content.pm.PackageManager;
23import android.os.Binder;
24import android.os.IBinder;
25import android.os.RemoteException;
26import android.os.SystemProperties;
27import android.os.UserHandle;
28import android.os.UserManager;
29import android.service.persistentdata.IPersistentDataBlockService;
30import android.service.persistentdata.PersistentDataBlockManager;
31import android.util.Slog;
32
33import com.android.internal.R;
34import com.android.internal.annotations.GuardedBy;
35
36import libcore.io.IoUtils;
37
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;
47import java.security.MessageDigest;
48import java.security.NoSuchAlgorithmException;
49import java.util.Arrays;
50import java.util.concurrent.CountDownLatch;
51import java.util.concurrent.TimeUnit;
52
53/**
54 * Service for reading and writing blocks to a persistent partition.
55 * 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.
57 *
58 * 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.
60 *
61 * Clients can query the size of the currently written block via
62 * {@link IPersistentDataBlockService#getDataBlockSize}
63 *
64 * Clients can read any number of bytes from the currently written block up to its total size by
65 * invoking {@link IPersistentDataBlockService#read}
66 */
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;
72    // Magic number to mark block device as adhering to the format consumed by this service
73    private static final int PARTITION_TYPE_MARKER = 0x19901873;
74    // Limit to 100k as blocks larger than this might cause strain on Binder.
75    private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
76    public static final int DIGEST_SIZE_BYTES = 32;
77    private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
78    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";
81
82    private final Context mContext;
83    private final String mDataBlockFile;
84    private final Object mLock = new Object();
85    private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
86
87    private int mAllowedUid = -1;
88    private long mBlockDeviceSize;
89
90    @GuardedBy("mLock")
91    private boolean mIsWritable = true;
92
93    public PersistentDataBlockService(Context context) {
94        super(context);
95        mContext = context;
96        mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
97        mBlockDeviceSize = -1; // Load lazily
98    }
99
100    private int getAllowedUid(int userHandle) {
101        String allowedPackage = mContext.getResources()
102                .getString(R.string.config_persistentDataPackageName);
103        PackageManager pm = mContext.getPackageManager();
104        int allowedUid = -1;
105        try {
106            allowedUid = pm.getPackageUidAsUser(allowedPackage,
107                    PackageManager.MATCH_SYSTEM_ONLY, userHandle);
108        } catch (PackageManager.NameNotFoundException e) {
109            // not expected
110            Slog.e(TAG, "not able to find package " + allowedPackage, e);
111        }
112        return allowedUid;
113    }
114
115    @Override
116    public void onStart() {
117        // Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY
118        SystemServerInitThreadPool.get().submit(() -> {
119            mAllowedUid = getAllowedUid(UserHandle.USER_SYSTEM);
120            enforceChecksumValidity();
121            formatIfOemUnlockEnabled();
122            publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
123            mInitDoneSignal.countDown();
124        }, TAG + ".onStart");
125    }
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);
141    }
142
143    private void formatIfOemUnlockEnabled() {
144        boolean enabled = doGetOemUnlockEnabled();
145        if (enabled) {
146            synchronized (mLock) {
147                formatPartitionLocked(true);
148            }
149        }
150
151        SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
152    }
153
154    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() {
165        mContext.enforceCallingOrSelfPermission(
166                Manifest.permission.OEM_UNLOCK_STATE,
167                "Can't modify OEM unlock state");
168    }
169
170    private void enforceUid(int callingUid) {
171        if (callingUid != mAllowedUid) {
172            throw new SecurityException("uid " + callingUid + " not allowed to access PST");
173        }
174    }
175
176    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");
182        }
183    }
184
185    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);
189        }
190    }
191
192    private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
193        // skip over checksum
194        inputStream.skipBytes(DIGEST_SIZE_BYTES);
195
196        int totalDataSize;
197        int blockId = inputStream.readInt();
198        if (blockId == PARTITION_TYPE_MARKER) {
199            totalDataSize = inputStream.readInt();
200        } else {
201            totalDataSize = 0;
202        }
203        return totalDataSize;
204    }
205
206    private long getBlockDeviceSize() {
207        synchronized (mLock) {
208            if (mBlockDeviceSize == -1) {
209                mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
210            }
211        }
212
213        return mBlockDeviceSize;
214    }
215
216    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...");
223                formatPartitionLocked(false);
224                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
300    private void formatPartitionLocked(boolean setOemUnlockEnabled) {
301        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
322        doSetOemUnlockEnabledLocked(setOemUnlockEnabled);
323        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 {
349            SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
350            IoUtils.closeQuietly(outputStream);
351        }
352    }
353
354    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
376    private native long nativeGetBlockDeviceSize(String path);
377    private native int nativeWipe(String path);
378
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
385            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;
389            }
390
391            DataOutputStream outputStream;
392            try {
393                outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
394            } catch (FileNotFoundException e) {
395                Slog.e(TAG, "partition not available?", e);
396                return -1;
397            }
398
399            ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
400            headerAndData.putInt(PARTITION_TYPE_MARKER);
401            headerAndData.putInt(data.length);
402            headerAndData.put(data);
403
404            synchronized (mLock) {
405                if (!mIsWritable) {
406                    IoUtils.closeQuietly(outputStream);
407                    return -1;
408                }
409
410                try {
411                    byte[] checksum = new byte[DIGEST_SIZE_BYTES];
412                    outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
413                    outputStream.write(headerAndData.array());
414                    outputStream.flush();
415                } catch (IOException e) {
416                    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;
426                }
427            }
428        }
429
430        @Override
431        public byte[] read() {
432            enforceUid(Binder.getCallingUid());
433            if (!enforceChecksumValidity()) {
434                return new byte[0];
435            }
436
437            DataInputStream inputStream;
438            try {
439                inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
440            } catch (FileNotFoundException e) {
441                Slog.e(TAG, "partition not available?", e);
442                return null;
443            }
444
445            try {
446                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                }
463            } catch (IOException e) {
464                Slog.e(TAG, "failed to read data", e);
465                return null;
466            } finally {
467                try {
468                    inputStream.close();
469                } catch (IOException e) {
470                    Slog.e(TAG, "failed to close OutputStream");
471                }
472            }
473        }
474
475        @Override
476        public void wipe() {
477            enforceOemUnlockWritePermission();
478
479            synchronized (mLock) {
480                int ret = nativeWipe(mDataBlockFile);
481
482                if (ret < 0) {
483                    Slog.e(TAG, "failed to wipe persistent partition");
484                } else {
485                    mIsWritable = false;
486                    Slog.i(TAG, "persistent partition now wiped and unwritable");
487                }
488            }
489        }
490
491        @Override
492        public void setOemUnlockEnabled(boolean enabled) throws SecurityException {
493            // do not allow monkey to flip the flag
494            if (ActivityManager.isUserAMonkey()) {
495                return;
496            }
497
498            enforceOemUnlockWritePermission();
499            enforceIsAdmin();
500
501            if (enabled) {
502                // 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);
505            }
506            synchronized (mLock) {
507                doSetOemUnlockEnabledLocked(enabled);
508                computeAndWriteDigestLocked();
509            }
510        }
511
512        @Override
513        public boolean getOemUnlockEnabled() {
514            enforceOemUnlockReadPermission();
515            return doGetOemUnlockEnabled();
516        }
517
518        @Override
519        public int getFlashLockState() {
520            enforceOemUnlockReadPermission();
521            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
533        public int getDataBlockSize() {
534            enforcePersistentDataBlockAccess();
535
536            DataInputStream inputStream;
537            try {
538                inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
539            } catch (FileNotFoundException e) {
540                Slog.e(TAG, "partition not available");
541                return 0;
542            }
543
544            try {
545                synchronized (mLock) {
546                    return getTotalDataSizeLocked(inputStream);
547                }
548            } catch (IOException e) {
549                Slog.e(TAG, "error reading data block size");
550                return 0;
551            } finally {
552                IoUtils.closeQuietly(inputStream);
553            }
554        }
555
556        private void enforcePersistentDataBlockAccess() {
557            if (mContext.checkCallingPermission(Manifest.permission.ACCESS_PDB_STATE)
558                    != PackageManager.PERMISSION_GRANTED) {
559                enforceUid(Binder.getCallingUid());
560            }
561        }
562
563        @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        }
568    };
569}
570