Merge "Add BlobStore atoms" into rvc-dev am: 38da493ae4 am: 5f06fdba56

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/11818786

Change-Id: I46b293bb3c2ac3949f1934c1af434a8df659329a
This commit is contained in:
Michael Wachenschwanz
2020-06-13 09:52:46 +00:00
committed by Automerger Merge Worker
7 changed files with 378 additions and 3 deletions

View File

@@ -127,6 +127,14 @@ class BlobAccessMode {
return false;
}
int getAccessType() {
return mAccessType;
}
int getNumWhitelistedPackages() {
return mWhitelistedPackages.size();
}
void dump(IndentingPrintWriter fout) {
fout.println("accessType: " + DebugUtils.flagsToString(
BlobAccessMode.class, "ACCESS_TYPE_", mAccessType));

View File

@@ -54,6 +54,8 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.StatsEvent;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -414,6 +416,49 @@ class BlobMetadata {
return true;
}
StatsEvent dumpAsStatsEvent(int atomTag) {
synchronized (mMetadataLock) {
ProtoOutputStream proto = new ProtoOutputStream();
// Write Committer data to proto format
for (int i = 0, size = mCommitters.size(); i < size; ++i) {
final Committer committer = mCommitters.valueAt(i);
final long token = proto.start(
BlobStatsEventProto.BlobCommitterListProto.COMMITTER);
proto.write(BlobStatsEventProto.BlobCommitterProto.UID, committer.uid);
proto.write(BlobStatsEventProto.BlobCommitterProto.COMMIT_TIMESTAMP_MILLIS,
committer.commitTimeMs);
proto.write(BlobStatsEventProto.BlobCommitterProto.ACCESS_MODE,
committer.blobAccessMode.getAccessType());
proto.write(BlobStatsEventProto.BlobCommitterProto.NUM_WHITELISTED_PACKAGE,
committer.blobAccessMode.getNumWhitelistedPackages());
proto.end(token);
}
final byte[] committersBytes = proto.getBytes();
proto = new ProtoOutputStream();
// Write Leasee data to proto format
for (int i = 0, size = mLeasees.size(); i < size; ++i) {
final Leasee leasee = mLeasees.valueAt(i);
final long token = proto.start(BlobStatsEventProto.BlobLeaseeListProto.LEASEE);
proto.write(BlobStatsEventProto.BlobLeaseeProto.UID, leasee.uid);
proto.write(BlobStatsEventProto.BlobLeaseeProto.LEASE_EXPIRY_TIMESTAMP_MILLIS,
leasee.expiryTimeMillis);
proto.end(token);
}
final byte[] leaseesBytes = proto.getBytes();
// Construct the StatsEvent to represent this Blob
return StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(mBlobId)
.writeLong(getSize())
.writeLong(mBlobHandle.getExpiryTimeMillis())
.writeByteArray(committersBytes)
.writeByteArray(leaseesBytes)
.build();
}
}
void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) {
fout.println("blobHandle:");
fout.increaseIndent();

View File

@@ -49,6 +49,9 @@ class BlobStoreConfig {
public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_SESSION_CREATION_TIME;
public static final long INVALID_BLOB_ID = 0;
public static final long INVALID_BLOB_SIZE = 0;
private static final String ROOT_DIR_NAME = "blobstore";
private static final String BLOBS_DIR_NAME = "blobs";
private static final String SESSIONS_INDEX_FILE_NAME = "sessions_index.xml";

View File

@@ -28,6 +28,8 @@ import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.os.UserHandle.USER_CURRENT;
import static android.os.UserHandle.USER_NULL;
import static com.android.server.blob.BlobStoreConfig.INVALID_BLOB_ID;
import static com.android.server.blob.BlobStoreConfig.INVALID_BLOB_SIZE;
import static com.android.server.blob.BlobStoreConfig.LOGV;
import static com.android.server.blob.BlobStoreConfig.TAG;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_CURRENT;
@@ -48,6 +50,7 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.StatsManager;
import android.app.blob.BlobHandle;
import android.app.blob.BlobInfo;
import android.app.blob.IBlobStoreManager;
@@ -80,6 +83,7 @@ import android.util.ExceptionUtils;
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.StatsEvent;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
@@ -88,6 +92,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
@@ -159,6 +164,8 @@ public class BlobStoreManagerService extends SystemService {
new SessionStateChangeListener();
private PackageManagerInternal mPackageManagerInternal;
private StatsManager mStatsManager;
private StatsPullAtomCallbackImpl mStatsCallbackImpl = new StatsPullAtomCallbackImpl();
private final Runnable mSaveBlobsInfoRunnable = this::writeBlobsInfo;
private final Runnable mSaveSessionsRunnable = this::writeBlobSessions;
@@ -192,6 +199,7 @@ public class BlobStoreManagerService extends SystemService {
LocalServices.addService(BlobStoreManagerInternal.class, new LocalService());
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mStatsManager = getContext().getSystemService(StatsManager.class);
registerReceivers();
LocalServices.getService(StorageStatsManagerInternal.class)
.registerStorageStatsAugmenter(new BlobStorageStatsAugmenter(), TAG);
@@ -207,6 +215,7 @@ public class BlobStoreManagerService extends SystemService {
readBlobSessionsLocked(allPackages);
readBlobsInfoLocked(allPackages);
}
registerBlobStorePuller();
} else if (phase == PHASE_BOOT_COMPLETED) {
BlobStoreIdleJobService.schedule(mContext);
}
@@ -219,7 +228,7 @@ public class BlobStoreManagerService extends SystemService {
long sessionId;
do {
sessionId = Math.abs(mRandom.nextLong());
if (mKnownBlobIds.indexOf(sessionId) < 0 && sessionId != 0) {
if (mKnownBlobIds.indexOf(sessionId) < 0 && sessionId != INVALID_BLOB_ID) {
return sessionId;
}
} while (n++ < 32);
@@ -376,9 +385,23 @@ public class BlobStoreManagerService extends SystemService {
.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
callingPackage, callingUid)) {
if (blobMetadata == null) {
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
INVALID_BLOB_ID, INVALID_BLOB_SIZE,
FrameworkStatsLog.BLOB_OPENED__RESULT__BLOB_DNE);
} else {
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
blobMetadata.getBlobId(), blobMetadata.getSize(),
FrameworkStatsLog.BLOB_LEASED__RESULT__ACCESS_NOT_ALLOWED);
}
throw new SecurityException("Caller not allowed to access " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_OPENED, callingUid,
blobMetadata.getBlobId(), blobMetadata.getSize(),
FrameworkStatsLog.BLOB_OPENED__RESULT__SUCCESS);
return blobMetadata.openForRead(callingPackage);
}
}
@@ -391,19 +414,41 @@ public class BlobStoreManagerService extends SystemService {
.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
callingPackage, callingUid)) {
if (blobMetadata == null) {
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
INVALID_BLOB_ID, INVALID_BLOB_SIZE,
FrameworkStatsLog.BLOB_LEASED__RESULT__BLOB_DNE);
} else {
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
blobMetadata.getBlobId(), blobMetadata.getSize(),
FrameworkStatsLog.BLOB_LEASED__RESULT__ACCESS_NOT_ALLOWED);
}
throw new SecurityException("Caller not allowed to access " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
}
if (leaseExpiryTimeMillis != 0 && blobHandle.expiryTimeMillis != 0
&& leaseExpiryTimeMillis > blobHandle.expiryTimeMillis) {
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
blobMetadata.getBlobId(), blobMetadata.getSize(),
FrameworkStatsLog.BLOB_LEASED__RESULT__LEASE_EXPIRY_INVALID);
throw new IllegalArgumentException(
"Lease expiry cannot be later than blobs expiry time");
}
if (blobMetadata.getSize()
> getRemainingLeaseQuotaBytesInternal(callingUid, callingPackage)) {
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
blobMetadata.getBlobId(), blobMetadata.getSize(),
FrameworkStatsLog.BLOB_LEASED__RESULT__DATA_SIZE_LIMIT_EXCEEDED);
throw new LimitExceededException("Total amount of data with an active lease"
+ " is exceeding the max limit");
}
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_LEASED, callingUid,
blobMetadata.getBlobId(), blobMetadata.getSize(),
FrameworkStatsLog.BLOB_LEASED__RESULT__SUCCESS);
blobMetadata.addOrReplaceLeasee(callingPackage, callingUid,
descriptionResId, description, leaseExpiryTimeMillis);
if (LOGV) {
@@ -587,6 +632,9 @@ public class BlobStoreManagerService extends SystemService {
blob.addOrReplaceCommitter(newCommitter);
try {
writeBlobsInfoLocked();
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED,
session.getOwnerUid(), blob.getBlobId(), blob.getSize(),
FrameworkStatsLog.BLOB_COMMITTED__RESULT__SUCCESS);
session.sendCommitCallbackResult(COMMIT_RESULT_SUCCESS);
} catch (Exception e) {
if (existingCommitter == null) {
@@ -595,6 +643,9 @@ public class BlobStoreManagerService extends SystemService {
blob.addOrReplaceCommitter(existingCommitter);
}
Slog.d(TAG, "Error committing the blob", e);
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED,
session.getOwnerUid(), blob.getBlobId(), blob.getSize(),
FrameworkStatsLog.BLOB_COMMITTED__RESULT__ERROR_DURING_COMMIT);
session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
}
getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
@@ -1684,6 +1735,40 @@ public class BlobStoreManagerService extends SystemService {
}
}
private void registerBlobStorePuller() {
mStatsManager.setPullAtomCallback(
FrameworkStatsLog.BLOB_INFO,
null, // use default PullAtomMetadata values
BackgroundThread.getExecutor(),
mStatsCallbackImpl
);
}
private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
@Override
public int onPullAtom(int atomTag, List<StatsEvent> data) {
switch (atomTag) {
case FrameworkStatsLog.BLOB_INFO:
return pullBlobData(atomTag, data);
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
}
}
private int pullBlobData(int atomTag, List<StatsEvent> data) {
synchronized (mBlobsLock) {
for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
final BlobMetadata blob = userBlobs.valueAt(j);
data.add(blob.dumpAsStatsEvent(atomTag));
}
}
}
return StatsManager.PULL_SUCCESS;
}
private class LocalService extends BlobStoreManagerInternal {
@Override
public void onIdleMaintenance() {

View File

@@ -53,6 +53,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
@@ -450,6 +451,9 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
+ ") didn't match the given BlobHandle.digest ("
+ BlobHandle.safeDigest(mBlobHandle.digest) + ")");
mState = STATE_VERIFIED_INVALID;
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED, getOwnerUid(), mSessionId,
getSize(), FrameworkStatsLog.BLOB_COMMITTED__RESULT__DIGEST_MISMATCH);
sendCommitCallbackResult(COMMIT_RESULT_ERROR);
}
mListener.onStateChanged(this);

View File

@@ -478,13 +478,16 @@ message Atom {
296;
MediametricsAudioDeviceConnectionReported mediametrics_audiodeviceconnection_reported =
297;
BlobCommitted blob_committed = 298 [(module) = "framework"];
BlobLeased blob_leased = 299 [(module) = "framework"];
BlobOpened blob_opened = 300 [(module) = "framework"];
// StatsdStats tracks platform atoms with ids upto 500.
// Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
}
// Pulled events will start at field 10000.
// Next: 10081
// Next: 10084
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -575,7 +578,7 @@ message Atom {
SimSlotState sim_slot_state = 10078 [(module) = "telephony"];
SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"];
SettingSnapshot setting_snapshot = 10080 [(module) = "framework"];
//10081 free for use
BlobInfo blob_info = 10081 [(module) = "framework"];
DataUsageBytesTransfer data_usage_bytes_transfer = 10082 [(module) = "framework"];
BytesTransferByTagAndMetered bytes_transfer_by_tag_and_metered =
10083 [(module) = "framework"];
@@ -4905,6 +4908,94 @@ message SnapshotMergeReported {
optional int64 cow_file_size_bytes = 5;
}
/**
* Event representing when BlobStoreManager.Session#commit() is called
*
* Logged from:
* frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
*/
message BlobCommitted {
// Uid of the Blob committer
optional int32 uid = 1 [(is_uid) = true];
// Id of the Blob committed
optional int64 blob_id = 2;
// Size of the Blob
optional int64 size = 3;
enum Result {
UNKNOWN = 0;
// Commit Succeeded
SUCCESS = 1;
// Commit Failed: Error occurred during commit
ERROR_DURING_COMMIT = 2;
// Commit Failed: Digest of the data did not match Blob digest
DIGEST_MISMATCH = 3;
}
optional Result result = 4;
}
/**
* Event representing when BlobStoreManager#acquireLease() is called
*
* Logged from:
* frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
*/
message BlobLeased{
// Uid of the Blob leasee
optional int32 uid = 1 [(is_uid) = true];
// Id of the Blob leased or 0 if the Blob does not exist
optional int64 blob_id = 2;
// Size of the Blob or 0 if the Blob does not exist
optional int64 size = 3;
enum Result {
UNKNOWN = 0;
// Lease Succeeded
SUCCESS = 1;
// Lease Failed: Blob does not exist
BLOB_DNE = 2;
// Lease Failed: Leasee does not have access to the Blob
ACCESS_NOT_ALLOWED = 3;
// Lease Failed: Leasee requested an invalid expiry duration
LEASE_EXPIRY_INVALID = 4;
// Lease Failed: Leasee has exceeded the total data lease limit
DATA_SIZE_LIMIT_EXCEEDED = 5;
}
optional Result result = 4;
}
/**
* Event representing when BlobStoreManager#openBlob() is called
*
* Logged from:
* frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
*/
message BlobOpened{
// Uid of the Blob opener
optional int32 uid = 1 [(is_uid) = true];
// Id of the Blob opened or 0 if the Blob does not exist
optional int64 blob_id = 2;
// Size of the Blob or 0 if the Blob does not exist
optional int64 size = 3;
enum Result {
UNKNOWN = 0;
// Open Succeeded
SUCCESS = 1;
// Open Failed: Blob does not exist
BLOB_DNE = 2;
// Open Failed: Opener does not have access to the Blob
ACCESS_NOT_ALLOWED = 3;
}
optional Result result = 4;
}
//////////////////////////////////////////////////////////////////////
// Pulled atoms below this line //
//////////////////////////////////////////////////////////////////////
@@ -10791,3 +10882,72 @@ message MediametricsAudioDeviceConnectionReported {
// Number of connections if aggregated statistics, otherwise 1.
optional int32 connection_count = 6;
}
// Blob Committer stats
// Keep in sync between:
// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
// frameworks/base/cmds/statsd/src/atoms.proto
message BlobCommitterProto {
// Committer app's uid
optional int32 uid = 1 [(is_uid) = true];
// Unix epoch timestamp of the commit in milliseconds
optional int64 commit_timestamp_millis = 2;
// Flags of what access types the committer has set for the Blob
optional int32 access_mode = 3;
// Number of packages that have been whitelisted for ACCESS_TYPE_WHITELIST
optional int32 num_whitelisted_package = 4;
}
// Blob Leasee stats
// Keep in sync between:
// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
// frameworks/base/cmds/statsd/src/atoms.proto
message BlobLeaseeProto {
// Leasee app's uid
optional int32 uid = 1 [(is_uid) = true];
// Unix epoch timestamp for lease expiration in milliseconds
optional int64 lease_expiry_timestamp_millis = 2;
}
// List of Blob Committers
// Keep in sync between:
// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
// frameworks/base/cmds/statsd/src/atoms.proto
message BlobCommitterListProto {
repeated BlobCommitterProto committer = 1;
}
// List of Blob Leasees
// Keep in sync between:
// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
// frameworks/base/cmds/statsd/src/atoms.proto
message BlobLeaseeListProto {
repeated BlobLeaseeProto leasee = 1;
}
/**
* Logs the current state of a Blob committed with BlobStoreManager
*
* Pulled from:
* frameworks/base/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
*/
message BlobInfo {
// Id of the Blob
optional int64 blob_id = 1;
// Size of the Blob data
optional int64 size = 2;
// Unix epoch timestamp of the Blob's expiration in milliseconds
optional int64 expiry_timestamp_millis = 3;
// List of committers of this Blob
optional BlobCommitterListProto committers = 4;
// List of leasees of this Blob
optional BlobLeaseeListProto leasees = 5;
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
syntax = "proto2";
package com.android.server.blob;
option java_multiple_files = true;
// The nested messages are used for statsd logging and should be kept in sync with the messages
// of the same name in frameworks/base/cmds/statsd/src/atoms.proto
message BlobStatsEventProto {
// Blob Committer stats
// Keep in sync between:
// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
// frameworks/base/cmds/statsd/src/atoms.proto
message BlobCommitterProto {
// Committer app's uid
optional int32 uid = 1;
// Unix epoch timestamp of the commit in milliseconds
optional int64 commit_timestamp_millis = 2;
// Flags of what access types the committer has set for the Blob
optional int32 access_mode = 3;
// Number of packages that have been whitelisted for ACCESS_TYPE_WHITELIST
optional int32 num_whitelisted_package = 4;
}
// Blob Leasee stats
// Keep in sync between:
// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
// frameworks/base/cmds/statsd/src/atoms.proto
message BlobLeaseeProto {
// Leasee app's uid
optional int32 uid = 1;
// Unix epoch timestamp for lease expiration in milliseconds
optional int64 lease_expiry_timestamp_millis = 2;
}
// List of Blob Committers
// Keep in sync between:
// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
// frameworks/base/cmds/statsd/src/atoms.proto
message BlobCommitterListProto {
repeated BlobCommitterProto committer = 1;
}
// List of Blob Leasees
// Keep in sync between:
// frameworks/base/core/proto/android/server/blobstoremanagerservice.proto
// frameworks/base/cmds/statsd/src/atoms.proto
message BlobLeaseeListProto {
repeated BlobLeaseeProto leasee = 1;
}
}