[RESTRICT AUTOMERGE] Add content observer to bug info activity.

As we've added copy to USB or upload from
BugReportInfoActivity, we'd like to see the
bugreport status changes from the activity.

Currently it requires us to close the activity,
and re-open it again to see the latest changes.

With this CL, the activity will reflect the latest
changes.

Test: tested on a hawk bench
Change-Id: Icb0b26b50a258a08d876a305b0347923ab9da940
diff --git a/tests/BugReportApp/AndroidManifest.xml b/tests/BugReportApp/AndroidManifest.xml
index 68bbc0c..d879396 100644
--- a/tests/BugReportApp/AndroidManifest.xml
+++ b/tests/BugReportApp/AndroidManifest.xml
@@ -29,6 +29,7 @@
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
     <uses-permission android:name="android.permission.DUMP"/>
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
 
     <application android:label="@string/app_name"
                  android:icon="@drawable/ic_launcher"
diff --git a/tests/BugReportApp/src/com/google/android/car/bugreport/BugReportInfoActivity.java b/tests/BugReportApp/src/com/google/android/car/bugreport/BugReportInfoActivity.java
index 574268d..46b5d45 100644
--- a/tests/BugReportApp/src/com/google/android/car/bugreport/BugReportInfoActivity.java
+++ b/tests/BugReportApp/src/com/google/android/car/bugreport/BugReportInfoActivity.java
@@ -22,9 +22,12 @@
 import android.content.ContentResolver;
 import android.content.Intent;
 import android.content.res.AssetFileDescriptor;
+import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
 import android.provider.DocumentsContract;
 import android.util.Log;
 import android.view.View;
@@ -57,6 +60,7 @@
     private NotificationManager mNotificationManager;
     private MetaBugReport mLastSelectedBugReport;
     private BugInfoAdapter.BugInfoViewHolder mLastSelectedBugInfoViewHolder;
+    private BugStorageObserver mBugStorageObserver;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -76,6 +80,8 @@
         mBugInfoAdapter = new BugInfoAdapter(this::onBugReportItemClicked);
         mRecyclerView.setAdapter(mBugInfoAdapter);
 
+        mBugStorageObserver = new BugStorageObserver(this, new Handler());
+
         findViewById(R.id.quit_button).setOnClickListener(this::onQuitButtonClick);
         findViewById(R.id.start_bug_report_button).setOnClickListener(
                 this::onStartBugReportButtonClick);
@@ -89,6 +95,15 @@
     protected void onStart() {
         super.onStart();
         new BugReportsLoaderAsyncTask(this).execute();
+        // As BugStorageProvider is running under user0, we register using USER_ALL.
+        getContentResolver().registerContentObserver(BugStorageProvider.BUGREPORT_CONTENT_URI, true,
+                mBugStorageObserver, UserHandle.USER_ALL);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        getContentResolver().unregisterContentObserver(mBugStorageObserver);
     }
 
     /**
@@ -155,6 +170,27 @@
         startActivity(intent);
     }
 
+    /** Observer for {@link BugStorageProvider}. */
+    private static class BugStorageObserver extends ContentObserver {
+        private final BugReportInfoActivity mInfoActivity;
+
+        /**
+         * Creates a content observer.
+         *
+         * @param activity A {@link BugReportInfoActivity} instance.
+         * @param handler The handler to run {@link #onChange} on, or null if none.
+         */
+        BugStorageObserver(BugReportInfoActivity activity, Handler handler) {
+            super(handler);
+            mInfoActivity = activity;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            new BugReportsLoaderAsyncTask(mInfoActivity).execute();
+        }
+    }
+
     /** Moves bugreport zip to a new location and updates RecyclerView. */
     private static final class AsyncMoveFilesTask extends AsyncTask<Void, Void, MetaBugReport> {
         private static final int COPY_BUFFER_SIZE = 4096; // bytes
@@ -250,7 +286,7 @@
         protected List<MetaBugReport> doInBackground(Void... voids) {
             BugReportInfoActivity activity = mBugReportInfoActivityWeakReference.get();
             if (activity == null) {
-                Log.w(TAG, "Activity is gone, cancelling BugReportInfoTask.");
+                Log.w(TAG, "Activity is gone, cancelling BugReportsLoaderAsyncTask.");
                 return new ArrayList<>();
             }
             return BugStorageUtils.getAllBugReportsDescending(activity);
diff --git a/tests/BugReportApp/src/com/google/android/car/bugreport/BugStorageProvider.java b/tests/BugReportApp/src/com/google/android/car/bugreport/BugStorageProvider.java
index b762cb3..92c1697 100644
--- a/tests/BugReportApp/src/com/google/android/car/bugreport/BugStorageProvider.java
+++ b/tests/BugReportApp/src/com/google/android/car/bugreport/BugStorageProvider.java
@@ -249,6 +249,7 @@
                 selectionArgs = new String[]{uri.getLastPathSegment()};
                 // Ignore the results of zip file deletion, likelihood of failure is too small.
                 deleteZipFile(Integer.parseInt(uri.getLastPathSegment()));
+                getContext().getContentResolver().notifyChange(uri, null);
                 return db.delete(BUG_REPORTS_TABLE, selection, selectionArgs);
             case URL_MATCHED_DELETE_ZIP_FILE:
                 if (selection != null || selectionArgs != null) {
@@ -256,6 +257,7 @@
                             + URL_MATCHED_DELETE_ZIP_FILE);
                 }
                 if (deleteZipFile(Integer.parseInt(uri.getLastPathSegment()))) {
+                    getContext().getContentResolver().notifyChange(uri, null);
                     return 1;
                 }
                 return 0;
@@ -349,6 +351,7 @@
                     JobSchedulingUtils.scheduleUploadJob(BugStorageProvider.this.getContext());
                 }
                 Log.i(TAG, "Finished adding bugreport " + path + " " + uri);
+                getContext().getContentResolver().notifyChange(uri, null);
             });
         } catch (IOException e) {
             // An IOException (for example not being able to open the file, will crash us.