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

Commit 2f0ff577 authored by Abhijeet Kaur's avatar Abhijeet Kaur Committed by Android (Google) Code Review
Browse files

Merge "Handle legacy broadcasts from dumpstate logic in onFinished() callback"

parents 12812746 af9fbc39
Loading
Loading
Loading
Loading
+132 −8
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.app.admin.DevicePolicyManager;
import android.content.ClipData;
import android.content.Context;
import android.content.DialogInterface;
@@ -107,6 +108,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -157,6 +160,8 @@ public class BugreportProgressService extends Service {
    private static final String TAG = "BugreportProgressService";
    private static final boolean DEBUG = false;

    private Intent startSelfIntent;

    private static final String AUTHORITY = "com.android.shell";

    // External intents sent by dumpstate.
@@ -235,6 +240,24 @@ public class BugreportProgressService extends Service {

    private static final String NOTIFICATION_CHANNEL_ID = "bugreports";

    /**
     * Always keep the newest 8 bugreport files.
     */
    private static final int MIN_KEEP_COUNT = 8;

    /**
     * Always keep bugreports taken in the last week.
     */
    private static final long MIN_KEEP_AGE = DateUtils.WEEK_IN_MILLIS;

    private static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";

    /** Always keep just the last 3 remote bugreport's files around. */
    private static final int REMOTE_BUGREPORT_FILES_AMOUNT = 3;

    /** Always keep remote bugreport files created in the last day. */
    private static final long REMOTE_MIN_KEEP_AGE = DateUtils.DAY_IN_MILLIS;

    private final Object mLock = new Object();

    /** Managed bugreport info (keyed by id) */
@@ -281,6 +304,7 @@ public class BugreportProgressService extends Service {
        mMainThreadHandler = new Handler(Looper.getMainLooper());
        mServiceHandler = new ServiceHandler("BugreportProgressServiceMainThread");
        mScreenshotHandler = new ScreenshotHandler("BugreportProgressServiceScreenshotThread");
        startSelfIntent = new Intent(this, this.getClass());

        mScreenshotsDir = new File(getFilesDir(), SCREENSHOT_DIR);
        if (!mScreenshotsDir.exists()) {
@@ -307,6 +331,9 @@ public class BugreportProgressService extends Service {
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.v(TAG, "onStartCommand(): " + dumpIntent(intent));
        if (intent != null) {
            if (!intent.hasExtra(EXTRA_ORIGINAL_INTENT) && !intent.hasExtra(EXTRA_ID)) {
                return START_NOT_STICKY;
            }
            // Handle it in a separate thread.
            final Message msg = mServiceHandler.obtainMessage();
            msg.what = MSG_SERVICE_COMMAND;
@@ -352,10 +379,11 @@ public class BugreportProgressService extends Service {

        private final BugreportInfo mInfo;

        BugreportCallbackImpl(String name, @Nullable String title, @Nullable String description) {
        BugreportCallbackImpl(String name, @Nullable String title, @Nullable String description,
                @BugreportParams.BugreportMode int type) {
            // pid not used in this workflow, so setting default = 0
            mInfo = new BugreportInfo(mContext, 0 /* pid */, name,
                    100 /* max progress*/, title, description);
                    100 /* max progress*/, title, description, type);
        }

        @Override
@@ -380,10 +408,9 @@ public class BugreportProgressService extends Service {

        @Override
        public void onFinished() {
            // TODO: Make all callback functions lock protected.
            trackInfoWithId();
            // Stop running on foreground, otherwise share notification cannot be dismissed.
            onBugreportFinished(mInfo.id);
            stopSelfWhenDone();
            sendBugreportFinishedBroadcast();
        }

        /**
@@ -400,6 +427,90 @@ public class BugreportProgressService extends Service {
            }
            return;
        }

        private void sendBugreportFinishedBroadcast() {
            final String bugreportFileName = mInfo.name + ".zip";
            final File bugreportFile = new File(BUGREPORT_DIR, bugreportFileName);
            final String bugreportFilePath = bugreportFile.getAbsolutePath();
            if (bugreportFile.length() == 0) {
                Log.e(TAG, "Bugreport file empty. File path = " + bugreportFilePath);
                return;
            }
            if (mInfo.type == BugreportParams.BUGREPORT_MODE_REMOTE) {
                sendRemoteBugreportFinishedBroadcast(bugreportFilePath, bugreportFile);
            } else {
                cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE);
                final Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
                intent.putExtra(EXTRA_BUGREPORT, bugreportFilePath);
                addScreenshotToIntent(intent);
                mContext.sendBroadcast(intent, android.Manifest.permission.DUMP);
                onBugreportFinished(mInfo.id);
            }
        }

        private void sendRemoteBugreportFinishedBroadcast(String bugreportFileName,
                File bugreportFile) {
            cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE);
            final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
            final Uri bugreportUri = getUri(mContext, bugreportFile);
            final String bugreportHash = generateFileHash(bugreportFileName);
            if (bugreportHash == null) {
                Log.e(TAG, "Error generating file hash for remote bugreport");
                return;
            }
            intent.setDataAndType(bugreportUri, BUGREPORT_MIMETYPE);
            intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
            intent.putExtra(EXTRA_BUGREPORT, bugreportFileName);
            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM,
                    android.Manifest.permission.DUMP);
        }

        private void addScreenshotToIntent(Intent intent) {
            final String screenshotFileName = mInfo.name + ".png";
            final File screenshotFile = new File(BUGREPORT_DIR, screenshotFileName);
            final String screenshotFilePath = screenshotFile.getAbsolutePath();
            if (screenshotFile.length() > 0) {
                intent.putExtra(EXTRA_SCREENSHOT, screenshotFilePath);
            }
            return;
        }

        private String generateFileHash(String fileName) {
            String fileHash = null;
            try {
                MessageDigest md = MessageDigest.getInstance("SHA-256");
                FileInputStream input = new FileInputStream(new File(fileName));
                byte[] buffer = new byte[65536];
                int size;
                while ((size = input.read(buffer)) > 0) {
                    md.update(buffer, 0, size);
                }
                input.close();
                byte[] hashBytes = md.digest();
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < hashBytes.length; i++) {
                    sb.append(String.format("%02x", hashBytes[i]));
                }
                fileHash = sb.toString();
            } catch (IOException | NoSuchAlgorithmException e) {
                Log.e(TAG, "generating file hash for bugreport file failed " + fileName, e);
            }
            return fileHash;
        }
    }

    static void cleanupOldFiles(final int minCount, final long minAge) {
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                try {
                    FileUtils.deleteOlderFiles(new File(BUGREPORT_DIR), minCount, minAge);
                } catch (RuntimeException e) {
                    Log.e(TAG, "RuntimeException deleting old files", e);
                }
                return null;
            }
        }.execute();
    }

    /**
@@ -598,7 +709,7 @@ public class BugreportProgressService extends Service {
                + " screenshot file fd: " + screenshotFd);

        BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName,
                shareTitle, shareDescription);
                shareTitle, shareDescription, bugreportType);
        try {
            mBugreportManager.startBugreport(bugreportFd, screenshotFd,
                    new BugreportParams(bugreportType), executor, bugreportCallback);
@@ -711,6 +822,9 @@ public class BugreportProgressService extends Service {
        } else {
            mForegroundId = id;
            Log.d(TAG, "Start running as foreground service on id " + mForegroundId);
            // Explicitly starting the service so that stopForeground() does not crash
            // Workaround for b/140997620
            startForegroundService(startSelfIntent);
            startForeground(mForegroundId, notification);
        }
    }
@@ -1924,11 +2038,20 @@ public class BugreportProgressService extends Service {
         */
        String shareDescription;

        /**
         * Type of the bugreport
         */
        int type;

        /**
         * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
         */
        BugreportInfo(Context context, int id, int pid, String name, int max) {
            this(context, pid, name, max, null, null);
            // bugreports triggered by STARTED broadcast do not use callback functions,
            // onFinished() callback method is the only function where type is used.
            // Set type to -1 as it is unused in this workflow.
            // This constructor will soon be removed.
            this(context, pid, name, max, null, null, -1);
            this.id = id;
        }

@@ -1936,13 +2059,14 @@ public class BugreportProgressService extends Service {
         * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED.
         */
        BugreportInfo(Context context, int pid, String name, int max, @Nullable String shareTitle,
                @Nullable String shareDescription) {
                @Nullable String shareDescription, int type) {
            this.context = context;
            this.pid = pid;
            this.name = name;
            this.max = this.realMax = max;
            this.shareTitle = shareTitle == null ? "" : shareTitle;
            this.shareDescription = shareDescription == null ? "" : shareDescription;
            this.type = type;
        }

        /**
+2 −0
Original line number Diff line number Diff line
@@ -8311,6 +8311,8 @@ public class ActivityManagerService extends IActivityManager.Stub
            triggerShellBugreport.setAction(INTENT_BUGREPORT_REQUESTED);
            triggerShellBugreport.setPackage(SHELL_APP_PACKAGE);
            triggerShellBugreport.putExtra(EXTRA_BUGREPORT_TYPE, bugreportType);
            triggerShellBugreport.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
            triggerShellBugreport.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
            if (shareTitle != null) {
                triggerShellBugreport.putExtra(EXTRA_TITLE, shareTitle);
            }