PersistentDataBlockService.java revision 68d4acd205e8c2da524e62734ca42847306cc029
1package com.android.server; 2 3import android.Manifest; 4import android.content.Context; 5import android.content.pm.PackageManager; 6import android.os.Binder; 7import android.os.IBinder; 8import android.os.RemoteException; 9import android.os.SystemProperties; 10import android.service.persistentdata.IPersistentDataBlockService; 11import android.util.Log; 12import com.android.internal.R; 13 14import java.io.DataInputStream; 15import java.io.DataOutputStream; 16import java.io.File; 17import java.io.FileInputStream; 18import java.io.FileNotFoundException; 19import java.io.FileOutputStream; 20import java.io.IOException; 21import java.nio.ByteBuffer; 22import java.nio.channels.FileChannel; 23 24/** 25 * Service for reading and writing blocks to a persistent partition. 26 * This data will live across factory resets. 27 * 28 * Allows writing one block at a time. Namely, each time 29 * {@link android.service.persistentdata.IPersistentDataBlockService}.write(byte[] data) 30 * is called, it will overwite the data that was previously written on the block. 31 * 32 * Clients can query the size of the currently written block via 33 * {@link android.service.persistentdata.IPersistentDataBlockService}.getTotalDataSize(). 34 * 35 * Clients can any number of bytes from the currently written block up to its total size by invoking 36 * {@link android.service.persistentdata.IPersistentDataBlockService}.read(byte[] data) 37 */ 38public class PersistentDataBlockService extends SystemService { 39 private static final String TAG = PersistentDataBlockService.class.getSimpleName(); 40 41 private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst"; 42 private static final int HEADER_SIZE = 8; 43 private static final int BLOCK_ID = 0x1990; 44 45 private final Context mContext; 46 private final String mDataBlockFile; 47 private long mBlockDeviceSize; 48 49 private final int mAllowedUid; 50 51 public PersistentDataBlockService(Context context) { 52 super(context); 53 mContext = context; 54 mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP); 55 mBlockDeviceSize = 0; // Load lazily 56 String allowedPackage = context.getResources() 57 .getString(R.string.config_persistentDataPackageName); 58 PackageManager pm = mContext.getPackageManager(); 59 int allowedUid = -1; 60 try { 61 allowedUid = pm.getPackageUid(allowedPackage, 62 Binder.getCallingUserHandle().getIdentifier()); 63 } catch (PackageManager.NameNotFoundException e) { 64 // not expected 65 Log.e(TAG, "not able to find package " + allowedPackage, e); 66 } 67 68 mAllowedUid = allowedUid; 69 } 70 71 @Override 72 public void onStart() { 73 publishBinderService(Context.PERSISTENT_DATA_BLOCK_SERVICE, mService); 74 } 75 76 private void enforceOemUnlockPermission() { 77 mContext.enforceCallingOrSelfPermission( 78 Manifest.permission.OEM_UNLOCK_STATE, 79 "Can't access OEM unlock state"); 80 } 81 82 private void enforceUid(int callingUid) { 83 if (callingUid != mAllowedUid) { 84 throw new SecurityException("uid " + callingUid + " not allowed to access PST"); 85 } 86 } 87 88 private int getTotalDataSize(DataInputStream inputStream) throws IOException { 89 int totalDataSize; 90 int blockId = inputStream.readInt(); 91 if (blockId == BLOCK_ID) { 92 totalDataSize = inputStream.readInt(); 93 } else { 94 totalDataSize = 0; 95 } 96 return totalDataSize; 97 } 98 99 private long maybeReadBlockDeviceSize() { 100 synchronized (this) { 101 if (mBlockDeviceSize == 0) { 102 mBlockDeviceSize = getBlockDeviceSize(mDataBlockFile); 103 } 104 } 105 106 return mBlockDeviceSize; 107 } 108 109 private native long getBlockDeviceSize(String path); 110 111 private final IBinder mService = new IPersistentDataBlockService.Stub() { 112 @Override 113 public int write(byte[] data) throws RemoteException { 114 enforceUid(Binder.getCallingUid()); 115 116 // Need to ensure we don't write over the last byte 117 if (data.length > maybeReadBlockDeviceSize() - HEADER_SIZE - 1) { 118 return -1; 119 } 120 121 DataOutputStream outputStream; 122 try { 123 outputStream = new DataOutputStream(new FileOutputStream(new File(mDataBlockFile))); 124 } catch (FileNotFoundException e) { 125 Log.e(TAG, "partition not available?", e); 126 return -1; 127 } 128 129 ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE); 130 headerAndData.putInt(BLOCK_ID); 131 headerAndData.putInt(data.length); 132 headerAndData.put(data); 133 134 try { 135 outputStream.write(headerAndData.array()); 136 return data.length; 137 } catch (IOException e) { 138 Log.e(TAG, "failed writing to the persistent data block", e); 139 return -1; 140 } finally { 141 try { 142 outputStream.close(); 143 } catch (IOException e) { 144 Log.e(TAG, "failed closing output stream", e); 145 } 146 } 147 } 148 149 @Override 150 public int read(byte[] data) { 151 enforceUid(Binder.getCallingUid()); 152 153 DataInputStream inputStream; 154 try { 155 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 156 } catch (FileNotFoundException e) { 157 Log.e(TAG, "partition not available?", e); 158 return -1; 159 } 160 161 try { 162 int totalDataSize = getTotalDataSize(inputStream); 163 return inputStream.read(data, 0, 164 (data.length > totalDataSize) ? totalDataSize : data.length); 165 } catch (IOException e) { 166 Log.e(TAG, "failed to read data", e); 167 return -1; 168 } finally { 169 try { 170 inputStream.close(); 171 } catch (IOException e) { 172 Log.e(TAG, "failed to close OutputStream"); 173 } 174 } 175 } 176 177 @Override 178 public void setOemUnlockEnabled(boolean enabled) { 179 enforceOemUnlockPermission(); 180 FileOutputStream outputStream; 181 try { 182 outputStream = new FileOutputStream(new File(mDataBlockFile)); 183 } catch (FileNotFoundException e) { 184 Log.e(TAG, "parition not available", e); 185 return; 186 } 187 188 try { 189 FileChannel channel = outputStream.getChannel(); 190 191 channel.position(maybeReadBlockDeviceSize() - 1); 192 193 ByteBuffer data = ByteBuffer.allocate(1); 194 data.put(enabled ? (byte) 1 : (byte) 0); 195 data.flip(); 196 197 channel.write(data); 198 } catch (IOException e) { 199 Log.e(TAG, "unable to access persistent partition", e); 200 } finally { 201 try { 202 outputStream.close(); 203 } catch (IOException e) { 204 Log.e(TAG, "failed to close OutputStream"); 205 } 206 } 207 } 208 209 @Override 210 public boolean getOemUnlockEnabled() { 211 enforceOemUnlockPermission(); 212 DataInputStream inputStream; 213 try { 214 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 215 } catch (FileNotFoundException e) { 216 Log.e(TAG, "partition not available"); 217 return false; 218 } 219 220 try { 221 inputStream.skip(maybeReadBlockDeviceSize() - 1); 222 return inputStream.readByte() != 0; 223 } catch (IOException e) { 224 Log.e(TAG, "unable to access persistent partition", e); 225 return false; 226 } finally { 227 try { 228 inputStream.close(); 229 } catch (IOException e) { 230 Log.e(TAG, "failed to close OutputStream"); 231 } 232 } 233 } 234 235 @Override 236 public int getDataBlockSize() { 237 enforceUid(Binder.getCallingUid()); 238 239 DataInputStream inputStream; 240 try { 241 inputStream = new DataInputStream(new FileInputStream(new File(mDataBlockFile))); 242 } catch (FileNotFoundException e) { 243 Log.e(TAG, "partition not available"); 244 return 0; 245 } 246 247 try { 248 return getTotalDataSize(inputStream); 249 } catch (IOException e) { 250 Log.e(TAG, "error reading data block size"); 251 return 0; 252 } finally { 253 try { 254 inputStream.close(); 255 } catch (IOException e) { 256 Log.e(TAG, "failed to close OutputStream"); 257 } 258 } 259 } 260 }; 261} 262