Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 981f524a authored by Sudheer Shanka's avatar Sudheer Shanka Committed by Android (Google) Code Review
Browse files

Merge "Add shell command to clear blobs data."

parents ba543689 5caeed5d
Loading
Loading
Loading
Loading
+30 −6
Original line number Diff line number Diff line
@@ -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 */
@@ -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) {
+3 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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:");
+227 −37
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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());
    }
@@ -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() {
@@ -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) {
@@ -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
@@ -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) {
@@ -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;
        }
    }

+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;
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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);
@@ -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:");