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

Commit 8425e266 authored by Abhijeet Kaur's avatar Abhijeet Kaur
Browse files

Add synchronization locks to shared objects

Make methods that read/write mBugreportInfos lock protected and add
"Locked" suffix to the method name. BugreportInfo objects are read/write
in different functions, to keep the code clean, add lock protected
getter/setter methods.

Bug: 142217059
Test: Takes interactive/full bugreports as expected
Change-Id: Iaadd6c9dce5009e40dd015e3b62bcd6e36966e00
parent 755bbc6a
Loading
Loading
Loading
Loading
+192 −89
Original line number Diff line number Diff line
@@ -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,6 +316,7 @@ public class BugreportProgressService extends Service {

    @Override
    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
        synchronized (mLock) {
            final int size = mBugreportInfos.size();
            if (size == 0) {
                writer.println("No monitored processes");
@@ -327,13 +327,15 @@ public class BugreportProgressService extends Service {
            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)));
                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) {
        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);
            }
        synchronized (mLock) {
            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);

        synchronized (mLock) {
            // Stop running on foreground, otherwise share notification cannot be dismissed.
        stopForegroundWhenDone(info.id);
            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;
            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);
    }