Loading apex/blobstore/framework/java/android/app/blob/BlobHandle.java +30 −6 Original line number Diff line number Diff line Loading @@ -214,12 +214,16 @@ public final class BlobHandle implements Parcelable { } /** @hide */ public void dump(IndentingPrintWriter fout) { public void dump(IndentingPrintWriter fout, boolean dumpFull) { if (dumpFull) { fout.println("algo: " + algorithm); fout.println("digest: " + Base64.encodeToString(digest, Base64.NO_WRAP)); fout.println("digest: " + (dumpFull ? encodeDigest() : safeDigest())); fout.println("label: " + label); fout.println("expiryMs: " + expiryTimeMillis); fout.println("tag: " + tag); } else { fout.println(toString()); } } /** @hide */ Loading @@ -233,6 +237,26 @@ public final class BlobHandle implements Parcelable { Preconditions.checkArgument(tag.length() <= LIMIT_BLOB_TAG_LENGTH, "tag too long"); } @Override public String toString() { return "BlobHandle {" + "algo:" + algorithm + "," + "digest:" + safeDigest() + "," + "label:" + label + "," + "expiryMs:" + expiryTimeMillis + "," + "tag:" + tag + "}"; } private String safeDigest() { final String digestStr = encodeDigest(); return digestStr.substring(0, 2) + ".." + digestStr.substring(digestStr.length() - 2); } private String encodeDigest() { return Base64.encodeToString(digest, Base64.NO_WRAP); } public static final @NonNull Creator<BlobHandle> CREATOR = new Creator<BlobHandle>() { @Override public @NonNull BlobHandle createFromParcel(@NonNull Parcel source) { Loading apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java +3 −2 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; import com.android.server.blob.BlobStoreManagerService.DumpArgs; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; Loading Loading @@ -240,10 +241,10 @@ class BlobMetadata { return revocableFd.getRevocableFileDescriptor(); } void dump(IndentingPrintWriter fout) { void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) { fout.println("blobHandle:"); fout.increaseIndent(); blobHandle.dump(fout); blobHandle.dump(fout, dumpArgs.shouldDumpFull()); fout.decreaseIndent(); fout.println("Committers:"); Loading apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +227 −37 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import android.annotation.IdRes; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.blob.BlobHandle; import android.app.blob.IBlobStoreManager; import android.app.blob.IBlobStoreSession; Loading Loading @@ -69,6 +70,7 @@ import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; Loading Loading @@ -121,6 +123,9 @@ public class BlobStoreManagerService extends SystemService { private PackageManagerInternal mPackageManagerInternal; private final Runnable mSaveBlobsInfoRunnable = this::writeBlobsInfo; private final Runnable mSaveSessionsRunnable = this::writeBlobSessions; public BlobStoreManagerService(Context context) { this(context, new Injector()); } Loading Loading @@ -533,9 +538,9 @@ public class BlobStoreManagerService extends SystemService { } private void writeBlobsInfoAsync() { mHandler.post(PooledLambda.obtainRunnable( BlobStoreManagerService::writeBlobsInfo, BlobStoreManagerService.this).recycleOnUse()); if (!mHandler.hasCallbacks(mSaveBlobsInfoRunnable)) { mHandler.post(mSaveBlobsInfoRunnable); } } private void writeBlobSessions() { Loading @@ -549,9 +554,9 @@ public class BlobStoreManagerService extends SystemService { } private void writeBlobSessionsAsync() { mHandler.post(PooledLambda.obtainRunnable( BlobStoreManagerService::writeBlobSessions, BlobStoreManagerService.this).recycleOnUse()); if (!mHandler.hasCallbacks(mSaveSessionsRunnable)) { mHandler.post(mSaveSessionsRunnable); } } private int getPackageUid(String packageName, int userId) { Loading Loading @@ -597,6 +602,7 @@ public class BlobStoreManagerService extends SystemService { return new AtomicFile(file, "blobs_index" /* commitLogTag */); } @VisibleForTesting void handlePackageRemoved(String packageName, int uid) { synchronized (mBlobsLock) { // Clean up any pending sessions Loading Loading @@ -659,6 +665,80 @@ public class BlobStoreManagerService extends SystemService { } } void runClearAllSessions(@UserIdInt int userId) { synchronized (mBlobsLock) { if (userId == UserHandle.USER_ALL) { mSessions.clear(); } else { mSessions.remove(userId); } writeBlobSessionsAsync(); } } void runClearAllBlobs(@UserIdInt int userId) { synchronized (mBlobsLock) { if (userId == UserHandle.USER_ALL) { mBlobsMap.clear(); } else { mBlobsMap.remove(userId); } writeBlobsInfoAsync(); } } @GuardedBy("mBlobsLock") private void dumpSessionsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) { for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) { final int userId = mSessions.keyAt(i); if (!dumpArgs.shouldDumpUser(userId)) { continue; } final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i); fout.println("List of sessions in user #" + userId + " (" + userSessions.size() + "):"); fout.increaseIndent(); for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) { final long sessionId = userSessions.keyAt(j); final BlobStoreSession session = userSessions.valueAt(j); if (!dumpArgs.shouldDumpSession(session.getOwnerPackageName(), session.getOwnerUid(), session.getSessionId())) { continue; } fout.println("Session #" + sessionId); fout.increaseIndent(); session.dump(fout, dumpArgs); fout.decreaseIndent(); } fout.decreaseIndent(); } } @GuardedBy("mBlobsLock") private void dumpBlobsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) { for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) { final int userId = mBlobsMap.keyAt(i); if (!dumpArgs.shouldDumpUser(userId)) { continue; } final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i); fout.println("List of blobs in user #" + userId + " (" + userBlobs.size() + "):"); fout.increaseIndent(); for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) { final BlobMetadata blobMetadata = userBlobs.valueAt(j); if (!dumpArgs.shouldDumpBlob(blobMetadata.blobId)) { continue; } fout.println("Blob #" + blobMetadata.blobId); fout.increaseIndent(); blobMetadata.dump(fout, dumpArgs); fout.decreaseIndent(); } fout.decreaseIndent(); } } private class PackageChangedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Loading Loading @@ -808,47 +888,157 @@ public class BlobStoreManagerService extends SystemService { public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) { // TODO: add proto-based version of this. if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return; if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, writer)) return; final DumpArgs dumpArgs = DumpArgs.parse(args); final IndentingPrintWriter fout = new IndentingPrintWriter(writer, " "); synchronized (mBlobsLock) { fout.println("mCurrentMaxSessionId: " + mCurrentMaxSessionId); fout.println(); for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) { final int userId = mSessions.keyAt(i); final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i); fout.println("List of sessions in user #" + userId + " (" + userSessions.size() + "):"); fout.increaseIndent(); for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) { final long sessionId = userSessions.keyAt(j); final BlobStoreSession session = userSessions.valueAt(j); fout.println("Session #" + sessionId); fout.increaseIndent(); session.dump(fout); fout.decreaseIndent(); if (dumpArgs.shouldDumpSessions()) { dumpSessionsLocked(fout, dumpArgs); fout.println(); } if (dumpArgs.shouldDumpBlobs()) { dumpBlobsLocked(fout, dumpArgs); fout.println(); } } fout.decreaseIndent(); } fout.print("\n\n"); @Override public int handleShellCommand(@NonNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args) { return (new BlobStoreManagerShellCommand(BlobStoreManagerService.this)).exec(this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args); } } for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) { final int userId = mBlobsMap.keyAt(i); final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i); fout.println("List of blobs in user #" + userId + " (" + userBlobs.size() + "):"); fout.increaseIndent(); for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) { final BlobMetadata blobMetadata = userBlobs.valueAt(j); fout.println("Blob #" + blobMetadata.blobId); fout.increaseIndent(); blobMetadata.dump(fout); fout.decreaseIndent(); static final class DumpArgs { private boolean mDumpFull; private final ArrayList<String> mDumpPackages = new ArrayList<>(); private final ArrayList<Integer> mDumpUids = new ArrayList<>(); private final ArrayList<Integer> mDumpUserIds = new ArrayList<>(); private final ArrayList<Long> mDumpBlobIds = new ArrayList<>(); private boolean mDumpOnlySelectedSections; private boolean mDumpSessions; private boolean mDumpBlobs; public boolean shouldDumpSession(String packageName, int uid, long blobId) { if (!CollectionUtils.isEmpty(mDumpPackages) && mDumpPackages.indexOf(packageName) < 0) { return false; } fout.decreaseIndent(); if (!CollectionUtils.isEmpty(mDumpUids) && mDumpUids.indexOf(uid) < 0) { return false; } if (!CollectionUtils.isEmpty(mDumpBlobIds) && mDumpBlobIds.indexOf(blobId) < 0) { return false; } return true; } public boolean shouldDumpSessions() { if (!mDumpOnlySelectedSections) { return true; } return mDumpSessions; } public boolean shouldDumpBlobs() { if (!mDumpOnlySelectedSections) { return true; } return mDumpBlobs; } public boolean shouldDumpBlob(long blobId) { return CollectionUtils.isEmpty(mDumpBlobIds) || mDumpBlobIds.indexOf(blobId) >= 0; } public boolean shouldDumpFull() { return mDumpFull; } public boolean shouldDumpUser(int userId) { return CollectionUtils.isEmpty(mDumpUserIds) || mDumpUserIds.indexOf(userId) >= 0; } private DumpArgs() {} public static DumpArgs parse(String[] args) { final DumpArgs dumpArgs = new DumpArgs(); if (args == null) { return dumpArgs; } for (int i = 0; i < args.length; ++i) { final String opt = args[i]; if ("--full".equals(opt) || "-f".equals(opt)) { final int callingUid = Binder.getCallingUid(); if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) { dumpArgs.mDumpFull = true; } } else if ("--sessions".equals(opt)) { dumpArgs.mDumpOnlySelectedSections = true; dumpArgs.mDumpSessions = true; } else if ("--blobs".equals(opt)) { dumpArgs.mDumpOnlySelectedSections = true; dumpArgs.mDumpBlobs = true; } else if ("--package".equals(opt) || "-p".equals(opt)) { dumpArgs.mDumpPackages.add(getStringArgRequired(args, ++i, "packageName")); } else if ("--uid".equals(opt) || "-u".equals(opt)) { dumpArgs.mDumpUids.add(getIntArgRequired(args, ++i, "uid")); } else if ("--user".equals(opt)) { dumpArgs.mDumpUserIds.add(getIntArgRequired(args, ++i, "userId")); } else if ("--blob".equals(opt) || "-b".equals(opt)) { dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, ++i, "blobId")); } else { // Everything else is assumed to be blob ids. dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, i, "blobId")); } } return dumpArgs; } private static String getStringArgRequired(String[] args, int index, String argName) { if (index >= args.length) { throw new IllegalArgumentException("Missing " + argName); } return args[index]; } private static int getIntArgRequired(String[] args, int index, String argName) { if (index >= args.length) { throw new IllegalArgumentException("Missing " + argName); } final int value; try { value = Integer.parseInt(args[index]); } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid " + argName + ": " + args[index]); } return value; } private static long getLongArgRequired(String[] args, int index, String argName) { if (index >= args.length) { throw new IllegalArgumentException("Missing " + argName); } final long value; try { value = Long.parseLong(args[index]); } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid " + argName + ": " + args[index]); } return value; } } Loading apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerShellCommand.java 0 → 100644 +111 −0 Original line number Diff line number Diff line /* * 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 com.android.server.blob; import android.os.ShellCommand; import android.os.UserHandle; import java.io.PrintWriter; class BlobStoreManagerShellCommand extends ShellCommand { private final BlobStoreManagerService mService; BlobStoreManagerShellCommand(BlobStoreManagerService blobStoreManagerService) { mService = blobStoreManagerService; } @Override public int onCommand(String cmd) { if (cmd == null) { return handleDefaultCommands(null); } final PrintWriter pw = getOutPrintWriter(); switch (cmd) { case "clear-all-sessions": return runClearAllSessions(pw); case "clear-all-blobs": return runClearAllBlobs(pw); default: return handleDefaultCommands(cmd); } } private int runClearAllSessions(PrintWriter pw) { final ParsedArgs args = new ParsedArgs(); args.userId = UserHandle.USER_ALL; if (parseOptions(pw, args) < 0) { return -1; } mService.runClearAllSessions(args.userId); return 0; } private int runClearAllBlobs(PrintWriter pw) { final ParsedArgs args = new ParsedArgs(); args.userId = UserHandle.USER_ALL; if (parseOptions(pw, args) < 0) { return -1; } mService.runClearAllBlobs(args.userId); return 0; } @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); pw.println("BlobStore service (blob_store) commands:"); pw.println("help"); pw.println(" Print this help text."); pw.println(); pw.println("clear-all-sessions [-u | --user USER_ID]"); pw.println(" Remove all sessions."); pw.println(" Options:"); pw.println(" -u or --user: specify which user's sessions to be removed;"); pw.println(" If not specified, sessions in all users are removed."); pw.println(); pw.println("clear-all-blobs [-u | --user USER_ID]"); pw.println(" Remove all blobs."); pw.println(" Options:"); pw.println(" -u or --user: specify which user's blobs to be removed;"); pw.println(" If not specified, blobs in all users are removed."); pw.println(); } private int parseOptions(PrintWriter pw, ParsedArgs args) { String opt; while ((opt = getNextOption()) != null) { switch (opt) { case "-u": case "--user": args.userId = Integer.parseInt(getNextArgRequired()); break; default: pw.println("Error: unknown option '" + opt + "'"); return -1; } } return 0; } private static class ParsedArgs { public int userId; } } apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java +3 −2 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.blob.BlobStoreManagerService.DumpArgs; import com.android.server.blob.BlobStoreManagerService.SessionStateChangeListener; import org.xmlpull.v1.XmlPullParser; Loading Loading @@ -453,7 +454,7 @@ class BlobStoreSession extends IBlobStoreSession.Stub { } } void dump(IndentingPrintWriter fout) { void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) { synchronized (mSessionLock) { fout.println("state: " + stateToString(mState)); fout.println("ownerUid: " + mOwnerUid); Loading @@ -461,7 +462,7 @@ class BlobStoreSession extends IBlobStoreSession.Stub { fout.println("blobHandle:"); fout.increaseIndent(); mBlobHandle.dump(fout); mBlobHandle.dump(fout, dumpArgs.shouldDumpFull()); fout.decreaseIndent(); fout.println("accessMode:"); Loading Loading
apex/blobstore/framework/java/android/app/blob/BlobHandle.java +30 −6 Original line number Diff line number Diff line Loading @@ -214,12 +214,16 @@ public final class BlobHandle implements Parcelable { } /** @hide */ public void dump(IndentingPrintWriter fout) { public void dump(IndentingPrintWriter fout, boolean dumpFull) { if (dumpFull) { fout.println("algo: " + algorithm); fout.println("digest: " + Base64.encodeToString(digest, Base64.NO_WRAP)); fout.println("digest: " + (dumpFull ? encodeDigest() : safeDigest())); fout.println("label: " + label); fout.println("expiryMs: " + expiryTimeMillis); fout.println("tag: " + tag); } else { fout.println(toString()); } } /** @hide */ Loading @@ -233,6 +237,26 @@ public final class BlobHandle implements Parcelable { Preconditions.checkArgument(tag.length() <= LIMIT_BLOB_TAG_LENGTH, "tag too long"); } @Override public String toString() { return "BlobHandle {" + "algo:" + algorithm + "," + "digest:" + safeDigest() + "," + "label:" + label + "," + "expiryMs:" + expiryTimeMillis + "," + "tag:" + tag + "}"; } private String safeDigest() { final String digestStr = encodeDigest(); return digestStr.substring(0, 2) + ".." + digestStr.substring(digestStr.length() - 2); } private String encodeDigest() { return Base64.encodeToString(digest, Base64.NO_WRAP); } public static final @NonNull Creator<BlobHandle> CREATOR = new Creator<BlobHandle>() { @Override public @NonNull BlobHandle createFromParcel(@NonNull Parcel source) { Loading
apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java +3 −2 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; import com.android.server.blob.BlobStoreManagerService.DumpArgs; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; Loading Loading @@ -240,10 +241,10 @@ class BlobMetadata { return revocableFd.getRevocableFileDescriptor(); } void dump(IndentingPrintWriter fout) { void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) { fout.println("blobHandle:"); fout.increaseIndent(); blobHandle.dump(fout); blobHandle.dump(fout, dumpArgs.shouldDumpFull()); fout.decreaseIndent(); fout.println("Committers:"); Loading
apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +227 −37 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import android.annotation.IdRes; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.blob.BlobHandle; import android.app.blob.IBlobStoreManager; import android.app.blob.IBlobStoreSession; Loading Loading @@ -69,6 +70,7 @@ import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; Loading Loading @@ -121,6 +123,9 @@ public class BlobStoreManagerService extends SystemService { private PackageManagerInternal mPackageManagerInternal; private final Runnable mSaveBlobsInfoRunnable = this::writeBlobsInfo; private final Runnable mSaveSessionsRunnable = this::writeBlobSessions; public BlobStoreManagerService(Context context) { this(context, new Injector()); } Loading Loading @@ -533,9 +538,9 @@ public class BlobStoreManagerService extends SystemService { } private void writeBlobsInfoAsync() { mHandler.post(PooledLambda.obtainRunnable( BlobStoreManagerService::writeBlobsInfo, BlobStoreManagerService.this).recycleOnUse()); if (!mHandler.hasCallbacks(mSaveBlobsInfoRunnable)) { mHandler.post(mSaveBlobsInfoRunnable); } } private void writeBlobSessions() { Loading @@ -549,9 +554,9 @@ public class BlobStoreManagerService extends SystemService { } private void writeBlobSessionsAsync() { mHandler.post(PooledLambda.obtainRunnable( BlobStoreManagerService::writeBlobSessions, BlobStoreManagerService.this).recycleOnUse()); if (!mHandler.hasCallbacks(mSaveSessionsRunnable)) { mHandler.post(mSaveSessionsRunnable); } } private int getPackageUid(String packageName, int userId) { Loading Loading @@ -597,6 +602,7 @@ public class BlobStoreManagerService extends SystemService { return new AtomicFile(file, "blobs_index" /* commitLogTag */); } @VisibleForTesting void handlePackageRemoved(String packageName, int uid) { synchronized (mBlobsLock) { // Clean up any pending sessions Loading Loading @@ -659,6 +665,80 @@ public class BlobStoreManagerService extends SystemService { } } void runClearAllSessions(@UserIdInt int userId) { synchronized (mBlobsLock) { if (userId == UserHandle.USER_ALL) { mSessions.clear(); } else { mSessions.remove(userId); } writeBlobSessionsAsync(); } } void runClearAllBlobs(@UserIdInt int userId) { synchronized (mBlobsLock) { if (userId == UserHandle.USER_ALL) { mBlobsMap.clear(); } else { mBlobsMap.remove(userId); } writeBlobsInfoAsync(); } } @GuardedBy("mBlobsLock") private void dumpSessionsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) { for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) { final int userId = mSessions.keyAt(i); if (!dumpArgs.shouldDumpUser(userId)) { continue; } final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i); fout.println("List of sessions in user #" + userId + " (" + userSessions.size() + "):"); fout.increaseIndent(); for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) { final long sessionId = userSessions.keyAt(j); final BlobStoreSession session = userSessions.valueAt(j); if (!dumpArgs.shouldDumpSession(session.getOwnerPackageName(), session.getOwnerUid(), session.getSessionId())) { continue; } fout.println("Session #" + sessionId); fout.increaseIndent(); session.dump(fout, dumpArgs); fout.decreaseIndent(); } fout.decreaseIndent(); } } @GuardedBy("mBlobsLock") private void dumpBlobsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) { for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) { final int userId = mBlobsMap.keyAt(i); if (!dumpArgs.shouldDumpUser(userId)) { continue; } final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i); fout.println("List of blobs in user #" + userId + " (" + userBlobs.size() + "):"); fout.increaseIndent(); for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) { final BlobMetadata blobMetadata = userBlobs.valueAt(j); if (!dumpArgs.shouldDumpBlob(blobMetadata.blobId)) { continue; } fout.println("Blob #" + blobMetadata.blobId); fout.increaseIndent(); blobMetadata.dump(fout, dumpArgs); fout.decreaseIndent(); } fout.decreaseIndent(); } } private class PackageChangedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Loading Loading @@ -808,47 +888,157 @@ public class BlobStoreManagerService extends SystemService { public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) { // TODO: add proto-based version of this. if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return; if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, writer)) return; final DumpArgs dumpArgs = DumpArgs.parse(args); final IndentingPrintWriter fout = new IndentingPrintWriter(writer, " "); synchronized (mBlobsLock) { fout.println("mCurrentMaxSessionId: " + mCurrentMaxSessionId); fout.println(); for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) { final int userId = mSessions.keyAt(i); final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i); fout.println("List of sessions in user #" + userId + " (" + userSessions.size() + "):"); fout.increaseIndent(); for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) { final long sessionId = userSessions.keyAt(j); final BlobStoreSession session = userSessions.valueAt(j); fout.println("Session #" + sessionId); fout.increaseIndent(); session.dump(fout); fout.decreaseIndent(); if (dumpArgs.shouldDumpSessions()) { dumpSessionsLocked(fout, dumpArgs); fout.println(); } if (dumpArgs.shouldDumpBlobs()) { dumpBlobsLocked(fout, dumpArgs); fout.println(); } } fout.decreaseIndent(); } fout.print("\n\n"); @Override public int handleShellCommand(@NonNull ParcelFileDescriptor in, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull String[] args) { return (new BlobStoreManagerShellCommand(BlobStoreManagerService.this)).exec(this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args); } } for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) { final int userId = mBlobsMap.keyAt(i); final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i); fout.println("List of blobs in user #" + userId + " (" + userBlobs.size() + "):"); fout.increaseIndent(); for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) { final BlobMetadata blobMetadata = userBlobs.valueAt(j); fout.println("Blob #" + blobMetadata.blobId); fout.increaseIndent(); blobMetadata.dump(fout); fout.decreaseIndent(); static final class DumpArgs { private boolean mDumpFull; private final ArrayList<String> mDumpPackages = new ArrayList<>(); private final ArrayList<Integer> mDumpUids = new ArrayList<>(); private final ArrayList<Integer> mDumpUserIds = new ArrayList<>(); private final ArrayList<Long> mDumpBlobIds = new ArrayList<>(); private boolean mDumpOnlySelectedSections; private boolean mDumpSessions; private boolean mDumpBlobs; public boolean shouldDumpSession(String packageName, int uid, long blobId) { if (!CollectionUtils.isEmpty(mDumpPackages) && mDumpPackages.indexOf(packageName) < 0) { return false; } fout.decreaseIndent(); if (!CollectionUtils.isEmpty(mDumpUids) && mDumpUids.indexOf(uid) < 0) { return false; } if (!CollectionUtils.isEmpty(mDumpBlobIds) && mDumpBlobIds.indexOf(blobId) < 0) { return false; } return true; } public boolean shouldDumpSessions() { if (!mDumpOnlySelectedSections) { return true; } return mDumpSessions; } public boolean shouldDumpBlobs() { if (!mDumpOnlySelectedSections) { return true; } return mDumpBlobs; } public boolean shouldDumpBlob(long blobId) { return CollectionUtils.isEmpty(mDumpBlobIds) || mDumpBlobIds.indexOf(blobId) >= 0; } public boolean shouldDumpFull() { return mDumpFull; } public boolean shouldDumpUser(int userId) { return CollectionUtils.isEmpty(mDumpUserIds) || mDumpUserIds.indexOf(userId) >= 0; } private DumpArgs() {} public static DumpArgs parse(String[] args) { final DumpArgs dumpArgs = new DumpArgs(); if (args == null) { return dumpArgs; } for (int i = 0; i < args.length; ++i) { final String opt = args[i]; if ("--full".equals(opt) || "-f".equals(opt)) { final int callingUid = Binder.getCallingUid(); if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) { dumpArgs.mDumpFull = true; } } else if ("--sessions".equals(opt)) { dumpArgs.mDumpOnlySelectedSections = true; dumpArgs.mDumpSessions = true; } else if ("--blobs".equals(opt)) { dumpArgs.mDumpOnlySelectedSections = true; dumpArgs.mDumpBlobs = true; } else if ("--package".equals(opt) || "-p".equals(opt)) { dumpArgs.mDumpPackages.add(getStringArgRequired(args, ++i, "packageName")); } else if ("--uid".equals(opt) || "-u".equals(opt)) { dumpArgs.mDumpUids.add(getIntArgRequired(args, ++i, "uid")); } else if ("--user".equals(opt)) { dumpArgs.mDumpUserIds.add(getIntArgRequired(args, ++i, "userId")); } else if ("--blob".equals(opt) || "-b".equals(opt)) { dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, ++i, "blobId")); } else { // Everything else is assumed to be blob ids. dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, i, "blobId")); } } return dumpArgs; } private static String getStringArgRequired(String[] args, int index, String argName) { if (index >= args.length) { throw new IllegalArgumentException("Missing " + argName); } return args[index]; } private static int getIntArgRequired(String[] args, int index, String argName) { if (index >= args.length) { throw new IllegalArgumentException("Missing " + argName); } final int value; try { value = Integer.parseInt(args[index]); } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid " + argName + ": " + args[index]); } return value; } private static long getLongArgRequired(String[] args, int index, String argName) { if (index >= args.length) { throw new IllegalArgumentException("Missing " + argName); } final long value; try { value = Long.parseLong(args[index]); } catch (NumberFormatException e) { throw new IllegalArgumentException("Invalid " + argName + ": " + args[index]); } return value; } } Loading
apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerShellCommand.java 0 → 100644 +111 −0 Original line number Diff line number Diff line /* * 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 com.android.server.blob; import android.os.ShellCommand; import android.os.UserHandle; import java.io.PrintWriter; class BlobStoreManagerShellCommand extends ShellCommand { private final BlobStoreManagerService mService; BlobStoreManagerShellCommand(BlobStoreManagerService blobStoreManagerService) { mService = blobStoreManagerService; } @Override public int onCommand(String cmd) { if (cmd == null) { return handleDefaultCommands(null); } final PrintWriter pw = getOutPrintWriter(); switch (cmd) { case "clear-all-sessions": return runClearAllSessions(pw); case "clear-all-blobs": return runClearAllBlobs(pw); default: return handleDefaultCommands(cmd); } } private int runClearAllSessions(PrintWriter pw) { final ParsedArgs args = new ParsedArgs(); args.userId = UserHandle.USER_ALL; if (parseOptions(pw, args) < 0) { return -1; } mService.runClearAllSessions(args.userId); return 0; } private int runClearAllBlobs(PrintWriter pw) { final ParsedArgs args = new ParsedArgs(); args.userId = UserHandle.USER_ALL; if (parseOptions(pw, args) < 0) { return -1; } mService.runClearAllBlobs(args.userId); return 0; } @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); pw.println("BlobStore service (blob_store) commands:"); pw.println("help"); pw.println(" Print this help text."); pw.println(); pw.println("clear-all-sessions [-u | --user USER_ID]"); pw.println(" Remove all sessions."); pw.println(" Options:"); pw.println(" -u or --user: specify which user's sessions to be removed;"); pw.println(" If not specified, sessions in all users are removed."); pw.println(); pw.println("clear-all-blobs [-u | --user USER_ID]"); pw.println(" Remove all blobs."); pw.println(" Options:"); pw.println(" -u or --user: specify which user's blobs to be removed;"); pw.println(" If not specified, blobs in all users are removed."); pw.println(); } private int parseOptions(PrintWriter pw, ParsedArgs args) { String opt; while ((opt = getNextOption()) != null) { switch (opt) { case "-u": case "--user": args.userId = Integer.parseInt(getNextArgRequired()); break; default: pw.println("Error: unknown option '" + opt + "'"); return -1; } } return 0; } private static class ParsedArgs { public int userId; } }
apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java +3 −2 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.blob.BlobStoreManagerService.DumpArgs; import com.android.server.blob.BlobStoreManagerService.SessionStateChangeListener; import org.xmlpull.v1.XmlPullParser; Loading Loading @@ -453,7 +454,7 @@ class BlobStoreSession extends IBlobStoreSession.Stub { } } void dump(IndentingPrintWriter fout) { void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) { synchronized (mSessionLock) { fout.println("state: " + stateToString(mState)); fout.println("ownerUid: " + mOwnerUid); Loading @@ -461,7 +462,7 @@ class BlobStoreSession extends IBlobStoreSession.Stub { fout.println("blobHandle:"); fout.increaseIndent(); mBlobHandle.dump(fout); mBlobHandle.dump(fout, dumpArgs.shouldDumpFull()); fout.decreaseIndent(); fout.println("accessMode:"); Loading