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.service.persistentdata.IPersistentDataBlockService; 29import android.util.Slog; 30 31import com.android.internal.R; 32 33import libcore.io.IoUtils; 34 35import java.io.DataInputStream; 36import java.io.DataOutputStream; 37import java.io.File; 38import java.io.FileInputStream; 39import java.io.FileNotFoundException; 40import java.io.FileOutputStream; 41import java.io.IOException; 42import java.nio.ByteBuffer; 43import java.nio.channels.FileChannel; 44 45/** 46 * Service for reading and writing blocks to a persistent partition. 47 * This data will live across factory resets not initiated via the Settings UI. 48 * When a device is factory reset through Settings this data is wiped. 49 * 50 * Allows writing one block at a time. Namely, each time 51 * {@link android.service.persistentdata.IPersistentDataBlockService}.write(byte[] data) 52 * is called, it will overwite the data that was previously written on the block. 53 * 54 * Clients can query the size of the currently written block via 55 * {@link android.service.persistentdata.IPersistentDataBlockService}.getTotalDataSize(). 56 * 57 * Clients can any number of bytes from the currently written block up to its total size by invoking 58 * {@link android.service.persistentdata.IPersistentDataBlockService}.read(byte[] data) 59 */ 60public class PersistentDataBlockService extends SystemService { 61 private static final String TAG = PersistentDataBlockService.class.getSimpleName(); 62 63 private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; 64 private static final int HEADER_SIZE = 8; 65 // Magic number to mark block device as adhering to the format consumed by this service 66 private static final int PARTITION_TYPE_MARKER = 0x1990; 67 // Limit to 100k as blocks larger than this might cause strain on Binder. 68 // TODO(anmorales): Consider splitting up too-large blocks in PersistentDataBlockManager 69 private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100; 70 71 private final Context mContext; 72 private final String mDataBlockFile; 73 private final Object mLock = new Object(); 74 75 private int mAllowedAppId = -1; 76 /* 77 * Separate lock for OEM unlock related operations as they can happen in parallel with regular 78 * block operations. 79 */ 80 private final Object mOemLock = new Object(); 81 82 private long mBlockDeviceSize; 83 84 public PersistentDataBlockService(Context context) { 85 super(context); 86 mContext = context; 87 mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP); 88 mBlockDeviceSize = -1; // Load lazily 89 mAllowedAppId = getAllowedAppId(UserHandle.USER_OWNER); 90 } 91 92 93 private int getAllowedAppId(int userHandle) { 94 String allowedPackage = mContext.getResources() 95 .getString(R.string.config_persistentDataPackageName); 96 PackageManager pm = mContext.getPackageManager(); 97 int allowedUid = -1; 98 try { 99 allowedUid = pm.getPackageUid(allowedPackage, userHandle); 100 } catch (PackageManager.NameNotFoundException e) { 101 // not expected 102 Slog.e(TAG, "not able to find package " + allowedPackage, e); 103 } 104 return UserHandle.getAppId(allowedUid); 105 } 106 107 @Override 108 public void onStart() { 109 publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService); 110 } 111 112 private void enforceOemUnlockPermission() { 113 mContext.enforceCallingOrSelfPermission( 114 Manifest.permission.OEM_UNLOCK_STATE, 115 "Can't access OEM unlock state"); 116 } 117 118 private void enforceUid(int callingUid) { 119 if (UserHandle.getAppId(callingUid) != mAllowedAppId) { 120 throw new SecurityException("uid " + callingUid + " not allowed to access PST"); 121 } 122 } 123 124 private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException { 125 int totalDataSize; 126 int blockId = inputStream.readInt(); 127 if (blockId == PARTITION_TYPE_MARKER) { 128 totalDataSize = inputStream.readInt(); 129 } else { 130 totalDataSize = 0; 131 } 132 return totalDataSize; 133 } 134 135 private long getBlockDeviceSize() { 136 synchronized (mLock) { 137 if (mBlockDeviceSize == -1) { 138 mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile); 139 } 140 } 141 142 return mBlockDeviceSize; 143 } 144 145 private native long nativeGetBlockDeviceSize(String path); 146 private native int nativeWipe(String path); 147 148 private final IBinder mService = new IPersistentDataBlockService.Stub() { 149 @Override 150 public int write(byte[] data) throws RemoteException { 151 enforceUid(Binder.getCallingUid()); 152 153 // Need to ensure we don't write over the last byte 154 long maxBlockSize = getBlockDeviceSize() - HEADER_SIZE - 1; 155 if (data.length > maxBlockSize) { 156 // partition is ~500k so shouldn't be a problem to downcast 157 return (int) -maxBlockSize; 158 } 159 160 DataOutputStream outputStream; 161 try { 162 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile))); 163 } catch (FileNotFoundException e) { 164 Slog.e(TAG, "partition not available?", e); 165 return -1; 166 } 167 168 ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE); 169 headerAndData.putInt(PARTITION_TYPE_MARKER); 170 headerAndData.putInt(data.length); 171 headerAndData.put(data); 172 173 try { 174 synchronized (mLock) { 175 outputStream.write(headerAndData.array()); 176 return data.length; 177 } 178 } catch (IOException e) { 179 Slog.e(TAG, "failed writing to the persistent data block", e); 180 return -1; 181 } finally { 182 try { 183 outputStream.close(); 184 } catch (IOException e) { 185 Slog.e(TAG, "failed closing output stream", e); 186 } 187 } 188 } 189 190 @Override 191 public byte[] read() { 192 enforceUid(Binder.getCallingUid()); 193 194 DataInputStream inputStream; 195 try { 196 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 197 } catch (FileNotFoundException e) { 198 Slog.e(TAG, "partition not available?", e); 199 return null; 200 } 201 202 try { 203 synchronized (mLock) { 204 int totalDataSize = getTotalDataSizeLocked(inputStream); 205 206 if (totalDataSize == 0) { 207 return new byte[0]; 208 } 209 210 byte[] data = new byte[totalDataSize]; 211 int read = inputStream.read(data, 0, totalDataSize); 212 if (read < totalDataSize) { 213 // something went wrong, not returning potentially corrupt data 214 Slog.e(TAG, "failed to read entire data block. bytes read: " + 215 read + "/" + totalDataSize); 216 return null; 217 } 218 return data; 219 } 220 } catch (IOException e) { 221 Slog.e(TAG, "failed to read data", e); 222 return null; 223 } finally { 224 try { 225 inputStream.close(); 226 } catch (IOException e) { 227 Slog.e(TAG, "failed to close OutputStream"); 228 } 229 } 230 } 231 232 @Override 233 public void wipe() { 234 enforceOemUnlockPermission(); 235 236 synchronized (mLock) { 237 int ret = nativeWipe(mDataBlockFile); 238 239 if (ret < 0) { 240 Slog.e(TAG, "failed to wipe persistent partition"); 241 } 242 } 243 } 244 245 @Override 246 public void setOemUnlockEnabled(boolean enabled) { 247 // do not allow monkey to flip the flag 248 if (ActivityManager.isUserAMonkey()) { 249 return; 250 } 251 enforceOemUnlockPermission(); 252 FileOutputStream outputStream; 253 try { 254 outputStream = new FileOutputStream(new File(mDataBlockFile)); 255 } catch (FileNotFoundException e) { 256 Slog.e(TAG, "parition not available", e); 257 return; 258 } 259 260 try { 261 FileChannel channel = outputStream.getChannel(); 262 263 channel.position(getBlockDeviceSize() - 1); 264 265 ByteBuffer data = ByteBuffer.allocate(1); 266 data.put(enabled ? (byte) 1 : (byte) 0); 267 data.flip(); 268 269 synchronized (mOemLock) { 270 channel.write(data); 271 } 272 } catch (IOException e) { 273 Slog.e(TAG, "unable to access persistent partition", e); 274 } finally { 275 IoUtils.closeQuietly(outputStream); 276 } 277 } 278 279 @Override 280 public boolean getOemUnlockEnabled() { 281 enforceOemUnlockPermission(); 282 DataInputStream inputStream; 283 try { 284 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 285 } catch (FileNotFoundException e) { 286 Slog.e(TAG, "partition not available"); 287 return false; 288 } 289 290 try { 291 inputStream.skip(getBlockDeviceSize() - 1); 292 synchronized (mOemLock) { 293 return inputStream.readByte() != 0; 294 } 295 } catch (IOException e) { 296 Slog.e(TAG, "unable to access persistent partition", e); 297 return false; 298 } finally { 299 IoUtils.closeQuietly(inputStream); 300 } 301 } 302 303 @Override 304 public int getDataBlockSize() { 305 enforceUid(Binder.getCallingUid()); 306 307 DataInputStream inputStream; 308 try { 309 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 310 } catch (FileNotFoundException e) { 311 Slog.e(TAG, "partition not available"); 312 return 0; 313 } 314 315 try { 316 synchronized (mLock) { 317 return getTotalDataSizeLocked(inputStream); 318 } 319 } catch (IOException e) { 320 Slog.e(TAG, "error reading data block size"); 321 return 0; 322 } finally { 323 IoUtils.closeQuietly(inputStream); 324 } 325 } 326 327 @Override 328 public long getMaximumDataBlockSize() { 329 long actualSize = getBlockDeviceSize() - HEADER_SIZE - 1; 330 return actualSize <= MAX_DATA_BLOCK_SIZE ? actualSize : MAX_DATA_BLOCK_SIZE; 331 } 332 333 }; 334} 335