blob: baafff53d072f3fe03bb386e13e3bed63d2b50ee [file] [log] [blame]
Sudheer Shankad2da0672019-12-20 15:51:20 -08001/*
2 * Copyright 2020 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 */
16package com.android.server.blob;
17
Sudheer Shankaab1d4162020-01-07 10:37:50 -080018import static android.app.blob.BlobStoreManager.COMMIT_RESULT_ERROR;
Sudheer Shankab2a17b72020-05-28 01:14:10 -070019import static android.app.blob.XmlTags.ATTR_CREATION_TIME_MS;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080020import static android.app.blob.XmlTags.ATTR_ID;
21import static android.app.blob.XmlTags.ATTR_PACKAGE;
22import static android.app.blob.XmlTags.ATTR_UID;
23import static android.app.blob.XmlTags.TAG_ACCESS_MODE;
24import static android.app.blob.XmlTags.TAG_BLOB_HANDLE;
Sudheer Shankad4ea5e12020-02-04 16:42:17 -080025import static android.os.Trace.TRACE_TAG_SYSTEM_SERVER;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080026import static android.system.OsConstants.O_CREAT;
Sudheer Shankadb483fa2020-01-22 12:31:33 -080027import static android.system.OsConstants.O_RDONLY;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080028import static android.system.OsConstants.O_RDWR;
29import static android.system.OsConstants.SEEK_SET;
Sudheer Shanka75351542020-06-05 22:42:56 -070030import static android.text.format.Formatter.FLAG_IEC_UNITS;
31import static android.text.format.Formatter.formatFileSize;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080032
33import static com.android.server.blob.BlobStoreConfig.TAG;
Sudheer Shankab2a17b72020-05-28 01:14:10 -070034import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_SESSION_CREATION_TIME;
35import static com.android.server.blob.BlobStoreConfig.hasSessionExpired;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080036
Sudheer Shankad2da0672019-12-20 15:51:20 -080037import android.annotation.BytesLong;
38import android.annotation.NonNull;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080039import android.annotation.Nullable;
40import android.app.blob.BlobHandle;
Sudheer Shankad2da0672019-12-20 15:51:20 -080041import android.app.blob.IBlobCommitCallback;
42import android.app.blob.IBlobStoreSession;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080043import android.content.Context;
44import android.os.Binder;
45import android.os.FileUtils;
Sudheer Shankad2da0672019-12-20 15:51:20 -080046import android.os.ParcelFileDescriptor;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080047import android.os.RemoteException;
48import android.os.RevocableFileDescriptor;
Sudheer Shankad4ea5e12020-02-04 16:42:17 -080049import android.os.Trace;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080050import android.os.storage.StorageManager;
51import android.system.ErrnoException;
52import android.system.Os;
53import android.util.ExceptionUtils;
54import android.util.Slog;
55
56import com.android.internal.annotations.GuardedBy;
Sudheer Shankabda89c12020-01-30 15:19:24 -080057import com.android.internal.annotations.VisibleForTesting;
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -070058import com.android.internal.util.FrameworkStatsLog;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080059import com.android.internal.util.IndentingPrintWriter;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080060import com.android.internal.util.Preconditions;
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080061import com.android.internal.util.XmlUtils;
Sudheer Shanka5caeed52020-02-01 12:54:33 -080062import com.android.server.blob.BlobStoreManagerService.DumpArgs;
Sudheer Shankaab1d4162020-01-07 10:37:50 -080063import com.android.server.blob.BlobStoreManagerService.SessionStateChangeListener;
64
Sudheer Shanka300d9cb2020-06-04 23:33:13 -070065import libcore.io.IoUtils;
66
Sudheer Shankaf6e23b92020-01-15 01:51:15 -080067import org.xmlpull.v1.XmlPullParser;
68import org.xmlpull.v1.XmlPullParserException;
69import org.xmlpull.v1.XmlSerializer;
70
Sudheer Shankaab1d4162020-01-07 10:37:50 -080071import java.io.File;
72import java.io.FileDescriptor;
73import java.io.IOException;
74import java.security.NoSuchAlgorithmException;
75import java.util.ArrayList;
76import java.util.Arrays;
Sudheer Shanka4824e3d2020-01-29 23:50:24 -080077import java.util.Objects;
Sudheer Shankad2da0672019-12-20 15:51:20 -080078
79/** TODO: add doc */
Sudheer Shankabda89c12020-01-30 15:19:24 -080080@VisibleForTesting
81class BlobStoreSession extends IBlobStoreSession.Stub {
Sudheer Shankad2da0672019-12-20 15:51:20 -080082
Sudheer Shankaab1d4162020-01-07 10:37:50 -080083 static final int STATE_OPENED = 1;
84 static final int STATE_CLOSED = 0;
85 static final int STATE_ABANDONED = 2;
86 static final int STATE_COMMITTED = 3;
87 static final int STATE_VERIFIED_VALID = 4;
88 static final int STATE_VERIFIED_INVALID = 5;
89
90 private final Object mSessionLock = new Object();
91
92 private final Context mContext;
93 private final SessionStateChangeListener mListener;
94
Sudheer Shankabda89c12020-01-30 15:19:24 -080095 private final BlobHandle mBlobHandle;
96 private final long mSessionId;
97 private final int mOwnerUid;
98 private final String mOwnerPackageName;
Sudheer Shankab2a17b72020-05-28 01:14:10 -070099 private final long mCreationTimeMs;
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800100
101 // Do not access this directly, instead use getSessionFile().
102 private File mSessionFile;
103
104 @GuardedBy("mRevocableFds")
Sudheer Shanka36b25a12020-06-13 22:27:28 -0700105 private final ArrayList<RevocableFileDescriptor> mRevocableFds = new ArrayList<>();
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800106
Sudheer Shankab76f7662020-02-27 15:17:43 -0800107 // This will be accessed from only one thread at any point of time, so no need to grab
108 // a lock for this.
109 private byte[] mDataDigest;
110
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800111 @GuardedBy("mSessionLock")
112 private int mState = STATE_CLOSED;
113
114 @GuardedBy("mSessionLock")
115 private final BlobAccessMode mBlobAccessMode = new BlobAccessMode();
116
117 @GuardedBy("mSessionLock")
118 private IBlobCommitCallback mBlobCommitCallback;
119
Sudheer Shankab2a17b72020-05-28 01:14:10 -0700120 private BlobStoreSession(Context context, long sessionId, BlobHandle blobHandle,
121 int ownerUid, String ownerPackageName, long creationTimeMs,
122 SessionStateChangeListener listener) {
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800123 this.mContext = context;
Sudheer Shankabda89c12020-01-30 15:19:24 -0800124 this.mBlobHandle = blobHandle;
125 this.mSessionId = sessionId;
126 this.mOwnerUid = ownerUid;
127 this.mOwnerPackageName = ownerPackageName;
Sudheer Shankab2a17b72020-05-28 01:14:10 -0700128 this.mCreationTimeMs = creationTimeMs;
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800129 this.mListener = listener;
130 }
131
Sudheer Shankab2a17b72020-05-28 01:14:10 -0700132 BlobStoreSession(Context context, long sessionId, BlobHandle blobHandle,
133 int ownerUid, String ownerPackageName, SessionStateChangeListener listener) {
134 this(context, sessionId, blobHandle, ownerUid, ownerPackageName,
135 System.currentTimeMillis(), listener);
136 }
137
Sudheer Shankabda89c12020-01-30 15:19:24 -0800138 public BlobHandle getBlobHandle() {
139 return mBlobHandle;
140 }
141
142 public long getSessionId() {
143 return mSessionId;
144 }
145
146 public int getOwnerUid() {
147 return mOwnerUid;
148 }
149
150 public String getOwnerPackageName() {
151 return mOwnerPackageName;
152 }
153
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800154 boolean hasAccess(int callingUid, String callingPackageName) {
Sudheer Shankabda89c12020-01-30 15:19:24 -0800155 return mOwnerUid == callingUid && mOwnerPackageName.equals(callingPackageName);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800156 }
157
158 void open() {
159 synchronized (mSessionLock) {
160 if (isFinalized()) {
161 throw new IllegalStateException("Not allowed to open session with state: "
162 + stateToString(mState));
163 }
164 mState = STATE_OPENED;
165 }
166 }
167
168 int getState() {
169 synchronized (mSessionLock) {
170 return mState;
171 }
172 }
173
174 void sendCommitCallbackResult(int result) {
175 synchronized (mSessionLock) {
176 try {
177 mBlobCommitCallback.onResult(result);
178 } catch (RemoteException e) {
179 Slog.d(TAG, "Error sending the callback result", e);
180 }
181 mBlobCommitCallback = null;
182 }
183 }
184
185 BlobAccessMode getBlobAccessMode() {
186 synchronized (mSessionLock) {
187 return mBlobAccessMode;
188 }
189 }
190
191 boolean isFinalized() {
192 synchronized (mSessionLock) {
193 return mState == STATE_COMMITTED || mState == STATE_ABANDONED;
194 }
195 }
196
Sudheer Shankab2a17b72020-05-28 01:14:10 -0700197 boolean isExpired() {
198 final long lastModifiedTimeMs = getSessionFile().lastModified();
199 return hasSessionExpired(lastModifiedTimeMs == 0
200 ? mCreationTimeMs : lastModifiedTimeMs);
201 }
202
Sudheer Shankad2da0672019-12-20 15:51:20 -0800203 @Override
204 @NonNull
205 public ParcelFileDescriptor openWrite(@BytesLong long offsetBytes,
206 @BytesLong long lengthBytes) {
Sudheer Shanka4824e3d2020-01-29 23:50:24 -0800207 Preconditions.checkArgumentNonnegative(offsetBytes, "offsetBytes must not be negative");
208
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800209 assertCallerIsOwner();
210 synchronized (mSessionLock) {
211 if (mState != STATE_OPENED) {
212 throw new IllegalStateException("Not allowed to write in state: "
213 + stateToString(mState));
214 }
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700215 }
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800216
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700217 FileDescriptor fd = null;
218 try {
219 fd = openWriteInternal(offsetBytes, lengthBytes);
220 final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd);
221 synchronized (mSessionLock) {
222 if (mState != STATE_OPENED) {
223 IoUtils.closeQuietly(fd);
224 throw new IllegalStateException("Not allowed to write in state: "
225 + stateToString(mState));
226 }
227 trackRevocableFdLocked(revocableFd);
228 return revocableFd.getRevocableFileDescriptor();
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800229 }
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700230 } catch (IOException e) {
231 IoUtils.closeQuietly(fd);
232 throw ExceptionUtils.wrap(e);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800233 }
234 }
235
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800236 @NonNull
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700237 private FileDescriptor openWriteInternal(@BytesLong long offsetBytes,
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800238 @BytesLong long lengthBytes) throws IOException {
239 // TODO: Add limit on active open sessions/writes/reads
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800240 try {
241 final File sessionFile = getSessionFile();
242 if (sessionFile == null) {
243 throw new IllegalStateException("Couldn't get the file for this session");
244 }
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700245 final FileDescriptor fd = Os.open(sessionFile.getPath(), O_CREAT | O_RDWR, 0600);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800246 if (offsetBytes > 0) {
247 final long curOffset = Os.lseek(fd, offsetBytes, SEEK_SET);
248 if (curOffset != offsetBytes) {
249 throw new IllegalStateException("Failed to seek " + offsetBytes
250 + "; curOffset=" + offsetBytes);
251 }
252 }
253 if (lengthBytes > 0) {
254 mContext.getSystemService(StorageManager.class).allocateBytes(fd, lengthBytes);
255 }
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700256 return fd;
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800257 } catch (ErrnoException e) {
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700258 throw e.rethrowAsIOException();
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800259 }
Sudheer Shankad2da0672019-12-20 15:51:20 -0800260 }
261
262 @Override
Sudheer Shankadb483fa2020-01-22 12:31:33 -0800263 @NonNull
264 public ParcelFileDescriptor openRead() {
265 assertCallerIsOwner();
266 synchronized (mSessionLock) {
267 if (mState != STATE_OPENED) {
268 throw new IllegalStateException("Not allowed to read in state: "
269 + stateToString(mState));
270 }
Sudheer Shankad6f47942020-06-18 16:46:36 -0700271 if (!BlobStoreConfig.shouldUseRevocableFdForReads()) {
272 try {
273 return new ParcelFileDescriptor(openReadInternal());
274 } catch (IOException e) {
275 throw ExceptionUtils.wrap(e);
276 }
277 }
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700278 }
Sudheer Shankadb483fa2020-01-22 12:31:33 -0800279
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700280 FileDescriptor fd = null;
281 try {
282 fd = openReadInternal();
283 final RevocableFileDescriptor revocableFd = new RevocableFileDescriptor(mContext, fd);
284 synchronized (mSessionLock) {
285 if (mState != STATE_OPENED) {
286 IoUtils.closeQuietly(fd);
287 throw new IllegalStateException("Not allowed to read in state: "
288 + stateToString(mState));
289 }
290 trackRevocableFdLocked(revocableFd);
291 return revocableFd.getRevocableFileDescriptor();
Sudheer Shankadb483fa2020-01-22 12:31:33 -0800292 }
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700293 } catch (IOException e) {
294 IoUtils.closeQuietly(fd);
295 throw ExceptionUtils.wrap(e);
Sudheer Shankadb483fa2020-01-22 12:31:33 -0800296 }
297 }
298
Sudheer Shankadb483fa2020-01-22 12:31:33 -0800299 @NonNull
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700300 private FileDescriptor openReadInternal() throws IOException {
Sudheer Shankadb483fa2020-01-22 12:31:33 -0800301 try {
302 final File sessionFile = getSessionFile();
303 if (sessionFile == null) {
304 throw new IllegalStateException("Couldn't get the file for this session");
305 }
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700306 final FileDescriptor fd = Os.open(sessionFile.getPath(), O_RDONLY, 0);
307 return fd;
Sudheer Shankadb483fa2020-01-22 12:31:33 -0800308 } catch (ErrnoException e) {
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700309 throw e.rethrowAsIOException();
Sudheer Shankadb483fa2020-01-22 12:31:33 -0800310 }
Sudheer Shankadb483fa2020-01-22 12:31:33 -0800311 }
312
313 @Override
Sudheer Shankad2da0672019-12-20 15:51:20 -0800314 @BytesLong
315 public long getSize() {
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800316 return getSessionFile().length();
Sudheer Shankad2da0672019-12-20 15:51:20 -0800317 }
318
319 @Override
320 public void allowPackageAccess(@NonNull String packageName,
321 @NonNull byte[] certificate) {
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800322 assertCallerIsOwner();
Sudheer Shanka4824e3d2020-01-29 23:50:24 -0800323 Objects.requireNonNull(packageName, "packageName must not be null");
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800324 synchronized (mSessionLock) {
325 if (mState != STATE_OPENED) {
326 throw new IllegalStateException("Not allowed to change access type in state: "
327 + stateToString(mState));
328 }
329 mBlobAccessMode.allowPackageAccess(packageName, certificate);
330 }
Sudheer Shankad2da0672019-12-20 15:51:20 -0800331 }
332
333 @Override
334 public void allowSameSignatureAccess() {
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800335 assertCallerIsOwner();
336 synchronized (mSessionLock) {
337 if (mState != STATE_OPENED) {
338 throw new IllegalStateException("Not allowed to change access type in state: "
339 + stateToString(mState));
340 }
341 mBlobAccessMode.allowSameSignatureAccess();
342 }
Sudheer Shankad2da0672019-12-20 15:51:20 -0800343 }
344
345 @Override
346 public void allowPublicAccess() {
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800347 assertCallerIsOwner();
348 synchronized (mSessionLock) {
349 if (mState != STATE_OPENED) {
350 throw new IllegalStateException("Not allowed to change access type in state: "
351 + stateToString(mState));
352 }
353 mBlobAccessMode.allowPublicAccess();
354 }
355 }
356
357 @Override
358 public boolean isPackageAccessAllowed(@NonNull String packageName,
359 @NonNull byte[] certificate) {
360 assertCallerIsOwner();
Sudheer Shanka4824e3d2020-01-29 23:50:24 -0800361 Objects.requireNonNull(packageName, "packageName must not be null");
362 Preconditions.checkByteArrayNotEmpty(certificate, "certificate");
363
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800364 synchronized (mSessionLock) {
365 if (mState != STATE_OPENED) {
366 throw new IllegalStateException("Not allowed to get access type in state: "
367 + stateToString(mState));
368 }
369 return mBlobAccessMode.isPackageAccessAllowed(packageName, certificate);
370 }
371 }
372
373 @Override
374 public boolean isSameSignatureAccessAllowed() {
375 assertCallerIsOwner();
376 synchronized (mSessionLock) {
377 if (mState != STATE_OPENED) {
378 throw new IllegalStateException("Not allowed to get access type in state: "
379 + stateToString(mState));
380 }
381 return mBlobAccessMode.isSameSignatureAccessAllowed();
382 }
383 }
384
385 @Override
386 public boolean isPublicAccessAllowed() {
387 assertCallerIsOwner();
388 synchronized (mSessionLock) {
389 if (mState != STATE_OPENED) {
390 throw new IllegalStateException("Not allowed to get access type in state: "
391 + stateToString(mState));
392 }
393 return mBlobAccessMode.isPublicAccessAllowed();
394 }
Sudheer Shankad2da0672019-12-20 15:51:20 -0800395 }
396
397 @Override
398 public void close() {
Sudheer Shankab5239e42020-03-04 23:23:54 -0800399 closeSession(STATE_CLOSED, false /* sendCallback */);
Sudheer Shankad2da0672019-12-20 15:51:20 -0800400 }
401
402 @Override
403 public void abandon() {
Sudheer Shankab5239e42020-03-04 23:23:54 -0800404 closeSession(STATE_ABANDONED, true /* sendCallback */);
Sudheer Shankad2da0672019-12-20 15:51:20 -0800405 }
406
407 @Override
408 public void commit(IBlobCommitCallback callback) {
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800409 synchronized (mSessionLock) {
410 mBlobCommitCallback = callback;
411
Sudheer Shankab5239e42020-03-04 23:23:54 -0800412 closeSession(STATE_COMMITTED, true /* sendCallback */);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800413 }
414 }
415
Sudheer Shankab5239e42020-03-04 23:23:54 -0800416 private void closeSession(int state, boolean sendCallback) {
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800417 assertCallerIsOwner();
418 synchronized (mSessionLock) {
419 if (mState != STATE_OPENED) {
420 if (state == STATE_CLOSED) {
421 // Just trying to close the session which is already deleted or abandoned,
422 // ignore.
423 return;
424 } else {
425 throw new IllegalStateException("Not allowed to delete or abandon a session"
426 + " with state: " + stateToString(mState));
427 }
428 }
429
430 mState = state;
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700431 revokeAllFds();
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800432
Sudheer Shankab5239e42020-03-04 23:23:54 -0800433 if (sendCallback) {
434 mListener.onStateChanged(this);
435 }
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800436 }
437 }
438
Sudheer Shankab76f7662020-02-27 15:17:43 -0800439 void computeDigest() {
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800440 try {
Sudheer Shankad4ea5e12020-02-04 16:42:17 -0800441 Trace.traceBegin(TRACE_TAG_SYSTEM_SERVER,
442 "computeBlobDigest-i" + mSessionId + "-l" + getSessionFile().length());
Sudheer Shankab76f7662020-02-27 15:17:43 -0800443 mDataDigest = FileUtils.digest(getSessionFile(), mBlobHandle.algorithm);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800444 } catch (IOException | NoSuchAlgorithmException e) {
445 Slog.e(TAG, "Error computing the digest", e);
Sudheer Shankad4ea5e12020-02-04 16:42:17 -0800446 } finally {
447 Trace.traceEnd(TRACE_TAG_SYSTEM_SERVER);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800448 }
Sudheer Shankab76f7662020-02-27 15:17:43 -0800449 }
450
451 void verifyBlobData() {
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800452 synchronized (mSessionLock) {
Sudheer Shankab76f7662020-02-27 15:17:43 -0800453 if (mDataDigest != null && Arrays.equals(mDataDigest, mBlobHandle.digest)) {
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800454 mState = STATE_VERIFIED_VALID;
455 // Commit callback will be sent once the data is persisted.
456 } else {
Sudheer Shanka16806422020-06-05 15:36:37 -0700457 Slog.d(TAG, "Digest of the data ("
458 + (mDataDigest == null ? "null" : BlobHandle.safeDigest(mDataDigest))
459 + ") didn't match the given BlobHandle.digest ("
460 + BlobHandle.safeDigest(mBlobHandle.digest) + ")");
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800461 mState = STATE_VERIFIED_INVALID;
Michael Wachenschwanz1a7f7ab2020-06-09 20:16:27 -0700462
463 FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED, getOwnerUid(), mSessionId,
464 getSize(), FrameworkStatsLog.BLOB_COMMITTED__RESULT__DIGEST_MISMATCH);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800465 sendCommitCallbackResult(COMMIT_RESULT_ERROR);
466 }
467 mListener.onStateChanged(this);
468 }
469 }
470
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700471 private void revokeAllFds() {
472 synchronized (mRevocableFds) {
473 for (int i = mRevocableFds.size() - 1; i >= 0; --i) {
474 mRevocableFds.get(i).revoke();
475 }
476 mRevocableFds.clear();
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800477 }
478 }
479
480 @GuardedBy("mSessionLock")
Sudheer Shanka300d9cb2020-06-04 23:33:13 -0700481 private void trackRevocableFdLocked(RevocableFileDescriptor revocableFd) {
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800482 synchronized (mRevocableFds) {
483 mRevocableFds.add(revocableFd);
484 }
485 revocableFd.addOnCloseListener((e) -> {
486 synchronized (mRevocableFds) {
487 mRevocableFds.remove(revocableFd);
488 }
489 });
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800490 }
491
492 @Nullable
493 File getSessionFile() {
494 if (mSessionFile == null) {
Sudheer Shankabda89c12020-01-30 15:19:24 -0800495 mSessionFile = BlobStoreConfig.prepareBlobFile(mSessionId);
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800496 }
497 return mSessionFile;
498 }
499
500 @NonNull
501 static String stateToString(int state) {
502 switch (state) {
503 case STATE_OPENED:
504 return "<opened>";
505 case STATE_CLOSED:
506 return "<closed>";
507 case STATE_ABANDONED:
508 return "<abandoned>";
509 case STATE_COMMITTED:
510 return "<committed>";
Sudheer Shankab5239e42020-03-04 23:23:54 -0800511 case STATE_VERIFIED_VALID:
512 return "<verified_valid>";
513 case STATE_VERIFIED_INVALID:
514 return "<verified_invalid>";
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800515 default:
516 Slog.wtf(TAG, "Unknown state: " + state);
517 return "<unknown>";
518 }
519 }
520
Sudheer Shankae53e1ed2020-02-03 17:15:24 -0800521 @Override
522 public String toString() {
523 return "BlobStoreSession {"
524 + "id:" + mSessionId
525 + ",handle:" + mBlobHandle
526 + ",uid:" + mOwnerUid
527 + ",pkg:" + mOwnerPackageName
528 + "}";
529 }
530
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800531 private void assertCallerIsOwner() {
532 final int callingUid = Binder.getCallingUid();
Sudheer Shankabda89c12020-01-30 15:19:24 -0800533 if (callingUid != mOwnerUid) {
534 throw new SecurityException(mOwnerUid + " is not the session owner");
Sudheer Shankaab1d4162020-01-07 10:37:50 -0800535 }
Sudheer Shankad2da0672019-12-20 15:51:20 -0800536 }
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800537
Sudheer Shanka5caeed52020-02-01 12:54:33 -0800538 void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) {
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800539 synchronized (mSessionLock) {
540 fout.println("state: " + stateToString(mState));
Sudheer Shankabda89c12020-01-30 15:19:24 -0800541 fout.println("ownerUid: " + mOwnerUid);
542 fout.println("ownerPkg: " + mOwnerPackageName);
Sudheer Shankab2a17b72020-05-28 01:14:10 -0700543 fout.println("creation time: " + BlobStoreUtils.formatTime(mCreationTimeMs));
Sudheer Shanka75351542020-06-05 22:42:56 -0700544 fout.println("size: " + formatFileSize(mContext, getSize(), FLAG_IEC_UNITS));
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800545
546 fout.println("blobHandle:");
547 fout.increaseIndent();
Sudheer Shanka5caeed52020-02-01 12:54:33 -0800548 mBlobHandle.dump(fout, dumpArgs.shouldDumpFull());
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800549 fout.decreaseIndent();
550
551 fout.println("accessMode:");
552 fout.increaseIndent();
553 mBlobAccessMode.dump(fout);
554 fout.decreaseIndent();
555
556 fout.println("Open fds: #" + mRevocableFds.size());
557 }
558 }
559
560 void writeToXml(@NonNull XmlSerializer out) throws IOException {
561 synchronized (mSessionLock) {
Sudheer Shankabda89c12020-01-30 15:19:24 -0800562 XmlUtils.writeLongAttribute(out, ATTR_ID, mSessionId);
563 XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, mOwnerPackageName);
564 XmlUtils.writeIntAttribute(out, ATTR_UID, mOwnerUid);
Sudheer Shankab2a17b72020-05-28 01:14:10 -0700565 XmlUtils.writeLongAttribute(out, ATTR_CREATION_TIME_MS, mCreationTimeMs);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800566
567 out.startTag(null, TAG_BLOB_HANDLE);
Sudheer Shankabda89c12020-01-30 15:19:24 -0800568 mBlobHandle.writeToXml(out);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800569 out.endTag(null, TAG_BLOB_HANDLE);
570
571 out.startTag(null, TAG_ACCESS_MODE);
572 mBlobAccessMode.writeToXml(out);
573 out.endTag(null, TAG_ACCESS_MODE);
574 }
575 }
576
577 @Nullable
Sudheer Shanka1406bc82020-02-03 12:27:24 -0800578 static BlobStoreSession createFromXml(@NonNull XmlPullParser in, int version,
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800579 @NonNull Context context, @NonNull SessionStateChangeListener stateChangeListener)
580 throws IOException, XmlPullParserException {
Sudheer Shanka34c24fb2020-02-18 14:50:40 -0800581 final long sessionId = XmlUtils.readLongAttribute(in, ATTR_ID);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800582 final String ownerPackageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
583 final int ownerUid = XmlUtils.readIntAttribute(in, ATTR_UID);
Sudheer Shankab2a17b72020-05-28 01:14:10 -0700584 final long creationTimeMs = version >= XML_VERSION_ADD_SESSION_CREATION_TIME
585 ? XmlUtils.readLongAttribute(in, ATTR_CREATION_TIME_MS)
586 : System.currentTimeMillis();
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800587
588 final int depth = in.getDepth();
589 BlobHandle blobHandle = null;
590 BlobAccessMode blobAccessMode = null;
591 while (XmlUtils.nextElementWithin(in, depth)) {
592 if (TAG_BLOB_HANDLE.equals(in.getName())) {
593 blobHandle = BlobHandle.createFromXml(in);
594 } else if (TAG_ACCESS_MODE.equals(in.getName())) {
595 blobAccessMode = BlobAccessMode.createFromXml(in);
596 }
597 }
598
599 if (blobHandle == null) {
600 Slog.wtf(TAG, "blobHandle should be available");
601 return null;
602 }
603 if (blobAccessMode == null) {
604 Slog.wtf(TAG, "blobAccessMode should be available");
605 return null;
606 }
607
608 final BlobStoreSession blobStoreSession = new BlobStoreSession(context, sessionId,
Sudheer Shankab2a17b72020-05-28 01:14:10 -0700609 blobHandle, ownerUid, ownerPackageName, creationTimeMs, stateChangeListener);
Sudheer Shankaf6e23b92020-01-15 01:51:15 -0800610 blobStoreSession.mBlobAccessMode.allow(blobAccessMode);
611 return blobStoreSession;
612 }
Sudheer Shankad2da0672019-12-20 15:51:20 -0800613}