Improved BugreportManagerServiceImpl.dump():

- Logs current dumpstate listener.
- Logs all finished bugreports.

Fixes: 274930566

Test: atest BugreportManagerServiceImplTest
Test: adb shell dummpsys bugreport

Change-Id: I911ae0c93ea5670dbecdde5e7cb4c6a6e3c01336
This commit is contained in:
Felipe Leme
2023-03-24 09:50:08 -07:00
parent c85e708693
commit 919a07f922

View File

@@ -39,6 +39,7 @@ import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Pair;
import android.util.Slog;
@@ -60,6 +61,7 @@ import java.util.OptionalInt;
*/
class BugreportManagerServiceImpl extends IDumpstate.Stub {
private static final int LOCAL_LOG_SIZE = 20;
private static final String TAG = "BugreportManagerService";
private static final boolean DEBUG = false;
@@ -73,9 +75,19 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
private final ArraySet<String> mBugreportAllowlistedPackages;
private final BugreportFileManager mBugreportFileManager;
@GuardedBy("mLock")
private OptionalInt mPreDumpedDataUid = OptionalInt.empty();
// Attributes below are just Used for dump() purposes
@Nullable
@GuardedBy("mLock")
private DumpstateListener mCurrentDumpstateListener;
@GuardedBy("mLock")
private int mNumberFinishedBugreports;
@GuardedBy("mLock")
private final LocalLog mFinishedBugreports = new LocalLog(LOCAL_LOG_SIZE);
/** Helper class for associating previously generated bugreports with their callers. */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
static class BugreportFileManager {
@@ -264,8 +276,9 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
}
// Wrap the listener so we can intercept binder events directly.
IDumpstateListener myListener = new DumpstateListener(listener, ds,
new Pair<>(callingUid, callingPackage));
DumpstateListener myListener = new DumpstateListener(listener, ds,
new Pair<>(callingUid, callingPackage), /* reportFinishedFile= */ true);
setCurrentDumpstateListenerLocked(myListener);
try {
ds.retrieveBugreport(callingUid, callingPackage, bugreportFd,
bugreportFile, myListener);
@@ -275,6 +288,16 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
}
}
@GuardedBy("mLock")
private void setCurrentDumpstateListenerLocked(DumpstateListener listener) {
if (mCurrentDumpstateListener != null) {
Slogf.w(TAG, "setCurrentDumpstateListenerLocked(%s): called when "
+ "mCurrentDumpstateListener is already set (%s)", listener,
mCurrentDumpstateListener);
}
mCurrentDumpstateListener = listener;
}
private void validateBugreportMode(@BugreportParams.BugreportMode int mode) {
if (mode != BugreportParams.BUGREPORT_MODE_FULL
&& mode != BugreportParams.BUGREPORT_MODE_INTERACTIVE
@@ -440,7 +463,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
}
}
boolean isConsentDeferred =
boolean reportFinishedFile =
(bugreportFlags & BugreportParams.BUGREPORT_FLAG_DEFER_CONSENT) != 0;
IDumpstate ds = startAndGetDumpstateBinderServiceLocked();
@@ -450,9 +473,9 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
return;
}
// Wrap the listener so we can intercept binder events directly.
IDumpstateListener myListener = new DumpstateListener(listener, ds,
isConsentDeferred ? new Pair<>(callingUid, callingPackage) : null);
DumpstateListener myListener = new DumpstateListener(listener, ds,
new Pair<>(callingUid, callingPackage), reportFinishedFile);
setCurrentDumpstateListenerLocked(myListener);
try {
ds.startBugreport(callingUid, callingPackage, bugreportFd, screenshotFd, bugreportMode,
bugreportFlags, myListener, isScreenshotRequested);
@@ -540,16 +563,31 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
} else {
pw.println(mPreDumpedDataUid.getAsInt());
}
if (mCurrentDumpstateListener == null) {
pw.println("Not taking a bug report");
} else {
mCurrentDumpstateListener.dump(pw);
}
if (mNumberFinishedBugreports == 0) {
pw.println("No finished bugreports");
} else {
pw.printf("%d finished bugreport%s. Last %d:\n", mNumberFinishedBugreports,
(mNumberFinishedBugreports > 1 ? "s" : ""),
Math.min(mNumberFinishedBugreports, LOCAL_LOG_SIZE));
mFinishedBugreports.dump(" ", pw);
}
}
synchronized (mBugreportFileManager.mLock) {
int numberFiles = mBugreportFileManager.mBugreportFiles.size();
pw.printf("%d call%s", numberFiles, (numberFiles > 1 ? "s" : ""));
pw.printf("%d pending file%s", numberFiles, (numberFiles > 1 ? "s" : ""));
if (numberFiles > 0) {
for (int i = 0; i < numberFiles; i++) {
Pair<Integer, String> caller = mBugreportFileManager.mBugreportFiles.keyAt(i);
ArraySet<String> files = mBugreportFileManager.mBugreportFiles.valueAt(i);
pw.printf(" %s/%d: %s\n", caller.second, caller.first, files);
pw.printf(" %s: %s\n", callerToString(caller), files);
}
} else {
pw.println();
@@ -557,6 +595,10 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
}
}
private static String callerToString(@Nullable Pair<Integer, String> caller) {
return (caller == null) ? "N/A" : caller.second + "/" + caller.first;
}
private int clearBugreportFlag(int flags, @BugreportParams.BugreportFlag int flag) {
flags &= ~flag;
return flags;
@@ -576,19 +618,28 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
throw new IllegalArgumentException(message);
}
private final class DumpstateListener extends IDumpstateListener.Stub
implements DeathRecipient {
private static int sNextId;
private final int mId = ++sNextId; // used for debugging purposes only
private final IDumpstateListener mListener;
private final IDumpstate mDs;
private boolean mDone = false;
private final Pair<Integer, String> mCaller;
private final boolean mReportFinishedFile;
private int mProgress; // used for debugging purposes only
private boolean mDone;
DumpstateListener(IDumpstateListener listener, IDumpstate ds,
@Nullable Pair<Integer, String> caller) {
Pair<Integer, String> caller, boolean reportFinishedFile) {
if (DEBUG) {
Slogf.d(TAG, "Starting DumpstateListener(id=%d) for caller %s", mId, caller);
}
mListener = listener;
mDs = ds;
mCaller = caller;
mReportFinishedFile = reportFinishedFile;
try {
mDs.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -601,6 +652,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
if (DEBUG) {
Slogf.d(TAG, "onProgress: %d", progress);
}
mProgress = progress;
mListener.onProgress(progress);
}
@@ -608,19 +660,23 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
public void onError(int errorCode) throws RemoteException {
Slogf.e(TAG, "onError(): %d", errorCode);
synchronized (mLock) {
mDone = true;
releaseItselfLocked();
reportFinishedLocked("ErroCode: " + errorCode);
}
mListener.onError(errorCode);
}
@Override
public void onFinished(String bugreportFile) throws RemoteException {
Slogf.e(TAG, "onFinished(): %s", bugreportFile);
Slogf.i(TAG, "onFinished(): %s", bugreportFile);
synchronized (mLock) {
mDone = true;
releaseItselfLocked();
reportFinishedLocked("File: " + bugreportFile);
}
if (mCaller != null) {
if (mReportFinishedFile) {
mBugreportFileManager.addBugreportFileForCaller(mCaller, bugreportFile);
} else if (DEBUG) {
Slog.d(TAG, "Not reporting finished file");
}
mListener.onFinished(bugreportFile);
}
@@ -663,5 +719,39 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
}
mDs.asBinder().unlinkToDeath(this, 0);
}
@Override
public String toString() {
return "DumpstateListener[id=" + mId + ", progress=" + mProgress + "]";
}
@GuardedBy("mLock")
private void reportFinishedLocked(String message) {
mNumberFinishedBugreports++;
mFinishedBugreports.log("Caller: " + callerToString(mCaller) + " " + message);
}
private void dump(PrintWriter pw) {
pw.println("DumpstateListener:");
pw.printf(" id: %d\n", mId);
pw.printf(" caller: %s\n", callerToString(mCaller));
pw.printf(" reports finished file: %b\n", mReportFinishedFile);
pw.printf(" progress: %d\n", mProgress);
pw.printf(" done: %b\n", mDone);
}
@GuardedBy("mLock")
private void releaseItselfLocked() {
mDone = true;
if (mCurrentDumpstateListener == this) {
if (DEBUG) {
Slogf.d(TAG, "releaseItselfLocked(): releasing %s", this);
}
mCurrentDumpstateListener = null;
} else {
Slogf.w(TAG, "releaseItselfLocked(): " + this + " is finished, but current listener"
+ " is " + mCurrentDumpstateListener);
}
}
}
}