summaryrefslogtreecommitdiff
path: root/packages/Shell/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/Shell/src')
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java281
1 files changed, 192 insertions, 89 deletions
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 18145939c384..997203189e90 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -126,8 +126,6 @@ import java.util.zip.ZipOutputStream;
* <li>This service calls startBugreport() and passes in local file descriptors to receive
* bugreport artifacts.
* </ol>
- *
- * TODO: There are multiple threads involved. Add synchronization accordingly.
*/
public class BugreportProgressService extends Service {
private static final String TAG = "BugreportProgressService";
@@ -219,6 +217,7 @@ public class BugreportProgressService extends Service {
private final Object mLock = new Object();
/** Managed bugreport info (keyed by id) */
+ @GuardedBy("mLock")
private final SparseArray<BugreportInfo> mBugreportInfos = new SparseArray<>();
private Context mContext;
@@ -317,23 +316,26 @@ public class BugreportProgressService extends Service {
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- final int size = mBugreportInfos.size();
- if (size == 0) {
- writer.println("No monitored processes");
- return;
- }
- writer.print("Foreground id: "); writer.println(mForegroundId);
- writer.println("\n");
- writer.println("Monitored dumpstate processes");
- writer.println("-----------------------------");
- for (int i = 0; i < size; i++) {
- writer.print("#"); writer.println(i + 1);
- writer.println(getInfo(mBugreportInfos.keyAt(i)));
+ synchronized (mLock) {
+ final int size = mBugreportInfos.size();
+ if (size == 0) {
+ writer.println("No monitored processes");
+ return;
+ }
+ writer.print("Foreground id: "); writer.println(mForegroundId);
+ writer.println("\n");
+ writer.println("Monitored dumpstate processes");
+ writer.println("-----------------------------");
+ for (int i = 0; i < size; i++) {
+ writer.print("#");
+ writer.println(i + 1);
+ writer.println(getInfoLocked(mBugreportInfos.keyAt(i)));
+ }
}
}
private static String getFileName(BugreportInfo info, String suffix) {
- return String.format("%s-%s%s", info.baseName, info.name, suffix);
+ return String.format("%s-%s%s", info.baseName, info.getName(), suffix);
}
private final class BugreportCallbackImpl extends BugreportCallback {
@@ -573,7 +575,8 @@ public class BugreportProgressService extends Service {
}
}
- private BugreportInfo getInfo(int id) {
+ @GuardedBy("mLock")
+ private BugreportInfo getInfoLocked(int id) {
final BugreportInfo bugreportInfo = mBugreportInfos.get(id);
if (bugreportInfo == null) {
Log.w(TAG, "Not monitoring bugreports with ID " + id);
@@ -669,12 +672,12 @@ public class BugreportProgressService extends Service {
* Updates the system notification for a given bugreport.
*/
private void updateProgress(BugreportInfo info) {
- if (info.progress < 0) {
- Log.e(TAG, "Invalid progress value for " + info);
+ if (info.getProgress() < 0) {
+ Log.e(TAG, "Invalid progress values for " + info);
return;
}
- if (info.finished) {
+ if (info.isFinished()) {
Log.w(TAG, "Not sending progress notification because bugreport has finished already ("
+ info + ")");
return;
@@ -683,7 +686,7 @@ public class BugreportProgressService extends Service {
final NumberFormat nf = NumberFormat.getPercentInstance();
nf.setMinimumFractionDigits(2);
nf.setMaximumFractionDigits(2);
- final String percentageText = nf.format((double) info.progress / 100);
+ final String percentageText = nf.format((double) info.getProgress() / 100);
String title = mContext.getString(R.string.bugreport_in_progress_title, info.id);
@@ -691,18 +694,19 @@ public class BugreportProgressService extends Service {
if (mIsWatch) {
nf.setMinimumFractionDigits(0);
nf.setMaximumFractionDigits(0);
- final String watchPercentageText = nf.format((double) info.progress / 100);
+ final String watchPercentageText = nf.format((double) info.getProgress() / 100);
title = title + "\n" + watchPercentageText;
}
final String name =
- info.name != null ? info.name : mContext.getString(R.string.bugreport_unnamed);
+ info.getName() != null ? info.getName()
+ : mContext.getString(R.string.bugreport_unnamed);
final Notification.Builder builder = newBaseNotification(mContext)
.setContentTitle(title)
.setTicker(title)
.setContentText(name)
- .setProgress(100 /* max value of progress percentage */, info.progress, false)
+ .setProgress(100 /* max value of progress percentage */, info.getProgress(), false)
.setOngoing(true);
// Wear and ATV bugreport doesn't need the bug info dialog, screenshot and cancel action.
@@ -731,10 +735,10 @@ public class BugreportProgressService extends Service {
.setActions(infoAction, screenshotAction, cancelAction);
}
// Show a debug log, every LOG_PROGRESS_STEP percent.
- final int progress = info.progress;
+ final int progress = info.getProgress();
- if ((info.progress == 0) || (info.progress >= 100) ||
- ((progress / LOG_PROGRESS_STEP) != (mLastProgressPercent / LOG_PROGRESS_STEP))) {
+ if ((info.getProgress() == 0) || (info.getProgress() >= 100)
+ || ((progress / LOG_PROGRESS_STEP) != (mLastProgressPercent / LOG_PROGRESS_STEP))) {
Log.d(TAG, "Progress #" + info.id + ": " + percentageText);
}
mLastProgressPercent = progress;
@@ -779,10 +783,10 @@ public class BugreportProgressService extends Service {
mBugreportInfos.remove(id);
}
// Must stop foreground service first, otherwise notif.cancel() will fail below.
- stopForegroundWhenDone(id);
+ stopForegroundWhenDoneLocked(id);
Log.d(TAG, "stopProgress(" + id + "): cancel notification");
NotificationManager.from(mContext).cancel(id);
- stopSelfWhenDone();
+ stopSelfWhenDoneLocked();
}
/**
@@ -792,13 +796,13 @@ public class BugreportProgressService extends Service {
MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_CANCEL);
Log.v(TAG, "cancel: ID=" + id);
mInfoDialog.cancel();
- final BugreportInfo info = getInfo(id);
- if (info != null && !info.finished) {
- Log.i(TAG, "Cancelling bugreport service (ID=" + id + ") on user's request");
- mBugreportManager.cancelBugreport();
- deleteScreenshots(info);
- }
synchronized (mLock) {
+ final BugreportInfo info = getInfoLocked(id);
+ if (info != null && !info.isFinished()) {
+ Log.i(TAG, "Cancelling bugreport service (ID=" + id + ") on user's request");
+ mBugreportManager.cancelBugreport();
+ deleteScreenshots(info);
+ }
stopProgressLocked(id);
}
}
@@ -809,7 +813,10 @@ public class BugreportProgressService extends Service {
*/
private void launchBugreportInfoDialog(int id) {
MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_DETAILS);
- final BugreportInfo info = getInfo(id);
+ final BugreportInfo info;
+ synchronized (mLock) {
+ info = getInfoLocked(id);
+ }
if (info == null) {
// Most likely am killed Shell before user tapped the notification. Since system might
// be too busy anwyays, it's better to ignore the notification and switch back to the
@@ -843,7 +850,11 @@ public class BugreportProgressService extends Service {
*/
private void takeScreenshot(int id) {
MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SCREENSHOT);
- if (getInfo(id) == null) {
+ BugreportInfo info;
+ synchronized (mLock) {
+ info = getInfoLocked(id);
+ }
+ if (info == null) {
// Most likely am killed Shell before user tapped the notification. Since system might
// be too busy anwyays, it's better to ignore the notification and switch back to the
// non-interactive mode (where the bugerport will be shared upon completion).
@@ -878,9 +889,11 @@ public class BugreportProgressService extends Service {
mServiceHandler.sendMessageDelayed(msg, DateUtils.SECOND_IN_MILLIS);
return;
}
-
+ final BugreportInfo info;
// It's time to take the screenshot: let the proper thread handle it
- final BugreportInfo info = getInfo(id);
+ synchronized (mLock) {
+ info = getInfoLocked(id);
+ }
if (info == null) {
return;
}
@@ -896,11 +909,11 @@ public class BugreportProgressService extends Service {
* SCREENSHOT button is enabled or disabled accordingly.
*/
private void setTakingScreenshot(boolean flag) {
- synchronized (BugreportProgressService.this) {
+ synchronized (mLock) {
mTakingScreenshot = flag;
for (int i = 0; i < mBugreportInfos.size(); i++) {
- final BugreportInfo info = getInfo(mBugreportInfos.keyAt(i));
- if (info.finished) {
+ final BugreportInfo info = getInfoLocked(mBugreportInfos.keyAt(i));
+ if (info.isFinished()) {
Log.d(TAG, "Not updating progress for " + info.id + " while taking screenshot"
+ " because share notification was already sent");
continue;
@@ -921,7 +934,10 @@ public class BugreportProgressService extends Service {
private void handleScreenshotResponse(Message resultMsg) {
final boolean taken = resultMsg.arg2 != 0;
- final BugreportInfo info = getInfo(resultMsg.arg1);
+ final BugreportInfo info;
+ synchronized (mLock) {
+ info = getInfoLocked(resultMsg.arg1);
+ }
if (info == null) {
return;
}
@@ -930,7 +946,7 @@ public class BugreportProgressService extends Service {
final String msg;
if (taken) {
info.addScreenshot(screenshotFile);
- if (info.finished) {
+ if (info.isFinished()) {
Log.d(TAG, "Screenshot finished after bugreport; updating share notification");
info.renameScreenshots();
sendBugreportNotification(info, mTakingScreenshot);
@@ -956,9 +972,10 @@ public class BugreportProgressService extends Service {
/**
* Stop running on foreground once there is no more active bugreports being watched.
*/
- private void stopForegroundWhenDone(int id) {
+ @GuardedBy("mLock")
+ private void stopForegroundWhenDoneLocked(int id) {
if (id != mForegroundId) {
- Log.d(TAG, "stopForegroundWhenDone(" + id + "): ignoring since foreground id is "
+ Log.d(TAG, "stopForegroundWhenDoneLocked(" + id + "): ignoring since foreground id is "
+ mForegroundId);
return;
}
@@ -971,8 +988,8 @@ public class BugreportProgressService extends Service {
final int total = mBugreportInfos.size();
if (total > 0) {
for (int i = 0; i < total; i++) {
- final BugreportInfo info = getInfo(mBugreportInfos.keyAt(i));
- if (!info.finished) {
+ final BugreportInfo info = getInfoLocked(mBugreportInfos.keyAt(i));
+ if (!info.isFinished()) {
updateProgress(info);
break;
}
@@ -983,7 +1000,8 @@ public class BugreportProgressService extends Service {
/**
* Finishes the service when it's not monitoring any more processes.
*/
- private void stopSelfWhenDone() {
+ @GuardedBy("mLock")
+ private void stopSelfWhenDoneLocked() {
if (mBugreportInfos.size() > 0) {
if (DEBUG) Log.d(TAG, "Staying alive, waiting for IDs " + mBugreportInfos);
return;
@@ -996,12 +1014,14 @@ public class BugreportProgressService extends Service {
* Wraps up bugreport generation and triggers a notification to share the bugreport.
*/
private void onBugreportFinished(BugreportInfo info) {
- Log.d(TAG, "Bugreport finished with title: " + info.title
+ Log.d(TAG, "Bugreport finished with title: " + info.getTitle()
+ " and shareDescription: " + info.shareDescription);
- info.finished = true;
+ info.setFinished(true);
- // Stop running on foreground, otherwise share notification cannot be dismissed.
- stopForegroundWhenDone(info.id);
+ synchronized (mLock) {
+ // Stop running on foreground, otherwise share notification cannot be dismissed.
+ stopForegroundWhenDoneLocked(info.id);
+ }
triggerLocalNotification(mContext, info);
}
@@ -1063,8 +1083,8 @@ public class BugreportProgressService extends Service {
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setType(mimeType);
- final String subject = !TextUtils.isEmpty(info.title) ?
- info.title : bugreportUri.getLastPathSegment();
+ final String subject = !TextUtils.isEmpty(info.getTitle())
+ ? info.getTitle() : bugreportUri.getLastPathSegment();
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
// EXTRA_TEXT should be an ArrayList, but some clients are expecting a single String.
@@ -1075,9 +1095,9 @@ public class BugreportProgressService extends Service {
.append("\nSerial number: ")
.append(SystemProperties.get("ro.serialno"));
int descriptionLength = 0;
- if (!TextUtils.isEmpty(info.description)) {
- messageBody.append("\nDescription: ").append(info.description);
- descriptionLength = info.description.length();
+ if (!TextUtils.isEmpty(info.getDescription())) {
+ messageBody.append("\nDescription: ").append(info.getDescription());
+ descriptionLength = info.getDescription().length();
}
intent.putExtra(Intent.EXTRA_TEXT, messageBody.toString());
final ClipData clipData = new ClipData(null, new String[] { mimeType },
@@ -1118,12 +1138,17 @@ public class BugreportProgressService extends Service {
*/
private void shareBugreport(int id, BugreportInfo sharedInfo) {
MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SHARE);
- BugreportInfo info = getInfo(id);
+ BugreportInfo info;
+ synchronized (mLock) {
+ info = getInfoLocked(id);
+ }
if (info == null) {
// Service was terminated but notification persisted
info = sharedInfo;
- Log.d(TAG, "shareBugreport(): no info for ID " + id + " on managed processes ("
- + mBugreportInfos + "), using info from intent instead (" + info + ")");
+ synchronized (mLock) {
+ Log.d(TAG, "shareBugreport(): no info for ID " + id + " on managed processes ("
+ + mBugreportInfos + "), using info from intent instead (" + info + ")");
+ }
} else {
Log.v(TAG, "shareBugReport(): id " + id + " info = " + info);
}
@@ -1195,10 +1220,10 @@ public class BugreportProgressService extends Service {
mContext.getString(R.string.bugreport_finished_pending_screenshot_text)
: mContext.getString(R.string.bugreport_finished_text);
final String title;
- if (TextUtils.isEmpty(info.title)) {
+ if (TextUtils.isEmpty(info.getTitle())) {
title = mContext.getString(R.string.bugreport_finished_title, info.id);
} else {
- title = info.title;
+ title = info.getTitle();
if (!TextUtils.isEmpty(info.shareDescription)) {
if(!takingScreenshot) content = info.shareDescription;
}
@@ -1212,8 +1237,8 @@ public class BugreportProgressService extends Service {
PendingIntent.FLAG_UPDATE_CURRENT))
.setDeleteIntent(newCancelIntent(mContext, info));
- if (!TextUtils.isEmpty(info.name)) {
- builder.setSubText(info.name);
+ if (!TextUtils.isEmpty(info.getName())) {
+ builder.setSubText(info.getName());
}
Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
@@ -1306,13 +1331,14 @@ public class BugreportProgressService extends Service {
}
}
+ @GuardedBy("mLock")
private void addDetailsToZipFileLocked(BugreportInfo info) {
if (info.bugreportFile == null) {
// One possible reason is a bug in the Parcelization code.
Log.wtf(TAG, "addDetailsToZipFile(): no bugreportFile on " + info);
return;
}
- if (TextUtils.isEmpty(info.title) && TextUtils.isEmpty(info.description)) {
+ if (TextUtils.isEmpty(info.getTitle()) && TextUtils.isEmpty(info.getDescription())) {
Log.d(TAG, "Not touching zip file since neither title nor description are set");
return;
}
@@ -1345,8 +1371,8 @@ public class BugreportProgressService extends Service {
}
// Then add the user-provided info.
- addEntry(zos, "title.txt", info.title);
- addEntry(zos, "description.txt", info.description);
+ addEntry(zos, "title.txt", info.getTitle());
+ addEntry(zos, "description.txt", info.getDescription());
} catch (IOException e) {
Log.e(TAG, "exception zipping file " + tmpZip, e);
Toast.makeText(mContext, R.string.bugreport_add_details_to_zip_failed,
@@ -1356,7 +1382,7 @@ public class BugreportProgressService extends Service {
// Make sure it only tries to add details once, even it fails the first time.
info.addedDetailsToZip = true;
info.addingDetailsToZip = false;
- stopForegroundWhenDone(info.id);
+ stopForegroundWhenDoneLocked(info.id);
}
if (!tmpZip.renameTo(info.bugreportFile)) {
@@ -1509,24 +1535,27 @@ public class BugreportProgressService extends Service {
* Updates the user-provided details of a bugreport.
*/
private void updateBugreportInfo(int id, String name, String title, String description) {
- final BugreportInfo info = getInfo(id);
+ final BugreportInfo info;
+ synchronized (mLock) {
+ info = getInfoLocked(id);
+ }
if (info == null) {
return;
}
- if (title != null && !title.equals(info.title)) {
+ if (title != null && !title.equals(info.getTitle())) {
Log.d(TAG, "updating bugreport title: " + title);
MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_DETAILS_TITLE_CHANGED);
}
- info.title = title;
- if (description != null && !description.equals(info.description)) {
+ info.setTitle(title);
+ if (description != null && !description.equals(info.getDescription())) {
Log.d(TAG, "updating bugreport description: " + description.length() + " chars");
MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_DETAILS_DESCRIPTION_CHANGED);
}
- info.description = description;
- if (name != null && !name.equals(info.name)) {
+ info.setDescription(description);
+ if (name != null && !name.equals(info.getName())) {
Log.d(TAG, "updating bugreport name: " + name);
MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_DETAILS_NAME_CHANGED);
- info.name = name;
+ info.setName(name);
updateProgress(info);
}
}
@@ -1641,14 +1670,14 @@ public class BugreportProgressService extends Service {
// Then set fields.
mId = info.id;
- if (!TextUtils.isEmpty(info.name)) {
- mInfoName.setText(info.name);
+ if (!TextUtils.isEmpty(info.getName())) {
+ mInfoName.setText(info.getName());
}
- if (!TextUtils.isEmpty(info.title)) {
- mInfoTitle.setText(info.title);
+ if (!TextUtils.isEmpty(info.getTitle())) {
+ mInfoTitle.setText(info.getTitle());
}
- if (!TextUtils.isEmpty(info.description)) {
- mInfoDescription.setText(info.description);
+ if (!TextUtils.isEmpty(info.getDescription())) {
+ mInfoDescription.setText(info.getDescription());
}
// And finally display it.
@@ -1667,7 +1696,7 @@ public class BugreportProgressService extends Service {
@Override
public void onClick(View view) {
MetricsLogger.action(context, MetricsEvent.ACTION_BUGREPORT_DETAILS_SAVED);
- sanitizeName(info.name);
+ sanitizeName(info.getName());
final String name = mInfoName.getText().toString();
final String title = mInfoTitle.getText().toString();
final String description = mInfoDescription.getText().toString();
@@ -1818,6 +1847,8 @@ public class BugreportProgressService extends Service {
*/
int type;
+ private final Object mLock = new Object();
+
/**
* Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED.
*/
@@ -1856,6 +1887,78 @@ public class BugreportProgressService extends Service {
return getFd(screenshotFiles.get(0));
}
+ void setFinished(boolean isFinished) {
+ synchronized (mLock) {
+ this.finished = isFinished;
+ }
+ }
+
+ boolean isFinished() {
+ synchronized (mLock) {
+ return finished;
+ }
+ }
+
+ void setTitle(String title) {
+ synchronized (mLock) {
+ this.title = title;
+ }
+ }
+
+ String getTitle() {
+ synchronized (mLock) {
+ return title;
+ }
+ }
+
+ void setName(String name) {
+ synchronized (mLock) {
+ this.name = name;
+ }
+ }
+
+ String getName() {
+ synchronized (mLock) {
+ return name;
+ }
+ }
+
+ void setDescription(String description) {
+ synchronized (mLock) {
+ this.description = description;
+ }
+ }
+
+ String getDescription() {
+ synchronized (mLock) {
+ return description;
+ }
+ }
+
+ void setProgress(int progress) {
+ synchronized (mLock) {
+ this.progress = progress;
+ }
+ }
+
+ int getProgress() {
+ synchronized (mLock) {
+ return progress;
+ }
+ }
+
+ void setLastUpdate(long lastUpdate) {
+ synchronized (mLock) {
+ this.lastUpdate = lastUpdate;
+ }
+ }
+
+ long getLastUpdate() {
+ synchronized (mLock) {
+ return lastUpdate;
+ }
+ }
+
/**
* Gets the name for next user triggered screenshot file.
*/
@@ -1925,9 +2028,9 @@ public class BugreportProgressService extends Service {
if (context == null) {
// Restored from Parcel
return formattedLastUpdate == null ?
- Long.toString(lastUpdate) : formattedLastUpdate;
+ Long.toString(getLastUpdate()) : formattedLastUpdate;
}
- return DateUtils.formatDateTime(context, lastUpdate,
+ return DateUtils.formatDateTime(context, getLastUpdate(),
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME);
}
@@ -2048,13 +2151,13 @@ public class BugreportProgressService extends Service {
progress = CAPPED_PROGRESS;
}
if (DEBUG) {
- if (progress != info.progress) {
- Log.v(TAG, "Updating progress for name " + info.name + "(id: " + info.id
- + ") from " + info.progress + " to " + progress);
+ if (progress != info.getProgress()) {
+ Log.v(TAG, "Updating progress for name " + info.getName() + "(id: " + info.id
+ + ") from " + info.getProgress() + " to " + progress);
}
}
- info.progress = progress;
- info.lastUpdate = System.currentTimeMillis();
+ info.setProgress(progress);
+ info.setLastUpdate(System.currentTimeMillis());
updateProgress(info);
}