PersistentDataBlockService.java revision a54f83c99c418d561764132c9b1485bebefac629
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    @GuardedBy("mLock")
94    private boolean mIsWritable = true;
95
96    public PersistentDataBlockService(Context context) {
97        super(context);
98        mContext = context;
99        mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
100        mBlockDeviceSize = -1; // Load lazily
101        mAllowedUid = getAllowedUid(UserHandle.USER_SYSTEM);
102    }
103
104    private int getAllowedUid(int userHandle) {
105        String allowedPackage = mContext.getResources()
106                .getString(R.string.config_persistentDataPackageName);
107        PackageManager pm = mContext.getPackageManager();
108        int allowedUid = -1;
109        try {
110            allowedUid = pm.getPackageUidAsUser(allowedPackage,
111                    PackageManager.MATCH_SYSTEM_ONLY, userHandle);
112        } catch (PackageManager.NameNotFoundException e) {
113            // not expected
114            Slog.e(TAG, "not able to find package " + allowedPackage, e);
115        }
116        return allowedUid;
117    }
118
119    @Override
120    public void onStart() {
121        // Do init on a separate thread, will join in PHASE_ACTIVITY_MANAGER_READY
122        FgThread.getHandler().post(() -> {
123            enforceChecksumValidity();
124            formatIfOemUnlockEnabled();
125            publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService);
126            mInitDoneSignal.countDown();
127        });
128    }
129
130    @Override
131    public void onBootPhase(int phase) {
132        // Wait for initialization in onStart to finish
133        if (phase == PHASE_SYSTEM_SERVICES_READY) {
134            try {
135                if (!mInitDoneSignal.await(10, TimeUnit.SECONDS)) {
136                    throw new IllegalStateException("Service " + TAG + " init timeout");
137                }
138            } catch (InterruptedException e) {
139                Thread.currentThread().interrupt();
140                throw new IllegalStateException("Service " + TAG + " init interrupted", e);
141            }
142        }
143        super.onBootPhase(phase);
144    }
145
146    private void formatIfOemUnlockEnabled() {
147        boolean enabled = doGetOemUnlockEnabled();
148        if (enabled) {
149            synchronized (mLock) {
150                formatPartitionLocked(true);
151            }
152        }
153
154        SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
155    }
156
157    private void enforceOemUnlockReadPermission() {
158        if (mContext.checkCallingOrSelfPermission(Manifest.permission.READ_OEM_UNLOCK_STATE)
159                == PackageManager.PERMISSION_DENIED
160                && mContext.checkCallingOrSelfPermission(Manifest.permission.OEM_UNLOCK_STATE)
161                == PackageManager.PERMISSION_DENIED) {
162            throw new SecurityException("Can't access OEM unlock state. Requires "
163                    + "READ_OEM_UNLOCK_STATE or OEM_UNLOCK_STATE permission.");
164        }
165    }
166
167    private void enforceOemUnlockWritePermission() {
168        mContext.enforceCallingOrSelfPermission(
169                Manifest.permission.OEM_UNLOCK_STATE,
170                "Can't modify OEM unlock state");
171    }
172
173    private void enforceUid(int callingUid) {
174        if (callingUid != mAllowedUid) {
175            throw new SecurityException("uid " + callingUid + " not allowed to access PST");
176        }
177    }
178
179    private void enforceIsAdmin() {
180        final int userId = UserHandle.getCallingUserId();
181        final boolean isAdmin = UserManager.get(mContext).isUserAdmin(userId);
182        if (!isAdmin) {
183            throw new SecurityException(
184                    "Only the Admin user is allowed to change OEM unlock state");
185        }
186    }
187
188    private void enforceUserRestriction(String userRestriction) {
189        if (UserManager.get(mContext).hasUserRestriction(userRestriction)) {
190            throw new SecurityException(
191                    "OEM unlock is disallowed by user restriction: " + userRestriction);
192        }
193    }
194
195    private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
196        // skip over checksum
197        inputStream.skipBytes(DIGEST_SIZE_BYTES);
198
199        int totalDataSize;
200        int blockId = inputStream.readInt();
201        if (blockId == PARTITION_TYPE_MARKER) {
202            totalDataSize = inputStream.readInt();
203        } else {
204            totalDataSize = 0;
205        }
206        return totalDataSize;
207    }
208
209    private long getBlockDeviceSize() {
210        synchronized (mLock) {
211            if (mBlockDeviceSize == -1) {
212                mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
213            }
214        }
215
216        return mBlockDeviceSize;
217    }
218
219    private boolean enforceChecksumValidity() {
220        byte[] storedDigest = new byte[DIGEST_SIZE_BYTES];
221
222        synchronized (mLock) {
223            byte[] digest = computeDigestLocked(storedDigest);
224            if (digest == null || !Arrays.equals(storedDigest, digest)) {
225                Slog.i(TAG, "Formatting FRP partition...");
226                formatPartitionLocked(false);
227                return false;
228            }
229        }
230
231        return true;
232    }
233
234    private boolean computeAndWriteDigestLocked() {
235        byte[] digest = computeDigestLocked(null);
236        if (digest != null) {
237            DataOutputStream outputStream;
238            try {
239                outputStream = new DataOutputStream(
240                        new FileOutputStream(new File(mDataBlockFile)));
241            } catch (FileNotFoundException e) {
242                Slog.e(TAG, "partition not available?", e);
243                return false;
244            }
245
246            try {
247                outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
248                outputStream.flush();
249            } catch (IOException e) {
250                Slog.e(TAG, "failed to write block checksum", e);
251                return false;
252            } finally {
253                IoUtils.closeQuietly(outputStream);
254            }
255            return true;
256        } else {
257            return false;
258        }
259    }
260
261    private byte[] computeDigestLocked(byte[] storedDigest) {
262        DataInputStream inputStream;
263        try {
264            inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
265        } catch (FileNotFoundException e) {
266            Slog.e(TAG, "partition not available?", e);
267            return null;
268        }
269
270        MessageDigest md;
271        try {
272            md = MessageDigest.getInstance("SHA-256");
273        } catch (NoSuchAlgorithmException e) {
274            // won't ever happen -- every implementation is required to support SHA-256
275            Slog.e(TAG, "SHA-256 not supported?", e);
276            IoUtils.closeQuietly(inputStream);
277            return null;
278        }
279
280        try {
281            if (storedDigest != null && storedDigest.length == DIGEST_SIZE_BYTES) {
282                inputStream.read(storedDigest);
283            } else {
284                inputStream.skipBytes(DIGEST_SIZE_BYTES);
285            }
286
287            int read;
288            byte[] data = new byte[1024];
289            md.update(data, 0, DIGEST_SIZE_BYTES); // include 0 checksum in digest
290            while ((read = inputStream.read(data)) != -1) {
291                md.update(data, 0, read);
292            }
293        } catch (IOException e) {
294            Slog.e(TAG, "failed to read partition", e);
295            return null;
296        } finally {
297            IoUtils.closeQuietly(inputStream);
298        }
299
300        return md.digest();
301    }
302
303    private void formatPartitionLocked(boolean setOemUnlockEnabled) {
304        DataOutputStream outputStream;
305        try {
306            outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
307        } catch (FileNotFoundException e) {
308            Slog.e(TAG, "partition not available?", e);
309            return;
310        }
311
312        byte[] data = new byte[DIGEST_SIZE_BYTES];
313        try {
314            outputStream.write(data, 0, DIGEST_SIZE_BYTES);
315            outputStream.writeInt(PARTITION_TYPE_MARKER);
316            outputStream.writeInt(0); // data size
317            outputStream.flush();
318        } catch (IOException e) {
319            Slog.e(TAG, "failed to format block", e);
320            return;
321        } finally {
322            IoUtils.closeQuietly(outputStream);
323        }
324
325        doSetOemUnlockEnabledLocked(setOemUnlockEnabled);
326        computeAndWriteDigestLocked();
327    }
328
329    private void doSetOemUnlockEnabledLocked(boolean enabled) {
330        FileOutputStream outputStream;
331        try {
332            outputStream = new FileOutputStream(new File(mDataBlockFile));
333        } catch (FileNotFoundException e) {
334            Slog.e(TAG, "partition not available", e);
335            return;
336        }
337
338        try {
339            FileChannel channel = outputStream.getChannel();
340
341            channel.position(getBlockDeviceSize() - 1);
342
343            ByteBuffer data = ByteBuffer.allocate(1);
344            data.put(enabled ? (byte) 1 : (byte) 0);
345            data.flip();
346            channel.write(data);
347            outputStream.flush();
348        } catch (IOException e) {
349            Slog.e(TAG, "unable to access persistent partition", e);
350            return;
351        } finally {
352            SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
353            IoUtils.closeQuietly(outputStream);
354        }
355    }
356
357    private boolean doGetOemUnlockEnabled() {
358        DataInputStream inputStream;
359        try {
360            inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
361        } catch (FileNotFoundException e) {
362            Slog.e(TAG, "partition not available");
363            return false;
364        }
365
366        try {
367            synchronized (mLock) {
368                inputStream.skip(getBlockDeviceSize() - 1);
369                return inputStream.readByte() != 0;
370            }
371        } catch (IOException e) {
372            Slog.e(TAG, "unable to access persistent partition", e);
373            return false;
374        } finally {
375            IoUtils.closeQuietly(inputStream);
376        }
377    }
378
379    private native long nativeGetBlockDeviceSize(String path);
380    private native int nativeWipe(String path);
381
382    private final IBinder mService = new IPersistentDataBlockService.Stub() {
383        @Override
384        public int write(byte[] data) throws RemoteException {
385            enforceUid(Binder.getCallingUid());
386
387            // Need to ensure we don't write over the last byte
388            long maxBlockSize = getBlockDeviceSize() - HEADER_SIZE - 1;
389            if (data.length > maxBlockSize) {
390                // partition is ~500k so shouldn't be a problem to downcast
391                return (int) -maxBlockSize;
392            }
393
394            DataOutputStream outputStream;
395            try {
396                outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile)));
397            } catch (FileNotFoundException e) {
398                Slog.e(TAG, "partition not available?", e);
399                return -1;
400            }
401
402            ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
403            headerAndData.putInt(PARTITION_TYPE_MARKER);
404            headerAndData.putInt(data.length);
405            headerAndData.put(data);
406
407            synchronized (mLock) {
408                if (!mIsWritable) {
409                    IoUtils.closeQuietly(outputStream);
410                    return -1;
411                }
412
413                try {
414                    byte[] checksum = new byte[DIGEST_SIZE_BYTES];
415                    outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
416                    outputStream.write(headerAndData.array());
417                    outputStream.flush();
418                } catch (IOException e) {
419                    Slog.e(TAG, "failed writing to the persistent data block", e);
420                    return -1;
421                } finally {
422                    IoUtils.closeQuietly(outputStream);
423                }
424
425                if (computeAndWriteDigestLocked()) {
426                    return data.length;
427                } else {
428                    return -1;
429                }
430            }
431        }
432
433        @Override
434        public byte[] read() {
435            enforceUid(Binder.getCallingUid());
436            if (!enforceChecksumValidity()) {
437                return new byte[0];
438            }
439
440            DataInputStream inputStream;
441            try {
442                inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
443            } catch (FileNotFoundException e) {
444                Slog.e(TAG, "partition not available?", e);
445                return null;
446            }
447
448            try {
449                synchronized (mLock) {
450                    int totalDataSize = getTotalDataSizeLocked(inputStream);
451
452                    if (totalDataSize == 0) {
453                        return new byte[0];
454                    }
455
456                    byte[] data = new byte[totalDataSize];
457                    int read = inputStream.read(data, 0, totalDataSize);
458                    if (read < totalDataSize) {
459                        // something went wrong, not returning potentially corrupt data
460                        Slog.e(TAG, "failed to read entire data block. bytes read: " +
461                                read + "/" + totalDataSize);
462                        return null;
463                    }
464                    return data;
465                }
466            } catch (IOException e) {
467                Slog.e(TAG, "failed to read data", e);
468                return null;
469            } finally {
470                try {
471                    inputStream.close();
472                } catch (IOException e) {
473                    Slog.e(TAG, "failed to close OutputStream");
474                }
475            }
476        }
477
478        @Override
479        public void wipe() {
480            enforceOemUnlockWritePermission();
481
482            synchronized (mLock) {
483                int ret = nativeWipe(mDataBlockFile);
484
485                if (ret < 0) {
486                    Slog.e(TAG, "failed to wipe persistent partition");
487                } else {
488                    mIsWritable = false;
489                    Slog.i(TAG, "persistent partition now wiped and unwritable");
490                }
491            }
492        }
493
494        @Override
495        public void setOemUnlockEnabled(boolean enabled) throws SecurityException {
496            // do not allow monkey to flip the flag
497            if (ActivityManager.isUserAMonkey()) {
498                return;
499            }
500
501            enforceOemUnlockWritePermission();
502            enforceIsAdmin();
503
504            if (enabled) {
505                // Do not allow oem unlock to be enabled if it's disallowed by a user restriction.
506                enforceUserRestriction(UserManager.DISALLOW_OEM_UNLOCK);
507                enforceUserRestriction(UserManager.DISALLOW_FACTORY_RESET);
508            }
509            synchronized (mLock) {
510                doSetOemUnlockEnabledLocked(enabled);
511                computeAndWriteDigestLocked();
512            }
513        }
514
515        @Override
516        public boolean getOemUnlockEnabled() {
517            enforceOemUnlockReadPermission();
518            return doGetOemUnlockEnabled();
519        }
520
521        @Override
522        public int getFlashLockState() {
523            enforceOemUnlockReadPermission();
524            String locked = SystemProperties.get(FLASH_LOCK_PROP);
525            switch (locked) {
526                case FLASH_LOCK_LOCKED:
527                    return PersistentDataBlockManager.FLASH_LOCK_LOCKED;
528                case FLASH_LOCK_UNLOCKED:
529                    return PersistentDataBlockManager.FLASH_LOCK_UNLOCKED;
530                default:
531                    return PersistentDataBlockManager.FLASH_LOCK_UNKNOWN;
532            }
533        }
534
535        @Override
536        public int getDataBlockSize() {
537            enforcePersistentDataBlockAccess();
538
539            DataInputStream inputStream;
540            try {
541                inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile)));
542            } catch (FileNotFoundException e) {
543                Slog.e(TAG, "partition not available");
544                return 0;
545            }
546
547            try {
548                synchronized (mLock) {
549                    return getTotalDataSizeLocked(inputStream);
550                }
551            } catch (IOException e) {
552                Slog.e(TAG, "error reading data block size");
553                return 0;
554            } finally {
555                IoUtils.closeQuietly(inputStream);
556            }
557        }
558
559        private void enforcePersistentDataBlockAccess() {
560            if (mContext.checkCallingPermission(Manifest.permission.ACCESS_PDB_STATE)
561                    != PackageManager.PERMISSION_GRANTED) {
562                enforceUid(Binder.getCallingUid());
563            }
564        }
565
566        @Override
567        public long getMaximumDataBlockSize() {
568            long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1;
569            return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE;
570        }
571    };
572}
573