summaryrefslogtreecommitdiff
path: root/apex/blobstore
diff options
context:
space:
mode:
Diffstat (limited to 'apex/blobstore')
-rw-r--r--apex/blobstore/framework/java/android/app/blob/AccessorInfo.java119
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobInfo.aidl19
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobInfo.java109
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java30
-rw-r--r--apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl4
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java36
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java70
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java50
8 files changed, 408 insertions, 29 deletions
diff --git a/apex/blobstore/framework/java/android/app/blob/AccessorInfo.java b/apex/blobstore/framework/java/android/app/blob/AccessorInfo.java
new file mode 100644
index 000000000000..3725ad4a6c09
--- /dev/null
+++ b/apex/blobstore/framework/java/android/app/blob/AccessorInfo.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+package android.app.blob;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * Class to provide information about an accessor of a shared blob.
+ *
+ * @hide
+ */
+public final class AccessorInfo implements Parcelable {
+ private final String mPackageName;
+ private final long mExpiryTimeMs;
+ private final int mDescriptionResId;
+ private final CharSequence mDescription;
+
+ public AccessorInfo(String packageName, long expiryTimeMs,
+ int descriptionResId, CharSequence description) {
+ mPackageName = packageName;
+ mExpiryTimeMs = expiryTimeMs;
+ mDescriptionResId = descriptionResId;
+ mDescription = description;
+ }
+
+ private AccessorInfo(Parcel in) {
+ mPackageName = in.readString();
+ mExpiryTimeMs = in.readLong();
+ mDescriptionResId = in.readInt();
+ mDescription = in.readCharSequence();
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public long getExpiryTimeMs() {
+ return mExpiryTimeMs;
+ }
+
+ public int getDescriptionResId() {
+ return mDescriptionResId;
+ }
+
+ public CharSequence getDescription() {
+ return mDescription;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mPackageName);
+ dest.writeLong(mExpiryTimeMs);
+ dest.writeInt(mDescriptionResId);
+ dest.writeCharSequence(mDescription);
+ }
+
+ @Override
+ public String toString() {
+ return "AccessorInfo {"
+ + "package: " + mPackageName + ","
+ + "expiryMs: " + mExpiryTimeMs + ","
+ + "descriptionResId: " + mDescriptionResId + ","
+ + "description: " + mDescription + ","
+ + "}";
+ }
+
+ private String toShortString() {
+ return mPackageName;
+ }
+
+ public static String toShortString(List<AccessorInfo> accessors) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ for (int i = 0, size = accessors.size(); i < size; ++i) {
+ sb.append(accessors.get(i).toShortString());
+ sb.append(",");
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<AccessorInfo> CREATOR = new Creator<AccessorInfo>() {
+ @Override
+ @NonNull
+ public AccessorInfo createFromParcel(Parcel source) {
+ return new AccessorInfo(source);
+ }
+
+ @Override
+ @NonNull
+ public AccessorInfo[] newArray(int size) {
+ return new AccessorInfo[size];
+ }
+ };
+}
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobInfo.aidl b/apex/blobstore/framework/java/android/app/blob/BlobInfo.aidl
new file mode 100644
index 000000000000..25497738f685
--- /dev/null
+++ b/apex/blobstore/framework/java/android/app/blob/BlobInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 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.
+ */
+package android.app.blob;
+
+/** {@hide} */
+parcelable BlobInfo; \ No newline at end of file
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobInfo.java b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
new file mode 100644
index 000000000000..9746dd023002
--- /dev/null
+++ b/apex/blobstore/framework/java/android/app/blob/BlobInfo.java
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+package android.app.blob;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Class to provide information about a shared blob.
+ *
+ * @hide
+ */
+public final class BlobInfo implements Parcelable {
+ private final long mId;
+ private final long mExpiryTimeMs;
+ private final CharSequence mLabel;
+ private final List<AccessorInfo> mAccessors;
+
+ public BlobInfo(long id, long expiryTimeMs, CharSequence label,
+ List<AccessorInfo> accessors) {
+ mId = id;
+ mExpiryTimeMs = expiryTimeMs;
+ mLabel = label;
+ mAccessors = accessors;
+ }
+
+ private BlobInfo(Parcel in) {
+ mId = in.readLong();
+ mExpiryTimeMs = in.readLong();
+ mLabel = in.readCharSequence();
+ mAccessors = in.readArrayList(null /* classloader */);
+ }
+
+ public long getId() {
+ return mId;
+ }
+
+ public long getExpiryTimeMs() {
+ return mExpiryTimeMs;
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ public List<AccessorInfo> getAccessors() {
+ return Collections.unmodifiableList(mAccessors);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mId);
+ dest.writeLong(mExpiryTimeMs);
+ dest.writeCharSequence(mLabel);
+ dest.writeList(mAccessors);
+ }
+
+ @Override
+ public String toString() {
+ return toShortString();
+ }
+
+ private String toShortString() {
+ return "BlobInfo {"
+ + "id: " + mId + ","
+ + "expiryMs: " + mExpiryTimeMs + ","
+ + "label: " + mLabel + ","
+ + "accessors: " + AccessorInfo.toShortString(mAccessors) + ","
+ + "}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Creator<BlobInfo> CREATOR = new Creator<BlobInfo>() {
+ @Override
+ @NonNull
+ public BlobInfo createFromParcel(Parcel source) {
+ return new BlobInfo(source);
+ }
+
+ @Override
+ @NonNull
+ public BlobInfo[] newArray(int size) {
+ return new BlobInfo[size];
+ }
+ };
+}
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index d1e28e99b6d6..7af3f662c195 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -27,11 +27,13 @@ import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.os.UserHandle;
import com.android.internal.util.function.pooled.PooledLambda;
import java.io.Closeable;
import java.io.IOException;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -145,9 +147,6 @@ public class BlobStoreManager {
/** @hide */
public static final int INVALID_RES_ID = -1;
- /** @hide */
- public static final String DESC_RES_TYPE_STRING = "string";
-
private final Context mContext;
private final IBlobStoreManager mService;
@@ -495,6 +494,31 @@ public class BlobStoreManager {
}
}
+ /** @hide */
+ @NonNull
+ public List<BlobInfo> queryBlobsForUser(@NonNull UserHandle user) throws IOException {
+ try {
+ return mService.queryBlobsForUser(user.getIdentifier());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /** @hide */
+ public void deleteBlob(@NonNull BlobInfo blobInfo) throws IOException {
+ try {
+ mService.deleteBlob(blobInfo.getId());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Represents an ongoing session of a blob's contribution to the blob store managed by the
* system.
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
index a85a25c9c4ad..beeb03f06ed7 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
@@ -16,6 +16,7 @@
package android.app.blob;
import android.app.blob.BlobHandle;
+import android.app.blob.BlobInfo;
import android.app.blob.IBlobStoreSession;
import android.os.RemoteCallback;
@@ -31,4 +32,7 @@ interface IBlobStoreManager {
void releaseLease(in BlobHandle handle, in String packageName);
void waitForIdle(in RemoteCallback callback);
+
+ List<BlobInfo> queryBlobsForUser(int userId);
+ void deleteBlob(long blobId);
} \ No newline at end of file
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index dab4797b313f..970766d2c8a6 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -15,7 +15,6 @@
*/
package com.android.server.blob;
-import static android.app.blob.BlobStoreManager.DESC_RES_TYPE_STRING;
import static android.app.blob.XmlTags.ATTR_DESCRIPTION;
import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_NAME;
import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME;
@@ -30,16 +29,16 @@ import static android.app.blob.XmlTags.TAG_LEASEE;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.O_RDONLY;
-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_ADD_DESC_RES_NAME;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC;
+import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId;
+import static com.android.server.blob.BlobStoreUtils.getPackageResources;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.blob.BlobHandle;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.content.res.ResourceId;
import android.content.res.Resources;
import android.os.ParcelFileDescriptor;
@@ -65,6 +64,7 @@ import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.Objects;
+import java.util.function.Consumer;
class BlobMetadata {
private final Object mMetadataLock = new Object();
@@ -281,6 +281,10 @@ class BlobMetadata {
return false;
}
+ void forEachLeasee(Consumer<Leasee> consumer) {
+ mLeasees.forEach(consumer);
+ }
+
File getBlobFile() {
if (mBlobFile == null) {
mBlobFile = BlobStoreConfig.getBlobFile(mBlobId);
@@ -506,16 +510,9 @@ class BlobMetadata {
if (resources == null) {
return null;
}
- try {
- final int resId = resources.getIdentifier(descriptionResEntryName,
- DESC_RES_TYPE_STRING, packageName);
- return resId <= 0 ? null : resources.getString(resId);
- } catch (Resources.NotFoundException e) {
- if (LOGV) {
- Slog.w(TAG, "Description resource not found", e);
- }
- return null;
- }
+ final int resId = getDescriptionResourceId(resources, descriptionResEntryName,
+ packageName);
+ return resId == Resources.ID_NULL ? null : resources.getString(resId);
}
@Nullable
@@ -546,19 +543,6 @@ class BlobMetadata {
return desc == null ? "<none>" : desc;
}
- @Nullable
- private static Resources getPackageResources(@NonNull Context context,
- @NonNull String packageName, int userId) {
- try {
- return context.getPackageManager()
- .getResourcesForApplicationAsUser(packageName, userId);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.d(TAG, "Unknown package in user " + userId + ": "
- + packageName, e);
- return null;
- }
- }
-
void writeToXml(@NonNull XmlSerializer out) throws IOException {
XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName);
XmlUtils.writeIntAttribute(out, ATTR_UID, uid);
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 96f7b7aee165..ed3dda44e131 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -36,6 +36,8 @@ import static com.android.server.blob.BlobStoreSession.STATE_COMMITTED;
import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_INVALID;
import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_VALID;
import static com.android.server.blob.BlobStoreSession.stateToString;
+import static com.android.server.blob.BlobStoreUtils.getDescriptionResourceId;
+import static com.android.server.blob.BlobStoreUtils.getPackageResources;
import android.annotation.CurrentTimeSecondsLong;
import android.annotation.IdRes;
@@ -43,7 +45,9 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.blob.AccessorInfo;
import android.app.blob.BlobHandle;
+import android.app.blob.BlobInfo;
import android.app.blob.IBlobStoreManager;
import android.app.blob.IBlobStoreSession;
import android.content.BroadcastReceiver;
@@ -100,6 +104,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.ArrayList;
@@ -110,6 +115,7 @@ import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Service responsible for maintaining and facilitating access to data blobs published by apps.
@@ -434,6 +440,48 @@ public class BlobStoreManagerService extends SystemService {
}
}
+ private List<BlobInfo> queryBlobsForUserInternal(int userId) {
+ final ArrayList<BlobInfo> blobInfos = new ArrayList<>();
+ synchronized (mBlobsLock) {
+ final ArrayMap<String, WeakReference<Resources>> resources = new ArrayMap<>();
+ final Function<String, Resources> resourcesGetter = (packageName) -> {
+ final WeakReference<Resources> resourcesRef = resources.get(packageName);
+ Resources packageResources = resourcesRef == null ? null : resourcesRef.get();
+ if (packageResources == null) {
+ packageResources = getPackageResources(mContext, packageName, userId);
+ resources.put(packageName, new WeakReference<>(packageResources));
+ }
+ return packageResources;
+ };
+ getUserBlobsLocked(userId).forEach((blobHandle, blobMetadata) -> {
+ final ArrayList<AccessorInfo> accessorInfos = new ArrayList<>();
+ blobMetadata.forEachLeasee(leasee -> {
+ final int descriptionResId = leasee.descriptionResEntryName == null
+ ? Resources.ID_NULL
+ : getDescriptionResourceId(resourcesGetter.apply(leasee.packageName),
+ leasee.descriptionResEntryName, leasee.packageName);
+ accessorInfos.add(new AccessorInfo(leasee.packageName, leasee.expiryTimeMillis,
+ descriptionResId, leasee.description));
+ });
+ blobInfos.add(new BlobInfo(blobMetadata.getBlobId(),
+ blobHandle.getExpiryTimeMillis(), blobHandle.getLabel(), accessorInfos));
+ });
+ }
+ return blobInfos;
+ }
+
+ private void deleteBlobInternal(long blobId, int callingUid) {
+ synchronized (mBlobsLock) {
+ final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(
+ UserHandle.getUserId(callingUid));
+ userBlobs.entrySet().removeIf(entry -> {
+ final BlobMetadata blobMetadata = entry.getValue();
+ return blobMetadata.getBlobId() == blobId;
+ });
+ writeBlobsInfoAsync();
+ }
+ }
+
private void verifyCallingPackage(int callingUid, String callingPackage) {
if (mPackageManagerInternal.getPackageUid(
callingPackage, 0, UserHandle.getUserId(callingUid)) != callingUid) {
@@ -1250,6 +1298,28 @@ public class BlobStoreManagerService extends SystemService {
}
@Override
+ @NonNull
+ public List<BlobInfo> queryBlobsForUser(@UserIdInt int userId) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only system uid is allowed to call "
+ + "queryBlobsForUser()");
+ }
+
+ return queryBlobsForUserInternal(userId);
+ }
+
+ @Override
+ public void deleteBlob(long blobId) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID) {
+ throw new SecurityException("Only system uid is allowed to call "
+ + "deleteBlob()");
+ }
+
+ deleteBlobInternal(blobId, callingUid);
+ }
+
+ @Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
@Nullable String[] args) {
// TODO: add proto-based version of this.
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
new file mode 100644
index 000000000000..6af540acd6a4
--- /dev/null
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package com.android.server.blob;
+
+import static com.android.server.blob.BlobStoreConfig.TAG;
+
+import android.annotation.IdRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.util.Slog;
+
+class BlobStoreUtils {
+ private static final String DESC_RES_TYPE_STRING = "string";
+
+ @Nullable
+ static Resources getPackageResources(@NonNull Context context,
+ @NonNull String packageName, int userId) {
+ try {
+ return context.getPackageManager()
+ .getResourcesForApplicationAsUser(packageName, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.d(TAG, "Unknown package in user " + userId + ": "
+ + packageName, e);
+ return null;
+ }
+ }
+
+ @IdRes
+ static int getDescriptionResourceId(@NonNull Resources resources,
+ @NonNull String resourceEntryName, @NonNull String packageName) {
+ return resources.getIdentifier(resourceEntryName, DESC_RES_TYPE_STRING, packageName);
+ }
+}