summaryrefslogtreecommitdiff
path: root/packages/Shell
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2020-08-31 21:21:38 -0700
committerXin Li <delphij@google.com>2020-08-31 21:21:38 -0700
commit628590d7ec80e10a3fc24b1c18a1afb55cca10a8 (patch)
tree4b1c3f52d86d7fb53afbe9e9438468588fa489f8 /packages/Shell
parentb11b8ec3aec8bb42f2c07e1c5ac7942da293baa8 (diff)
parentd2d3a20624d968199353ccf6ddbae6f3ac39c9af (diff)
Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)
Bug: 166295507 Merged-In: I3d92a6de21a938f6b352ec26dc23420c0fe02b27 Change-Id: Ifdb80563ef042738778ebb8a7581a97c4e3d96e2
Diffstat (limited to 'packages/Shell')
-rw-r--r--packages/Shell/AndroidManifest.xml80
-rw-r--r--packages/Shell/res/layout/dialog_bugreport_info.xml107
-rw-r--r--packages/Shell/res/values-ca/strings.xml4
-rw-r--r--packages/Shell/res/values-hu/strings.xml2
-rw-r--r--packages/Shell/res/values-in/strings.xml6
-rw-r--r--packages/Shell/res/values-kk/strings.xml2
-rw-r--r--packages/Shell/res/values-ky/strings.xml2
-rw-r--r--packages/Shell/res/values-mk/strings.xml2
-rw-r--r--packages/Shell/res/values-ne/strings.xml2
-rw-r--r--packages/Shell/res/values-or/strings.xml14
-rw-r--r--packages/Shell/res/values-pa/strings.xml2
-rw-r--r--packages/Shell/res/values-pt-rBR/strings.xml16
-rw-r--r--packages/Shell/res/values-pt/strings.xml16
-rw-r--r--packages/Shell/res/values-uk/strings.xml20
-rw-r--r--packages/Shell/res/values-zh-rTW/strings.xml12
-rw-r--r--packages/Shell/src/com/android/shell/BugreportProgressService.java1210
-rw-r--r--packages/Shell/src/com/android/shell/BugreportReceiver.java88
-rw-r--r--packages/Shell/src/com/android/shell/BugreportRequestedReceiver.java47
-rw-r--r--packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java67
-rw-r--r--packages/Shell/tests/AndroidManifest.xml1
-rw-r--r--packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java8
21 files changed, 921 insertions, 787 deletions
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index cf4f75cc45f7..319b44ce216f 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -83,6 +83,8 @@
<uses-permission android:name="android.permission.READ_INPUT_STATE" />
<uses-permission android:name="android.permission.SET_ORIENTATION" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+ <!-- TODO(b/152310230): remove once APIs are confirmed to be sufficient -->
+ <uses-permission android:name="com.android.permission.USE_INSTALLER_V2" />
<uses-permission android:name="android.permission.MOVE_PACKAGE" />
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
@@ -105,6 +107,8 @@
<uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" />
+ <!-- Permission required to test onPermissionsChangedListener -->
+ <uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
<uses-permission android:name="android.permission.SET_KEYBOARD_LAYOUT" />
<uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
<uses-permission android:name="android.permission.SET_SCREEN_COMPATIBILITY" />
@@ -138,6 +142,7 @@
<uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
<uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
<uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.ACCESS_VIBRATOR_STATE" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
<uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
<uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" />
@@ -171,6 +176,11 @@
<uses-permission android:name="android.permission.MANAGE_SENSORS" />
<uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" />
<uses-permission android:name="android.permission.MANAGE_CAMERA" />
+ <!-- Permissions needed to test system only camera devices -->
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.SYSTEM_CAMERA" />
+ <!-- Permissions needed for CTS camera test: RecordingTest.java when assuming shell id -->
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Permission needed to enable/disable Bluetooth/Wifi -->
<uses-permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED" />
<uses-permission android:name="android.permission.MANAGE_WIFI_WHEN_WIRELESS_CONSENT_REQUIRED" />
@@ -186,7 +196,10 @@
<uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
<!-- Permission needed to wipe the device for Test Harness Mode -->
<uses-permission android:name="android.permission.ENABLE_TEST_HARNESS_MODE" />
+
+ <!-- Permissions required to test CompanionDeviceManager teses in CTS -->
<uses-permission android:name="android.permission.MANAGE_COMPANION_DEVICES" />
+ <uses-permission android:name="android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS" />
<uses-permission android:name="android.permission.MANAGE_APPOPS" />
@@ -234,37 +247,79 @@
<!-- Permission required for CTS test - UiModeManagerTest -->
<uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/>
- <!-- Permission required for CTS test - CarModeInCallServiceTest -->
+ <!-- Permission required for CTS test - SystemConfigTest -->
+ <uses-permission android:name="android.permission.READ_CARRIER_APP_INFO"/>
+
+ <!-- Permission required for CTS test - CarModeInCallServiceTest -->
<uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+ <!-- Permission requried for CTS test - CellBroadcastIntentsTest -->
+ <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS"/>
+
<!-- Permission required for CTS test - TetheringManagerTest -->
<uses-permission android:name="android.permission.TETHER_PRIVILEGED"/>
+ <!-- Permission required for CTS test - CtsOsTestCases -->
+ <uses-permission android:name="android.permission.MANAGE_CRATES"/>
+
+ <!-- Allows setting brightness from the shell -->
+ <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
+
+ <!-- Permission required for CTS test - ShortcutManagerUsageTest -->
+ <uses-permission android:name="android.permission.ACCESS_SHORTCUTS"/>
+
+ <!-- Permissions required for CTS test - UsageStatsTest -->
+ <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS"/>
+ <uses-permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"/>
+
<!-- Permissions required to test ambient display. -->
<uses-permission android:name="android.permission.READ_DREAM_STATE"/>
<uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
- <!-- Permission needed to test mainline permission module rollback -->
- <uses-permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" />
-
<!-- Permission required for CTS test - CtsLightsManagerTest -->
<uses-permission android:name="android.permission.CONTROL_DEVICE_LIGHTS" />
+ <!-- Permission needed to test mainline permission module rollback -->
+ <uses-permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" />
+
<!-- Permission needed to read wifi network credentials for CtsNetTestCases -->
<uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL" />
<!-- Permission needed to use wifi usability API's for CtsNetTestCases -->
<uses-permission android:name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE" />
+ <!-- Permission needed to test registering pull atom callbacks -->
+ <uses-permission android:name="android.permission.REGISTER_STATS_PULL_ATOM" />
+
+ <!-- Permission needed to modify settings overrideable by restore in CTS tests -->
+ <uses-permission android:name="android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE" />
+
<!-- Permission required for testing system audio effect APIs. -->
<uses-permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"/>
+ <!-- Permissions required for CTS test - TunerTest -->
+ <uses-permission android:name="android.permission.ACCESS_TV_DESCRAMBLER" />
+ <uses-permission android:name="android.permission.ACCESS_TV_TUNER" />
+ <uses-permission android:name="android.permission.TUNER_RESOURCE_ACCESS" />
+
+ <!-- Permissions required for CTS test - AutoRevokeTest -->
+ <uses-permission android:name="android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS" />
+
<!-- Permissions needed to test shared libraries -->
<uses-permission android:name="android.permission.ACCESS_SHARED_LIBRARIES" />
+ <!-- Permissions required for CTS test - TVInputManagerTest -->
+ <uses-permission android:name="android.permission.TV_INPUT_HARDWARE" />
+
+ <!-- Permission needed for CTS test - PrivilegedLocationPermissionTest -->
+ <uses-permission android:name="android.permission.LOCATION_HARDWARE" />
+
<!-- Permissions required for GTS test - GtsDialerAudioTestCases -->
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
+ <!-- Permissions required for CTS test - AdbManagerTest -->
+ <uses-permission android:name="android.permission.MANAGE_DEBUGGING" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
@@ -312,24 +367,17 @@
android:exported="false" />
<receiver
- android:name=".BugreportReceiver"
- android:permission="android.permission.DUMP">
- <intent-filter>
- <action android:name="com.android.internal.intent.action.BUGREPORT_STARTED" />
- <action android:name="com.android.internal.intent.action.BUGREPORT_FINISHED" />
- </intent-filter>
- </receiver>
-
- <receiver
- android:name=".RemoteBugreportReceiver"
- android:permission="android.permission.DUMP">
+ android:name=".BugreportRequestedReceiver"
+ android:exported="true"
+ android:permission="android.permission.TRIGGER_SHELL_BUGREPORT">
<intent-filter>
- <action android:name="com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED" />
+ <action android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" />
</intent-filter>
</receiver>
<receiver
android:name=".HeapDumpReceiver"
+ android:exported="true"
android:permission="android.permission.DUMP">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
diff --git a/packages/Shell/res/layout/dialog_bugreport_info.xml b/packages/Shell/res/layout/dialog_bugreport_info.xml
index 4bd871103193..ea2427920285 100644
--- a/packages/Shell/res/layout/dialog_bugreport_info.xml
+++ b/packages/Shell/res/layout/dialog_bugreport_info.xml
@@ -14,58 +14,63 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:paddingTop="15dp"
- android:paddingStart="24dp"
- android:paddingEnd="24dp"
- android:focusableInTouchMode="false"
- android:focusable="false"
- android:importantForAutofill="noExcludeDescendants"
- android:layout_width="wrap_content"
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
android:layout_height="wrap_content">
- <TextView
+ <LinearLayout
+ android:orientation="vertical"
+ android:paddingTop="15dp"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
android:focusableInTouchMode="false"
android:focusable="false"
- android:inputType="textNoSuggestions"
+ android:importantForAutofill="noExcludeDescendants"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/bugreport_info_name"/>
- <EditText
- android:id="@+id/name"
- android:nextFocusDown="@+id/title"
- android:maxLength="30"
- android:singleLine="true"
- android:inputType="textNoSuggestions"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <TextView
- android:focusableInTouchMode="false"
- android:focusable="false"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/bugreport_info_title"/>
- <EditText
- android:id="@+id/title"
- android:nextFocusUp="@+id/name"
- android:nextFocusDown="@+id/description"
- android:maxLength="80"
- android:singleLine="true"
- android:inputType="textAutoCorrect|textCapSentences"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- <TextView
- android:focusableInTouchMode="false"
- android:focusable="false"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:editable="false"
- android:text="@string/bugreport_info_description"/>
- <EditText
- android:id="@+id/description"
- android:nextFocusUp="@+id/title"
- android:singleLine="false"
- android:inputType="textMultiLine|textAutoCorrect|textCapSentences"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
-</LinearLayout>
+ android:layout_height="wrap_content">
+ <TextView
+ android:focusableInTouchMode="false"
+ android:focusable="false"
+ android:inputType="textNoSuggestions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/bugreport_info_name"/>
+ <EditText
+ android:id="@+id/name"
+ android:nextFocusDown="@+id/title"
+ android:maxLength="30"
+ android:singleLine="true"
+ android:inputType="textNoSuggestions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:focusableInTouchMode="false"
+ android:focusable="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/bugreport_info_title"/>
+ <EditText
+ android:id="@+id/title"
+ android:nextFocusUp="@+id/name"
+ android:nextFocusDown="@+id/description"
+ android:maxLength="80"
+ android:singleLine="true"
+ android:inputType="textAutoCorrect|textCapSentences"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:focusableInTouchMode="false"
+ android:focusable="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:editable="false"
+ android:text="@string/bugreport_info_description"/>
+ <EditText
+ android:id="@+id/description"
+ android:nextFocusUp="@+id/title"
+ android:singleLine="false"
+ android:inputType="textMultiLine|textAutoCorrect|textCapSentences"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+</ScrollView>
diff --git a/packages/Shell/res/values-ca/strings.xml b/packages/Shell/res/values-ca/strings.xml
index 048c94d3275b..7fac7407bf6d 100644
--- a/packages/Shell/res/values-ca/strings.xml
+++ b/packages/Shell/res/values-ca/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Protecció"</string>
- <string name="bugreport_notification_channel" msgid="2574150205913861141">"Informes d\'error"</string>
+ <string name="bugreport_notification_channel" msgid="2574150205913861141">"Informes d\'errors"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"S\'està generant l\'informe d\'errors <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"S\'ha capturat l\'informe d\'errors <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"S\'estan afegint detalls a l\'informe d\'errors"</string>
@@ -30,7 +30,7 @@
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Toca per compartir l\'informe d\'errors sense captura de pantalla o espera que es creï la captura"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Els informes d\'errors contenen dades dels diferents fitxers de registre del sistema, inclosa informació que pot ser confidencial (com ara l\'ús d\'aplicacions i les dades d\'ubicació). Comparteix-los només amb aplicacions i persones de confiança."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"No ho tornis a mostrar"</string>
- <string name="bugreport_storage_title" msgid="5332488144740527109">"Informes d\'error"</string>
+ <string name="bugreport_storage_title" msgid="5332488144740527109">"Informes d\'errors"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"No s\'ha pogut llegir el fitxer de l\'informe d\'errors"</string>
<string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"No s\'ha pogut afegir la informació detallada de l\'informe d\'errors al fitxer ZIP"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"sense nom"</string>
diff --git a/packages/Shell/res/values-hu/strings.xml b/packages/Shell/res/values-hu/strings.xml
index 90966fcd061b..441446cf4f8a 100644
--- a/packages/Shell/res/values-hu/strings.xml
+++ b/packages/Shell/res/values-hu/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="3701846017049540910">"Héj"</string>
+ <string name="app_label" msgid="3701846017049540910">"Parancsértelmező"</string>
<string name="bugreport_notification_channel" msgid="2574150205913861141">"Hibajelentések"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Hibajelentés (<xliff:g id="ID">#%d</xliff:g>) létrehozása folyamatban"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Hibajelentés (<xliff:g id="ID">#%d</xliff:g>) rögzítve"</string>
diff --git a/packages/Shell/res/values-in/strings.xml b/packages/Shell/res/values-in/strings.xml
index cf2ebe504c0f..5c5ba816e5d7 100644
--- a/packages/Shell/res/values-in/strings.xml
+++ b/packages/Shell/res/values-in/strings.xml
@@ -24,10 +24,10 @@
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Harap tunggu..."</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Laporan bug akan segera muncul di ponsel"</string>
<string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Pilih untuk membagikan laporan bug Anda"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Tap untuk membagikan laporan bug"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Ketuk untuk membagikan laporan bug"</string>
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Pilih untuk membagikan laporan bug tanpa screenshot atau menunggu screenshot selesai"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Tap untuk membagikan laporan bug tanpa screenshot atau menunggu screenshot selesai"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Tap untuk membagikan laporan bug tanpa screenshot atau menunggu screenshot selesai"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Ketuk untuk membagikan laporan bug tanpa screenshot atau menunggu screenshot selesai"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Ketuk untuk membagikan laporan bug tanpa screenshot atau menunggu screenshot selesai"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Laporan bug berisi data dari berbagai file log sistem, yang mungkin mencakup data yang dianggap sensitif (seperti data penggunaan aplikasi dan lokasi). Hanya bagikan laporan bug dengan aplikasi dan orang yang Anda percaya."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Jangan tampilkan lagi"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Laporan bug"</string>
diff --git a/packages/Shell/res/values-kk/strings.xml b/packages/Shell/res/values-kk/strings.xml
index 6ee1cc524d24..82c02a19a863 100644
--- a/packages/Shell/res/values-kk/strings.xml
+++ b/packages/Shell/res/values-kk/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="3701846017049540910">"Қабыршық"</string>
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_notification_channel" msgid="2574150205913861141">"Қате туралы есептер"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"<xliff:g id="ID">#%d</xliff:g> қате туралы есебі жасалуда"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"<xliff:g id="ID">#%d</xliff:g> қате туралы есебі жазып алынды"</string>
diff --git a/packages/Shell/res/values-ky/strings.xml b/packages/Shell/res/values-ky/strings.xml
index 4941bab24eed..2499aba99ce6 100644
--- a/packages/Shell/res/values-ky/strings.xml
+++ b/packages/Shell/res/values-ky/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="3701846017049540910">"Командалык кабык"</string>
+ <string name="app_label" msgid="3701846017049540910">"Кабык"</string>
<string name="bugreport_notification_channel" msgid="2574150205913861141">"Мүчүлүштүк тууралуу кабар берүүлөр"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Мүчүлүштүк тууралуу билдирүү <xliff:g id="ID">#%d</xliff:g> түзүлүүдө"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Мүчүлүштүк тууралуу билдирүү <xliff:g id="ID">#%d</xliff:g> жаздырылды"</string>
diff --git a/packages/Shell/res/values-mk/strings.xml b/packages/Shell/res/values-mk/strings.xml
index 983e66406ed5..3d18d30715c2 100644
--- a/packages/Shell/res/values-mk/strings.xml
+++ b/packages/Shell/res/values-mk/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="3701846017049540910">"Обвивка"</string>
+ <string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_notification_channel" msgid="2574150205913861141">"Извештаи за грешки"</string>
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Се генерира извештајот за грешки <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Извештајот за грешки <xliff:g id="ID">#%d</xliff:g> е снимен"</string>
diff --git a/packages/Shell/res/values-ne/strings.xml b/packages/Shell/res/values-ne/strings.xml
index ae0a92fc27e9..05ff41274cd5 100644
--- a/packages/Shell/res/values-ne/strings.xml
+++ b/packages/Shell/res/values-ne/strings.xml
@@ -28,7 +28,7 @@
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"तपाईंको बग रिपोर्ट स्क्रिनसट बिना आदान प्रदान गर्नाका लागि चयन गर्नुहोस् वा स्क्रिनसट लिने प्रक्रिया पूरा हुने प्रतीक्षा गर्नुहोस्"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"तपाईँको बग रिपोर्टलाई स्क्रिनसट बिना साझेदारी गर्नाका लागि ट्याप गर्नुहोस् वा स्क्रिनसट लिने प्रक्रिया पूरा हुन प्रतीक्षा गर्नुहोस्"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"तपाईँको बग रिपोर्टलाई स्क्रिनसट बिना साझेदारी गर्नाका लागि ट्याप गर्नुहोस् वा स्क्रिनसट लिने प्रक्रिया पूरा हुन प्रतीक्षा गर्नुहोस्"</string>
- <string name="bugreport_confirm" msgid="5917407234515812495">"बग रिपोर्टहरूमा प्रणालीका विभिन्न लग फाइलहरूको डेटा हुन्छ जसमा तपाईँले संवेदनशील मानेको डेटा समावेश हुन सक्छ (जस्तै अनुप्रयोगको प्रयोग र स्थान सम्बन्धी डेटा)। तपाईँले विश्वास गर्ने व्यक्ति र अनुप्रयोगहरूसँग मात्र बग रिपोर्टहरूलाई साझेदारी गर्नुहोस्।"</string>
+ <string name="bugreport_confirm" msgid="5917407234515812495">"बग रिपोर्टहरूमा प्रणालीका विभिन्न लग फाइलहरूको डेटा हुन्छ जसमा तपाईँले संवेदनशील मानेको डेटा समावेश हुन सक्छ (जस्तै अनुप्रयोगको प्रयोग र स्थानसम्बन्धी डेटा)। तपाईँले विश्वास गर्ने व्यक्ति र अनुप्रयोगहरूसँग मात्र बग रिपोर्टहरूलाई साझेदारी गर्नुहोस्।"</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"फेरि नदेखाउनुहोस्"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"बग रिपोर्टहरू"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रिपोर्ट फाइल पढ्न सकिएन"</string>
diff --git a/packages/Shell/res/values-or/strings.xml b/packages/Shell/res/values-or/strings.xml
index 438b71568475..bd2b6cd6a740 100644
--- a/packages/Shell/res/values-or/strings.xml
+++ b/packages/Shell/res/values-or/strings.xml
@@ -23,12 +23,12 @@
<string name="bugreport_updating_title" msgid="4423539949559634214">"ବଗ୍‍ ରିପୋର୍ଟରେ ବିବରଣୀ ଯୋଡ଼ାଯାଉଛି"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"ଦୟାକରି ଅପେକ୍ଷା କରନ୍ତୁ…"</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"ଅଳ୍ପ ସମୟ ମଧ୍ୟରେ ଫୋନ୍‌ରେ ବଗ୍‍ ରିପୋର୍ଟ ଦେଖାଯିବ"</string>
- <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"ଆପଣଙ୍କ ବଗ୍‍ ରିପୋର୍ଟ ଶେୟାର୍‍ କରିବାକୁ ଚୟନ କରନ୍ତୁ"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"ଆପଣଙ୍କ ବଗ୍‍ ରିପୋର୍ଟ ଶେୟାର୍‍ କରିବା ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"ଗୋଟିଏ ସ୍କ୍ରୀନଶଟ୍‍ ସହିତ ଆପଣଙ୍କ ବଗ୍‍ ରିପୋର୍ଟ ଶେୟାର୍‍ କରିବାକୁ ଚୟନ କରନ୍ତୁ କିମ୍ବା ସ୍କ୍ରୀନଶଟ୍‍ ସମାପ୍ତ ହେବାକୁ ଅପେକ୍ଷା କରନ୍ତୁ"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"ସ୍କ୍ରୀନଶଟ୍‍ ବିନା ଆପଣଙ୍କ ବଗ୍‍ ରିପୋର୍ଟ ଶେୟାର୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ କିମ୍ବା ସ୍କ୍ରୀନଶଟ୍‍ ସମାପ୍ତ ହେବା ପାଇଁ ଅପେକ୍ଷା କରନ୍ତୁ"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"ସ୍କ୍ରୀନଶଟ୍‍ ବିନା ଆପଣଙ୍କ ବଗ୍‍ ରିପୋର୍ଟ ଶେୟାର୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ କିମ୍ବା ସ୍କ୍ରୀନଶଟ୍‍ ସମାପ୍ତ ହେବା ପାଇଁ ଅପେକ୍ଷା କରନ୍ତୁ"</string>
- <string name="bugreport_confirm" msgid="5917407234515812495">"ବଗ୍‍ ରିପୋର୍ଟରେ ସିଷ୍ଟମର ବିଭିନ୍ନ ଲଗ୍‍ ଫାଇଲ୍‌ରୁ ଆସିଥିବା ଡାଟା ରହିଛି, ଯେଉଁଥିରେ ଆପଣଙ୍କ ସମ୍ବେଦନଶୀଳ ଡାଟା (ଯେପରିକି ଆପ୍‌-ବ୍ୟବହାର ଓ ଲୋକେଶନ୍‍ ଡାଟା) ରହିଥାଇପାରେ। ଆପଣ ବିଶ୍ୱାସ କରୁଥିବା ଲୋକ ତଥା ଆପ୍‍ ସହ ଏପରି ବଗ୍‍ ରିପୋର୍ଟ ଶେୟାର୍‍ କରନ୍ତୁ।"</string>
+ <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"ଆପଣଙ୍କ ବଗ୍‍ ରିପୋର୍ଟ ସେୟାର୍‍ କରିବାକୁ ଚୟନ କରନ୍ତୁ"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"ଆପଣଙ୍କ ବଗ୍‍ ରିପୋର୍ଟ ସେୟାର୍‍ କରିବା ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"ଗୋଟିଏ ସ୍କ୍ରୀନଶଟ୍‍ ସହିତ ଆପଣଙ୍କ ବଗ୍‍ ରିପୋର୍ଟ ସେୟାର୍‍ କରିବାକୁ ଚୟନ କରନ୍ତୁ କିମ୍ବା ସ୍କ୍ରୀନଶଟ୍‍ ସମାପ୍ତ ହେବାକୁ ଅପେକ୍ଷା କରନ୍ତୁ"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"ସ୍କ୍ରୀନଶଟ୍‍ ବିନା ଆପଣଙ୍କ ବଗ୍‍ ରିପୋର୍ଟ ସେୟାର୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ କିମ୍ବା ସ୍କ୍ରୀନଶଟ୍‍ ସମାପ୍ତ ହେବା ପାଇଁ ଅପେକ୍ଷା କରନ୍ତୁ"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"ସ୍କ୍ରୀନଶଟ୍‍ ବିନା ଆପଣଙ୍କ ବଗ୍‍ ରିପୋର୍ଟ ସେୟାର୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ କିମ୍ବା ସ୍କ୍ରୀନଶଟ୍‍ ସମାପ୍ତ ହେବା ପାଇଁ ଅପେକ୍ଷା କରନ୍ତୁ"</string>
+ <string name="bugreport_confirm" msgid="5917407234515812495">"ବଗ୍‍ ରିପୋର୍ଟରେ ସିଷ୍ଟମର ବିଭିନ୍ନ ଲଗ୍‍ ଫାଇଲ୍‌ରୁ ଆସିଥିବା ଡାଟା ରହିଛି, ଯେଉଁଥିରେ ଆପଣଙ୍କ ସମ୍ବେଦନଶୀଳ ଡାଟା (ଯେପରିକି ଆପ୍‌-ବ୍ୟବହାର ଓ ଲୋକେସନ୍‍ ଡାଟା) ରହିଥାଇପାରେ। ଆପଣ ବିଶ୍ୱାସ କରୁଥିବା ଲୋକ ତଥା ଆପ୍‍ ସହ ଏପରି ବଗ୍‍ ରିପୋର୍ଟ ସେୟାର୍‍ କରନ୍ତୁ।"</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"ପୁଣି ଦେଖାନ୍ତୁ ନାହିଁ"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"ବଗ୍ ରିପୋର୍ଟ"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"ବଗ୍‍ ରିପୋର୍ଟ ଫାଇଲ୍‍ ପଢ଼ାଯାଇପାରିଲା ନାହିଁ"</string>
@@ -43,5 +43,5 @@
<string name="bugreport_info_title" msgid="2306030793918239804">"ବଗ୍‍ର ନାମ"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"ବଗ୍‍ର ସଂକ୍ଷିପ୍ତ ବିବରଣୀ"</string>
<string name="save" msgid="4781509040564835759">"ସେଭ୍‌ କରନ୍ତୁ"</string>
- <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"ବଗ୍‍ ରିପୋର୍ଟ ଶେୟାର୍‍ କରନ୍ତୁ"</string>
+ <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"ବଗ୍‍ ରିପୋର୍ଟ ସେୟାର୍‍ କରନ୍ତୁ"</string>
</resources>
diff --git a/packages/Shell/res/values-pa/strings.xml b/packages/Shell/res/values-pa/strings.xml
index cdf37695fa21..88948143fa35 100644
--- a/packages/Shell/res/values-pa/strings.xml
+++ b/packages/Shell/res/values-pa/strings.xml
@@ -35,7 +35,7 @@
<string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"ਬੱਗ ਰਿਪੋਰਟ ਵੇਰਵਿਆਂ ਨੂੰ ਜ਼ਿਪ ਫ਼ਾਈਲ ਵਿੱਚ ਸ਼ਾਮਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"ਬਿਨਾਂ-ਨਾਮ"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"ਵੇਰਵੇ"</string>
- <string name="bugreport_screenshot_action" msgid="8677781721940614995">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
+ <string name="bugreport_screenshot_action" msgid="8677781721940614995">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਲਵੋ"</string>
<string name="bugreport_screenshot_taken" msgid="5684211273096253120">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸਫਲਤਾਪੂਰਵਕ ਲਿਆ ਗਿਆ।"</string>
<string name="bugreport_screenshot_failed" msgid="5853049140806834601">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਨਹੀਂ ਲਿਆ ਜਾ ਸਕਿਆ।"</string>
<string name="bugreport_info_dialog_title" msgid="1355948594292983332">"ਬੱਗ ਰਿਪੋਰਟ <xliff:g id="ID">#%d</xliff:g> ਵੇਰਵੇ"</string>
diff --git a/packages/Shell/res/values-pt-rBR/strings.xml b/packages/Shell/res/values-pt-rBR/strings.xml
index 71483c84fc69..394f852d3a33 100644
--- a/packages/Shell/res/values-pt-rBR/strings.xml
+++ b/packages/Shell/res/values-pt-rBR/strings.xml
@@ -18,21 +18,21 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_notification_channel" msgid="2574150205913861141">"Relatórios de bugs"</string>
- <string name="bugreport_in_progress_title" msgid="4311705936714972757">"O relatório do bug <xliff:g id="ID">#%d</xliff:g> está sendo gerado"</string>
- <string name="bugreport_finished_title" msgid="4429132808670114081">"Relatório do bug <xliff:g id="ID">#%d</xliff:g> capturado"</string>
- <string name="bugreport_updating_title" msgid="4423539949559634214">"Adicionando detalhes ao relatório do bug"</string>
+ <string name="bugreport_in_progress_title" msgid="4311705936714972757">"O relatório de bug <xliff:g id="ID">#%d</xliff:g> está sendo gerado"</string>
+ <string name="bugreport_finished_title" msgid="4429132808670114081">"Relatório de bug <xliff:g id="ID">#%d</xliff:g> capturado"</string>
+ <string name="bugreport_updating_title" msgid="4423539949559634214">"Adicionando detalhes ao relatório de bug"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Aguarde…"</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"O relatório de bugs será exibido no smartphone em breve"</string>
- <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Selecione para compartilhar o relatório do bug"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toque para compartilhar seu relatório do bug"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Selecione para compartilhar seu relatório do bug sem uma captura de tela ou aguarde a conclusão"</string>
+ <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Selecione para compartilhar o relatório de bug"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toque para compartilhar seu relatório de bug"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Selecione para compartilhar seu relatório de bug sem uma captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Toque para compartilhar seu relatório de bug sem captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Toque para compartilhar seu relatório de bug sem captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Os relatórios de bugs contêm dados dos diversos arquivos de registros do sistema, que podem incluir dados que você considera confidenciais (como dados de uso de apps e de local). Compartilhe relatórios de bugs somente com pessoas e apps nos quais você confia."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Não mostrar novamente"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de bugs"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"Não foi possível ler o arquivo de relatório de bug"</string>
- <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Não foi possível adicionar detalhes do relatório do bug ao arquivo ZIP"</string>
+ <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Não foi possível adicionar detalhes do relatório de bug ao arquivo ZIP"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"sem nome"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"Detalhes"</string>
<string name="bugreport_screenshot_action" msgid="8677781721940614995">"Capturas de tela"</string>
@@ -43,5 +43,5 @@
<string name="bugreport_info_title" msgid="2306030793918239804">"Título do bug"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"Resumo do bug"</string>
<string name="save" msgid="4781509040564835759">"Salvar"</string>
- <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Compartilhar relatório do bug"</string>
+ <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Compartilhar relatório de bug"</string>
</resources>
diff --git a/packages/Shell/res/values-pt/strings.xml b/packages/Shell/res/values-pt/strings.xml
index 71483c84fc69..394f852d3a33 100644
--- a/packages/Shell/res/values-pt/strings.xml
+++ b/packages/Shell/res/values-pt/strings.xml
@@ -18,21 +18,21 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_notification_channel" msgid="2574150205913861141">"Relatórios de bugs"</string>
- <string name="bugreport_in_progress_title" msgid="4311705936714972757">"O relatório do bug <xliff:g id="ID">#%d</xliff:g> está sendo gerado"</string>
- <string name="bugreport_finished_title" msgid="4429132808670114081">"Relatório do bug <xliff:g id="ID">#%d</xliff:g> capturado"</string>
- <string name="bugreport_updating_title" msgid="4423539949559634214">"Adicionando detalhes ao relatório do bug"</string>
+ <string name="bugreport_in_progress_title" msgid="4311705936714972757">"O relatório de bug <xliff:g id="ID">#%d</xliff:g> está sendo gerado"</string>
+ <string name="bugreport_finished_title" msgid="4429132808670114081">"Relatório de bug <xliff:g id="ID">#%d</xliff:g> capturado"</string>
+ <string name="bugreport_updating_title" msgid="4423539949559634214">"Adicionando detalhes ao relatório de bug"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Aguarde…"</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"O relatório de bugs será exibido no smartphone em breve"</string>
- <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Selecione para compartilhar o relatório do bug"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toque para compartilhar seu relatório do bug"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Selecione para compartilhar seu relatório do bug sem uma captura de tela ou aguarde a conclusão"</string>
+ <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Selecione para compartilhar o relatório de bug"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Toque para compartilhar seu relatório de bug"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Selecione para compartilhar seu relatório de bug sem uma captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Toque para compartilhar seu relatório de bug sem captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Toque para compartilhar seu relatório de bug sem captura de tela ou aguarde a conclusão"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Os relatórios de bugs contêm dados dos diversos arquivos de registros do sistema, que podem incluir dados que você considera confidenciais (como dados de uso de apps e de local). Compartilhe relatórios de bugs somente com pessoas e apps nos quais você confia."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Não mostrar novamente"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de bugs"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"Não foi possível ler o arquivo de relatório de bug"</string>
- <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Não foi possível adicionar detalhes do relatório do bug ao arquivo ZIP"</string>
+ <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Não foi possível adicionar detalhes do relatório de bug ao arquivo ZIP"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"sem nome"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"Detalhes"</string>
<string name="bugreport_screenshot_action" msgid="8677781721940614995">"Capturas de tela"</string>
@@ -43,5 +43,5 @@
<string name="bugreport_info_title" msgid="2306030793918239804">"Título do bug"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"Resumo do bug"</string>
<string name="save" msgid="4781509040564835759">"Salvar"</string>
- <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Compartilhar relatório do bug"</string>
+ <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Compartilhar relatório de bug"</string>
</resources>
diff --git a/packages/Shell/res/values-uk/strings.xml b/packages/Shell/res/values-uk/strings.xml
index 28f3aecca17b..f62d117a248b 100644
--- a/packages/Shell/res/values-uk/strings.xml
+++ b/packages/Shell/res/values-uk/strings.xml
@@ -18,27 +18,27 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Оболонка"</string>
<string name="bugreport_notification_channel" msgid="2574150205913861141">"Звіти про помилки"</string>
- <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Генерується повідомлення про помилку <xliff:g id="ID">#%d</xliff:g>"</string>
- <string name="bugreport_finished_title" msgid="4429132808670114081">"Повідомлення про помилку <xliff:g id="ID">#%d</xliff:g> створено"</string>
- <string name="bugreport_updating_title" msgid="4423539949559634214">"Додаються деталі до повідомлення про помилку"</string>
+ <string name="bugreport_in_progress_title" msgid="4311705936714972757">"Створюється звіт про помилку <xliff:g id="ID">#%d</xliff:g>"</string>
+ <string name="bugreport_finished_title" msgid="4429132808670114081">"Звіт про помилку <xliff:g id="ID">#%d</xliff:g> створено"</string>
+ <string name="bugreport_updating_title" msgid="4423539949559634214">"У звіт про помилку додаються деталі"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"Зачекайте…"</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Звіт про помилку невдовзі з’явиться на телефоні"</string>
- <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Виберіть, щоб надіслати повідомлення про помилку"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Торкніться, щоб надіслати повідомлення про помилку"</string>
+ <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Виберіть, щоб надіслати звіт про помилку"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Торкніться, щоб надіслати звіт про помилку"</string>
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Виберіть, щоб надіслати повідомлення про помилку без знімка екрана або зачекайте на знімок"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Торкніться, щоб надіслати повідомлення про помилку без знімка екрана або зачекайте на знімок"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Торкніться, щоб надіслати повідомлення про помилку без знімка екрана або зачекайте на знімок"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Торкніться, щоб надіслати звіт про помилку без знімка екрана, або зачекайте, доки буде збережено знімок"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Торкніться, щоб надіслати звіт про помилку без знімка екрана, або зачекайте, доки буде збережено знімок"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"Звіти про помилки містять дані з різних файлів журналів системи, зокрема відомості, які ви вважаєте конфіденційними (як-от інформація про місцезнаходження та використання додатка). Діліться звітами про помилки лише з людьми та в додатках, яким довіряєте."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Більше не показувати"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Звіти про помилки"</string>
- <string name="bugreport_unreadable_text" msgid="586517851044535486">"Не вдалося прочитати звіт про помилки"</string>
- <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Не вдалося додати деталі повідомлення про помилку у файл .zip"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"Не вдалося прочитати звіт про помилку"</string>
+ <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Не вдалося додати деталі звіту про помилку у файл .zip"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"без назви"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"Деталі"</string>
<string name="bugreport_screenshot_action" msgid="8677781721940614995">"Знімок екрана"</string>
<string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Знімок екрана зроблено."</string>
<string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Не вдалося зробити знімок екрана."</string>
- <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Деталі повідомлення про помилку <xliff:g id="ID">#%d</xliff:g>"</string>
+ <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Деталі звіту про помилку <xliff:g id="ID">#%d</xliff:g>"</string>
<string name="bugreport_info_name" msgid="4414036021935139527">"Назва файлу"</string>
<string name="bugreport_info_title" msgid="2306030793918239804">"Назва помилки"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"Опис помилки"</string>
diff --git a/packages/Shell/res/values-zh-rTW/strings.xml b/packages/Shell/res/values-zh-rTW/strings.xml
index fc6397dc2013..96671c8c0175 100644
--- a/packages/Shell/res/values-zh-rTW/strings.xml
+++ b/packages/Shell/res/values-zh-rTW/strings.xml
@@ -25,9 +25,9 @@
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"系統即將在手機上顯示錯誤報告"</string>
<string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"選取即可分享錯誤報告"</string>
<string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"輕觸即可分享錯誤報告"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"選取即可分享不包含螢幕擷取畫面的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"輕觸即可分享無螢幕擷圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"輕觸即可分享無螢幕擷圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"選取即可分享不包含螢幕截圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"輕觸即可分享無螢幕截圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"輕觸即可分享無螢幕截圖的錯誤報告;你也可以等候螢幕畫面擷取完畢"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"錯誤報告的資料來自系統的各種記錄檔,當中可能包含敏感資料 (例如應用程式使用情形和位置資料)。請務必只與你信任的使用者和應用程式分享錯誤報告。"</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"不要再顯示"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"錯誤報告"</string>
@@ -35,9 +35,9 @@
<string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"無法在 ZIP 檔案中加入錯誤報告"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"未命名"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"詳細資料"</string>
- <string name="bugreport_screenshot_action" msgid="8677781721940614995">"螢幕擷取畫面"</string>
- <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"已成功拍攝螢幕擷取畫面。"</string>
- <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"無法拍攝螢幕擷取畫面。"</string>
+ <string name="bugreport_screenshot_action" msgid="8677781721940614995">"螢幕截圖"</string>
+ <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"已成功拍攝螢幕截圖。"</string>
+ <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"無法拍攝螢幕截圖。"</string>
<string name="bugreport_info_dialog_title" msgid="1355948594292983332">"錯誤報告 <xliff:g id="ID">#%d</xliff:g> 的詳細資料"</string>
<string name="bugreport_info_name" msgid="4414036021935139527">"檔案名稱"</string>
<string name="bugreport_info_title" msgid="2306030793918239804">"錯誤標題"</string>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 29c61ee70f7d..f6c2ee264cb5 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -24,40 +24,12 @@ import static com.android.shell.BugreportPrefs.STATE_HIDE;
import static com.android.shell.BugreportPrefs.STATE_UNKNOWN;
import static com.android.shell.BugreportPrefs.getWarningState;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
-import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipOutputStream;
-
-import libcore.io.Streams;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.ChooserActivity;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.FastPrintWriter;
-
-import com.google.android.collect.Lists;
-
import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.app.ActivityThread;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.Notification.Action;
@@ -65,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;
@@ -74,25 +47,25 @@ import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.BugreportManager;
+import android.os.BugreportManager.BugreportCallback;
+import android.os.BugreportManager.BugreportCallback.BugreportErrorCode;
+import android.os.BugreportParams;
import android.os.Bundle;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.IDumpstate;
-import android.os.IDumpstateListener;
-import android.os.IDumpstateToken;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
-import androidx.core.content.FileProvider;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
@@ -103,51 +76,75 @@ import android.view.ContextThemeWrapper;
import android.view.IWindowManager;
import android.view.View;
import android.view.WindowManager;
-import android.view.View.OnFocusChangeListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
+import androidx.core.content.FileProvider;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ChooserActivity;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import com.google.android.collect.Lists;
+
+import libcore.io.Streams;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+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;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
/**
- * Service used to keep progress of bugreport processes ({@code dumpstate}).
+ * Service used to trigger system bugreports.
* <p>
- * The workflow is:
- * <ol>
- * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with a sequential id,
- * its pid, and the estimated total effort.
- * <li>{@link BugreportReceiver} receives the intent and delegates it to this service.
- * <li>Upon start, this service:
+ * The workflow uses Bugreport API({@code BugreportManager}) and is as follows:
* <ol>
- * <li>Issues a system notification so user can watch the progresss (which is 0% initially).
- * <li>Polls the {@link SystemProperties} for updates on the {@code dumpstate} progress.
- * <li>If the progress changed, it updates the system notification.
- * </ol>
- * <li>As {@code dumpstate} progresses, it updates the system property.
- * <li>When {@code dumpstate} finishes, it sends a {@code BUGREPORT_FINISHED} intent.
- * <li>{@link BugreportReceiver} receives the intent and delegates it to this service, which in
- * turn:
- * <ol>
- * <li>Updates the system notification so user can share the bugreport.
- * <li>Stops monitoring that {@code dumpstate} process.
- * <li>Stops itself if it doesn't have any process left to monitor.
- * </ol>
+ * <li>System apps like Settings or SysUI broadcasts {@code BUGREPORT_REQUESTED}.
+ * <li>{@link BugreportRequestedReceiver} receives the intent and delegates it to this service.
+ * <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";
private static final boolean DEBUG = false;
+ private Intent startSelfIntent;
+
private static final String AUTHORITY = "com.android.shell";
- // External intents sent by dumpstate.
- static final String INTENT_BUGREPORT_STARTED =
- "com.android.internal.intent.action.BUGREPORT_STARTED";
+ // External intent used to trigger bugreport API.
+ static final String INTENT_BUGREPORT_REQUESTED =
+ "com.android.internal.intent.action.BUGREPORT_REQUESTED";
+
+ // Intent sent to notify external apps that bugreport finished
static final String INTENT_BUGREPORT_FINISHED =
"com.android.internal.intent.action.BUGREPORT_FINISHED";
- static final String INTENT_REMOTE_BUGREPORT_FINISHED =
- "com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED";
// Internal intents used on notification actions.
static final String INTENT_BUGREPORT_CANCEL = "android.intent.action.BUGREPORT_CANCEL";
@@ -158,10 +155,9 @@ public class BugreportProgressService extends Service {
"android.intent.action.BUGREPORT_SCREENSHOT";
static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
+ static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE";
static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
static final String EXTRA_ID = "android.intent.extra.ID";
- static final String EXTRA_PID = "android.intent.extra.PID";
- static final String EXTRA_MAX = "android.intent.extra.MAX";
static final String EXTRA_NAME = "android.intent.extra.NAME";
static final String EXTRA_TITLE = "android.intent.extra.TITLE";
static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION";
@@ -178,7 +174,6 @@ public class BugreportProgressService extends Service {
// Maximum progress displayed in %.
private static final int CAPPED_PROGRESS = 99;
- private static final int CAPPED_MAX = 100;
/** Show the progress log every this percent. */
private static final int LOG_PROGRESS_STEP = 10;
@@ -190,14 +185,9 @@ public class BugreportProgressService extends Service {
*/
static final int SCREENSHOT_DELAY_SECONDS = 3;
- // TODO: will be gone once fully migrated to Binder
- /** System properties used to communicate with dumpstate progress. */
- private static final String DUMPSTATE_PREFIX = "dumpstate.";
- private static final String NAME_SUFFIX = ".name";
+ /** System property where dumpstate stores last triggered bugreport id */
+ private static final String PROPERTY_LAST_ID = "dumpstate.last_id";
- /** System property (and value) used to stop dumpstate. */
- // TODO: should call ActiveManager API instead
- private static final String CTL_STOP = "ctl.stop";
private static final String BUGREPORT_SERVICE = "bugreport";
/**
@@ -205,14 +195,33 @@ public class BugreportProgressService extends Service {
* <p>
* Must be a path supported by its FileProvider.
*/
- private static final String SCREENSHOT_DIR = "bugreports";
+ private static final String BUGREPORT_DIR = "bugreports";
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 dumpstate processes (keyed by id) */
- private final SparseArray<DumpstateListener> mProcesses = new SparseArray<>();
+ /** Managed bugreport info (keyed by id) */
+ @GuardedBy("mLock")
+ private final SparseArray<BugreportInfo> mBugreportInfos = new SparseArray<>();
private Context mContext;
@@ -222,7 +231,9 @@ public class BugreportProgressService extends Service {
private final BugreportInfoDialog mInfoDialog = new BugreportInfoDialog();
- private File mScreenshotsDir;
+ private File mBugreportsDir;
+
+ private BugreportManager mBugreportManager;
/**
* id of the notification used to set service on foreground.
@@ -243,20 +254,20 @@ public class BugreportProgressService extends Service {
private boolean mIsWatch;
private boolean mIsTv;
- private int mLastProgressPercent;
-
@Override
public void onCreate() {
mContext = getApplicationContext();
mMainThreadHandler = new Handler(Looper.getMainLooper());
mServiceHandler = new ServiceHandler("BugreportProgressServiceMainThread");
mScreenshotHandler = new ScreenshotHandler("BugreportProgressServiceScreenshotThread");
-
- mScreenshotsDir = new File(getFilesDir(), SCREENSHOT_DIR);
- if (!mScreenshotsDir.exists()) {
- Log.i(TAG, "Creating directory " + mScreenshotsDir + " to store temporary screenshots");
- if (!mScreenshotsDir.mkdir()) {
- Log.w(TAG, "Could not create directory " + mScreenshotsDir);
+ startSelfIntent = new Intent(this, this.getClass());
+
+ mBugreportsDir = new File(getFilesDir(), BUGREPORT_DIR);
+ if (!mBugreportsDir.exists()) {
+ Log.i(TAG, "Creating directory " + mBugreportsDir
+ + " to store bugreports and screenshots");
+ if (!mBugreportsDir.mkdir()) {
+ Log.w(TAG, "Could not create directory " + mBugreportsDir);
}
}
final Configuration conf = mContext.getResources().getConfiguration();
@@ -277,6 +288,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;
@@ -303,21 +317,171 @@ public class BugreportProgressService extends Service {
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- final int size = mProcesses.size();
- if (size == 0) {
- writer.println("No monitored processes");
+ 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.getName(), suffix);
+ }
+
+ private final class BugreportCallbackImpl extends BugreportCallback {
+
+ @GuardedBy("mLock")
+ private final BugreportInfo mInfo;
+
+ BugreportCallbackImpl(BugreportInfo info) {
+ mInfo = info;
+ }
+
+ @Override
+ public void onProgress(float progress) {
+ synchronized (mLock) {
+ checkProgressUpdatedLocked(mInfo, (int) progress);
+ }
+ }
+
+ /**
+ * Logs errors and stops the service on which this bugreport was running.
+ * Also stops progress notification (if any).
+ */
+ @Override
+ public void onError(@BugreportErrorCode int errorCode) {
+ synchronized (mLock) {
+ stopProgressLocked(mInfo.id);
+ mInfo.deleteEmptyFiles();
+ }
+ Log.e(TAG, "Bugreport API callback onError() errorCode = " + errorCode);
+ return;
+ }
+
+ @Override
+ public void onFinished() {
+ mInfo.renameBugreportFile();
+ mInfo.renameScreenshots();
+ synchronized (mLock) {
+ sendBugreportFinishedBroadcastLocked();
+ }
+ }
+
+ /**
+ * Reads bugreport id and links it to the bugreport info to track a bugreport that is in
+ * process. id is incremented in the dumpstate code.
+ * We do not track a bugreport if there is already a bugreport with the same id being
+ * tracked.
+ */
+ @GuardedBy("mLock")
+ private void trackInfoWithIdLocked() {
+ final int id = SystemProperties.getInt(PROPERTY_LAST_ID, 1);
+ if (mBugreportInfos.get(id) == null) {
+ mInfo.id = id;
+ mBugreportInfos.put(mInfo.id, mInfo);
+ }
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(mProcesses.valueAt(i).info);
+
+ @GuardedBy("mLock")
+ private void sendBugreportFinishedBroadcastLocked() {
+ final String bugreportFilePath = mInfo.bugreportFile.getAbsolutePath();
+ if (mInfo.bugreportFile.length() == 0) {
+ Log.e(TAG, "Bugreport file empty. File path = " + bugreportFilePath);
+ return;
+ }
+ if (mInfo.type == BugreportParams.BUGREPORT_MODE_REMOTE) {
+ sendRemoteBugreportFinishedBroadcast(mContext, bugreportFilePath,
+ mInfo.bugreportFile);
+ } else {
+ cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE, mBugreportsDir);
+ final Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
+ intent.putExtra(EXTRA_BUGREPORT, bugreportFilePath);
+ intent.putExtra(EXTRA_SCREENSHOT, getScreenshotForIntent(mInfo));
+ mContext.sendBroadcast(intent, android.Manifest.permission.DUMP);
+ onBugreportFinished(mInfo);
+ }
}
}
+ private static void sendRemoteBugreportFinishedBroadcast(Context context,
+ String bugreportFileName, File bugreportFile) {
+ cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE,
+ bugreportFile.getParentFile());
+ final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
+ final Uri bugreportUri = getUri(context, bugreportFile);
+ final String bugreportHash = generateFileHash(bugreportFileName);
+ if (bugreportHash == null) {
+ Log.e(TAG, "Error generating file hash for remote bugreport");
+ }
+ intent.setDataAndType(bugreportUri, BUGREPORT_MIMETYPE);
+ intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
+ intent.putExtra(EXTRA_BUGREPORT, bugreportFileName);
+ context.sendBroadcastAsUser(intent, UserHandle.SYSTEM,
+ android.Manifest.permission.DUMP);
+ }
+
+ /**
+ * Checks if screenshot array is non-empty and returns the first screenshot's path. The first
+ * screenshot is the default screenshot for the bugreport types that take it.
+ */
+ private static String getScreenshotForIntent(BugreportInfo info) {
+ if (!info.screenshotFiles.isEmpty()) {
+ final File screenshotFile = info.screenshotFiles.get(0);
+ final String screenshotFilePath = screenshotFile.getAbsolutePath();
+ return screenshotFilePath;
+ }
+ return null;
+ }
+
+ private static 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, File bugreportsDir) {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ FileUtils.deleteOlderFiles(bugreportsDir, minCount, minAge);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "RuntimeException deleting old files", e);
+ }
+ return null;
+ }
+ }.execute();
+ }
+
/**
* Main thread used to handle all requests but taking screenshots.
*/
@@ -354,34 +518,21 @@ public class BugreportProgressService extends Service {
Log.v(TAG, "handleMessage(): " + dumpIntent((Intent) parcel));
final Intent intent;
if (parcel instanceof Intent) {
- // The real intent was passed to BugreportReceiver, which delegated to the service.
+ // The real intent was passed to BugreportRequestedReceiver,
+ // which delegated to the service.
intent = (Intent) parcel;
} else {
intent = (Intent) msg.obj;
}
final String action = intent.getAction();
- final int pid = intent.getIntExtra(EXTRA_PID, 0);
final int id = intent.getIntExtra(EXTRA_ID, 0);
- final int max = intent.getIntExtra(EXTRA_MAX, -1);
final String name = intent.getStringExtra(EXTRA_NAME);
if (DEBUG)
- Log.v(TAG, "action: " + action + ", name: " + name + ", id: " + id + ", pid: "
- + pid + ", max: " + max);
+ Log.v(TAG, "action: " + action + ", name: " + name + ", id: " + id);
switch (action) {
- case INTENT_BUGREPORT_STARTED:
- if (!startProgress(name, id, pid, max)) {
- stopSelfWhenDone();
- return;
- }
- break;
- case INTENT_BUGREPORT_FINISHED:
- if (id == 0) {
- // Shouldn't happen, unless BUGREPORT_FINISHED is received from a legacy,
- // out-of-sync dumpstate process.
- Log.w(TAG, "Missing " + EXTRA_ID + " on intent " + intent);
- }
- onBugreportFinished(id, intent);
+ case INTENT_BUGREPORT_REQUESTED:
+ startBugreportAPI(intent);
break;
case INTENT_BUGREPORT_INFO_LAUNCH:
launchBugreportInfoDialog(id);
@@ -421,52 +572,113 @@ public class BugreportProgressService extends Service {
}
}
- private BugreportInfo getInfo(int id) {
- final DumpstateListener listener = mProcesses.get(id);
- if (listener == null) {
- Log.w(TAG, "Not monitoring process with ID " + 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);
return null;
}
- return listener.info;
+ return bugreportInfo;
}
- /**
- * Creates the {@link BugreportInfo} for a process and issue a system notification to
- * indicate its progress.
- *
- * @return whether it succeeded or not.
- */
- private boolean startProgress(String name, int id, int pid, int max) {
- if (name == null) {
- Log.w(TAG, "Missing " + EXTRA_NAME + " on start intent");
+ private String getBugreportBaseName(@BugreportParams.BugreportMode int type) {
+ String buildId = SystemProperties.get("ro.build.id", "UNKNOWN_BUILD");
+ String deviceName = SystemProperties.get("ro.product.name", "UNKNOWN_DEVICE");
+ String typeSuffix = null;
+ if (type == BugreportParams.BUGREPORT_MODE_WIFI) {
+ typeSuffix = "wifi";
+ } else if (type == BugreportParams.BUGREPORT_MODE_TELEPHONY) {
+ typeSuffix = "telephony";
+ } else {
+ return String.format("bugreport-%s-%s", deviceName, buildId);
}
- if (id == -1) {
- Log.e(TAG, "Missing " + EXTRA_ID + " on start intent");
- return false;
+ return String.format("bugreport-%s-%s-%s", deviceName, buildId, typeSuffix);
+ }
+
+ private void startBugreportAPI(Intent intent) {
+ String shareTitle = intent.getStringExtra(EXTRA_TITLE);
+ String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
+ int bugreportType = intent.getIntExtra(EXTRA_BUGREPORT_TYPE,
+ BugreportParams.BUGREPORT_MODE_INTERACTIVE);
+ String baseName = getBugreportBaseName(bugreportType);
+ String name = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
+
+ BugreportInfo info = new BugreportInfo(mContext, baseName, name,
+ shareTitle, shareDescription, bugreportType, mBugreportsDir);
+ ParcelFileDescriptor bugreportFd = info.getBugreportFd();
+ if (bugreportFd == null) {
+ Log.e(TAG, "Failed to start bugreport generation as "
+ + " bugreport parcel file descriptor is null.");
+ return;
}
- if (pid == -1) {
- Log.e(TAG, "Missing " + EXTRA_PID + " on start intent");
- return false;
+ ParcelFileDescriptor screenshotFd = null;
+ if (isDefaultScreenshotRequired(bugreportType, /* hasScreenshotButton= */ !mIsTv)) {
+ screenshotFd = info.getDefaultScreenshotFd();
+ if (screenshotFd == null) {
+ Log.e(TAG, "Failed to start bugreport generation as"
+ + " screenshot parcel file descriptor is null. Deleting bugreport file");
+ FileUtils.closeQuietly(bugreportFd);
+ info.bugreportFile.delete();
+ return;
+ }
}
- if (max <= 0) {
- Log.e(TAG, "Invalid value for extra " + EXTRA_MAX + ": " + max);
- return false;
+
+ mBugreportManager = (BugreportManager) mContext.getSystemService(
+ Context.BUGREPORT_SERVICE);
+ final Executor executor = ActivityThread.currentActivityThread().getExecutor();
+
+ Log.i(TAG, "bugreport type = " + bugreportType
+ + " bugreport file fd: " + bugreportFd
+ + " screenshot file fd: " + screenshotFd);
+
+ BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(info);
+ try {
+ synchronized (mLock) {
+ mBugreportManager.startBugreport(bugreportFd, screenshotFd,
+ new BugreportParams(bugreportType), executor, bugreportCallback);
+ bugreportCallback.trackInfoWithIdLocked();
+ }
+ } catch (RuntimeException e) {
+ Log.i(TAG, "Error in generating bugreports: ", e);
+ // The binder call didn't go through successfully, so need to close the fds.
+ // If the calls went through API takes ownership.
+ FileUtils.closeQuietly(bugreportFd);
+ if (screenshotFd != null) {
+ FileUtils.closeQuietly(screenshotFd);
+ }
}
+ }
+
+ private static boolean isDefaultScreenshotRequired(
+ @BugreportParams.BugreportMode int bugreportType,
+ boolean hasScreenshotButton) {
+ // Modify dumpstate#SetOptionsFromMode as well for default system screenshots.
+ // We override dumpstate for interactive bugreports with a screenshot button.
+ return (bugreportType == BugreportParams.BUGREPORT_MODE_INTERACTIVE && !hasScreenshotButton)
+ || bugreportType == BugreportParams.BUGREPORT_MODE_FULL
+ || bugreportType == BugreportParams.BUGREPORT_MODE_WEAR;
+ }
- final BugreportInfo info = new BugreportInfo(mContext, id, pid, name, max);
- if (mProcesses.indexOfKey(id) >= 0) {
- // BUGREPORT_STARTED intent was already received; ignore it.
- Log.w(TAG, "ID " + id + " already watched");
- return true;
+ private static ParcelFileDescriptor getFd(File file) {
+ try {
+ return ParcelFileDescriptor.open(file,
+ ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND);
+ } catch (FileNotFoundException e) {
+ Log.i(TAG, "Error in generating bugreports: ", e);
}
- final DumpstateListener listener = new DumpstateListener(info);
- mProcesses.put(info.id, listener);
- if (listener.connect()) {
- updateProgress(info);
- return true;
- } else {
- Log.w(TAG, "not updating progress because it could not connect to dumpstate");
- return false;
+ return null;
+ }
+
+ private static void createReadWriteFile(File file) {
+ try {
+ if (!file.exists()) {
+ file.createNewFile();
+ file.setReadable(true, true);
+ file.setWritable(true, true);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Error in creating bugreport file: ", e);
}
}
@@ -474,12 +686,12 @@ public class BugreportProgressService extends Service {
* Updates the system notification for a given bugreport.
*/
private void updateProgress(BugreportInfo info) {
- if (info.max <= 0 || info.progress < 0) {
+ if (info.progress.intValue() < 0) {
Log.e(TAG, "Invalid progress values for " + info);
return;
}
- if (info.finished) {
+ if (info.finished.get()) {
Log.w(TAG, "Not sending progress notification because bugreport has finished already ("
+ info + ")");
return;
@@ -488,7 +700,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 / info.max);
+ final String percentageText = nf.format((double) info.progress.intValue() / 100);
String title = mContext.getString(R.string.bugreport_in_progress_title, info.id);
@@ -496,18 +708,20 @@ public class BugreportProgressService extends Service {
if (mIsWatch) {
nf.setMinimumFractionDigits(0);
nf.setMaximumFractionDigits(0);
- final String watchPercentageText = nf.format((double) info.progress / info.max);
+ final String watchPercentageText = nf.format((double) info.progress.intValue() / 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(info.max, info.progress, false)
+ .setProgress(100 /* max value of progress percentage */,
+ info.progress.intValue(), false)
.setOngoing(true);
// Wear and ATV bugreport doesn't need the bug info dialog, screenshot and cancel action.
@@ -536,13 +750,14 @@ public class BugreportProgressService extends Service {
.setActions(infoAction, screenshotAction, cancelAction);
}
// Show a debug log, every LOG_PROGRESS_STEP percent.
- final int progress = (info.progress * 100) / info.max;
+ final int progress = info.progress.intValue();
- if ((info.progress == 0) || (info.progress >= 100) ||
- ((progress / LOG_PROGRESS_STEP) != (mLastProgressPercent / LOG_PROGRESS_STEP))) {
+ if ((progress == 0) || (progress >= 100)
+ || ((progress / LOG_PROGRESS_STEP)
+ != (info.lastProgress.intValue() / LOG_PROGRESS_STEP))) {
Log.d(TAG, "Progress #" + info.id + ": " + percentageText);
}
- mLastProgressPercent = progress;
+ info.lastProgress.set(progress);
sendForegroundabledNotification(info.id, builder.build());
}
@@ -554,6 +769,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);
}
}
@@ -572,18 +790,19 @@ public class BugreportProgressService extends Service {
/**
* Finalizes the progress on a given bugreport and cancel its notification.
*/
- private void stopProgress(int id) {
- if (mProcesses.indexOfKey(id) < 0) {
+ @GuardedBy("mLock")
+ private void stopProgressLocked(int id) {
+ if (mBugreportInfos.indexOfKey(id) < 0) {
Log.w(TAG, "ID not watched: " + id);
} else {
Log.d(TAG, "Removing ID " + id);
- mProcesses.remove(id);
+ 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();
}
/**
@@ -593,13 +812,16 @@ 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");
- setSystemProperty(CTL_STOP, BUGREPORT_SERVICE);
- deleteScreenshots(info);
+ synchronized (mLock) {
+ final BugreportInfo info = getInfoLocked(id);
+ if (info != null && !info.finished.get()) {
+ Log.i(TAG, "Cancelling bugreport service (ID=" + id + ") on user's request");
+ mBugreportManager.cancelBugreport();
+ info.deleteScreenshots();
+ info.deleteBugreportFile();
+ }
+ stopProgressLocked(id);
}
- stopProgress(id);
}
/**
@@ -608,7 +830,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
@@ -642,7 +867,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).
@@ -677,14 +906,16 @@ 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;
}
final String screenshotPath =
- new File(mScreenshotsDir, info.getPathNextScreenshot()).getAbsolutePath();
+ new File(mBugreportsDir, info.getPathNextScreenshot()).getAbsolutePath();
Message.obtain(mScreenshotHandler, MSG_SCREENSHOT_REQUEST, id, UNUSED_ARG2, screenshotPath)
.sendToTarget();
@@ -695,11 +926,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 < mProcesses.size(); i++) {
- final BugreportInfo info = mProcesses.valueAt(i).info;
- if (info.finished) {
+ for (int i = 0; i < mBugreportInfos.size(); i++) {
+ final BugreportInfo info = getInfoLocked(mBugreportInfos.keyAt(i));
+ if (info.finished.get()) {
Log.d(TAG, "Not updating progress for " + info.id + " while taking screenshot"
+ " because share notification was already sent");
continue;
@@ -720,7 +951,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;
}
@@ -729,9 +963,9 @@ public class BugreportProgressService extends Service {
final String msg;
if (taken) {
info.addScreenshot(screenshotFile);
- if (info.finished) {
+ if (info.finished.get()) {
Log.d(TAG, "Screenshot finished after bugreport; updating share notification");
- info.renameScreenshots(mScreenshotsDir);
+ info.renameScreenshots();
sendBugreportNotification(info, mTakingScreenshot);
}
msg = mContext.getString(R.string.bugreport_screenshot_taken);
@@ -743,21 +977,12 @@ public class BugreportProgressService extends Service {
}
/**
- * Deletes all screenshots taken for a given bugreport.
- */
- private void deleteScreenshots(BugreportInfo info) {
- for (File file : info.screenshotFiles) {
- Log.i(TAG, "Deleting screenshot file " + file);
- file.delete();
- }
- }
-
- /**
* 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;
}
@@ -767,11 +992,11 @@ public class BugreportProgressService extends Service {
mForegroundId = -1;
// Might need to restart foreground using a new notification id.
- final int total = mProcesses.size();
+ final int total = mBugreportInfos.size();
if (total > 0) {
for (int i = 0; i < total; i++) {
- final BugreportInfo info = mProcesses.valueAt(i).info;
- if (!info.finished) {
+ final BugreportInfo info = getInfoLocked(mBugreportInfos.keyAt(i));
+ if (!info.finished.get()) {
updateProgress(info);
break;
}
@@ -782,9 +1007,10 @@ public class BugreportProgressService extends Service {
/**
* Finishes the service when it's not monitoring any more processes.
*/
- private void stopSelfWhenDone() {
- if (mProcesses.size() > 0) {
- if (DEBUG) Log.d(TAG, "Staying alive, waiting for IDs " + mProcesses);
+ @GuardedBy("mLock")
+ private void stopSelfWhenDoneLocked() {
+ if (mBugreportInfos.size() > 0) {
+ if (DEBUG) Log.d(TAG, "Staying alive, waiting for IDs " + mBugreportInfos);
return;
}
Log.v(TAG, "No more processes to handle, shutting down");
@@ -792,58 +1018,20 @@ public class BugreportProgressService extends Service {
}
/**
- * Handles the BUGREPORT_FINISHED intent sent by {@code dumpstate}.
- */
- private void onBugreportFinished(int id, Intent intent) {
- final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
- if (bugreportFile == null) {
- // Should never happen, dumpstate always set the file.
- Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent);
- return;
- }
- final int max = intent.getIntExtra(EXTRA_MAX, -1);
- final File screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT);
- final String shareTitle = intent.getStringExtra(EXTRA_TITLE);
- final String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
- onBugreportFinished(id, bugreportFile, screenshotFile, shareTitle, shareDescription, max);
- }
-
- /**
* Wraps up bugreport generation and triggers a notification to share the bugreport.
*/
- private void onBugreportFinished(int id, File bugreportFile, @Nullable File screenshotFile,
- String shareTitle, String shareDescription, int max) {
- mInfoDialog.onBugreportFinished();
- BugreportInfo info = getInfo(id);
- if (info == null) {
- // Happens when BUGREPORT_FINISHED was received without a BUGREPORT_STARTED first.
- Log.v(TAG, "Creating info for untracked ID " + id);
- info = new BugreportInfo(mContext, id);
- mProcesses.put(id, new DumpstateListener(info));
- }
- info.renameScreenshots(mScreenshotsDir);
- info.bugreportFile = bugreportFile;
- if (screenshotFile != null) {
- info.addScreenshot(screenshotFile);
+ private void onBugreportFinished(BugreportInfo info) {
+ if (!TextUtils.isEmpty(info.shareTitle)) {
+ info.setTitle(info.shareTitle);
}
+ Log.d(TAG, "Bugreport finished with title: " + info.getTitle()
+ + " and shareDescription: " + info.shareDescription);
+ info.finished.set(true);
- if (max != -1) {
- MetricsLogger.histogram(this, "dumpstate_duration", max);
- info.max = max;
- }
-
- if (!TextUtils.isEmpty(shareTitle)) {
- info.title = shareTitle;
- if (!TextUtils.isEmpty(shareDescription)) {
- info.shareDescription= shareDescription;
- }
- Log.d(TAG, "Bugreport title is " + info.title + ","
- + " shareDescription is " + info.shareDescription);
+ synchronized (mLock) {
+ // Stop running on foreground, otherwise share notification cannot be dismissed.
+ stopForegroundWhenDoneLocked(info.id);
}
- info.finished = true;
-
- // Stop running on foreground, otherwise share notification cannot be dismissed.
- stopForegroundWhenDone(id);
triggerLocalNotification(mContext, info);
}
@@ -858,7 +1046,9 @@ public class BugreportProgressService extends Service {
if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) {
Log.e(TAG, "Could not read bugreport file " + info.bugreportFile);
Toast.makeText(context, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show();
- stopProgress(info.id);
+ synchronized (mLock) {
+ stopProgressLocked(info.id);
+ }
return;
}
@@ -882,6 +1072,9 @@ public class BugreportProgressService extends Service {
* Build {@link Intent} that can be used to share the given bugreport.
*/
private static Intent buildSendIntent(Context context, BugreportInfo info) {
+ // Rename files (if required) before sharing
+ info.renameBugreportFile();
+ info.renameScreenshots();
// Files are kept on private storage, so turn into Uris that we can
// grant temporary permissions for.
final Uri bugreportUri;
@@ -900,8 +1093,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.
@@ -912,9 +1105,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 },
@@ -955,12 +1148,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 ("
- + mProcesses + "), 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);
}
@@ -970,7 +1168,9 @@ public class BugreportProgressService extends Service {
final Intent sendIntent = buildSendIntent(mContext, info);
if (sendIntent == null) {
Log.w(TAG, "Stopping progres on ID " + id + " because share intent could not be built");
- stopProgress(id);
+ synchronized (mLock) {
+ stopProgressLocked(id);
+ }
return;
}
@@ -993,9 +1193,10 @@ public class BugreportProgressService extends Service {
} else {
mContext.startActivity(notifIntent);
}
-
- // ... and stop watching this process.
- stopProgress(id);
+ synchronized (mLock) {
+ // ... and stop watching this process.
+ stopProgressLocked(id);
+ }
}
static void sendShareIntent(Context context, Intent intent) {
@@ -1029,10 +1230,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;
}
@@ -1046,8 +1247,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);
@@ -1140,13 +1341,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;
}
@@ -1179,8 +1381,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,
@@ -1190,7 +1392,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)) {
@@ -1292,13 +1494,11 @@ public class BugreportProgressService extends Service {
}
String action = intent.getAction();
if (action == null) {
- // Happens when BugreportReceiver calls startService...
+ // Happens when startService is called...
action = "no action";
}
final StringBuilder buffer = new StringBuilder(action).append(" extras: ");
addExtra(buffer, intent, EXTRA_ID);
- addExtra(buffer, intent, EXTRA_PID);
- addExtra(buffer, intent, EXTRA_MAX);
addExtra(buffer, intent, EXTRA_NAME);
addExtra(buffer, intent, EXTRA_DESCRIPTION);
addExtra(buffer, intent, EXTRA_BUGREPORT);
@@ -1342,36 +1542,30 @@ public class BugreportProgressService extends Service {
}
/**
- * Updates the system property used by {@code dumpstate} to rename the final bugreport files.
- */
- private boolean setBugreportNameProperty(int pid, String name) {
- Log.d(TAG, "Updating bugreport name to " + name);
- final String key = DUMPSTATE_PREFIX + pid + NAME_SUFFIX;
- return setSystemProperty(key, name);
- }
-
- /**
* 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);
}
}
@@ -1434,30 +1628,6 @@ public class BugreportProgressService extends Service {
private AlertDialog mDialog;
private Button mOkButton;
private int mId;
- private int mPid;
-
- /**
- * Last "committed" value of the bugreport name.
- * <p>
- * Once initially set, it's only updated when user clicks the OK button.
- */
- private String mSavedName;
-
- /**
- * Last value of the bugreport name as entered by the user.
- * <p>
- * Every time it's changed the equivalent system property is changed as well, but if the
- * user clicks CANCEL, the old value (stored on {@code mSavedName} is restored.
- * <p>
- * This logic handles the corner-case scenario where {@code dumpstate} finishes after the
- * user changed the name but didn't clicked OK yet (for example, because the user is typing
- * the description). The only drawback is that if the user changes the name while
- * {@code dumpstate} is running but clicks CANCEL after it finishes, then the final name
- * will be the one that has been canceled. But when {@code dumpstate} finishes the {code
- * name} UI is disabled and the old name restored anyways, so the user will be "alerted" of
- * such drawback.
- */
- private String mTempName;
/**
* Sets its internal state and displays the dialog.
@@ -1477,18 +1647,6 @@ public class BugreportProgressService extends Service {
mInfoName = (EditText) view.findViewById(R.id.name);
mInfoTitle = (EditText) view.findViewById(R.id.title);
mInfoDescription = (EditText) view.findViewById(R.id.description);
-
- mInfoName.setOnFocusChangeListener(new OnFocusChangeListener() {
-
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- return;
- }
- sanitizeName();
- }
- });
-
mDialog = new AlertDialog.Builder(themedContext)
.setView(view)
.setTitle(dialogTitle)
@@ -1503,11 +1661,6 @@ public class BugreportProgressService extends Service {
{
MetricsLogger.action(context,
MetricsEvent.ACTION_BUGREPORT_DETAILS_CANCELED);
- if (!mTempName.equals(mSavedName)) {
- // Must restore dumpstate's name since it was changed
- // before user clicked OK.
- setBugreportNameProperty(mPid, mSavedName);
- }
}
})
.create();
@@ -1526,17 +1679,15 @@ public class BugreportProgressService extends Service {
}
// Then set fields.
- mSavedName = mTempName = info.name;
mId = info.id;
- mPid = info.pid;
- 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.
@@ -1555,7 +1706,7 @@ public class BugreportProgressService extends Service {
@Override
public void onClick(View view) {
MetricsLogger.action(context, MetricsEvent.ACTION_BUGREPORT_DETAILS_SAVED);
- sanitizeName();
+ sanitizeName(info.getName());
final String name = mInfoName.getText().toString();
final String title = mInfoTitle.getText().toString();
final String description = mInfoDescription.getText().toString();
@@ -1571,9 +1722,9 @@ public class BugreportProgressService extends Service {
* Sanitizes the user-provided value for the {@code name} field, automatically replacing
* invalid characters if necessary.
*/
- private void sanitizeName() {
+ private void sanitizeName(String savedName) {
String name = mInfoName.getText().toString();
- if (name.equals(mTempName)) {
+ if (name.equals(savedName)) {
if (DEBUG) Log.v(TAG, "name didn't change, no need to sanitize: " + name);
return;
}
@@ -1593,25 +1744,6 @@ public class BugreportProgressService extends Service {
name = safeName.toString();
mInfoName.setText(name);
}
- mTempName = name;
-
- // Must update system property for the cases where dumpstate finishes
- // while the user is still entering other fields (like title or
- // description)
- setBugreportNameProperty(mPid, name);
- }
-
- /**
- * Notifies the dialog that the bugreport has finished so it disables the {@code name}
- * field.
- * <p>Once the bugreport is finished dumpstate has already generated the final files, so
- * changing the name would have no effect.
- */
- void onBugreportFinished() {
- if (mInfoName != null) {
- mInfoName.setEnabled(false);
- mInfoName.setText(mSavedName);
- }
}
void cancel() {
@@ -1630,57 +1762,66 @@ public class BugreportProgressService extends Service {
/**
* Sequential, user-friendly id used to identify the bugreport.
*/
- final int id;
+ int id;
/**
- * {@code pid} of the {@code dumpstate} process generating the bugreport.
+ * Prefix name of the bugreport, this is uneditable.
+ * The baseName consists of the string "bugreport" + deviceName + buildID
+ * This will end with the string "wifi"/"telephony" for wifi/telephony bugreports.
+ * Bugreport zip file name = "<baseName>-<name>.zip"
*/
- final int pid;
+ private final String baseName;
/**
- * Name of the bugreport, will be used to rename the final files.
- * <p>
- * Initial value is the bugreport filename reported by {@code dumpstate}, but user can
- * change it later to a more meaningful name.
+ * Suffix name of the bugreport/screenshot, is set to timestamp initially. User can make
+ * modifications to this using interface.
*/
- String name;
+ private String name;
/**
- * User-provided, one-line summary of the bug; when set, will be used as the subject
- * of the {@link Intent#ACTION_SEND_MULTIPLE} intent.
+ * Initial value of the field name. This is required to rename the files later on, as they
+ * are created using initial value of name.
*/
- String title;
+ private final String initialName;
/**
- * User-provided, detailed description of the bugreport; when set, will be added to the body
+ * User-provided, one-line summary of the bug; when set, will be used as the subject
* of the {@link Intent#ACTION_SEND_MULTIPLE} intent.
*/
- String description;
+ private String title;
/**
- * Maximum progress of the bugreport generation as displayed by the UI.
+ * One-line summary of the bug; when set, will be used as the subject of the
+ * {@link Intent#ACTION_SEND_MULTIPLE} intent. This is the predefined title which is
+ * set initially when the request to take a bugreport is made. This overrides any changes
+ * in the title that the user makes after the bugreport starts.
*/
- int max;
+ private final String shareTitle;
/**
- * Current progress of the bugreport generation as displayed by the UI.
+ * User-provided, detailed description of the bugreport; when set, will be added to the body
+ * of the {@link Intent#ACTION_SEND_MULTIPLE} intent. This is shown in the app where the
+ * bugreport is being shared as an attachment. This is not related/dependant on
+ * {@code shareDescription}.
*/
- int progress;
+ private String description;
/**
- * Maximum progress of the bugreport generation as reported by dumpstate.
+ * Current value of progress (in percentage) of the bugreport generation as
+ * displayed by the UI.
*/
- int realMax;
+ final AtomicInteger progress = new AtomicInteger(0);
/**
- * Current progress of the bugreport generation as reported by dumpstate.
+ * Last value of progress (in percentage) of the bugreport generation for which
+ * system notification was updated.
*/
- int realProgress;
+ final AtomicInteger lastProgress = new AtomicInteger(0);
/**
* Time of the last progress update.
*/
- long lastUpdate = System.currentTimeMillis();
+ final AtomicLong lastUpdate = new AtomicLong(System.currentTimeMillis());
/**
* Time of the last progress update when Parcel was created.
@@ -1700,7 +1841,7 @@ public class BugreportProgressService extends Service {
/**
* Whether dumpstate sent an intent informing it has finished.
*/
- boolean finished;
+ final AtomicBoolean finished = new AtomicBoolean(false);
/**
* Whether the details entries have been added to the bugreport yet.
@@ -1714,36 +1855,106 @@ public class BugreportProgressService extends Service {
int screenshotCounter;
/**
- * Descriptive text that will be shown to the user in the notification message.
+ * Descriptive text that will be shown to the user in the notification message. This is the
+ * predefined description which is set initially when the request to take a bugreport is
+ * made.
*/
- String shareDescription;
+ private final String shareDescription;
/**
- * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
+ * Type of the bugreport
*/
- BugreportInfo(Context context, int id, int pid, String name, int max) {
- this.context = context;
- this.id = id;
- this.pid = pid;
- this.name = name;
- this.max = this.realMax = max;
- }
+ final int type;
+
+ private final Object mLock = new Object();
/**
- * Constructor for untracked bugreports - typically called upon receiving BUGREPORT_FINISHED
- * without a previous call to BUGREPORT_STARTED.
+ * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED.
*/
- BugreportInfo(Context context, int id) {
- this(context, id, id, null, 0);
- this.finished = true;
+ BugreportInfo(Context context, String baseName, String name,
+ @Nullable String shareTitle, @Nullable String shareDescription,
+ @BugreportParams.BugreportMode int type, File bugreportsDir) {
+ this.context = context;
+ this.name = this.initialName = name;
+ this.shareTitle = shareTitle == null ? "" : shareTitle;
+ this.shareDescription = shareDescription == null ? "" : shareDescription;
+ this.type = type;
+ this.baseName = baseName;
+ createBugreportFile(bugreportsDir);
+ createScreenshotFile(bugreportsDir);
+ }
+
+ void createBugreportFile(File bugreportsDir) {
+ bugreportFile = new File(bugreportsDir, getFileName(this, ".zip"));
+ createReadWriteFile(bugreportFile);
+ }
+
+ void createScreenshotFile(File bugreportsDir) {
+ File screenshotFile = new File(bugreportsDir, getScreenshotName("default"));
+ addScreenshot(screenshotFile);
+ createReadWriteFile(screenshotFile);
+ }
+
+ ParcelFileDescriptor getBugreportFd() {
+ return getFd(bugreportFile);
+ }
+
+ ParcelFileDescriptor getDefaultScreenshotFd() {
+ if (screenshotFiles.isEmpty()) {
+ return null;
+ }
+ return getFd(screenshotFiles.get(0));
+ }
+
+ 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;
+ }
}
/**
- * Gets the name for next screenshot file.
+ * Gets the name for next user triggered screenshot file.
*/
String getPathNextScreenshot() {
screenshotCounter ++;
- return "screenshot-" + pid + "-" + screenshotCounter + ".png";
+ return getScreenshotName(Integer.toString(screenshotCounter));
+ }
+
+ /**
+ * Gets the name for screenshot file based on the suffix that is passed.
+ */
+ String getScreenshotName(String suffix) {
+ return "screenshot-" + initialName + "-" + suffix + ".png";
}
/**
@@ -1754,49 +1965,100 @@ public class BugreportProgressService extends Service {
}
/**
- * Rename all screenshots files so that they contain the user-generated name instead of pid.
+ * Deletes all screenshots taken for a given bugreport.
+ */
+ private void deleteScreenshots() {
+ for (File file : screenshotFiles) {
+ Log.i(TAG, "Deleting screenshot file " + file);
+ file.delete();
+ }
+ }
+
+ /**
+ * Deletes bugreport file for a given bugreport.
+ */
+ private void deleteBugreportFile() {
+ Log.i(TAG, "Deleting bugreport file " + bugreportFile);
+ bugreportFile.delete();
+ }
+
+ /**
+ * Deletes empty files for a given bugreport.
+ */
+ private void deleteEmptyFiles() {
+ if (bugreportFile.length() == 0) {
+ Log.i(TAG, "Deleting empty bugreport file: " + bugreportFile);
+ bugreportFile.delete();
+ }
+ for (File file : screenshotFiles) {
+ if (file.length() == 0) {
+ Log.i(TAG, "Deleting empty screenshot file: " + file);
+ file.delete();
+ }
+ }
+ }
+
+ /**
+ * Rename all screenshots files so that they contain the new {@code name} instead of the
+ * {@code initialName} if user has changed it.
*/
- void renameScreenshots(File screenshotDir) {
+ void renameScreenshots() {
if (TextUtils.isEmpty(name)) {
return;
}
final List<File> renamedFiles = new ArrayList<>(screenshotFiles.size());
for (File oldFile : screenshotFiles) {
final String oldName = oldFile.getName();
- final String newName = oldName.replaceFirst(Integer.toString(pid), name);
+ final String newName = oldName.replaceFirst(initialName, name);
final File newFile;
if (!newName.equals(oldName)) {
- final File renamedFile = new File(screenshotDir, newName);
+ final File renamedFile = new File(oldFile.getParentFile(), newName);
Log.d(TAG, "Renaming screenshot file " + oldFile + " to " + renamedFile);
newFile = oldFile.renameTo(renamedFile) ? renamedFile : oldFile;
} else {
- Log.w(TAG, "Name didn't change: " + oldName); // Shouldn't happen.
+ Log.w(TAG, "Name didn't change: " + oldName);
newFile = oldFile;
}
- renamedFiles.add(newFile);
+ if (newFile.length() > 0) {
+ renamedFiles.add(newFile);
+ } else if (newFile.delete()) {
+ Log.d(TAG, "screenshot file: " + newFile + "deleted successfully.");
+ }
}
screenshotFiles = renamedFiles;
}
+ /**
+ * Rename bugreport file to include the name given by user via UI
+ */
+ void renameBugreportFile() {
+ File newBugreportFile = new File(bugreportFile.getParentFile(),
+ getFileName(this, ".zip"));
+ if (!newBugreportFile.getPath().equals(bugreportFile.getPath())) {
+ if (bugreportFile.renameTo(newBugreportFile)) {
+ bugreportFile = newBugreportFile;
+ }
+ }
+ }
+
String getFormattedLastUpdate() {
if (context == null) {
// Restored from Parcel
return formattedLastUpdate == null ?
- Long.toString(lastUpdate) : formattedLastUpdate;
+ Long.toString(lastUpdate.longValue()) : formattedLastUpdate;
}
- return DateUtils.formatDateTime(context, lastUpdate,
+ return DateUtils.formatDateTime(context, lastUpdate.longValue(),
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME);
}
@Override
public String toString() {
- final float percent = ((float) progress * 100 / max);
- final float realPercent = ((float) realProgress * 100 / realMax);
final StringBuilder builder = new StringBuilder()
.append("\tid: ").append(id)
- .append(", pid: ").append(pid)
+ .append(", baseName: ").append(baseName)
.append(", name: ").append(name)
+ .append(", initialName: ").append(initialName)
.append(", finished: ").append(finished)
.append("\n\ttitle: ").append(title)
.append("\n\tdescription: ");
@@ -1812,14 +2074,12 @@ public class BugreportProgressService extends Service {
return builder
.append("\n\tfile: ").append(bugreportFile)
.append("\n\tscreenshots: ").append(screenshotFiles)
- .append("\n\tprogress: ").append(progress).append("/").append(max)
- .append(" (").append(percent).append(")")
- .append("\n\treal progress: ").append(realProgress).append("/").append(realMax)
- .append(" (").append(realPercent).append(")")
+ .append("\n\tprogress: ").append(progress)
.append("\n\tlast_update: ").append(getFormattedLastUpdate())
.append("\n\taddingDetailsToZip: ").append(addingDetailsToZip)
.append(" addedDetailsToZip: ").append(addedDetailsToZip)
.append("\n\tshareDescription: ").append(shareDescription)
+ .append("\n\tshareTitle: ").append(shareTitle)
.toString();
}
@@ -1827,15 +2087,13 @@ public class BugreportProgressService extends Service {
protected BugreportInfo(Parcel in) {
context = null;
id = in.readInt();
- pid = in.readInt();
+ baseName = in.readString();
name = in.readString();
+ initialName = in.readString();
title = in.readString();
description = in.readString();
- max = in.readInt();
- progress = in.readInt();
- realMax = in.readInt();
- realProgress = in.readInt();
- lastUpdate = in.readLong();
+ progress.set(in.readInt());
+ lastUpdate.set(in.readLong());
formattedLastUpdate = in.readString();
bugreportFile = readFile(in);
@@ -1844,23 +2102,23 @@ public class BugreportProgressService extends Service {
screenshotFiles.add(readFile(in));
}
- finished = in.readInt() == 1;
+ finished.set(in.readInt() == 1);
screenshotCounter = in.readInt();
shareDescription = in.readString();
+ shareTitle = in.readString();
+ type = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
- dest.writeInt(pid);
+ dest.writeString(baseName);
dest.writeString(name);
+ dest.writeString(initialName);
dest.writeString(title);
dest.writeString(description);
- dest.writeInt(max);
- dest.writeInt(progress);
- dest.writeInt(realMax);
- dest.writeInt(realProgress);
- dest.writeLong(lastUpdate);
+ dest.writeInt(progress.intValue());
+ dest.writeLong(lastUpdate.longValue());
dest.writeString(getFormattedLastUpdate());
writeFile(dest, bugreportFile);
@@ -1869,9 +2127,11 @@ public class BugreportProgressService extends Service {
writeFile(dest, screenshotFile);
}
- dest.writeInt(finished ? 1 : 0);
+ dest.writeInt(finished.get() ? 1 : 0);
dest.writeInt(screenshotCounter);
dest.writeString(shareDescription);
+ dest.writeString(shareTitle);
+ dest.writeInt(type);
}
@Override
@@ -1901,96 +2161,22 @@ public class BugreportProgressService extends Service {
return new BugreportInfo[size];
}
};
-
}
- private final class DumpstateListener extends IDumpstateListener.Stub
- implements DeathRecipient {
-
- private final BugreportInfo info;
- private IDumpstateToken token;
-
- DumpstateListener(BugreportInfo info) {
- this.info = info;
+ @GuardedBy("mLock")
+ private void checkProgressUpdatedLocked(BugreportInfo info, int progress) {
+ if (progress > CAPPED_PROGRESS) {
+ progress = CAPPED_PROGRESS;
}
-
- /**
- * Connects to the {@code dumpstate} binder to receive updates.
- */
- boolean connect() {
- if (token != null) {
- Log.d(TAG, "connect(): " + info.id + " already connected");
- return true;
- }
- final IBinder service = ServiceManager.getService("dumpstate");
- if (service == null) {
- Log.d(TAG, "dumpstate service not bound yet");
- return true;
+ if (DEBUG) {
+ if (progress != info.progress.intValue()) {
+ Log.v(TAG, "Updating progress for name " + info.getName() + "(id: " + info.id
+ + ") from " + info.progress.intValue() + " to " + progress);
}
- final IDumpstate dumpstate = IDumpstate.Stub.asInterface(service);
- try {
- token = dumpstate.setListener("Shell", this, /* perSectionDetails= */ false);
- if (token != null) {
- token.asBinder().linkToDeath(this, 0);
- }
- } catch (Exception e) {
- Log.e(TAG, "Could not set dumpstate listener: " + e);
- }
- return token != null;
}
+ info.progress.set(progress);
+ info.lastUpdate.set(System.currentTimeMillis());
- @Override
- public void binderDied() {
- if (!info.finished) {
- // TODO: linkToDeath() might be called BEFORE Shell received the
- // BUGREPORT_FINISHED broadcast, in which case the statements below
- // spam logcat (but are harmless).
- // The right, long-term solution is to provide an onFinished() callback
- // on IDumpstateListener and call it instead of using a broadcast.
- Log.w(TAG, "Dumpstate process died:\n" + info);
- stopProgress(info.id);
- }
- token.asBinder().unlinkToDeath(this, 0);
- }
-
- @Override
- public void onProgress(int progress) throws RemoteException {
- if (progress > CAPPED_PROGRESS) {
- progress = CAPPED_PROGRESS;
- }
- updateProgressInfo(progress, CAPPED_MAX);
- }
-
- @Override
- public void onError(int errorCode) throws RemoteException {
- // TODO(b/111441001): implement
- }
-
- @Override
- public void onFinished() throws RemoteException {
- // TODO(b/111441001): implement
- }
-
- public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("token: "); pw.println(token);
- }
-
- private void updateProgressInfo(int progress, int max) {
- if (DEBUG) {
- if (progress != info.progress) {
- Log.v(TAG, "Updating progress for PID " + info.pid + "(id: " + info.id
- + ") from " + info.progress + " to " + progress);
- }
- if (max != info.max) {
- Log.v(TAG, "Updating max progress for PID " + info.pid + "(id: " + info.id
- + ") from " + info.max + " to " + max);
- }
- }
- info.progress = progress;
- info.max = max;
- info.lastUpdate = System.currentTimeMillis();
-
- updateProgress(info);
- }
+ updateProgress(info);
}
}
diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java
deleted file mode 100644
index 15ce90fa6418..000000000000
--- a/packages/Shell/src/com/android/shell/BugreportReceiver.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.shell;
-
-import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
-import static com.android.shell.BugreportProgressService.EXTRA_ORIGINAL_INTENT;
-import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED;
-import static com.android.shell.BugreportProgressService.getFileExtra;
-import static com.android.shell.BugreportProgressService.dumpIntent;
-
-import java.io.File;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.FileUtils;
-import android.text.format.DateUtils;
-import android.util.Log;
-
-/**
- * Receiver that handles finished bugreports, usually by attaching them to an
- * {@link Intent#ACTION_SEND_MULTIPLE}.
- */
-public class BugreportReceiver extends BroadcastReceiver {
- private static final String TAG = "BugreportReceiver";
-
- /**
- * 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;
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.d(TAG, "onReceive(): " + dumpIntent(intent));
- // Clean up older bugreports in background
- cleanupOldFiles(this, intent, INTENT_BUGREPORT_FINISHED, MIN_KEEP_COUNT, MIN_KEEP_AGE);
-
- // Delegate intent handling to service.
- Intent serviceIntent = new Intent(context, BugreportProgressService.class);
- serviceIntent.putExtra(EXTRA_ORIGINAL_INTENT, intent);
- context.startService(serviceIntent);
- }
-
- static void cleanupOldFiles(BroadcastReceiver br, Intent intent, String expectedAction,
- final int minCount, final long minAge) {
- if (!expectedAction.equals(intent.getAction())) {
- return;
- }
- final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
- if (bugreportFile == null || !bugreportFile.exists()) {
- Log.e(TAG, "Not deleting old files because file " + bugreportFile + " doesn't exist");
- return;
- }
- final PendingResult result = br.goAsync();
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- try {
- FileUtils.deleteOlderFiles(bugreportFile.getParentFile(), minCount, minAge);
- } catch (RuntimeException e) {
- Log.e(TAG, "RuntimeException deleting old files", e);
- }
- result.finish();
- return null;
- }
- }.execute();
- }
-}
diff --git a/packages/Shell/src/com/android/shell/BugreportRequestedReceiver.java b/packages/Shell/src/com/android/shell/BugreportRequestedReceiver.java
new file mode 100644
index 000000000000..da919bef6387
--- /dev/null
+++ b/packages/Shell/src/com/android/shell/BugreportRequestedReceiver.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.shell;
+
+import static com.android.shell.BugreportProgressService.EXTRA_ORIGINAL_INTENT;
+import static com.android.shell.BugreportProgressService.dumpIntent;
+
+import android.annotation.RequiresPermission;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+/**
+ * Receiver that listens to {@link Intent#INTENT_BUGREPORT_REQUESTED}
+ * and starts up BugreportProgressService to start a new bugreport
+ */
+public class BugreportRequestedReceiver extends BroadcastReceiver {
+ private static final String TAG = "BugreportRequestedReceiver";
+
+ @Override
+ @RequiresPermission(android.Manifest.permission.TRIGGER_SHELL_BUGREPORT)
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "onReceive(): " + dumpIntent(intent));
+
+ // Delegate intent handling to service.
+ Intent serviceIntent = new Intent(context, BugreportProgressService.class);
+ Log.d(TAG, "onReceive() ACTION: " + serviceIntent.getAction());
+ serviceIntent.setAction(intent.getAction());
+ serviceIntent.putExtra(EXTRA_ORIGINAL_INTENT, intent);
+ context.startService(serviceIntent);
+ }
+}
diff --git a/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java b/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java
deleted file mode 100644
index 634c3b47c787..000000000000
--- a/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.shell;
-
-import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
-import static com.android.shell.BugreportProgressService.INTENT_REMOTE_BUGREPORT_FINISHED;
-import static com.android.shell.BugreportProgressService.getFileExtra;
-import static com.android.shell.BugreportProgressService.getUri;
-import static com.android.shell.BugreportReceiver.cleanupOldFiles;
-
-import java.io.File;
-
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.text.format.DateUtils;
-
-/**
- * Receiver that handles finished remote bugreports, by re-sending
- * the intent with appended bugreport zip file URI.
- *
- * <p> Remote bugreport never contains a screenshot.
- */
-public class RemoteBugreportReceiver extends BroadcastReceiver {
-
- private static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
-
- /** Always keep just the last 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 MIN_KEEP_AGE = DateUtils.DAY_IN_MILLIS;
-
- @Override
- public void onReceive(Context context, Intent intent) {
- cleanupOldFiles(this, intent, INTENT_REMOTE_BUGREPORT_FINISHED,
- REMOTE_BUGREPORT_FILES_AMOUNT, MIN_KEEP_AGE);
-
- final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
- final Uri bugreportUri = getUri(context, bugreportFile);
- final String bugreportHash = intent.getStringExtra(
- DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH);
-
- final Intent newIntent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
- newIntent.setDataAndType(bugreportUri, BUGREPORT_MIMETYPE);
- newIntent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
- context.sendBroadcastAsUser(newIntent, UserHandle.SYSTEM,
- android.Manifest.permission.DUMP);
- }
-}
diff --git a/packages/Shell/tests/AndroidManifest.xml b/packages/Shell/tests/AndroidManifest.xml
index e845ef95b28e..23877fd7a66a 100644
--- a/packages/Shell/tests/AndroidManifest.xml
+++ b/packages/Shell/tests/AndroidManifest.xml
@@ -27,6 +27,7 @@
android:label="ActionSendMultipleConsumer"
android:theme="@android:style/Theme.NoDisplay"
android:noHistory="true"
+ android:exported="true"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 3a71632cf1ca..b95092a9384c 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -27,12 +27,9 @@ import static com.android.shell.BugreportPrefs.getWarningState;
import static com.android.shell.BugreportPrefs.setWarningState;
import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
import static com.android.shell.BugreportProgressService.EXTRA_ID;
-import static com.android.shell.BugreportProgressService.EXTRA_MAX;
import static com.android.shell.BugreportProgressService.EXTRA_NAME;
-import static com.android.shell.BugreportProgressService.EXTRA_PID;
import static com.android.shell.BugreportProgressService.EXTRA_SCREENSHOT;
import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED;
-import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_STARTED;
import static com.android.shell.BugreportProgressService.SCREENSHOT_DELAY_SECONDS;
import static org.junit.Assert.assertEquals;
@@ -145,6 +142,11 @@ public class BugreportReceiverTest {
private static final String TITLE2 = "Master of the Universe";
private static final String DESCRIPTION = "One's description...";
private static final String DESCRIPTION2 = "...is another's treasure.";
+ // TODO(b/143130523): Fix (update) tests and add to presubmit
+ private static final String EXTRA_MAX = "android.intent.extra.MAX";
+ private static final String EXTRA_PID = "android.intent.extra.PID";
+ private static final String INTENT_BUGREPORT_STARTED =
+ "com.android.internal.intent.action.BUGREPORT_STARTED";
private static final String NO_DESCRIPTION = null;
private static final String NO_NAME = null;