1b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme/* 2b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * Copyright (C) 2015 The Android Open Source Project 3b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * 4b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * Licensed under the Apache License, Version 2.0 (the "License"); 5b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * you may not use this file except in compliance with the License. 6b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * You may obtain a copy of the License at 7b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * 8b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * http://www.apache.org/licenses/LICENSE-2.0 9b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * 10b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * Unless required by applicable law or agreed to in writing, software 11b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * distributed under the License is distributed on an "AS IS" BASIS, 12b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * See the License for the specific language governing permissions and 14b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * limitations under the License. 15b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme */ 16b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 17b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemepackage com.android.shell; 18b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 19d1e0f12979441733753b538611f6d73e5527c43cFelipe Lemeimport static android.os.Process.THREAD_PRIORITY_BACKGROUND; 20fcca68dfb137c061952d23e1873e995e6bcf172dFelipe Lemeimport static com.android.shell.BugreportPrefs.STATE_HIDE; 21fcca68dfb137c061952d23e1873e995e6bcf172dFelipe Lemeimport static com.android.shell.BugreportPrefs.STATE_UNKNOWN; 22b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport static com.android.shell.BugreportPrefs.getWarningState; 23b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 24b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.io.BufferedOutputStream; 254967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Lemeimport java.io.ByteArrayInputStream; 26b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.io.File; 2769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport java.io.FileDescriptor; 28b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.io.FileInputStream; 29b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.io.FileOutputStream; 30b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.io.IOException; 31b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.io.InputStream; 3269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport java.io.PrintWriter; 334967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Lemeimport java.nio.charset.StandardCharsets; 3469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport java.text.NumberFormat; 35b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.util.ArrayList; 364967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Lemeimport java.util.Enumeration; 37d1e0f12979441733753b538611f6d73e5527c43cFelipe Lemeimport java.util.List; 38b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.util.zip.ZipEntry; 394967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Lemeimport java.util.zip.ZipFile; 40b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.util.zip.ZipOutputStream; 41b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 42b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport libcore.io.Streams; 43b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 44bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport com.android.internal.annotations.VisibleForTesting; 456605bd89c53494b59717a826f9a17641bc32da41Felipe Lemeimport com.android.internal.logging.MetricsLogger; 466605bd89c53494b59717a826f9a17641bc32da41Felipe Lemeimport com.android.internal.logging.MetricsProto.MetricsEvent; 47b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport com.google.android.collect.Lists; 48b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 49b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.accounts.Account; 50b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.accounts.AccountManager; 51bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.annotation.SuppressLint; 52bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.app.AlertDialog; 53b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.app.Notification; 549cadb75714bea7d435f34c8b7d06f698fa907a8dFelipe Lemeimport android.app.Notification.Action; 55b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.app.NotificationManager; 56b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.app.PendingIntent; 57b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.app.Service; 58b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.content.ClipData; 59b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.content.Context; 60bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.content.DialogInterface; 61b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.content.Intent; 62b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.content.res.Configuration; 63aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Lemeimport android.graphics.Bitmap; 64aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Lemeimport android.hardware.display.DisplayManagerGlobal; 65b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.net.Uri; 66b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.os.AsyncTask; 6765a9c6760ef6cf1c8e1762a271aa43c626d27048Felipe Lemeimport android.os.Bundle; 6869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.os.Handler; 6969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.os.HandlerThread; 70b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.os.IBinder; 7169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.os.Looper; 7269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.os.Message; 73c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Lemeimport android.os.Parcel; 7469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.os.Parcelable; 75b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.os.SystemProperties; 76d1e0f12979441733753b538611f6d73e5527c43cFelipe Lemeimport android.os.Vibrator; 77b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.support.v4.content.FileProvider; 78bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.text.TextUtils; 7969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.text.format.DateUtils; 80b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.util.Log; 81b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.util.Patterns; 8269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.util.SparseArray; 83aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Lemeimport android.view.Display; 84aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Lemeimport android.view.KeyEvent; 85bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.view.View; 86bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.view.WindowManager; 87bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.view.View.OnFocusChangeListener; 88bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.view.inputmethod.EditorInfo; 89bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.widget.Button; 90bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.widget.EditText; 91b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.widget.Toast; 92b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 9369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme/** 9446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Service used to keep progress of bugreport processes ({@code dumpstate}). 9569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <p> 9669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * The workflow is: 9769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <ol> 98fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with a sequential id, 99fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme * its pid, and the estimated total effort. 10069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>{@link BugreportReceiver} receives the intent and delegates it to this service. 10169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>Upon start, this service: 10269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <ol> 10369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>Issues a system notification so user can watch the progresss (which is 0% initially). 10469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>Polls the {@link SystemProperties} for updates on the {@code dumpstate} progress. 10569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>If the progress changed, it updates the system notification. 10669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * </ol> 10769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>As {@code dumpstate} progresses, it updates the system property. 10869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>When {@code dumpstate} finishes, it sends a {@code BUGREPORT_FINISHED} intent. 10969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>{@link BugreportReceiver} receives the intent and delegates it to this service, which in 11069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * turn: 11169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <ol> 11246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * <li>Updates the system notification so user can share the bugreport. 11369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>Stops monitoring that {@code dumpstate} process. 11469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>Stops itself if it doesn't have any process left to monitor. 11569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * </ol> 11669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * </ol> 11769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 118b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemepublic class BugreportProgressService extends Service { 119c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme private static final String TAG = "BugreportProgressService"; 12069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme private static final boolean DEBUG = false; 121b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 122b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme private static final String AUTHORITY = "com.android.shell"; 123b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 12446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme // External intents sent by dumpstate. 12569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme static final String INTENT_BUGREPORT_STARTED = "android.intent.action.BUGREPORT_STARTED"; 12669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme static final String INTENT_BUGREPORT_FINISHED = "android.intent.action.BUGREPORT_FINISHED"; 127226940ed8550c02875a987f7e46699e6003ec1c0Michal Karpinski static final String INTENT_REMOTE_BUGREPORT_FINISHED = 128226940ed8550c02875a987f7e46699e6003ec1c0Michal Karpinski "android.intent.action.REMOTE_BUGREPORT_FINISHED"; 12946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 13046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme // Internal intents used on notification actions. 1319cadb75714bea7d435f34c8b7d06f698fa907a8dFelipe Leme static final String INTENT_BUGREPORT_CANCEL = "android.intent.action.BUGREPORT_CANCEL"; 13246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme static final String INTENT_BUGREPORT_SHARE = "android.intent.action.BUGREPORT_SHARE"; 133bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme static final String INTENT_BUGREPORT_INFO_LAUNCH = 134bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme "android.intent.action.BUGREPORT_INFO_LAUNCH"; 135d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme static final String INTENT_BUGREPORT_SCREENSHOT = 136d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme "android.intent.action.BUGREPORT_SCREENSHOT"; 13769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 138b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT"; 139b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT"; 140fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme static final String EXTRA_ID = "android.intent.extra.ID"; 14169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme static final String EXTRA_PID = "android.intent.extra.PID"; 14269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme static final String EXTRA_MAX = "android.intent.extra.MAX"; 14369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme static final String EXTRA_NAME = "android.intent.extra.NAME"; 144bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme static final String EXTRA_TITLE = "android.intent.extra.TITLE"; 145bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION"; 14669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme static final String EXTRA_ORIGINAL_INTENT = "android.intent.extra.ORIGINAL_INTENT"; 147c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme static final String EXTRA_INFO = "android.intent.extra.INFO"; 14869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 14969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme private static final int MSG_SERVICE_COMMAND = 1; 15069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme private static final int MSG_POLL = 2; 151d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private static final int MSG_DELAYED_SCREENSHOT = 3; 152d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private static final int MSG_SCREENSHOT_REQUEST = 4; 153d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private static final int MSG_SCREENSHOT_RESPONSE = 5; 154d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 1558648a15406d43c8af12e9cfe2355b1eee201d479Felipe Leme // Passed to Message.obtain() when msg.arg2 is not used. 1568648a15406d43c8af12e9cfe2355b1eee201d479Felipe Leme private static final int UNUSED_ARG2 = -2; 1578648a15406d43c8af12e9cfe2355b1eee201d479Felipe Leme 1583fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme // Maximum progress displayed (like 99.00%). 1593fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme private static final int CAPPED_PROGRESS = 9900; 1603fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme private static final int CAPPED_MAX = 10000; 1613fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme 162d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 163d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Delay before a screenshot is taken. 164d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * <p> 165d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Should be at least 3 seconds, otherwise its toast might show up in the screenshot. 166d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 167d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme static final int SCREENSHOT_DELAY_SECONDS = 3; 16869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 16969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** Polling frequency, in milliseconds. */ 170bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme static final long POLLING_FREQUENCY = 2 * DateUtils.SECOND_IN_MILLIS; 17169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 17269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** How long (in ms) a dumpstate process will be monitored if it didn't show progress. */ 1731eee1996f5d57b42d4beed2790321480fbe03a4cFelipe Leme private static final long INACTIVITY_TIMEOUT = 10 * DateUtils.MINUTE_IN_MILLIS; 17469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 175719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme /** System properties used for monitoring progress. */ 176719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme private static final String DUMPSTATE_PREFIX = "dumpstate."; 177719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme private static final String PROGRESS_SUFFIX = ".progress"; 178719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme private static final String MAX_SUFFIX = ".max"; 179bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private static final String NAME_SUFFIX = ".name"; 1809cadb75714bea7d435f34c8b7d06f698fa907a8dFelipe Leme 181bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** System property (and value) used to stop dumpstate. */ 182d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme // TODO: should call ActiveManager API instead 183719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme private static final String CTL_STOP = "ctl.stop"; 1844cc863338d5e43b6189e05498d7cb53ebba135e1Felipe Leme private static final String BUGREPORT_SERVICE = "bugreportplus"; 18569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 186d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 187d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Directory on Shell's data storage where screenshots will be stored. 188d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * <p> 189d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Must be a path supported by its FileProvider. 190d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 191d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private static final String SCREENSHOT_DIR = "bugreports"; 192d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 193fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme /** Managed dumpstate processes (keyed by id) */ 19469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme private final SparseArray<BugreportInfo> mProcesses = new SparseArray<>(); 19569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 196d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private Context mContext; 197d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private ServiceHandler mMainHandler; 198d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private ScreenshotHandler mScreenshotHandler; 19969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 200bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private final BugreportInfoDialog mInfoDialog = new BugreportInfoDialog(); 201bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 202d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private File mScreenshotsDir; 203d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 204d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 20569c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme * id of the notification used to set service on foreground. 20669c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme */ 20769c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme private int mForegroundId = -1; 20869c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme 20969c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme /** 210d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Flag indicating whether a screenshot is being taken. 211d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * <p> 212d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * This is the only state that is shared between the 2 handlers and hence must have synchronized 213d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * access. 214d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 215d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private boolean mTakingScreenshot; 216d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 21765a9c6760ef6cf1c8e1762a271aa43c626d27048Felipe Leme private static final Bundle sNotificationBundle = new Bundle(); 21865a9c6760ef6cf1c8e1762a271aa43c626d27048Felipe Leme 2199f3554176019543e654be7dba5410de2bbe3b55fWei Liu private boolean mIsWatch; 2209f3554176019543e654be7dba5410de2bbe3b55fWei Liu 22169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme @Override 22269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public void onCreate() { 223d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mContext = getApplicationContext(); 224d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mMainHandler = new ServiceHandler("BugreportProgressServiceMainThread"); 225d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mScreenshotHandler = new ScreenshotHandler("BugreportProgressServiceScreenshotThread"); 226d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 2274f663f6c25488a83a438a01dfef8372aa4e6b2aaFelipe Leme mScreenshotsDir = new File(getFilesDir(), SCREENSHOT_DIR); 228d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (!mScreenshotsDir.exists()) { 229d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.i(TAG, "Creating directory " + mScreenshotsDir + " to store temporary screenshots"); 230d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (!mScreenshotsDir.mkdir()) { 231d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.w(TAG, "Could not create directory " + mScreenshotsDir); 232d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 233d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 2349f3554176019543e654be7dba5410de2bbe3b55fWei Liu final Configuration conf = mContext.getResources().getConfiguration(); 2359f3554176019543e654be7dba5410de2bbe3b55fWei Liu mIsWatch = (conf.uiMode & Configuration.UI_MODE_TYPE_MASK) == 2369f3554176019543e654be7dba5410de2bbe3b55fWei Liu Configuration.UI_MODE_TYPE_WATCH; 23769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 238b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 239b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme @Override 240b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme public int onStartCommand(Intent intent, int flags, int startId) { 241abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme Log.v(TAG, "onStartCommand(): " + dumpIntent(intent)); 242b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (intent != null) { 24369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme // Handle it in a separate thread. 244d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Message msg = mMainHandler.obtainMessage(); 24569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme msg.what = MSG_SERVICE_COMMAND; 24669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme msg.obj = intent; 247d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mMainHandler.sendMessage(msg); 248b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 24969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 25069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme // If service is killed it cannot be recreated because it would not know which 251fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme // dumpstate IDs it would have to watch. 252b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return START_NOT_STICKY; 253b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 254b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 255b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme @Override 256b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme public IBinder onBind(Intent intent) { 257b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return null; 258b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 259b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 26069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme @Override 26169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public void onDestroy() { 262d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mMainHandler.getLooper().quit(); 263d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mScreenshotHandler.getLooper().quit(); 26469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme super.onDestroy(); 26569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 266b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 26769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme @Override 26869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 269d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final int size = mProcesses.size(); 270d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (size == 0) { 271d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme writer.printf("No monitored processes"); 272d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 273d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 27469c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme writer.printf("Foreground id: %d\n\n", mForegroundId); 275d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme writer.printf("Monitored dumpstate processes\n"); 276d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme writer.printf("-----------------------------\n"); 277d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme for (int i = 0; i < size; i++) { 278d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme writer.printf("%s\n", mProcesses.valueAt(i)); 27969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 28069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 28169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 282d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 283d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Main thread used to handle all requests but taking screenshots. 284d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 28569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme private final class ServiceHandler extends Handler { 286d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme public ServiceHandler(String name) { 287d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme super(newLooper(name)); 28869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 28969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 29069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme @Override 29169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public void handleMessage(Message msg) { 29269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme if (msg.what == MSG_POLL) { 293923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme poll(); 29469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return; 29569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 29669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 297d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (msg.what == MSG_DELAYED_SCREENSHOT) { 298d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme takeScreenshot(msg.arg1, msg.arg2); 299d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 300d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 301d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 302d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (msg.what == MSG_SCREENSHOT_RESPONSE) { 303d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme handleScreenshotResponse(msg); 304d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 305d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 306d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 30769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme if (msg.what != MSG_SERVICE_COMMAND) { 30869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme // Sanity check. 30969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme Log.e(TAG, "Invalid message type: " + msg.what); 31069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return; 31169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 31269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 31346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme // At this point it's handling onStartCommand(), with the intent passed as an Extra. 31469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme if (!(msg.obj instanceof Intent)) { 31569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme // Sanity check. 316af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme Log.wtf(TAG, "handleMessage(): invalid msg.obj type: " + msg.obj); 31769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return; 31869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 31969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme final Parcelable parcel = ((Intent) msg.obj).getParcelableExtra(EXTRA_ORIGINAL_INTENT); 320abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme Log.v(TAG, "handleMessage(): " + dumpIntent((Intent) parcel)); 32146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final Intent intent; 32246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme if (parcel instanceof Intent) { 32346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme // The real intent was passed to BugreportReceiver, which delegated to the service. 32446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme intent = (Intent) parcel; 32546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } else { 32646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme intent = (Intent) msg.obj; 32769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 32869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme final String action = intent.getAction(); 32946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final int pid = intent.getIntExtra(EXTRA_PID, 0); 33085ae3cf46ad66d71e5a29a93e89a0f569d74288bFelipe Leme final int id = intent.getIntExtra(EXTRA_ID, 0); 33146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final int max = intent.getIntExtra(EXTRA_MAX, -1); 33246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final String name = intent.getStringExtra(EXTRA_NAME); 33369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 334fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme if (DEBUG) 335fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.v(TAG, "action: " + action + ", name: " + name + ", id: " + id + ", pid: " 336fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme + pid + ", max: " + max); 33769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme switch (action) { 33869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme case INTENT_BUGREPORT_STARTED: 339fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme if (!startProgress(name, id, pid, max)) { 34069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme stopSelfWhenDone(); 34169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return; 34269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 34346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme poll(); 34469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme break; 34569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme case INTENT_BUGREPORT_FINISHED: 346fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme if (id == 0) { 34769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme // Shouldn't happen, unless BUGREPORT_FINISHED is received from a legacy, 34869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme // out-of-sync dumpstate process. 349fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.w(TAG, "Missing " + EXTRA_ID + " on intent " + intent); 35069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 351fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme onBugreportFinished(id, intent); 35246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme break; 353bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme case INTENT_BUGREPORT_INFO_LAUNCH: 354fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme launchBugreportInfoDialog(id); 355bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme break; 356d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme case INTENT_BUGREPORT_SCREENSHOT: 357079f89614c49364bb907783b008827fbc306dd73Felipe Leme takeScreenshot(id); 358d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme break; 35946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme case INTENT_BUGREPORT_SHARE: 360fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme shareBugreport(id, (BugreportInfo) intent.getParcelableExtra(EXTRA_INFO)); 36169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme break; 3629cadb75714bea7d435f34c8b7d06f698fa907a8dFelipe Leme case INTENT_BUGREPORT_CANCEL: 363fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme cancel(id); 3649cadb75714bea7d435f34c8b7d06f698fa907a8dFelipe Leme break; 36569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme default: 36669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme Log.w(TAG, "Unsupported intent: " + action); 36769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 36869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return; 36969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 37069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 37169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 372923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme private void poll() { 373923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme if (pollProgress()) { 374923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme // Keep polling... 375923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme sendEmptyMessageDelayed(MSG_POLL, POLLING_FREQUENCY); 37646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } else { 37746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme Log.i(TAG, "Stopped polling"); 37869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 379923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 380923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 38169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 382923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme /** 383d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Separate thread used only to take screenshots so it doesn't block the main thread. 384d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 385d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private final class ScreenshotHandler extends Handler { 386d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme public ScreenshotHandler(String name) { 387d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme super(newLooper(name)); 388d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 389d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 390d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme @Override 391d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme public void handleMessage(Message msg) { 392d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (msg.what != MSG_SCREENSHOT_REQUEST) { 393d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.e(TAG, "Invalid message type: " + msg.what); 394d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 395d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 396d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme handleScreenshotRequest(msg); 397d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 398d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 399d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 400fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme private BugreportInfo getInfo(int id) { 401fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme final BugreportInfo info = mProcesses.get(id); 402d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 403fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.w(TAG, "Not monitoring process with ID " + id); 404d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 405d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return info; 406d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 407d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 408d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 409923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * Creates the {@link BugreportInfo} for a process and issue a system notification to 410923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * indicate its progress. 411923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * 412923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * @return whether it succeeded or not. 413923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme */ 414fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme private boolean startProgress(String name, int id, int pid, int max) { 415923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme if (name == null) { 416923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme Log.w(TAG, "Missing " + EXTRA_NAME + " on start intent"); 417923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 418fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme if (id == -1) { 419fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.e(TAG, "Missing " + EXTRA_ID + " on start intent"); 420fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme return false; 421fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme } 422923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme if (pid == -1) { 423923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme Log.e(TAG, "Missing " + EXTRA_PID + " on start intent"); 424923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme return false; 425923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 426923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme if (max <= 0) { 427923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme Log.e(TAG, "Invalid value for extra " + EXTRA_MAX + ": " + max); 428923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme return false; 42969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 43069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 431fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme final BugreportInfo info = new BugreportInfo(mContext, id, pid, name, max); 432fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme if (mProcesses.indexOfKey(id) >= 0) { 4331ae5a69bc495154d0baf504caa95d7eddbc7177cFelipe Leme // BUGREPORT_STARTED intent was already received; ignore it. 434fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.w(TAG, "ID " + id + " already watched"); 4351ae5a69bc495154d0baf504caa95d7eddbc7177cFelipe Leme return true; 436923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 4371ae5a69bc495154d0baf504caa95d7eddbc7177cFelipe Leme mProcesses.put(info.id, info); 438923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme updateProgress(info); 439923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme return true; 440923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 44169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 442923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme /** 44346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Updates the system notification for a given bugreport. 444923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme */ 445923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme private void updateProgress(BugreportInfo info) { 446923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme if (info.max <= 0 || info.progress < 0) { 447923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme Log.e(TAG, "Invalid progress values for " + info); 448923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme return; 44969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 45069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 4519f3554176019543e654be7dba5410de2bbe3b55fWei Liu if (info.finished) { 4529f3554176019543e654be7dba5410de2bbe3b55fWei Liu Log.w(TAG, "Not sending progress notification because bugreport has finished already (" 4539f3554176019543e654be7dba5410de2bbe3b55fWei Liu + info + ")"); 4549f3554176019543e654be7dba5410de2bbe3b55fWei Liu return; 4559f3554176019543e654be7dba5410de2bbe3b55fWei Liu } 4569f3554176019543e654be7dba5410de2bbe3b55fWei Liu 457923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme final NumberFormat nf = NumberFormat.getPercentInstance(); 458923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme nf.setMinimumFractionDigits(2); 459923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme nf.setMaximumFractionDigits(2); 4603fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme final String percentageText = nf.format((double) info.progress / info.max); 4619f3554176019543e654be7dba5410de2bbe3b55fWei Liu 4629f3554176019543e654be7dba5410de2bbe3b55fWei Liu String title = mContext.getString(R.string.bugreport_in_progress_title, info.id); 4639f3554176019543e654be7dba5410de2bbe3b55fWei Liu 4649f3554176019543e654be7dba5410de2bbe3b55fWei Liu // TODO: Remove this workaround when notification progress is implemented on Wear. 4659f3554176019543e654be7dba5410de2bbe3b55fWei Liu if (mIsWatch) { 4669f3554176019543e654be7dba5410de2bbe3b55fWei Liu nf.setMinimumFractionDigits(0); 4679f3554176019543e654be7dba5410de2bbe3b55fWei Liu nf.setMaximumFractionDigits(0); 4689f3554176019543e654be7dba5410de2bbe3b55fWei Liu final String watchPercentageText = nf.format((double) info.progress / info.max); 4699f3554176019543e654be7dba5410de2bbe3b55fWei Liu title = title + "\n" + watchPercentageText; 4709f3554176019543e654be7dba5410de2bbe3b55fWei Liu } 471923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme 472923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme final String name = 473d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.name != null ? info.name : mContext.getString(R.string.bugreport_unnamed); 474923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme 4759f3554176019543e654be7dba5410de2bbe3b55fWei Liu final Notification.Builder builder = newBaseNotification(mContext) 476923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .setContentTitle(title) 477923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .setTicker(title) 478923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .setContentText(name) 479923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .setProgress(info.max, info.progress, false) 4809f3554176019543e654be7dba5410de2bbe3b55fWei Liu .setOngoing(true); 4819f3554176019543e654be7dba5410de2bbe3b55fWei Liu 4829f3554176019543e654be7dba5410de2bbe3b55fWei Liu // Wear bugreport doesn't need the bug info dialog, screenshot and cancel action. 4839f3554176019543e654be7dba5410de2bbe3b55fWei Liu if (!mIsWatch) { 4849f3554176019543e654be7dba5410de2bbe3b55fWei Liu final Action cancelAction = new Action.Builder(null, mContext.getString( 4859f3554176019543e654be7dba5410de2bbe3b55fWei Liu com.android.internal.R.string.cancel), newCancelIntent(mContext, info)).build(); 4869f3554176019543e654be7dba5410de2bbe3b55fWei Liu final Intent infoIntent = new Intent(mContext, BugreportProgressService.class); 4879f3554176019543e654be7dba5410de2bbe3b55fWei Liu infoIntent.setAction(INTENT_BUGREPORT_INFO_LAUNCH); 4889f3554176019543e654be7dba5410de2bbe3b55fWei Liu infoIntent.putExtra(EXTRA_ID, info.id); 4899f3554176019543e654be7dba5410de2bbe3b55fWei Liu final PendingIntent infoPendingIntent = 4909f3554176019543e654be7dba5410de2bbe3b55fWei Liu PendingIntent.getService(mContext, info.id, infoIntent, 4919f3554176019543e654be7dba5410de2bbe3b55fWei Liu PendingIntent.FLAG_UPDATE_CURRENT); 4929f3554176019543e654be7dba5410de2bbe3b55fWei Liu final Action infoAction = new Action.Builder(null, 4939f3554176019543e654be7dba5410de2bbe3b55fWei Liu mContext.getString(R.string.bugreport_info_action), 4949f3554176019543e654be7dba5410de2bbe3b55fWei Liu infoPendingIntent).build(); 4959f3554176019543e654be7dba5410de2bbe3b55fWei Liu final Intent screenshotIntent = new Intent(mContext, BugreportProgressService.class); 4969f3554176019543e654be7dba5410de2bbe3b55fWei Liu screenshotIntent.setAction(INTENT_BUGREPORT_SCREENSHOT); 4979f3554176019543e654be7dba5410de2bbe3b55fWei Liu screenshotIntent.putExtra(EXTRA_ID, info.id); 4989f3554176019543e654be7dba5410de2bbe3b55fWei Liu PendingIntent screenshotPendingIntent = mTakingScreenshot ? null : PendingIntent 4999f3554176019543e654be7dba5410de2bbe3b55fWei Liu .getService(mContext, info.id, screenshotIntent, 5009f3554176019543e654be7dba5410de2bbe3b55fWei Liu PendingIntent.FLAG_UPDATE_CURRENT); 5019f3554176019543e654be7dba5410de2bbe3b55fWei Liu final Action screenshotAction = new Action.Builder(null, 5029f3554176019543e654be7dba5410de2bbe3b55fWei Liu mContext.getString(R.string.bugreport_screenshot_action), 5039f3554176019543e654be7dba5410de2bbe3b55fWei Liu screenshotPendingIntent).build(); 5049f3554176019543e654be7dba5410de2bbe3b55fWei Liu builder.setContentIntent(infoPendingIntent) 5059f3554176019543e654be7dba5410de2bbe3b55fWei Liu .setActions(infoAction, screenshotAction, cancelAction); 5062288129d5208cd26ab41191db69a418d15ead9eeFelipe Leme } 5079f3554176019543e654be7dba5410de2bbe3b55fWei Liu 508262887834c7938b7a7bbbd7ef026b5965d6412fcFelipe Leme if (DEBUG) { 50969c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme Log.d(TAG, "Sending 'Progress' notification for id " + info.id + " (pid " + info.pid 5103fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme + "): " + percentageText); 511262887834c7938b7a7bbbd7ef026b5965d6412fcFelipe Leme } 5129f3554176019543e654be7dba5410de2bbe3b55fWei Liu sendForegroundabledNotification(info.id, builder.build()); 51369c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme } 51469c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme 51569c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme private void sendForegroundabledNotification(int id, Notification notification) { 51669c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme if (mForegroundId >= 0) { 51769c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme if (DEBUG) Log.d(TAG, "Already running as foreground service"); 51869c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme NotificationManager.from(mContext).notify(id, notification); 51969c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme } else { 52069c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme mForegroundId = id; 52169c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme Log.d(TAG, "Start running as foreground service on id " + mForegroundId); 52269c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme startForeground(mForegroundId, notification); 52369c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme } 524923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 525923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme 526923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme /** 52746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Creates a {@link PendingIntent} for a notification action used to cancel a bugreport. 52846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 52946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme private static PendingIntent newCancelIntent(Context context, BugreportInfo info) { 53046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final Intent intent = new Intent(INTENT_BUGREPORT_CANCEL); 53146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme intent.setClass(context, BugreportProgressService.class); 532fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme intent.putExtra(EXTRA_ID, info.id); 533fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme return PendingIntent.getService(context, info.id, intent, 534bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme PendingIntent.FLAG_UPDATE_CURRENT); 53546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } 53646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 53746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 53846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Finalizes the progress on a given bugreport and cancel its notification. 539923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme */ 540fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme private void stopProgress(int id) { 541fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme if (mProcesses.indexOfKey(id) < 0) { 542fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.w(TAG, "ID not watched: " + id); 543d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } else { 544fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.d(TAG, "Removing ID " + id); 545fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme mProcesses.remove(id); 54669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 54769c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme // Must stop foreground service first, otherwise notif.cancel() will fail below. 54869c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme stopForegroundWhenDone(id); 54969c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme Log.d(TAG, "stopProgress(" + id + "): cancel notification"); 55069c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme NotificationManager.from(mContext).cancel(id); 5510f2daaf2f7f0f9a35512e452231fd34e743ddc51Felipe Leme stopSelfWhenDone(); 552923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 553923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme 554923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme /** 555923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * Cancels a bugreport upon user's request. 556923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme */ 557fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme private void cancel(int id) { 5586605bd89c53494b59717a826f9a17641bc32da41Felipe Leme MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_CANCEL); 559fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.v(TAG, "cancel: ID=" + id); 560fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme final BugreportInfo info = getInfo(id); 561d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info != null && !info.finished) { 562fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.i(TAG, "Cancelling bugreport service (ID=" + id + ") on user's request"); 563d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme setSystemProperty(CTL_STOP, BUGREPORT_SERVICE); 564d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme deleteScreenshots(info); 565bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 566fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme stopProgress(id); 567923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 5689cadb75714bea7d435f34c8b7d06f698fa907a8dFelipe Leme 569923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme /** 570923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * Poll {@link SystemProperties} to get the progress on each monitored process. 571923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * 572923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * @return whether it should keep polling. 573923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme */ 574923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme private boolean pollProgress() { 575d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final int total = mProcesses.size(); 576d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (total == 0) { 577d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.d(TAG, "No process to poll progress."); 578d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 579d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme int activeProcesses = 0; 580d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme for (int i = 0; i < total; i++) { 581d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final BugreportInfo info = mProcesses.valueAt(i); 582af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme if (info == null) { 583fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.wtf(TAG, "pollProgress(): null info at index " + i + "(ID = " 584af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme + mProcesses.keyAt(i) + ")"); 585af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme continue; 586af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme } 587af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme 588af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme final int pid = info.pid; 589fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme final int id = info.id; 590d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info.finished) { 59185ae3cf46ad66d71e5a29a93e89a0f569d74288bFelipe Leme if (DEBUG) Log.v(TAG, "Skipping finished process " + pid + " (id: " + id + ")"); 592d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme continue; 593923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 594d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme activeProcesses++; 595d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final String progressKey = DUMPSTATE_PREFIX + pid + PROGRESS_SUFFIX; 5963fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme info.realProgress = SystemProperties.getInt(progressKey, 0); 5973fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme if (info.realProgress == 0) { 598d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.v(TAG, "System property " + progressKey + " is not set yet"); 599d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 6003fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme final String maxKey = DUMPSTATE_PREFIX + pid + MAX_SUFFIX; 6013fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme info.realMax = SystemProperties.getInt(maxKey, info.max); 6023fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme if (info.realMax <= 0 ) { 6033fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme Log.w(TAG, "Property " + maxKey + " is not positive: " + info.max); 6043fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme continue; 6053fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme } 6063fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme /* 6073fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme * Checks whether the progress changed in a way that should be displayed to the user: 6083fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme * - info.progress / info.max represents the displayed progress 6093fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme * - info.realProgress / info.realMax represents the real progress 6103fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme * - since the real progress can decrease, the displayed progress is only updated if it 6113fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme * increases 6123fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme * - the displayed progress is capped at a maximum (like 99%) 6133fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme */ 6143fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme final int oldPercentage = (CAPPED_MAX * info.progress) / info.max; 6153fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme int newPercentage = (CAPPED_MAX * info.realProgress) / info.realMax; 6163fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme int max = info.realMax; 6173fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme int progress = info.realProgress; 6183fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme 6193fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme if (newPercentage > CAPPED_PROGRESS) { 6203fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme progress = newPercentage = CAPPED_PROGRESS; 6213fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme max = CAPPED_MAX; 6223fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme } 6233fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme 6243fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme if (newPercentage > oldPercentage) { 6253fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme if (DEBUG) { 6263fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme if (progress != info.progress) { 6273fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme Log.v(TAG, "Updating progress for PID " + pid + "(id: " + id + ") from " 6283fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme + info.progress + " to " + progress); 6293fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme } 6303fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme if (max != info.max) { 6313fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme Log.v(TAG, "Updating max progress for PID " + pid + "(id: " + id + ") from " 6323fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme + info.max + " to " + max); 6333fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme } 63469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 6353fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme info.progress = progress; 6363fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme info.max = max; 637d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.lastUpdate = System.currentTimeMillis(); 638d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme updateProgress(info); 639d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } else { 640d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme long inactiveTime = System.currentTimeMillis() - info.lastUpdate; 641d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (inactiveTime >= INACTIVITY_TIMEOUT) { 642fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.w(TAG, "No progress update for PID " + pid + " since " 643d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme + info.getFormattedLastUpdate()); 644fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme stopProgress(info.id); 64569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 64669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 64769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 648d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (DEBUG) Log.v(TAG, "pollProgress() total=" + total + ", actives=" + activeProcesses); 649d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return activeProcesses > 0; 650923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 65169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 652923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme /** 653bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Fetches a {@link BugreportInfo} for a given process and launches a dialog where the user can 654bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * change its values. 655bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 656fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme private void launchBugreportInfoDialog(int id) { 6576605bd89c53494b59717a826f9a17641bc32da41Felipe Leme MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_DETAILS); 658bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // Copy values so it doesn't lock mProcesses while UI is being updated 659bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final String name, title, description; 660fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme final BugreportInfo info = getInfo(id); 661d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 6621eee1996f5d57b42d4beed2790321480fbe03a4cFelipe Leme // Most likely am killed Shell before user tapped the notification. Since system might 6631eee1996f5d57b42d4beed2790321480fbe03a4cFelipe Leme // be too busy anwyays, it's better to ignore the notification and switch back to the 6641eee1996f5d57b42d4beed2790321480fbe03a4cFelipe Leme // non-interactive mode (where the bugerport will be shared upon completion). 665bbd91e58444f092e1080f5a0a746fcd4b21ce113Felipe Leme Log.w(TAG, "launchBugreportInfoDialog(): canceling notification because id " + id 666bbd91e58444f092e1080f5a0a746fcd4b21ce113Felipe Leme + " was not found"); 6671eee1996f5d57b42d4beed2790321480fbe03a4cFelipe Leme // TODO: add test case to make sure notification is canceled. 66869c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme NotificationManager.from(mContext).cancel(id); 669d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 670d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 671d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 672d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme collapseNotificationBar(); 673fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme mInfoDialog.initialize(mContext, info); 674d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 675d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 676d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 677d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Starting point for taking a screenshot. 678d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * <p> 679079f89614c49364bb907783b008827fbc306dd73Felipe Leme * It first display a toast message and waits {@link #SCREENSHOT_DELAY_SECONDS} seconds before 680079f89614c49364bb907783b008827fbc306dd73Felipe Leme * taking the screenshot. 681d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 682079f89614c49364bb907783b008827fbc306dd73Felipe Leme private void takeScreenshot(int id) { 683079f89614c49364bb907783b008827fbc306dd73Felipe Leme MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SCREENSHOT); 6841eee1996f5d57b42d4beed2790321480fbe03a4cFelipe Leme if (getInfo(id) == null) { 6851eee1996f5d57b42d4beed2790321480fbe03a4cFelipe Leme // Most likely am killed Shell before user tapped the notification. Since system might 6861eee1996f5d57b42d4beed2790321480fbe03a4cFelipe Leme // be too busy anwyays, it's better to ignore the notification and switch back to the 6871eee1996f5d57b42d4beed2790321480fbe03a4cFelipe Leme // non-interactive mode (where the bugerport will be shared upon completion). 688bbd91e58444f092e1080f5a0a746fcd4b21ce113Felipe Leme Log.w(TAG, "takeScreenshot(): canceling notification because id " + id 689bbd91e58444f092e1080f5a0a746fcd4b21ce113Felipe Leme + " was not found"); 6901eee1996f5d57b42d4beed2790321480fbe03a4cFelipe Leme // TODO: add test case to make sure notification is canceled. 69169c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme NotificationManager.from(mContext).cancel(id); 6921eee1996f5d57b42d4beed2790321480fbe03a4cFelipe Leme return; 6931eee1996f5d57b42d4beed2790321480fbe03a4cFelipe Leme } 694d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme setTakingScreenshot(true); 695079f89614c49364bb907783b008827fbc306dd73Felipe Leme collapseNotificationBar(); 696079f89614c49364bb907783b008827fbc306dd73Felipe Leme final String msg = mContext.getResources() 697079f89614c49364bb907783b008827fbc306dd73Felipe Leme .getQuantityString(com.android.internal.R.plurals.bugreport_countdown, 698079f89614c49364bb907783b008827fbc306dd73Felipe Leme SCREENSHOT_DELAY_SECONDS, SCREENSHOT_DELAY_SECONDS); 699079f89614c49364bb907783b008827fbc306dd73Felipe Leme Log.i(TAG, msg); 700079f89614c49364bb907783b008827fbc306dd73Felipe Leme // Show a toast just once, otherwise it might be captured in the screenshot. 701079f89614c49364bb907783b008827fbc306dd73Felipe Leme Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show(); 702079f89614c49364bb907783b008827fbc306dd73Felipe Leme 703079f89614c49364bb907783b008827fbc306dd73Felipe Leme takeScreenshot(id, SCREENSHOT_DELAY_SECONDS); 704d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 705d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 706d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 707d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Takes a screenshot after {@code delay} seconds. 708d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 709fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme private void takeScreenshot(int id, int delay) { 710d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (delay > 0) { 711fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.d(TAG, "Taking screenshot for " + id + " in " + delay + " seconds"); 712d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Message msg = mMainHandler.obtainMessage(); 713d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme msg.what = MSG_DELAYED_SCREENSHOT; 714fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme msg.arg1 = id; 715d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme msg.arg2 = delay - 1; 716d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mMainHandler.sendMessageDelayed(msg, DateUtils.SECOND_IN_MILLIS); 717d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 718d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 719d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 720d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme // It's time to take the screenshot: let the proper thread handle it 721fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme final BugreportInfo info = getInfo(id); 722d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 723d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 724d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 725d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final String screenshotPath = 726d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme new File(mScreenshotsDir, info.getPathNextScreenshot()).getAbsolutePath(); 727d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 7288648a15406d43c8af12e9cfe2355b1eee201d479Felipe Leme Message.obtain(mScreenshotHandler, MSG_SCREENSHOT_REQUEST, id, UNUSED_ARG2, screenshotPath) 7298648a15406d43c8af12e9cfe2355b1eee201d479Felipe Leme .sendToTarget(); 730d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 731d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 732d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 733d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Sets the internal {@code mTakingScreenshot} state and updates all notifications so their 734d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * SCREENSHOT button is enabled or disabled accordingly. 735d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 736d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void setTakingScreenshot(boolean flag) { 737d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme synchronized (BugreportProgressService.this) { 738d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mTakingScreenshot = flag; 739d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme for (int i = 0; i < mProcesses.size(); i++) { 7402288129d5208cd26ab41191db69a418d15ead9eeFelipe Leme final BugreportInfo info = mProcesses.valueAt(i); 7412288129d5208cd26ab41191db69a418d15ead9eeFelipe Leme if (info.finished) { 742abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme Log.d(TAG, "Not updating progress for " + info.id + " while taking screenshot" 743abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme + " because share notification was already sent"); 7442288129d5208cd26ab41191db69a418d15ead9eeFelipe Leme continue; 7452288129d5208cd26ab41191db69a418d15ead9eeFelipe Leme } 7462288129d5208cd26ab41191db69a418d15ead9eeFelipe Leme updateProgress(info); 747bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 748bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 749d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 750bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 751d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void handleScreenshotRequest(Message requestMsg) { 752d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme String screenshotFile = (String) requestMsg.obj; 753d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme boolean taken = takeScreenshot(mContext, screenshotFile); 754d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme setTakingScreenshot(false); 755d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 7568648a15406d43c8af12e9cfe2355b1eee201d479Felipe Leme Message.obtain(mMainHandler, MSG_SCREENSHOT_RESPONSE, requestMsg.arg1, taken ? 1 : 0, 7578648a15406d43c8af12e9cfe2355b1eee201d479Felipe Leme screenshotFile).sendToTarget(); 758d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 759bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 760d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void handleScreenshotResponse(Message resultMsg) { 761d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final boolean taken = resultMsg.arg2 != 0; 762d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final BugreportInfo info = getInfo(resultMsg.arg1); 763d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 764d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 765d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 766d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final File screenshotFile = new File((String) resultMsg.obj); 767d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 7685d9000aa45c19de0e7ec4131c1aca3d366e9a793Felipe Leme final String msg; 769d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (taken) { 770d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.addScreenshot(screenshotFile); 771c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme if (info.finished) { 772c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme Log.d(TAG, "Screenshot finished after bugreport; updating share notification"); 773c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme info.renameScreenshots(mScreenshotsDir); 77469c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme sendBugreportNotification(info, mTakingScreenshot); 775c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme } 7765d9000aa45c19de0e7ec4131c1aca3d366e9a793Felipe Leme msg = mContext.getString(R.string.bugreport_screenshot_taken); 777d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } else { 7785d9000aa45c19de0e7ec4131c1aca3d366e9a793Felipe Leme msg = mContext.getString(R.string.bugreport_screenshot_failed); 7795d9000aa45c19de0e7ec4131c1aca3d366e9a793Felipe Leme Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show(); 780d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 781d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.d(TAG, msg); 782d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 783d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 784d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 785d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Deletes all screenshots taken for a given bugreport. 786d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 787d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void deleteScreenshots(BugreportInfo info) { 788d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme for (File file : info.screenshotFiles) { 789d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.i(TAG, "Deleting screenshot file " + file); 790d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme file.delete(); 791d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 792bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 793bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 794bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 79569c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme * Stop running on foreground once there is no more active bugreports being watched. 79669c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme */ 79769c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme private void stopForegroundWhenDone(int id) { 79869c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme if (id != mForegroundId) { 79969c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme Log.d(TAG, "stopForegroundWhenDone(" + id + "): ignoring since foreground id is " 80069c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme + mForegroundId); 80169c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme return; 80269c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme } 80369c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme 80469c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme Log.d(TAG, "detaching foreground from id " + mForegroundId); 80569c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme stopForeground(Service.STOP_FOREGROUND_DETACH); 80669c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme mForegroundId = -1; 80769c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme 80869c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme // Might need to restart foreground using a new notification id. 80969c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme final int total = mProcesses.size(); 81069c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme if (total > 0) { 81169c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme for (int i = 0; i < total; i++) { 81269c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme final BugreportInfo info = mProcesses.valueAt(i); 81369c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme if (!info.finished) { 81469c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme updateProgress(info); 81569c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme break; 81669c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme } 81769c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme } 81869c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme } 81969c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme } 82069c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme 82169c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme /** 822923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * Finishes the service when it's not monitoring any more processes. 823923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme */ 824923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme private void stopSelfWhenDone() { 825d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (mProcesses.size() > 0) { 826fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme if (DEBUG) Log.d(TAG, "Staying alive, waiting for IDs " + mProcesses); 827d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 82869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 829fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.v(TAG, "No more processes to handle, shutting down"); 830d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme stopSelf(); 831923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 83269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 833bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 834bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Handles the BUGREPORT_FINISHED intent sent by {@code dumpstate}. 835bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 836fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme private void onBugreportFinished(int id, Intent intent) { 837af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT); 838c6905cfb1133627dfd500491c60b6528a3e593e0Ben Lin // Since BugreportProvider and BugreportProgressService aren't tightly coupled, 839c6905cfb1133627dfd500491c60b6528a3e593e0Ben Lin // we need to make sure they are explicitly tied to a single unique notification URI 840c6905cfb1133627dfd500491c60b6528a3e593e0Ben Lin // so that the service can alert the provider of changes it has done (ie. new bug 841c6905cfb1133627dfd500491c60b6528a3e593e0Ben Lin // reports) 842c6905cfb1133627dfd500491c60b6528a3e593e0Ben Lin // See { @link Cursor#setNotificationUri } and {@link ContentResolver#notifyChanges } 843c6905cfb1133627dfd500491c60b6528a3e593e0Ben Lin final Uri notificationUri = BugreportStorageProvider.getNotificationUri(); 844c6905cfb1133627dfd500491c60b6528a3e593e0Ben Lin mContext.getContentResolver().notifyChange(notificationUri, null, false); 845c6905cfb1133627dfd500491c60b6528a3e593e0Ben Lin 846af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme if (bugreportFile == null) { 847af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme // Should never happen, dumpstate always set the file. 848af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent); 849af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme return; 850af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme } 851fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme mInfoDialog.onBugreportFinished(id); 852fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme BugreportInfo info = getInfo(id); 853d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 854d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme // Happens when BUGREPORT_FINISHED was received without a BUGREPORT_STARTED first. 855fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.v(TAG, "Creating info for untracked ID " + id); 856fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme info = new BugreportInfo(mContext, id); 857fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme mProcesses.put(id, info); 858d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 859d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.renameScreenshots(mScreenshotsDir); 860af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme info.bugreportFile = bugreportFile; 861af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme 862510e922e47fec69839dd48c5473540f93d79a508Felipe Leme final int max = intent.getIntExtra(EXTRA_MAX, -1); 863510e922e47fec69839dd48c5473540f93d79a508Felipe Leme if (max != -1) { 864510e922e47fec69839dd48c5473540f93d79a508Felipe Leme MetricsLogger.histogram(this, "dumpstate_duration", max); 865510e922e47fec69839dd48c5473540f93d79a508Felipe Leme info.max = max; 866510e922e47fec69839dd48c5473540f93d79a508Felipe Leme } 867510e922e47fec69839dd48c5473540f93d79a508Felipe Leme 868d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final File screenshot = getFileExtra(intent, EXTRA_SCREENSHOT); 869d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (screenshot != null) { 870d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.addScreenshot(screenshot); 87146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } 872d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.finished = true; 87369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 87469c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme // Stop running on foreground, otherwise share notification cannot be dismissed. 87569c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme stopForegroundWhenDone(id); 87669c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme 8779f3554176019543e654be7dba5410de2bbe3b55fWei Liu triggerLocalNotification(mContext, info); 878b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 879b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 880b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme /** 88169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * Responsible for triggering a notification that allows the user to start a "share" intent with 88246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * the bugreport. On watches we have other methods to allow the user to start this intent 88369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * (usually by triggering it on another connected device); we don't need to display the 88469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * notification in this case. 885b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme */ 886d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void triggerLocalNotification(final Context context, final BugreportInfo info) { 88746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) { 88846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme Log.e(TAG, "Could not read bugreport file " + info.bugreportFile); 889d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Toast.makeText(context, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show(); 890fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme stopProgress(info.id); 891b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return; 892b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 893b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 89446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme boolean isPlainText = info.bugreportFile.getName().toLowerCase().endsWith(".txt"); 895b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (!isPlainText) { 896b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // Already zipped, send it right away. 89769c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme sendBugreportNotification(info, mTakingScreenshot); 898b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } else { 899b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // Asynchronously zip the file first, then send it. 90069c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme sendZippedBugreportNotification(info, mTakingScreenshot); 901b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 902b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 903b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 904b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme private static Intent buildWarningIntent(Context context, Intent sendIntent) { 905b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final Intent intent = new Intent(context, BugreportWarningActivity.class); 906b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.putExtra(Intent.EXTRA_INTENT, sendIntent); 907b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return intent; 908b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 909b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 910b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme /** 911b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * Build {@link Intent} that can be used to share the given bugreport. 912b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme */ 913bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private static Intent buildSendIntent(Context context, BugreportInfo info) { 914bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // Files are kept on private storage, so turn into Uris that we can 915bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // grant temporary permissions for. 916abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme final Uri bugreportUri; 917abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme try { 918abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme bugreportUri = getUri(context, info.bugreportFile); 919abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme } catch (IllegalArgumentException e) { 920abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme // Should not happen on production, but happens when a Shell is sideloaded and 921abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme // FileProvider cannot find a configured root for it. 922abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme Log.wtf(TAG, "Could not get URI for " + info.bugreportFile, e); 923abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme return null; 924abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme } 925bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 926b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE); 927b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final String mimeType = "application/vnd.android.bugreport"; 928b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 929b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.addCategory(Intent.CATEGORY_DEFAULT); 930b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.setType(mimeType); 931b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 932c8e2b6092c0fbf87e71f81fd2cffbb29ff8d9039Felipe Leme final String subject = !TextUtils.isEmpty(info.title) ? 933c8e2b6092c0fbf87e71f81fd2cffbb29ff8d9039Felipe Leme info.title : bugreportUri.getLastPathSegment(); 934bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme intent.putExtra(Intent.EXTRA_SUBJECT, subject); 935b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 936b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // EXTRA_TEXT should be an ArrayList, but some clients are expecting a single String. 937b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // So, to avoid an exception on Intent.migrateExtraStreamToClipData(), we need to manually 938b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // create the ClipData object with the attachments URIs. 939d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final StringBuilder messageBody = new StringBuilder("Build info: ") 940bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .append(SystemProperties.get("ro.build.description")) 941bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .append("\nSerial number: ") 942bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .append(SystemProperties.get("ro.serialno")); 943bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (!TextUtils.isEmpty(info.description)) { 944bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme messageBody.append("\nDescription: ").append(info.description); 945bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 946bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme intent.putExtra(Intent.EXTRA_TEXT, messageBody.toString()); 947b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final ClipData clipData = new ClipData(null, new String[] { mimeType }, 948b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme new ClipData.Item(null, null, null, bugreportUri)); 949b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final ArrayList<Uri> attachments = Lists.newArrayList(bugreportUri); 950d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme for (File screenshot : info.screenshotFiles) { 951d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Uri screenshotUri = getUri(context, screenshot); 952b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme clipData.addItem(new ClipData.Item(null, null, null, screenshotUri)); 953b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme attachments.add(screenshotUri); 954b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 955b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.setClipData(clipData); 956b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachments); 957b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 958b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final Account sendToAccount = findSendToAccount(context); 959b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (sendToAccount != null) { 960b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.putExtra(Intent.EXTRA_EMAIL, new String[] { sendToAccount.name }); 961b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 962b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 963b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return intent; 964b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 965b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 966b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme /** 96746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Shares the bugreport upon user's request by issuing a {@link Intent#ACTION_SEND_MULTIPLE} 96846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * intent, but issuing a warning dialog the first time. 969b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme */ 970fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme private void shareBugreport(int id, BugreportInfo sharedInfo) { 9716605bd89c53494b59717a826f9a17641bc32da41Felipe Leme MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_NOTIFICATION_ACTION_SHARE); 972fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme BugreportInfo info = getInfo(id); 973d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 974c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme // Service was terminated but notification persisted 975c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme info = sharedInfo; 976fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.d(TAG, "shareBugreport(): no info for ID " + id + " on managed processes (" 977c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme + mProcesses + "), using info from intent instead (" + info + ")"); 9784f663f6c25488a83a438a01dfef8372aa4e6b2aaFelipe Leme } else { 9794f663f6c25488a83a438a01dfef8372aa4e6b2aaFelipe Leme Log.v(TAG, "shareBugReport(): id " + id + " info = " + info); 98046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } 9814967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme 98269c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme addDetailsToZipFile(info); 9834967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme 984d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Intent sendIntent = buildSendIntent(mContext, info); 985abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme if (sendIntent == null) { 986abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme Log.w(TAG, "Stopping progres on ID " + id + " because share intent could not be built"); 987abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme stopProgress(id); 988abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme return; 989abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme } 990abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme 99146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final Intent notifIntent; 992b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 993b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // Send through warning dialog by default 994fcca68dfb137c061952d23e1873e995e6bcf172dFelipe Leme if (getWarningState(mContext, STATE_UNKNOWN) != STATE_HIDE) { 995d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme notifIntent = buildWarningIntent(mContext, sendIntent); 996b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } else { 997b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme notifIntent = sendIntent; 998b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 999b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme notifIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1000b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 100146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme // Send the share intent... 1002d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mContext.startActivity(notifIntent); 100346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 100446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme // ... and stop watching this process. 1005fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme stopProgress(id); 100646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } 100746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 100846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 10092758d5d93970f26867d778c944605371e55b751eFelipe Leme * Sends a notification indicating the bugreport has finished so use can share it. 101046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 101169c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme private void sendBugreportNotification(BugreportInfo info, boolean takingScreenshot) { 101218b5892950b7f21e66c9268129323cbc0e865699Felipe Leme 101318b5892950b7f21e66c9268129323cbc0e865699Felipe Leme // Since adding the details can take a while, do it before notifying user. 101469c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme addDetailsToZipFile(info); 101518b5892950b7f21e66c9268129323cbc0e865699Felipe Leme 101646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final Intent shareIntent = new Intent(INTENT_BUGREPORT_SHARE); 101769c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme shareIntent.setClass(mContext, BugreportProgressService.class); 101846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme shareIntent.setAction(INTENT_BUGREPORT_SHARE); 1019fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme shareIntent.putExtra(EXTRA_ID, info.id); 1020c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme shareIntent.putExtra(EXTRA_INFO, info); 102146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 102269c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme final String title = mContext.getString(R.string.bugreport_finished_title, info.id); 1023a43d139359346ad57604e8335d92de57f3d47171Felipe Leme final String content = takingScreenshot ? 102469c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme mContext.getString(R.string.bugreport_finished_pending_screenshot_text) 102569c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme : mContext.getString(R.string.bugreport_finished_text); 102669c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme final Notification.Builder builder = newBaseNotification(mContext) 102769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme .setContentTitle(title) 102869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme .setTicker(title) 10295ee846dd18022307341c9808c9aacded2b2f60fdFelipe Leme .setContentText(content) 103069c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme .setContentIntent(PendingIntent.getService(mContext, info.id, shareIntent, 1031bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme PendingIntent.FLAG_UPDATE_CURRENT)) 103269c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme .setDeleteIntent(newCancelIntent(mContext, info)); 1033b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 1034bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (!TextUtils.isEmpty(info.name)) { 1035bc3b0458bf4281a92f65dc98d87ad6ff40c92f98Selim Cinek builder.setSubText(info.name); 1036bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1037bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1038fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title); 103969c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme NotificationManager.from(mContext).notify(info.id, builder.build()); 1040b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 1041b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 1042b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme /** 10432758d5d93970f26867d778c944605371e55b751eFelipe Leme * Sends a notification indicating the bugreport is being updated so the user can wait until it 10442758d5d93970f26867d778c944605371e55b751eFelipe Leme * finishes - at this point there is nothing to be done other than waiting, hence it has no 10452758d5d93970f26867d778c944605371e55b751eFelipe Leme * pending action. 10462758d5d93970f26867d778c944605371e55b751eFelipe Leme */ 104769c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme private void sendBugreportBeingUpdatedNotification(Context context, int id) { 10482758d5d93970f26867d778c944605371e55b751eFelipe Leme final String title = context.getString(R.string.bugreport_updating_title); 1049208b1881ae924cd0c2bed326555e4aa18424d927Felipe Leme final Notification.Builder builder = newBaseNotification(context) 10502758d5d93970f26867d778c944605371e55b751eFelipe Leme .setContentTitle(title) 10512758d5d93970f26867d778c944605371e55b751eFelipe Leme .setTicker(title) 1052208b1881ae924cd0c2bed326555e4aa18424d927Felipe Leme .setContentText(context.getString(R.string.bugreport_updating_wait)); 1053208b1881ae924cd0c2bed326555e4aa18424d927Felipe Leme Log.v(TAG, "Sending 'Updating zip' notification for ID " + id + ": " + title); 105469c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme sendForegroundabledNotification(id, builder.build()); 1055208b1881ae924cd0c2bed326555e4aa18424d927Felipe Leme } 1056208b1881ae924cd0c2bed326555e4aa18424d927Felipe Leme 1057208b1881ae924cd0c2bed326555e4aa18424d927Felipe Leme private static Notification.Builder newBaseNotification(Context context) { 105865a9c6760ef6cf1c8e1762a271aa43c626d27048Felipe Leme if (sNotificationBundle.isEmpty()) { 105965a9c6760ef6cf1c8e1762a271aa43c626d27048Felipe Leme // Rename notifcations from "Shell" to "Android System" 106065a9c6760ef6cf1c8e1762a271aa43c626d27048Felipe Leme sNotificationBundle.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, 106165a9c6760ef6cf1c8e1762a271aa43c626d27048Felipe Leme context.getString(com.android.internal.R.string.android_system_label)); 106265a9c6760ef6cf1c8e1762a271aa43c626d27048Felipe Leme } 1063208b1881ae924cd0c2bed326555e4aa18424d927Felipe Leme return new Notification.Builder(context) 106465a9c6760ef6cf1c8e1762a271aa43c626d27048Felipe Leme .addExtras(sNotificationBundle) 1065208b1881ae924cd0c2bed326555e4aa18424d927Felipe Leme .setCategory(Notification.CATEGORY_SYSTEM) 1066208b1881ae924cd0c2bed326555e4aa18424d927Felipe Leme .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) 10672758d5d93970f26867d778c944605371e55b751eFelipe Leme .setLocalOnly(true) 10682758d5d93970f26867d778c944605371e55b751eFelipe Leme .setColor(context.getColor( 10692758d5d93970f26867d778c944605371e55b751eFelipe Leme com.android.internal.R.color.system_notification_accent_color)); 10702758d5d93970f26867d778c944605371e55b751eFelipe Leme } 10712758d5d93970f26867d778c944605371e55b751eFelipe Leme 10722758d5d93970f26867d778c944605371e55b751eFelipe Leme /** 1073b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * Sends a zipped bugreport notification. 1074b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme */ 107569c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme private void sendZippedBugreportNotification( final BugreportInfo info, 107669c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme final boolean takingScreenshot) { 1077b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme new AsyncTask<Void, Void, Void>() { 1078b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme @Override 1079b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme protected Void doInBackground(Void... params) { 10804967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme zipBugreport(info); 108169c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme sendBugreportNotification(info, takingScreenshot); 1082b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return null; 1083b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 1084b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme }.execute(); 1085b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 1086b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 1087b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme /** 1088b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * Zips a bugreport file, returning the path to the new file (or to the 1089b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * original in case of failure). 1090b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme */ 10914967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme private static void zipBugreport(BugreportInfo info) { 10924967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme final String bugreportPath = info.bugreportFile.getAbsolutePath(); 10934967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme final String zippedPath = bugreportPath.replace(".txt", ".zip"); 1094b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme Log.v(TAG, "zipping " + bugreportPath + " as " + zippedPath); 10954967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme final File bugreportZippedFile = new File(zippedPath); 10964967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme try (InputStream is = new FileInputStream(info.bugreportFile); 109769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme ZipOutputStream zos = new ZipOutputStream( 109869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme new BufferedOutputStream(new FileOutputStream(bugreportZippedFile)))) { 10994967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme addEntry(zos, info.bugreportFile.getName(), is); 11004967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme // Delete old file 11014967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme final boolean deleted = info.bugreportFile.delete(); 1102b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (deleted) { 1103b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme Log.v(TAG, "deleted original bugreport (" + bugreportPath + ")"); 1104b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } else { 1105b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme Log.e(TAG, "could not delete original bugreport (" + bugreportPath + ")"); 1106b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 11074967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme info.bugreportFile = bugreportZippedFile; 1108b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } catch (IOException e) { 110969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme Log.e(TAG, "exception zipping file " + zippedPath, e); 1110b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 1111b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 1112b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 1113b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme /** 11144967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme * Adds the user-provided info into the bugreport zip file. 11154967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme * <p> 11164967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme * If user provided a title, it will be saved into a {@code title.txt} entry; similarly, the 11174967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme * description will be saved on {@code description.txt}. 11184967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme */ 111969c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme private void addDetailsToZipFile(BugreportInfo info) { 1120c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme if (info.bugreportFile == null) { 1121c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme // One possible reason is a bug in the Parcelization code. 1122af6fd4086c8f24e8b70a810fe83081b67e5db236Felipe Leme Log.wtf(TAG, "addDetailsToZipFile(): no bugreportFile on " + info); 1123c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme return; 1124c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme } 1125b9d598c47b69406174b015ec9f064448e4b3b8b5Felipe Leme if (TextUtils.isEmpty(info.title) && TextUtils.isEmpty(info.description)) { 1126b9d598c47b69406174b015ec9f064448e4b3b8b5Felipe Leme Log.d(TAG, "Not touching zip file since neither title nor description are set"); 1127b9d598c47b69406174b015ec9f064448e4b3b8b5Felipe Leme return; 1128b9d598c47b69406174b015ec9f064448e4b3b8b5Felipe Leme } 112918b5892950b7f21e66c9268129323cbc0e865699Felipe Leme if (info.addedDetailsToZip || info.addingDetailsToZip) { 113018b5892950b7f21e66c9268129323cbc0e865699Felipe Leme Log.d(TAG, "Already added details to zip file for " + info); 113118b5892950b7f21e66c9268129323cbc0e865699Felipe Leme return; 113218b5892950b7f21e66c9268129323cbc0e865699Felipe Leme } 113318b5892950b7f21e66c9268129323cbc0e865699Felipe Leme info.addingDetailsToZip = true; 11342758d5d93970f26867d778c944605371e55b751eFelipe Leme 11354967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme // It's not possible to add a new entry into an existing file, so we need to create a new 11364967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme // zip, copy all entries, then rename it. 113769c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme sendBugreportBeingUpdatedNotification(mContext, info.id); // ...and that takes time 113869c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme 11394967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme final File dir = info.bugreportFile.getParentFile(); 11404967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme final File tmpZip = new File(dir, "tmp-" + info.bugreportFile.getName()); 11412758d5d93970f26867d778c944605371e55b751eFelipe Leme Log.d(TAG, "Writing temporary zip file (" + tmpZip + ") with title and/or description"); 11424967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme try (ZipFile oldZip = new ZipFile(info.bugreportFile); 11434967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(tmpZip))) { 11444967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme 11454967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme // First copy contents from original zip. 11464967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme Enumeration<? extends ZipEntry> entries = oldZip.entries(); 11474967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme while (entries.hasMoreElements()) { 11484967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme final ZipEntry entry = entries.nextElement(); 11494967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme final String entryName = entry.getName(); 11504967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme if (!entry.isDirectory()) { 11514967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme addEntry(zos, entryName, entry.getTime(), oldZip.getInputStream(entry)); 11524967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme } else { 11534967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme Log.w(TAG, "skipping directory entry: " + entryName); 11544967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme } 11554967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme } 11564967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme 11574967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme // Then add the user-provided info. 11584967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme addEntry(zos, "title.txt", info.title); 11594967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme addEntry(zos, "description.txt", info.description); 11604967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme } catch (IOException e) { 11614967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme Log.e(TAG, "exception zipping file " + tmpZip, e); 116245a905bbafcbcbf89933e9b9226977995bae1afaFelipe Leme Toast.makeText(mContext, R.string.bugreport_add_details_to_zip_failed, 116345a905bbafcbcbf89933e9b9226977995bae1afaFelipe Leme Toast.LENGTH_LONG).show(); 11644967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme return; 116551a4ede593b1bba96d7cc14fca54e8e5f02850e0Felipe Leme } finally { 116651a4ede593b1bba96d7cc14fca54e8e5f02850e0Felipe Leme // Make sure it only tries to add details once, even it fails the first time. 116751a4ede593b1bba96d7cc14fca54e8e5f02850e0Felipe Leme info.addedDetailsToZip = true; 116851a4ede593b1bba96d7cc14fca54e8e5f02850e0Felipe Leme info.addingDetailsToZip = false; 116969c53e65b941235a8d2e193d5b9a783f7787af0dFelipe Leme stopForegroundWhenDone(info.id); 11704967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme } 11714967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme 11724967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme if (!tmpZip.renameTo(info.bugreportFile)) { 11734967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme Log.e(TAG, "Could not rename " + tmpZip + " to " + info.bugreportFile); 11744967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme } 11754967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme } 11764967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme 11774967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme private static void addEntry(ZipOutputStream zos, String entry, String text) 11784967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme throws IOException { 11794967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme if (DEBUG) Log.v(TAG, "adding entry '" + entry + "': " + text); 11804967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme if (!TextUtils.isEmpty(text)) { 11814967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme addEntry(zos, entry, new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8))); 11824967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme } 11834967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme } 11844967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme 11854967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme private static void addEntry(ZipOutputStream zos, String entryName, InputStream is) 11864967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme throws IOException { 11874967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme addEntry(zos, entryName, System.currentTimeMillis(), is); 11884967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme } 11894967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme 11904967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme private static void addEntry(ZipOutputStream zos, String entryName, long timestamp, 11914967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme InputStream is) throws IOException { 11924967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme final ZipEntry entry = new ZipEntry(entryName); 11934967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme entry.setTime(timestamp); 11944967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme zos.putNextEntry(entry); 11954967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme final int totalBytes = Streams.copy(is, zos); 11964967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme if (DEBUG) Log.v(TAG, "size of '" + entryName + "' entry: " + totalBytes + " bytes"); 11974967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme zos.closeEntry(); 11984967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme } 11994967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme 12004967f737d9906e3d5c9bf3a0584a7b7cf83b5a8cFelipe Leme /** 1201b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * Find the best matching {@link Account} based on build properties. 1202b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme */ 1203b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme private static Account findSendToAccount(Context context) { 1204b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final AccountManager am = (AccountManager) context.getSystemService( 1205b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme Context.ACCOUNT_SERVICE); 1206b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 1207b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme String preferredDomain = SystemProperties.get("sendbug.preferred.domain"); 1208b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (!preferredDomain.startsWith("@")) { 1209b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme preferredDomain = "@" + preferredDomain; 1210b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 1211b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 1212213e355c77993c718298c89bc04908a0d6c5adddFelipe Leme final Account[] accounts; 1213213e355c77993c718298c89bc04908a0d6c5adddFelipe Leme try { 1214213e355c77993c718298c89bc04908a0d6c5adddFelipe Leme accounts = am.getAccounts(); 1215213e355c77993c718298c89bc04908a0d6c5adddFelipe Leme } catch (RuntimeException e) { 1216213e355c77993c718298c89bc04908a0d6c5adddFelipe Leme Log.e(TAG, "Could not get accounts for preferred domain " + preferredDomain, e); 1217213e355c77993c718298c89bc04908a0d6c5adddFelipe Leme return null; 1218213e355c77993c718298c89bc04908a0d6c5adddFelipe Leme } 1219213e355c77993c718298c89bc04908a0d6c5adddFelipe Leme if (DEBUG) Log.d(TAG, "Number of accounts: " + accounts.length); 1220b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme Account foundAccount = null; 1221b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme for (Account account : accounts) { 1222b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) { 1223b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (!preferredDomain.isEmpty()) { 1224b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // if we have a preferred domain and it matches, return; otherwise keep 1225b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // looking 1226b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (account.name.endsWith(preferredDomain)) { 1227b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return account; 1228b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } else { 1229b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme foundAccount = account; 1230b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 1231b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // if we don't have a preferred domain, just return since it looks like 1232b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // an email address 1233b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } else { 1234b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return account; 1235b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 1236b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 1237b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 1238b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return foundAccount; 1239b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 1240b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 1241226940ed8550c02875a987f7e46699e6003ec1c0Michal Karpinski static Uri getUri(Context context, File file) { 1242b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return file != null ? FileProvider.getUriForFile(context, AUTHORITY, file) : null; 1243b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 1244b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 1245b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme static File getFileExtra(Intent intent, String key) { 1246b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final String path = intent.getStringExtra(key); 1247b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (path != null) { 1248b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return new File(path); 1249b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } else { 1250b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return null; 1251b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 1252b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 125369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 1254abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme /** 1255abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme * Dumps an intent, extracting the relevant extras. 1256abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme */ 1257abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme static String dumpIntent(Intent intent) { 1258abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme if (intent == null) { 1259abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme return "NO INTENT"; 1260abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme } 1261abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme String action = intent.getAction(); 1262abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme if (action == null) { 1263abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme // Happens when BugreportReceiver calls startService... 1264abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme action = "no action"; 1265abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme } 1266abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme final StringBuilder buffer = new StringBuilder(action).append(" extras: "); 1267abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme addExtra(buffer, intent, EXTRA_ID); 1268abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme addExtra(buffer, intent, EXTRA_PID); 1269abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme addExtra(buffer, intent, EXTRA_MAX); 1270abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme addExtra(buffer, intent, EXTRA_NAME); 1271abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme addExtra(buffer, intent, EXTRA_DESCRIPTION); 1272abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme addExtra(buffer, intent, EXTRA_BUGREPORT); 1273abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme addExtra(buffer, intent, EXTRA_SCREENSHOT); 1274abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme addExtra(buffer, intent, EXTRA_INFO); 1275abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme 1276abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme if (intent.hasExtra(EXTRA_ORIGINAL_INTENT)) { 1277abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme buffer.append(SHORT_EXTRA_ORIGINAL_INTENT).append(": "); 1278abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme final Intent originalIntent = intent.getParcelableExtra(EXTRA_ORIGINAL_INTENT); 1279abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme buffer.append(dumpIntent(originalIntent)); 1280abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme } else { 1281abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme buffer.append("no ").append(SHORT_EXTRA_ORIGINAL_INTENT); 1282abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme } 1283abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme 1284abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme return buffer.toString(); 1285abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme } 1286abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme 1287abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme private static final String SHORT_EXTRA_ORIGINAL_INTENT = 1288abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme EXTRA_ORIGINAL_INTENT.substring(EXTRA_ORIGINAL_INTENT.lastIndexOf('.') + 1); 1289abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme 1290abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme private static void addExtra(StringBuilder buffer, Intent intent, String name) { 1291abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme final String shortName = name.substring(name.lastIndexOf('.') + 1); 1292abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme if (intent.hasExtra(name)) { 1293abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme buffer.append(shortName).append('=').append(intent.getExtra(name)); 1294abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme } else { 1295abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme buffer.append("no ").append(shortName); 1296abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme } 1297abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme buffer.append(", "); 1298abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme } 1299abeab723d10b56302b86b1929ea994cf4a861fd0Felipe Leme 1300bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private static boolean setSystemProperty(String key, String value) { 1301bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme try { 130285ae3cf46ad66d71e5a29a93e89a0f569d74288bFelipe Leme if (DEBUG) Log.v(TAG, "Setting system property " + key + " to " + value); 1303bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme SystemProperties.set(key, value); 1304bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } catch (IllegalArgumentException e) { 1305bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme Log.e(TAG, "Could not set property " + key + " to " + value, e); 1306bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return false; 1307bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1308bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return true; 1309bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1310bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1311bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1312bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Updates the system property used by {@code dumpstate} to rename the final bugreport files. 1313bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1314bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private boolean setBugreportNameProperty(int pid, String name) { 1315bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme Log.d(TAG, "Updating bugreport name to " + name); 1316bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final String key = DUMPSTATE_PREFIX + pid + NAME_SUFFIX; 1317bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return setSystemProperty(key, name); 1318bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1319bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1320bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1321bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Updates the user-provided details of a bugreport. 1322bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1323fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme private void updateBugreportInfo(int id, String name, String title, String description) { 1324fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme final BugreportInfo info = getInfo(id); 1325d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 1326d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 1327d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 13286605bd89c53494b59717a826f9a17641bc32da41Felipe Leme if (title != null && !title.equals(info.title)) { 13296605bd89c53494b59717a826f9a17641bc32da41Felipe Leme MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_DETAILS_TITLE_CHANGED); 13306605bd89c53494b59717a826f9a17641bc32da41Felipe Leme } 1331d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.title = title; 13326605bd89c53494b59717a826f9a17641bc32da41Felipe Leme if (description != null && !description.equals(info.description)) { 13336605bd89c53494b59717a826f9a17641bc32da41Felipe Leme MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_DETAILS_DESCRIPTION_CHANGED); 13346605bd89c53494b59717a826f9a17641bc32da41Felipe Leme } 1335d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.description = description; 13361eee1996f5d57b42d4beed2790321480fbe03a4cFelipe Leme if (name != null && !name.equals(info.name)) { 13376605bd89c53494b59717a826f9a17641bc32da41Felipe Leme MetricsLogger.action(this, MetricsEvent.ACTION_BUGREPORT_DETAILS_NAME_CHANGED); 1338d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.name = name; 1339d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme updateProgress(info); 1340d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1341d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1342d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 1343d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void collapseNotificationBar() { 1344d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); 1345d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1346d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 1347d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private static Looper newLooper(String name) { 1348d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final HandlerThread thread = new HandlerThread(name, THREAD_PRIORITY_BACKGROUND); 1349d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme thread.start(); 1350d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return thread.getLooper(); 1351d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1352d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 1353d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 1354d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Takes a screenshot and save it to the given location. 1355d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 1356aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme private static boolean takeScreenshot(Context context, String path) { 1357aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme final Bitmap bitmap = Screenshooter.takeScreenshot(); 1358aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme if (bitmap == null) { 1359aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme return false; 1360aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme } 1361aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme boolean status; 1362aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme try (final FileOutputStream fos = new FileOutputStream(path)) { 1363aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)) { 1364aa00f2d909dcc48b61b9338cd2ab7c33850a69d9Felipe Leme ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)).vibrate(150); 1365d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return true; 1366aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme } else { 1367aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme Log.e(TAG, "Failed to save screenshot on " + path); 1368bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1369aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme } catch (IOException e ) { 1370aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme Log.e(TAG, "Failed to save screenshot on " + path, e); 1371aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme return false; 1372aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme } finally { 1373aba9743643d85e9bf5627da9d1fdc8ded25f22deFelipe Leme bitmap.recycle(); 1374bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1375d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return false; 1376bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1377bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1378bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1379bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Checks whether a character is valid on bugreport names. 1380bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1381bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme @VisibleForTesting 1382bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme static boolean isValid(char c) { 1383bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') 1384bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme || c == '_' || c == '-'; 1385bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1386bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1387bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1388bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Helper class encapsulating the UI elements and logic used to display a dialog where user 1389bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * can change the details of a bugreport. 1390bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1391bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private final class BugreportInfoDialog { 1392bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private EditText mInfoName; 1393bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private EditText mInfoTitle; 1394bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private EditText mInfoDescription; 1395bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private AlertDialog mDialog; 1396bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private Button mOkButton; 1397fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme private int mId; 1398bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private int mPid; 1399bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1400bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1401bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Last "committed" value of the bugreport name. 1402bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * <p> 1403bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Once initially set, it's only updated when user clicks the OK button. 1404bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1405bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private String mSavedName; 1406bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1407bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1408bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Last value of the bugreport name as entered by the user. 1409bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * <p> 1410bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Every time it's changed the equivalent system property is changed as well, but if the 1411bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * user clicks CANCEL, the old value (stored on {@code mSavedName} is restored. 1412bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * <p> 1413bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * This logic handles the corner-case scenario where {@code dumpstate} finishes after the 1414bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * user changed the name but didn't clicked OK yet (for example, because the user is typing 1415bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * the description). The only drawback is that if the user changes the name while 1416bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * {@code dumpstate} is running but clicks CANCEL after it finishes, then the final name 1417bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * will be the one that has been canceled. But when {@code dumpstate} finishes the {code 1418bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * name} UI is disabled and the old name restored anyways, so the user will be "alerted" of 1419bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * such drawback. 1420bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1421bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private String mTempName; 1422bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1423bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1424bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Sets its internal state and displays the dialog. 1425bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 14266605bd89c53494b59717a826f9a17641bc32da41Felipe Leme private void initialize(final Context context, BugreportInfo info) { 1427262887834c7938b7a7bbbd7ef026b5965d6412fcFelipe Leme final String dialogTitle = 1428262887834c7938b7a7bbbd7ef026b5965d6412fcFelipe Leme context.getString(R.string.bugreport_info_dialog_title, info.id); 1429bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // First initializes singleton. 1430bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (mDialog == null) { 1431bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme @SuppressLint("InflateParams") 1432bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // It's ok pass null ViewRoot on AlertDialogs. 1433bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final View view = View.inflate(context, R.layout.dialog_bugreport_info, null); 1434bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1435bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoName = (EditText) view.findViewById(R.id.name); 1436bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoTitle = (EditText) view.findViewById(R.id.title); 1437bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoDescription = (EditText) view.findViewById(R.id.description); 1438bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1439bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoName.setOnFocusChangeListener(new OnFocusChangeListener() { 1440bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1441bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme @Override 1442bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme public void onFocusChange(View v, boolean hasFocus) { 1443bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (hasFocus) { 1444bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return; 1445bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1446bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme sanitizeName(); 1447bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1448bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme }); 1449bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1450bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mDialog = new AlertDialog.Builder(context) 1451bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .setView(view) 1452262887834c7938b7a7bbbd7ef026b5965d6412fcFelipe Leme .setTitle(dialogTitle) 1453bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .setCancelable(false) 1454bbd91e58444f092e1080f5a0a746fcd4b21ce113Felipe Leme .setPositiveButton(context.getString(R.string.save), 1455bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme null) 1456bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .setNegativeButton(context.getString(com.android.internal.R.string.cancel), 1457bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme new DialogInterface.OnClickListener() 1458bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme { 1459bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme @Override 1460bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme public void onClick(DialogInterface dialog, int id) 1461bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme { 14626605bd89c53494b59717a826f9a17641bc32da41Felipe Leme MetricsLogger.action(context, 14636605bd89c53494b59717a826f9a17641bc32da41Felipe Leme MetricsEvent.ACTION_BUGREPORT_DETAILS_CANCELED); 1464bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (!mTempName.equals(mSavedName)) { 1465bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // Must restore dumpstate's name since it was changed 1466bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // before user clicked OK. 1467bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme setBugreportNameProperty(mPid, mSavedName); 1468bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1469bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1470bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme }) 1471bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .create(); 1472bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1473bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mDialog.getWindow().setAttributes( 1474bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme new WindowManager.LayoutParams( 1475bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG)); 1476bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1477262887834c7938b7a7bbbd7ef026b5965d6412fcFelipe Leme } else { 1478262887834c7938b7a7bbbd7ef026b5965d6412fcFelipe Leme // Re-use view, but reset fields first. 1479262887834c7938b7a7bbbd7ef026b5965d6412fcFelipe Leme mDialog.setTitle(dialogTitle); 1480262887834c7938b7a7bbbd7ef026b5965d6412fcFelipe Leme mInfoName.setText(null); 1481262887834c7938b7a7bbbd7ef026b5965d6412fcFelipe Leme mInfoTitle.setText(null); 1482262887834c7938b7a7bbbd7ef026b5965d6412fcFelipe Leme mInfoDescription.setText(null); 1483bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1484bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1485bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // Then set fields. 1486fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme mSavedName = mTempName = info.name; 1487fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme mId = info.id; 1488fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme mPid = info.pid; 1489fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme if (!TextUtils.isEmpty(info.name)) { 1490fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme mInfoName.setText(info.name); 1491bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1492fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme if (!TextUtils.isEmpty(info.title)) { 1493fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme mInfoTitle.setText(info.title); 1494bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1495fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme if (!TextUtils.isEmpty(info.description)) { 1496fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme mInfoDescription.setText(info.description); 1497bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1498bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1499bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // And finally display it. 1500bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mDialog.show(); 1501bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1502bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // TODO: in a traditional AlertDialog, when the positive button is clicked the 1503bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // dialog is always closed, but we need to validate the name first, so we need to 1504bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // get a reference to it, which is only available after it's displayed. 1505bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // It would be cleaner to use a regular dialog instead, but let's keep this 1506bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // workaround for now and change it later, when we add another button to take 1507bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // extra screenshots. 1508bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (mOkButton == null) { 1509bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mOkButton = mDialog.getButton(DialogInterface.BUTTON_POSITIVE); 1510bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mOkButton.setOnClickListener(new View.OnClickListener() { 1511bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1512bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme @Override 1513bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme public void onClick(View view) { 15146605bd89c53494b59717a826f9a17641bc32da41Felipe Leme MetricsLogger.action(context, MetricsEvent.ACTION_BUGREPORT_DETAILS_SAVED); 1515bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme sanitizeName(); 1516bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final String name = mInfoName.getText().toString(); 1517bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final String title = mInfoTitle.getText().toString(); 1518bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final String description = mInfoDescription.getText().toString(); 1519bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1520fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme updateBugreportInfo(mId, name, title, description); 1521bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mDialog.dismiss(); 1522bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1523bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme }); 1524bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1525bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1526bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1527bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1528bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Sanitizes the user-provided value for the {@code name} field, automatically replacing 1529bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * invalid characters if necessary. 1530bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1531d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void sanitizeName() { 1532bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme String name = mInfoName.getText().toString(); 1533bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (name.equals(mTempName)) { 1534bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (DEBUG) Log.v(TAG, "name didn't change, no need to sanitize: " + name); 1535bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return; 1536bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1537bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final StringBuilder safeName = new StringBuilder(name.length()); 1538bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme boolean changed = false; 1539bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme for (int i = 0; i < name.length(); i++) { 1540bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final char c = name.charAt(i); 1541bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (isValid(c)) { 1542bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme safeName.append(c); 1543bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } else { 1544bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme changed = true; 1545bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme safeName.append('_'); 1546bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1547bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1548bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (changed) { 1549bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme Log.v(TAG, "changed invalid name '" + name + "' to '" + safeName + "'"); 1550bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme name = safeName.toString(); 1551bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoName.setText(name); 1552bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1553bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mTempName = name; 1554bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1555bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // Must update system property for the cases where dumpstate finishes 1556bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // while the user is still entering other fields (like title or 1557bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // description) 155885ae3cf46ad66d71e5a29a93e89a0f569d74288bFelipe Leme setBugreportNameProperty(mPid, name); 1559bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1560bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1561bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1562bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Notifies the dialog that the bugreport has finished so it disables the {@code name} 1563bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * field. 1564bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * <p>Once the bugreport is finished dumpstate has already generated the final files, so 1565bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * changing the name would have no effect. 1566bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1567fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme private void onBugreportFinished(int id) { 1568bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (mInfoName != null) { 1569bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoName.setEnabled(false); 1570bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoName.setText(mSavedName); 1571bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1572bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1573bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1574bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1575bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 157669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 157746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Information about a bugreport process while its in progress. 157869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 1579c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme private static final class BugreportInfo implements Parcelable { 1580719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme private final Context context; 1581719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme 158269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 1583fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme * Sequential, user-friendly id used to identify the bugreport. 1584fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme */ 1585fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme final int id; 1586fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme 1587fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme /** 158846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * {@code pid} of the {@code dumpstate} process generating the bugreport. 158969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 159069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme final int pid; 159169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 159269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 159346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Name of the bugreport, will be used to rename the final files. 159469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <p> 159546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Initial value is the bugreport filename reported by {@code dumpstate}, but user can 159669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * change it later to a more meaningful name. 159769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 1598719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme String name; 159969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 160069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 1601bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * User-provided, one-line summary of the bug; when set, will be used as the subject 1602bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * of the {@link Intent#ACTION_SEND_MULTIPLE} intent. 1603bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1604bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme String title; 1605bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1606bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1607bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * User-provided, detailed description of the bugreport; when set, will be added to the body 1608bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * of the {@link Intent#ACTION_SEND_MULTIPLE} intent. 1609bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1610bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme String description; 1611bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1612bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 16133fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme * Maximum progress of the bugreport generation as displayed by the UI. 161469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 1615719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme int max; 161669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 161769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 16183fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme * Current progress of the bugreport generation as displayed by the UI. 161969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 162069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme int progress; 162169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 162269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 16233fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme * Maximum progress of the bugreport generation as reported by dumpstate. 16243fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme */ 16253fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme int realMax; 16263fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme 16273fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme /** 16283fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme * Current progress of the bugreport generation as reported by dumpstate. 16293fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme */ 16303fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme int realProgress; 16313fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme 16323fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme /** 163369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * Time of the last progress update. 163469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 163569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme long lastUpdate = System.currentTimeMillis(); 163669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 163746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 1638c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme * Time of the last progress update when Parcel was created. 1639c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme */ 1640c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme String formattedLastUpdate; 1641c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme 1642c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme /** 164346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Path of the main bugreport file. 164446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 164546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme File bugreportFile; 164646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 164746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 1648d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Path of the screenshot files. 164946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 1650d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme List<File> screenshotFiles = new ArrayList<>(1); 165146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 165246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 165346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Whether dumpstate sent an intent informing it has finished. 165446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 165546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme boolean finished; 165646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 165746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 165818b5892950b7f21e66c9268129323cbc0e865699Felipe Leme * Whether the details entries have been added to the bugreport yet. 165918b5892950b7f21e66c9268129323cbc0e865699Felipe Leme */ 166018b5892950b7f21e66c9268129323cbc0e865699Felipe Leme boolean addingDetailsToZip; 166118b5892950b7f21e66c9268129323cbc0e865699Felipe Leme boolean addedDetailsToZip; 166218b5892950b7f21e66c9268129323cbc0e865699Felipe Leme 166318b5892950b7f21e66c9268129323cbc0e865699Felipe Leme /** 1664d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Internal counter used to name screenshot files. 1665d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 1666d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme int screenshotCounter; 1667d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 1668d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 166946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED. 167046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 1671fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme BugreportInfo(Context context, int id, int pid, String name, int max) { 1672719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme this.context = context; 1673fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme this.id = id; 167469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme this.pid = pid; 167569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme this.name = name; 167669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme this.max = max; 167769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 167869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 167946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 168046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Constructor for untracked bugreports - typically called upon receiving BUGREPORT_FINISHED 168146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * without a previous call to BUGREPORT_STARTED. 168246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 1683fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme BugreportInfo(Context context, int id) { 1684fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme this(context, id, id, null, 0); 168546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme this.finished = true; 168646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } 168746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 1688d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 1689d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Gets the name for next screenshot file. 1690d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 1691d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme String getPathNextScreenshot() { 1692d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme screenshotCounter ++; 1693d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return "screenshot-" + pid + "-" + screenshotCounter + ".png"; 1694d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1695d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 1696d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 1697d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Saves the location of a taken screenshot so it can be sent out at the end. 1698d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 1699d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme void addScreenshot(File screenshot) { 1700d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme screenshotFiles.add(screenshot); 1701d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1702d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 1703d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 1704d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Rename all screenshots files so that they contain the user-generated name instead of pid. 1705d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 1706d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme void renameScreenshots(File screenshotDir) { 1707d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (TextUtils.isEmpty(name)) { 1708d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 1709d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1710d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final List<File> renamedFiles = new ArrayList<>(screenshotFiles.size()); 1711d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme for (File oldFile : screenshotFiles) { 1712d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final String oldName = oldFile.getName(); 171385ae3cf46ad66d71e5a29a93e89a0f569d74288bFelipe Leme final String newName = oldName.replaceFirst(Integer.toString(pid), name); 1714d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final File newFile; 1715d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (!newName.equals(oldName)) { 1716d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final File renamedFile = new File(screenshotDir, newName); 17174f663f6c25488a83a438a01dfef8372aa4e6b2aaFelipe Leme Log.d(TAG, "Renaming screenshot file " + oldFile + " to " + renamedFile); 1718d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme newFile = oldFile.renameTo(renamedFile) ? renamedFile : oldFile; 1719d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } else { 1720d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.w(TAG, "Name didn't change: " + oldName); // Shouldn't happen. 1721d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme newFile = oldFile; 1722d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1723d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme renamedFiles.add(newFile); 1724d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1725d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme screenshotFiles = renamedFiles; 1726d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1727d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 172869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme String getFormattedLastUpdate() { 1729c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme if (context == null) { 1730c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme // Restored from Parcel 1731c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme return formattedLastUpdate == null ? 1732c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme Long.toString(lastUpdate) : formattedLastUpdate; 1733c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme } 1734719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme return DateUtils.formatDateTime(context, lastUpdate, 1735719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME); 173669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 173769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 173869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme @Override 173969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public String toString() { 174069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme final float percent = ((float) progress * 100 / max); 17413fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme final float realPercent = ((float) realProgress * 100 / realMax); 1742fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme return "id: " + id + ", pid: " + pid + ", name: " + name + ", finished: " + finished 1743bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme + "\n\ttitle: " + title + "\n\tdescription: " + description 1744d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme + "\n\tfile: " + bugreportFile + "\n\tscreenshots: " + screenshotFiles 1745510e922e47fec69839dd48c5473540f93d79a508Felipe Leme + "\n\tprogress: " + progress + "/" + max + " (" + percent + ")" 17463fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme + "\n\treal progress: " + realProgress + "/" + realMax + " (" + realPercent + ")" 174718b5892950b7f21e66c9268129323cbc0e865699Felipe Leme + "\n\tlast_update: " + getFormattedLastUpdate() 174818b5892950b7f21e66c9268129323cbc0e865699Felipe Leme + "\naddingDetailsToZip: " + addingDetailsToZip 174918b5892950b7f21e66c9268129323cbc0e865699Felipe Leme + " addedDetailsToZip: " + addedDetailsToZip; 175069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 1751c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme 1752c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme // Parcelable contract 1753c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme protected BugreportInfo(Parcel in) { 1754c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme context = null; 1755fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme id = in.readInt(); 1756c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme pid = in.readInt(); 1757c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme name = in.readString(); 1758c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme title = in.readString(); 1759c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme description = in.readString(); 1760c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme max = in.readInt(); 1761c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme progress = in.readInt(); 17623fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme realMax = in.readInt(); 17633fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme realProgress = in.readInt(); 1764c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme lastUpdate = in.readLong(); 1765c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme formattedLastUpdate = in.readString(); 1766c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme bugreportFile = readFile(in); 1767c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme 1768c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme int screenshotSize = in.readInt(); 1769c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme for (int i = 1; i <= screenshotSize; i++) { 1770c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme screenshotFiles.add(readFile(in)); 1771c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme } 1772c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme 1773c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme finished = in.readInt() == 1; 1774c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme screenshotCounter = in.readInt(); 1775c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme } 1776c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme 1777c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme @Override 1778c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme public void writeToParcel(Parcel dest, int flags) { 1779fd8ea077c0ded14002c32ee346df4fa22a30625aFelipe Leme dest.writeInt(id); 1780c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme dest.writeInt(pid); 1781c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme dest.writeString(name); 1782c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme dest.writeString(title); 1783c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme dest.writeString(description); 1784c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme dest.writeInt(max); 1785c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme dest.writeInt(progress); 17863fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme dest.writeInt(realMax); 17873fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme dest.writeInt(realProgress); 1788c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme dest.writeLong(lastUpdate); 1789c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme dest.writeString(getFormattedLastUpdate()); 1790c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme writeFile(dest, bugreportFile); 1791c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme 1792c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme dest.writeInt(screenshotFiles.size()); 1793c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme for (File screenshotFile : screenshotFiles) { 1794c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme writeFile(dest, screenshotFile); 1795c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme } 1796c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme 1797c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme dest.writeInt(finished ? 1 : 0); 1798c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme dest.writeInt(screenshotCounter); 1799c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme } 1800c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme 1801c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme @Override 1802c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme public int describeContents() { 1803c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme return 0; 1804c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme } 1805c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme 1806c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme private void writeFile(Parcel dest, File file) { 1807c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme dest.writeString(file == null ? null : file.getPath()); 1808c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme } 1809c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme 1810c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme private File readFile(Parcel in) { 1811c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme final String path = in.readString(); 1812c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme return path == null ? null : new File(path); 1813c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme } 1814c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme 1815c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme public static final Parcelable.Creator<BugreportInfo> CREATOR = 1816c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme new Parcelable.Creator<BugreportInfo>() { 1817c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme public BugreportInfo createFromParcel(Parcel source) { 1818c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme return new BugreportInfo(source); 1819c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme } 1820c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme 1821c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme public BugreportInfo[] newArray(int size) { 1822c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme return new BugreportInfo[size]; 1823c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme } 1824c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme }; 1825c4f6467702e308844ef0769ba17dcb7b7b32a9e6Felipe Leme 182669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 1827b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme} 1828