BugreportProgressService.java revision d1e0f12979441733753b538611f6d73e5527c43c
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; 20b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport static com.android.shell.BugreportPrefs.STATE_SHOW; 21b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport static com.android.shell.BugreportPrefs.getWarningState; 22b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 23b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.io.BufferedOutputStream; 24b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.io.File; 2569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport java.io.FileDescriptor; 26b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.io.FileInputStream; 27b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.io.FileOutputStream; 28b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.io.IOException; 29b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.io.InputStream; 3069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport java.io.PrintWriter; 3169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport java.text.NumberFormat; 32b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.util.ArrayList; 33d1e0f12979441733753b538611f6d73e5527c43cFelipe Lemeimport java.util.List; 34b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.util.zip.ZipEntry; 35b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport java.util.zip.ZipOutputStream; 36b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 37b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport libcore.io.Streams; 38b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 39bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport com.android.internal.annotations.VisibleForTesting; 40b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport com.google.android.collect.Lists; 41b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 42b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.accounts.Account; 43b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.accounts.AccountManager; 44bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.annotation.SuppressLint; 45bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.app.AlertDialog; 46b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.app.Notification; 479cadb75714bea7d435f34c8b7d06f698fa907a8dFelipe Lemeimport android.app.Notification.Action; 48b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.app.NotificationManager; 49b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.app.PendingIntent; 50b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.app.Service; 51b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.content.ClipData; 52b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.content.Context; 53d1e0f12979441733753b538611f6d73e5527c43cFelipe Lemeimport android.content.ContextWrapper; 54bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.content.DialogInterface; 55b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.content.Intent; 56b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.content.res.Configuration; 57b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.net.Uri; 58b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.os.AsyncTask; 5969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.os.Handler; 6069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.os.HandlerThread; 61b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.os.IBinder; 6269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.os.Looper; 6369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.os.Message; 6469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.os.Parcelable; 65b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.os.SystemProperties; 66d1e0f12979441733753b538611f6d73e5527c43cFelipe Lemeimport android.os.Vibrator; 67b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.support.v4.content.FileProvider; 68bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.text.TextUtils; 6969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.text.format.DateUtils; 70b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.util.Log; 71b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.util.Patterns; 7269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Lemeimport android.util.SparseArray; 73bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.view.View; 74bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.view.WindowManager; 75bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.view.View.OnFocusChangeListener; 76bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.view.inputmethod.EditorInfo; 77bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.widget.Button; 78bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Lemeimport android.widget.EditText; 79b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemeimport android.widget.Toast; 80b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 8169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme/** 8246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Service used to keep progress of bugreport processes ({@code dumpstate}). 8369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <p> 8469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * The workflow is: 8569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <ol> 8669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with its pid and the 8769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * estimated total effort. 8869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>{@link BugreportReceiver} receives the intent and delegates it to this service. 8969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>Upon start, this service: 9069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <ol> 9169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>Issues a system notification so user can watch the progresss (which is 0% initially). 9269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>Polls the {@link SystemProperties} for updates on the {@code dumpstate} progress. 9369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>If the progress changed, it updates the system notification. 9469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * </ol> 9569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>As {@code dumpstate} progresses, it updates the system property. 9669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>When {@code dumpstate} finishes, it sends a {@code BUGREPORT_FINISHED} intent. 9769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>{@link BugreportReceiver} receives the intent and delegates it to this service, which in 9869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * turn: 9969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <ol> 10046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * <li>Updates the system notification so user can share the bugreport. 10169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>Stops monitoring that {@code dumpstate} process. 10269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <li>Stops itself if it doesn't have any process left to monitor. 10369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * </ol> 10469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * </ol> 10569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 106b9238b37838d653c38ce4e712421adb61978fc22Felipe Lemepublic class BugreportProgressService extends Service { 107a0bf0336f0b6ff39cd90aabe0eb48b022d008ed6Felipe Leme static final String TAG = "Shell"; 10869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme private static final boolean DEBUG = false; 109b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 110b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme private static final String AUTHORITY = "com.android.shell"; 111b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 11246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme // External intents sent by dumpstate. 11369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme static final String INTENT_BUGREPORT_STARTED = "android.intent.action.BUGREPORT_STARTED"; 11469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme static final String INTENT_BUGREPORT_FINISHED = "android.intent.action.BUGREPORT_FINISHED"; 11546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 11646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme // Internal intents used on notification actions. 1179cadb75714bea7d435f34c8b7d06f698fa907a8dFelipe Leme static final String INTENT_BUGREPORT_CANCEL = "android.intent.action.BUGREPORT_CANCEL"; 11846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme static final String INTENT_BUGREPORT_SHARE = "android.intent.action.BUGREPORT_SHARE"; 119bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme static final String INTENT_BUGREPORT_INFO_LAUNCH = 120bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme "android.intent.action.BUGREPORT_INFO_LAUNCH"; 121d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme static final String INTENT_BUGREPORT_SCREENSHOT = 122d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme "android.intent.action.BUGREPORT_SCREENSHOT"; 12369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 124b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT"; 125b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT"; 12669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme static final String EXTRA_PID = "android.intent.extra.PID"; 12769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme static final String EXTRA_MAX = "android.intent.extra.MAX"; 12869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme static final String EXTRA_NAME = "android.intent.extra.NAME"; 129bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme static final String EXTRA_TITLE = "android.intent.extra.TITLE"; 130bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION"; 13169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme static final String EXTRA_ORIGINAL_INTENT = "android.intent.extra.ORIGINAL_INTENT"; 13269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 13369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme private static final int MSG_SERVICE_COMMAND = 1; 13469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme private static final int MSG_POLL = 2; 135d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private static final int MSG_DELAYED_SCREENSHOT = 3; 136d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private static final int MSG_SCREENSHOT_REQUEST = 4; 137d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private static final int MSG_SCREENSHOT_RESPONSE = 5; 138d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 139d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 140d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Delay before a screenshot is taken. 141d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * <p> 142d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Should be at least 3 seconds, otherwise its toast might show up in the screenshot. 143d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 144d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme static final int SCREENSHOT_DELAY_SECONDS = 3; 14569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 14669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** Polling frequency, in milliseconds. */ 147bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme static final long POLLING_FREQUENCY = 2 * DateUtils.SECOND_IN_MILLIS; 14869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 14969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** How long (in ms) a dumpstate process will be monitored if it didn't show progress. */ 15069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme private static final long INACTIVITY_TIMEOUT = 3 * DateUtils.MINUTE_IN_MILLIS; 15169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 152719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme /** System properties used for monitoring progress. */ 153719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme private static final String DUMPSTATE_PREFIX = "dumpstate."; 154719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme private static final String PROGRESS_SUFFIX = ".progress"; 155719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme private static final String MAX_SUFFIX = ".max"; 156bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private static final String NAME_SUFFIX = ".name"; 1579cadb75714bea7d435f34c8b7d06f698fa907a8dFelipe Leme 158bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** System property (and value) used to stop dumpstate. */ 159d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme // TODO: should call ActiveManager API instead 160719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme private static final String CTL_STOP = "ctl.stop"; 1614cc863338d5e43b6189e05498d7cb53ebba135e1Felipe Leme private static final String BUGREPORT_SERVICE = "bugreportplus"; 16269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 163d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 164d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Directory on Shell's data storage where screenshots will be stored. 165d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * <p> 166d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Must be a path supported by its FileProvider. 167d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 168d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private static final String SCREENSHOT_DIR = "bugreports"; 169d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 17069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** Managed dumpstate processes (keyed by pid) */ 17169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme private final SparseArray<BugreportInfo> mProcesses = new SparseArray<>(); 17269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 173d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private Context mContext; 174d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private ServiceHandler mMainHandler; 175d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private ScreenshotHandler mScreenshotHandler; 17669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 177bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private final BugreportInfoDialog mInfoDialog = new BugreportInfoDialog(); 178bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 179d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private File mScreenshotsDir; 180d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 181d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 182d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Flag indicating whether a screenshot is being taken. 183d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * <p> 184d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * This is the only state that is shared between the 2 handlers and hence must have synchronized 185d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * access. 186d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 187d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private boolean mTakingScreenshot; 188d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 18969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme @Override 19069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public void onCreate() { 191d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mContext = getApplicationContext(); 192d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mMainHandler = new ServiceHandler("BugreportProgressServiceMainThread"); 193d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mScreenshotHandler = new ScreenshotHandler("BugreportProgressServiceScreenshotThread"); 194d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 195d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mScreenshotsDir = new File(new ContextWrapper(mContext).getFilesDir(), SCREENSHOT_DIR); 196d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (!mScreenshotsDir.exists()) { 197d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.i(TAG, "Creating directory " + mScreenshotsDir + " to store temporary screenshots"); 198d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (!mScreenshotsDir.mkdir()) { 199d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.w(TAG, "Could not create directory " + mScreenshotsDir); 200d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 201d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 20269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 203b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 204b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme @Override 205b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme public int onStartCommand(Intent intent, int flags, int startId) { 206b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (intent != null) { 20769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme // Handle it in a separate thread. 208d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Message msg = mMainHandler.obtainMessage(); 20969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme msg.what = MSG_SERVICE_COMMAND; 21069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme msg.obj = intent; 211d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mMainHandler.sendMessage(msg); 212b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 21369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 21469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme // If service is killed it cannot be recreated because it would not know which 21569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme // dumpstate PIDs it would have to watch. 216b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return START_NOT_STICKY; 217b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 218b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 219b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme @Override 220b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme public IBinder onBind(Intent intent) { 221b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return null; 222b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 223b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 22469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme @Override 22569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public void onDestroy() { 226d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mMainHandler.getLooper().quit(); 227d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mScreenshotHandler.getLooper().quit(); 22869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme super.onDestroy(); 22969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 230b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 23169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme @Override 23269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 233d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final int size = mProcesses.size(); 234d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (size == 0) { 235d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme writer.printf("No monitored processes"); 236d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 237d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 238d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme writer.printf("Monitored dumpstate processes\n"); 239d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme writer.printf("-----------------------------\n"); 240d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme for (int i = 0; i < size; i++) { 241d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme writer.printf("%s\n", mProcesses.valueAt(i)); 24269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 24369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 24469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 245d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 246d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Main thread used to handle all requests but taking screenshots. 247d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 24869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme private final class ServiceHandler extends Handler { 249d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme public ServiceHandler(String name) { 250d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme super(newLooper(name)); 25169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 25269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 25369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme @Override 25469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public void handleMessage(Message msg) { 25569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme if (msg.what == MSG_POLL) { 256923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme poll(); 25769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return; 25869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 25969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 260d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (msg.what == MSG_DELAYED_SCREENSHOT) { 261d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme takeScreenshot(msg.arg1, msg.arg2); 262d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 263d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 264d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 265d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (msg.what == MSG_SCREENSHOT_RESPONSE) { 266d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme handleScreenshotResponse(msg); 267d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 268d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 269d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 27069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme if (msg.what != MSG_SERVICE_COMMAND) { 27169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme // Sanity check. 27269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme Log.e(TAG, "Invalid message type: " + msg.what); 27369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return; 27469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 27569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 27646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme // At this point it's handling onStartCommand(), with the intent passed as an Extra. 27769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme if (!(msg.obj instanceof Intent)) { 27869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme // Sanity check. 27969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme Log.e(TAG, "Internal error: invalid msg.obj: " + msg.obj); 28069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return; 28169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 28269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme final Parcelable parcel = ((Intent) msg.obj).getParcelableExtra(EXTRA_ORIGINAL_INTENT); 28346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final Intent intent; 28446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme if (parcel instanceof Intent) { 28546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme // The real intent was passed to BugreportReceiver, which delegated to the service. 28646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme intent = (Intent) parcel; 28746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } else { 28846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme intent = (Intent) msg.obj; 28969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 29069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme final String action = intent.getAction(); 29146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final int pid = intent.getIntExtra(EXTRA_PID, 0); 29246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final int max = intent.getIntExtra(EXTRA_MAX, -1); 29346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final String name = intent.getStringExtra(EXTRA_NAME); 29469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 29569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme if (DEBUG) Log.v(TAG, "action: " + action + ", name: " + name + ", pid: " + pid 29669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme + ", max: "+ max); 29769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme switch (action) { 29869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme case INTENT_BUGREPORT_STARTED: 29969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme if (!startProgress(name, pid, max)) { 30069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme stopSelfWhenDone(); 30169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return; 30269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 30346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme poll(); 30469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme break; 30569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme case INTENT_BUGREPORT_FINISHED: 30646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme if (pid == 0) { 30769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme // Shouldn't happen, unless BUGREPORT_FINISHED is received from a legacy, 30869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme // out-of-sync dumpstate process. 30969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme Log.w(TAG, "Missing " + EXTRA_PID + " on intent " + intent); 31069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 31146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme onBugreportFinished(pid, intent); 31246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme break; 313bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme case INTENT_BUGREPORT_INFO_LAUNCH: 314bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme launchBugreportInfoDialog(pid); 315bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme break; 316d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme case INTENT_BUGREPORT_SCREENSHOT: 317d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme takeScreenshot(pid, true); 318d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme break; 31946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme case INTENT_BUGREPORT_SHARE: 32046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme shareBugreport(pid); 32169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme break; 3229cadb75714bea7d435f34c8b7d06f698fa907a8dFelipe Leme case INTENT_BUGREPORT_CANCEL: 3239cadb75714bea7d435f34c8b7d06f698fa907a8dFelipe Leme cancel(pid); 3249cadb75714bea7d435f34c8b7d06f698fa907a8dFelipe Leme break; 32569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme default: 32669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme Log.w(TAG, "Unsupported intent: " + action); 32769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 32869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return; 32969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 33069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 33169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 332923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme private void poll() { 333923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme if (pollProgress()) { 334923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme // Keep polling... 335923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme sendEmptyMessageDelayed(MSG_POLL, POLLING_FREQUENCY); 33646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } else { 33746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme Log.i(TAG, "Stopped polling"); 33869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 339923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 340923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 34169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 342923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme /** 343d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Separate thread used only to take screenshots so it doesn't block the main thread. 344d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 345d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private final class ScreenshotHandler extends Handler { 346d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme public ScreenshotHandler(String name) { 347d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme super(newLooper(name)); 348d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 349d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 350d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme @Override 351d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme public void handleMessage(Message msg) { 352d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (msg.what != MSG_SCREENSHOT_REQUEST) { 353d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.e(TAG, "Invalid message type: " + msg.what); 354d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 355d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 356d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme handleScreenshotRequest(msg); 357d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 358d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 359d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 360d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private BugreportInfo getInfo(int pid) { 361d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final BugreportInfo info = mProcesses.get(pid); 362d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 363d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.w(TAG, "Not monitoring process with PID " + pid); 364d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 365d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return info; 366d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 367d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 368d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 369923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * Creates the {@link BugreportInfo} for a process and issue a system notification to 370923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * indicate its progress. 371923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * 372923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * @return whether it succeeded or not. 373923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme */ 374923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme private boolean startProgress(String name, int pid, int max) { 375923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme if (name == null) { 376923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme Log.w(TAG, "Missing " + EXTRA_NAME + " on start intent"); 377923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 378923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme if (pid == -1) { 379923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme Log.e(TAG, "Missing " + EXTRA_PID + " on start intent"); 380923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme return false; 381923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 382923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme if (max <= 0) { 383923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme Log.e(TAG, "Invalid value for extra " + EXTRA_MAX + ": " + max); 384923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme return false; 38569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 38669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 387d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final BugreportInfo info = new BugreportInfo(mContext, pid, name, max); 388d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (mProcesses.indexOfKey(pid) >= 0) { 389d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.w(TAG, "PID " + pid + " already watched"); 390d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } else { 391d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mProcesses.put(info.pid, info); 392923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 393d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme // Take initial screenshot. 394d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme takeScreenshot(pid, false); 395923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme updateProgress(info); 396923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme return true; 397923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 39869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 399923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme /** 40046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Updates the system notification for a given bugreport. 401923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme */ 402923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme private void updateProgress(BugreportInfo info) { 403923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme if (info.max <= 0 || info.progress < 0) { 404923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme Log.e(TAG, "Invalid progress values for " + info); 405923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme return; 40669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 40769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 408923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme final NumberFormat nf = NumberFormat.getPercentInstance(); 409923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme nf.setMinimumFractionDigits(2); 410923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme nf.setMaximumFractionDigits(2); 411923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme final String percentText = nf.format((double) info.progress / info.max); 412d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Action cancelAction = new Action.Builder(null, mContext.getString( 413d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme com.android.internal.R.string.cancel), newCancelIntent(mContext, info)).build(); 414d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Intent infoIntent = new Intent(mContext, BugreportProgressService.class); 415bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme infoIntent.setAction(INTENT_BUGREPORT_INFO_LAUNCH); 416bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme infoIntent.putExtra(EXTRA_PID, info.pid); 417bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final Action infoAction = new Action.Builder(null, 418d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mContext.getString(R.string.bugreport_info_action), 419d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme PendingIntent.getService(mContext, info.pid, infoIntent, 420bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme PendingIntent.FLAG_UPDATE_CURRENT)).build(); 421d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Intent screenshotIntent = new Intent(mContext, BugreportProgressService.class); 422d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme screenshotIntent.setAction(INTENT_BUGREPORT_SCREENSHOT); 423d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme screenshotIntent.putExtra(EXTRA_PID, info.pid); 424d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme PendingIntent screenshotPendingIntent = mTakingScreenshot ? null : PendingIntent 425d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme .getService(mContext, info.pid, screenshotIntent, 426d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme PendingIntent.FLAG_UPDATE_CURRENT); 427d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Action screenshotAction = new Action.Builder(null, 428d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mContext.getString(R.string.bugreport_screenshot_action), 429d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme screenshotPendingIntent).build(); 430d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 431d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final String title = mContext.getString(R.string.bugreport_in_progress_title); 432923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme 433923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme final String name = 434d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.name != null ? info.name : mContext.getString(R.string.bugreport_unnamed); 435923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme 436d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Notification notification = new Notification.Builder(mContext) 437923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) 438923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .setContentTitle(title) 439923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .setTicker(title) 440923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .setContentText(name) 441923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .setContentInfo(percentText) 442923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .setProgress(info.max, info.progress, false) 443923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .setOngoing(true) 444923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .setLocalOnly(true) 445d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme .setColor(mContext.getColor( 446923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme com.android.internal.R.color.system_notification_accent_color)) 447bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .addAction(infoAction) 448d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme .addAction(screenshotAction) 449923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .addAction(cancelAction) 450923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme .build(); 451923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme 452d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme NotificationManager.from(mContext).notify(TAG, info.pid, notification); 453923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 454923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme 455923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme /** 45646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Creates a {@link PendingIntent} for a notification action used to cancel a bugreport. 45746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 45846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme private static PendingIntent newCancelIntent(Context context, BugreportInfo info) { 45946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final Intent intent = new Intent(INTENT_BUGREPORT_CANCEL); 46046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme intent.setClass(context, BugreportProgressService.class); 46146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme intent.putExtra(EXTRA_PID, info.pid); 462bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return PendingIntent.getService(context, info.pid, intent, 463bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme PendingIntent.FLAG_UPDATE_CURRENT); 46446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } 46546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 46646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 46746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Finalizes the progress on a given bugreport and cancel its notification. 468923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme */ 46946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme private void stopProgress(int pid) { 470d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (mProcesses.indexOfKey(pid) < 0) { 471d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.w(TAG, "PID not watched: " + pid); 472d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } else { 473d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mProcesses.remove(pid); 47469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 475d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme stopSelfWhenDone(); 476bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme Log.v(TAG, "stopProgress(" + pid + "): cancel notification"); 477d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme NotificationManager.from(mContext).cancel(TAG, pid); 478923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 479923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme 480923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme /** 481923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * Cancels a bugreport upon user's request. 482923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme */ 483923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme private void cancel(int pid) { 484bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme Log.v(TAG, "cancel: pid=" + pid); 485d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final BugreportInfo info = getInfo(pid); 486d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info != null && !info.finished) { 487d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.i(TAG, "Cancelling bugreport service (pid=" + pid + ") on user's request"); 488d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme setSystemProperty(CTL_STOP, BUGREPORT_SERVICE); 489d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme deleteScreenshots(info); 490bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 49146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme stopProgress(pid); 492923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 4939cadb75714bea7d435f34c8b7d06f698fa907a8dFelipe Leme 494923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme /** 495923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * Poll {@link SystemProperties} to get the progress on each monitored process. 496923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * 497923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * @return whether it should keep polling. 498923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme */ 499923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme private boolean pollProgress() { 500d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final int total = mProcesses.size(); 501d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (total == 0) { 502d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.d(TAG, "No process to poll progress."); 503d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 504d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme int activeProcesses = 0; 505d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme for (int i = 0; i < total; i++) { 506d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final int pid = mProcesses.keyAt(i); 507d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final BugreportInfo info = mProcesses.valueAt(i); 508d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info.finished) { 509d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (DEBUG) Log.v(TAG, "Skipping finished process " + pid); 510d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme continue; 511923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 512d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme activeProcesses++; 513d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final String progressKey = DUMPSTATE_PREFIX + pid + PROGRESS_SUFFIX; 514d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final int progress = SystemProperties.getInt(progressKey, 0); 515d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (progress == 0) { 516d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.v(TAG, "System property " + progressKey + " is not set yet"); 517d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 518d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final int max = SystemProperties.getInt(DUMPSTATE_PREFIX + pid + MAX_SUFFIX, 0); 519d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final boolean maxChanged = max > 0 && max != info.max; 520d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final boolean progressChanged = progress > 0 && progress != info.progress; 521d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 522d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (progressChanged || maxChanged) { 523d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (progressChanged) { 524d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (DEBUG) Log.v(TAG, "Updating progress for PID " + pid + " from " 525d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme + info.progress + " to " + progress); 526d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.progress = progress; 52746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } 528d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (maxChanged) { 529d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.i(TAG, "Updating max progress for PID " + pid + " from " + info.max 530d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme + " to " + max); 531d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.max = max; 53269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 533d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.lastUpdate = System.currentTimeMillis(); 534d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme updateProgress(info); 535d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } else { 536d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme long inactiveTime = System.currentTimeMillis() - info.lastUpdate; 537d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (inactiveTime >= INACTIVITY_TIMEOUT) { 538d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.w(TAG, "No progress update for process " + pid + " since " 539d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme + info.getFormattedLastUpdate()); 540d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme stopProgress(info.pid); 54169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 54269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 54369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 544d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (DEBUG) Log.v(TAG, "pollProgress() total=" + total + ", actives=" + activeProcesses); 545d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return activeProcesses > 0; 546923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 54769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 548923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme /** 549bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Fetches a {@link BugreportInfo} for a given process and launches a dialog where the user can 550bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * change its values. 551bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 552bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private void launchBugreportInfoDialog(int pid) { 553bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // Copy values so it doesn't lock mProcesses while UI is being updated 554bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final String name, title, description; 555d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final BugreportInfo info = getInfo(pid); 556d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 557d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 558d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 559d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme name = info.name; 560d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme title = info.title; 561d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme description = info.description; 562d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 563d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme collapseNotificationBar(); 564d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mInfoDialog.initialize(mContext, pid, name, title, description); 565d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 566d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 567d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 568d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Starting point for taking a screenshot. 569d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * <p> 570d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * If {@code delayed} is set, it first display a toast message and waits 571d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * {@link #SCREENSHOT_DELAY_SECONDS} seconds before taking it, otherwise it takes the screenshot 572d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * right away. 573d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * <p> 574d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Typical usage is delaying when taken from the notification action, and taking it right away 575d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * upon receiving a {@link #INTENT_BUGREPORT_STARTED}. 576d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 577d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void takeScreenshot(int pid, boolean delayed) { 578d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme setTakingScreenshot(true); 579d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (delayed) { 580d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme collapseNotificationBar(); 581d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final String msg = mContext.getResources() 582d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme .getQuantityString(com.android.internal.R.plurals.bugreport_countdown, 583d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme SCREENSHOT_DELAY_SECONDS, SCREENSHOT_DELAY_SECONDS); 584d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.i(TAG, msg); 585d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme // Show a toast just once, otherwise it might be captured in the screenshot. 586d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show(); 587d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 588d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme takeScreenshot(pid, SCREENSHOT_DELAY_SECONDS); 589d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } else { 590d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme takeScreenshot(pid, 0); 591d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 592d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 593d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 594d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 595d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Takes a screenshot after {@code delay} seconds. 596d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 597d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void takeScreenshot(int pid, int delay) { 598d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (delay > 0) { 599d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.d(TAG, "Taking screenshot for " + pid + " in " + delay + " seconds"); 600d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Message msg = mMainHandler.obtainMessage(); 601d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme msg.what = MSG_DELAYED_SCREENSHOT; 602d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme msg.arg1 = pid; 603d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme msg.arg2 = delay - 1; 604d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mMainHandler.sendMessageDelayed(msg, DateUtils.SECOND_IN_MILLIS); 605d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 606d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 607d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 608d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme // It's time to take the screenshot: let the proper thread handle it 609d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final BugreportInfo info = getInfo(pid); 610d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 611d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 612d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 613d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final String screenshotPath = 614d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme new File(mScreenshotsDir, info.getPathNextScreenshot()).getAbsolutePath(); 615d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 616d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Message requestMsg = new Message(); 617d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme requestMsg.what = MSG_SCREENSHOT_REQUEST; 618d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme requestMsg.arg1 = pid; 619d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme requestMsg.obj = screenshotPath; 620d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mScreenshotHandler.sendMessage(requestMsg); 621d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 622d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 623d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 624d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Sets the internal {@code mTakingScreenshot} state and updates all notifications so their 625d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * SCREENSHOT button is enabled or disabled accordingly. 626d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 627d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void setTakingScreenshot(boolean flag) { 628d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme synchronized (BugreportProgressService.this) { 629d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mTakingScreenshot = flag; 630d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme for (int i = 0; i < mProcesses.size(); i++) { 631d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme updateProgress(mProcesses.valueAt(i)); 632bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 633bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 634d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 635bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 636d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void handleScreenshotRequest(Message requestMsg) { 637d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme String screenshotFile = (String) requestMsg.obj; 638d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme boolean taken = takeScreenshot(mContext, screenshotFile); 639d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme setTakingScreenshot(false); 640d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 641d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Message resultMsg = new Message(); 642d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme resultMsg.what = MSG_SCREENSHOT_RESPONSE; 643d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme resultMsg.arg1 = requestMsg.arg1; 644d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme resultMsg.arg2 = taken ? 1 : 0; 645d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme resultMsg.obj = screenshotFile; 646d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mMainHandler.sendMessage(resultMsg); 647d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 648bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 649d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void handleScreenshotResponse(Message resultMsg) { 650d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final boolean taken = resultMsg.arg2 != 0; 651d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final BugreportInfo info = getInfo(resultMsg.arg1); 652d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 653d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 654d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 655d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final File screenshotFile = new File((String) resultMsg.obj); 656d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 657d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final int msgId; 658d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (taken) { 659d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.addScreenshot(screenshotFile); 660d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme msgId = R.string.bugreport_screenshot_taken; 661d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } else { 662d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme // TODO: try again using Framework APIs instead of relying on screencap. 663d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme msgId = R.string.bugreport_screenshot_failed; 664d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 665d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final String msg = mContext.getString(msgId); 666d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.d(TAG, msg); 667d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show(); 668d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 669d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 670d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 671d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Deletes all screenshots taken for a given bugreport. 672d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 673d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void deleteScreenshots(BugreportInfo info) { 674d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme for (File file : info.screenshotFiles) { 675d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.i(TAG, "Deleting screenshot file " + file); 676d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme file.delete(); 677d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 678bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 679bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 680bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 681923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme * Finishes the service when it's not monitoring any more processes. 682923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme */ 683923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme private void stopSelfWhenDone() { 684d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (mProcesses.size() > 0) { 685d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (DEBUG) Log.v(TAG, "Staying alive, waiting for pids " + mProcesses); 686d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 68769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 688d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.v(TAG, "No more pids to handle, shutting down"); 689d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme stopSelf(); 690923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme } 69169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 692bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 693bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Handles the BUGREPORT_FINISHED intent sent by {@code dumpstate}. 694bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 695923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme private void onBugreportFinished(int pid, Intent intent) { 696bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoDialog.onBugreportFinished(pid); 697d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme BugreportInfo info = getInfo(pid); 698d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 699d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme // Happens when BUGREPORT_FINISHED was received without a BUGREPORT_STARTED first. 700d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.v(TAG, "Creating info for untracked pid " + pid); 701d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info = new BugreportInfo(mContext, pid); 702d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mProcesses.put(pid, info); 703d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 704d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.renameScreenshots(mScreenshotsDir); 705d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT); 706d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final File screenshot = getFileExtra(intent, EXTRA_SCREENSHOT); 707d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (screenshot != null) { 708d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.addScreenshot(screenshot); 70946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } 710d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.finished = true; 71169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 712d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Configuration conf = mContext.getResources().getConfiguration(); 713923afa9fe1bbc9a5395309622e596ad03c3d8481Felipe Leme if ((conf.uiMode & Configuration.UI_MODE_TYPE_MASK) != Configuration.UI_MODE_TYPE_WATCH) { 714d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme triggerLocalNotification(mContext, info); 715b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 716b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 717b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 718b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme /** 71969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * Responsible for triggering a notification that allows the user to start a "share" intent with 72046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * the bugreport. On watches we have other methods to allow the user to start this intent 72169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * (usually by triggering it on another connected device); we don't need to display the 72269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * notification in this case. 723b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme */ 724d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void triggerLocalNotification(final Context context, final BugreportInfo info) { 72546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) { 72646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme Log.e(TAG, "Could not read bugreport file " + info.bugreportFile); 727d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Toast.makeText(context, R.string.bugreport_unreadable_text, Toast.LENGTH_LONG).show(); 728d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme stopProgress(info.pid); 729b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return; 730b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 731b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 73246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme boolean isPlainText = info.bugreportFile.getName().toLowerCase().endsWith(".txt"); 733b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (!isPlainText) { 734b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // Already zipped, send it right away. 73546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme sendBugreportNotification(context, info); 736b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } else { 737b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // Asynchronously zip the file first, then send it. 73846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme sendZippedBugreportNotification(context, info); 739b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 740b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 741b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 742b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme private static Intent buildWarningIntent(Context context, Intent sendIntent) { 743b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final Intent intent = new Intent(context, BugreportWarningActivity.class); 744b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.putExtra(Intent.EXTRA_INTENT, sendIntent); 745b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return intent; 746b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 747b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 748b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme /** 749b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * Build {@link Intent} that can be used to share the given bugreport. 750b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme */ 751bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private static Intent buildSendIntent(Context context, BugreportInfo info) { 752bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // Files are kept on private storage, so turn into Uris that we can 753bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // grant temporary permissions for. 754bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final Uri bugreportUri = getUri(context, info.bugreportFile); 755bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 756b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE); 757b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final String mimeType = "application/vnd.android.bugreport"; 758b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 759b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.addCategory(Intent.CATEGORY_DEFAULT); 760b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.setType(mimeType); 761b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 762bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final String subject = info.title != null ? info.title : bugreportUri.getLastPathSegment(); 763bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme intent.putExtra(Intent.EXTRA_SUBJECT, subject); 764b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 765b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // EXTRA_TEXT should be an ArrayList, but some clients are expecting a single String. 766b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // So, to avoid an exception on Intent.migrateExtraStreamToClipData(), we need to manually 767b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // create the ClipData object with the attachments URIs. 768d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final StringBuilder messageBody = new StringBuilder("Build info: ") 769bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .append(SystemProperties.get("ro.build.description")) 770bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .append("\nSerial number: ") 771bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .append(SystemProperties.get("ro.serialno")); 772bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (!TextUtils.isEmpty(info.description)) { 773bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme messageBody.append("\nDescription: ").append(info.description); 774bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 775bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme intent.putExtra(Intent.EXTRA_TEXT, messageBody.toString()); 776b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final ClipData clipData = new ClipData(null, new String[] { mimeType }, 777b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme new ClipData.Item(null, null, null, bugreportUri)); 778b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final ArrayList<Uri> attachments = Lists.newArrayList(bugreportUri); 779d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme for (File screenshot : info.screenshotFiles) { 780d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Uri screenshotUri = getUri(context, screenshot); 781b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme clipData.addItem(new ClipData.Item(null, null, null, screenshotUri)); 782b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme attachments.add(screenshotUri); 783b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 784b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.setClipData(clipData); 785b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachments); 786b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 787b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final Account sendToAccount = findSendToAccount(context); 788b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (sendToAccount != null) { 789b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme intent.putExtra(Intent.EXTRA_EMAIL, new String[] { sendToAccount.name }); 790b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 791b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 792b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return intent; 793b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 794b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 795b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme /** 79646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Shares the bugreport upon user's request by issuing a {@link Intent#ACTION_SEND_MULTIPLE} 79746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * intent, but issuing a warning dialog the first time. 798b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme */ 79946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme private void shareBugreport(int pid) { 800d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final BugreportInfo info = getInfo(pid); 801d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 802d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme // Should not happen, so log if it does... 803d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.e(TAG, "INTERNAL ERROR: no info for PID " + pid + ": " + mProcesses); 804d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 80546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } 806d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final Intent sendIntent = buildSendIntent(mContext, info); 80746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final Intent notifIntent; 808b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 809b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // Send through warning dialog by default 810d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (getWarningState(mContext, STATE_SHOW) == STATE_SHOW) { 811d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme notifIntent = buildWarningIntent(mContext, sendIntent); 812b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } else { 813b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme notifIntent = sendIntent; 814b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 815b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme notifIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 816b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 81746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme // Send the share intent... 818d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme mContext.startActivity(notifIntent); 81946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 82046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme // ... and stop watching this process. 82146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme stopProgress(pid); 82246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } 82346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 82446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 82546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Sends a notitication indicating the bugreport has finished so use can share it. 82646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 82746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme private static void sendBugreportNotification(Context context, BugreportInfo info) { 82846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final Intent shareIntent = new Intent(INTENT_BUGREPORT_SHARE); 82946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme shareIntent.setClass(context, BugreportProgressService.class); 83046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme shareIntent.setAction(INTENT_BUGREPORT_SHARE); 83146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme shareIntent.putExtra(EXTRA_PID, info.pid); 83246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 83369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme final String title = context.getString(R.string.bugreport_finished_title); 834b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final Notification.Builder builder = new Notification.Builder(context) 835b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) 83669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme .setContentTitle(title) 83769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme .setTicker(title) 838b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme .setContentText(context.getString(R.string.bugreport_finished_text)) 839bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .setContentIntent(PendingIntent.getService(context, info.pid, shareIntent, 840bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme PendingIntent.FLAG_UPDATE_CURRENT)) 84146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme .setDeleteIntent(newCancelIntent(context, info)) 842b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme .setLocalOnly(true) 843b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme .setColor(context.getColor( 844b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme com.android.internal.R.color.system_notification_accent_color)); 845b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 846bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (!TextUtils.isEmpty(info.name)) { 847bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme builder.setContentInfo(info.name); 848bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 849bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 85046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme NotificationManager.from(context).notify(TAG, info.pid, builder.build()); 851b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 852b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 853b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme /** 854b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * Sends a zipped bugreport notification. 855b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme */ 856b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme private static void sendZippedBugreportNotification(final Context context, 85746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme final BugreportInfo info) { 858b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme new AsyncTask<Void, Void, Void>() { 859b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme @Override 860b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme protected Void doInBackground(Void... params) { 86146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme info.bugreportFile = zipBugreport(info.bugreportFile); 86246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme sendBugreportNotification(context, info); 863b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return null; 864b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 865b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme }.execute(); 866b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 867b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 868b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme /** 869b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * Zips a bugreport file, returning the path to the new file (or to the 870b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * original in case of failure). 871b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme */ 872b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme private static File zipBugreport(File bugreportFile) { 873b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme String bugreportPath = bugreportFile.getAbsolutePath(); 874b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme String zippedPath = bugreportPath.replace(".txt", ".zip"); 875b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme Log.v(TAG, "zipping " + bugreportPath + " as " + zippedPath); 876b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme File bugreportZippedFile = new File(zippedPath); 877b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme try (InputStream is = new FileInputStream(bugreportFile); 87869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme ZipOutputStream zos = new ZipOutputStream( 87969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme new BufferedOutputStream(new FileOutputStream(bugreportZippedFile)))) { 880b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme ZipEntry entry = new ZipEntry(bugreportFile.getName()); 881b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme entry.setTime(bugreportFile.lastModified()); 882b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme zos.putNextEntry(entry); 883b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme int totalBytes = Streams.copy(is, zos); 884b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme Log.v(TAG, "size of original bugreport: " + totalBytes + " bytes"); 885b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme zos.closeEntry(); 886b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // Delete old file; 887b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme boolean deleted = bugreportFile.delete(); 888b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (deleted) { 889b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme Log.v(TAG, "deleted original bugreport (" + bugreportPath + ")"); 890b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } else { 891b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme Log.e(TAG, "could not delete original bugreport (" + bugreportPath + ")"); 892b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 893b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return bugreportZippedFile; 894b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } catch (IOException e) { 89569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme Log.e(TAG, "exception zipping file " + zippedPath, e); 89669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return bugreportFile; // Return original. 897b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 898b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 899b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 900b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme /** 901b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme * Find the best matching {@link Account} based on build properties. 902b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme */ 903b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme private static Account findSendToAccount(Context context) { 904b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final AccountManager am = (AccountManager) context.getSystemService( 905b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme Context.ACCOUNT_SERVICE); 906b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 907b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme String preferredDomain = SystemProperties.get("sendbug.preferred.domain"); 908b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (!preferredDomain.startsWith("@")) { 909b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme preferredDomain = "@" + preferredDomain; 910b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 911b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 912b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final Account[] accounts = am.getAccounts(); 913b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme Account foundAccount = null; 914b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme for (Account account : accounts) { 915b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (Patterns.EMAIL_ADDRESS.matcher(account.name).matches()) { 916b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (!preferredDomain.isEmpty()) { 917b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // if we have a preferred domain and it matches, return; otherwise keep 918b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // looking 919b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (account.name.endsWith(preferredDomain)) { 920b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return account; 921b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } else { 922b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme foundAccount = account; 923b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 924b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // if we don't have a preferred domain, just return since it looks like 925b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme // an email address 926b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } else { 927b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return account; 928b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 929b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 930b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 931b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return foundAccount; 932b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 933b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 934b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme private static Uri getUri(Context context, File file) { 935b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return file != null ? FileProvider.getUriForFile(context, AUTHORITY, file) : null; 936b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 937b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme 938b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme static File getFileExtra(Intent intent, String key) { 939b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme final String path = intent.getStringExtra(key); 940b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme if (path != null) { 941b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return new File(path); 942b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } else { 943b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme return null; 944b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 945b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme } 94669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 947bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private static boolean setSystemProperty(String key, String value) { 948bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme try { 949bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (DEBUG) Log.v(TAG, "Setting system property" + key + " to " + value); 950bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme SystemProperties.set(key, value); 951bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } catch (IllegalArgumentException e) { 952bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme Log.e(TAG, "Could not set property " + key + " to " + value, e); 953bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return false; 954bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 955bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return true; 956bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 957bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 958bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 959bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Updates the system property used by {@code dumpstate} to rename the final bugreport files. 960bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 961bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private boolean setBugreportNameProperty(int pid, String name) { 962bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme Log.d(TAG, "Updating bugreport name to " + name); 963bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final String key = DUMPSTATE_PREFIX + pid + NAME_SUFFIX; 964bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return setSystemProperty(key, name); 965bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 966bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 967bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 968bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Updates the user-provided details of a bugreport. 969bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 970bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private void updateBugreportInfo(int pid, String name, String title, String description) { 971d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final BugreportInfo info = getInfo(pid); 972d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (info == null) { 973d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 974d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 975d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.title = title; 976d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.description = description; 977d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (name != null && !info.name.equals(name)) { 978d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme info.name = name; 979d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme updateProgress(info); 980d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 981d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 982d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 983d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void collapseNotificationBar() { 984d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); 985d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 986d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 987d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private static Looper newLooper(String name) { 988d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final HandlerThread thread = new HandlerThread(name, THREAD_PRIORITY_BACKGROUND); 989d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme thread.start(); 990d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return thread.getLooper(); 991d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 992d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 993d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 994d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Takes a screenshot and save it to the given location. 995d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 996d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private static boolean takeScreenshot(Context context, String screenshotFile) { 997d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme ((Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE)) 998d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme .vibrate(150); 999d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final ProcessBuilder screencap = new ProcessBuilder() 1000d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme .command("/system/bin/screencap", "-p", screenshotFile); 1001d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.d(TAG, "Taking screenshot using " + screencap.command()); 1002d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme try { 1003d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final int exitValue = screencap.start().waitFor(); 1004d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (exitValue == 0) { 1005d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return true; 1006bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1007d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.e(TAG, "screencap (" + screencap.command() + ") failed: " + exitValue); 1008d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } catch (IOException e) { 1009d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.e(TAG, "screencap (" + screencap.command() + ") failed", e); 1010d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } catch (InterruptedException e) { 1011d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.w(TAG, "Thread interrupted while screencap still running"); 1012d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Thread.currentThread().interrupt(); 1013bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1014d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return false; 1015bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1016bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1017bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1018bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Checks whether a character is valid on bugreport names. 1019bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1020bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme @VisibleForTesting 1021bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme static boolean isValid(char c) { 1022bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') 1023bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme || c == '_' || c == '-'; 1024bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1025bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1026bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1027bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Helper class encapsulating the UI elements and logic used to display a dialog where user 1028bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * can change the details of a bugreport. 1029bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1030bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private final class BugreportInfoDialog { 1031bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private EditText mInfoName; 1032bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private EditText mInfoTitle; 1033bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private EditText mInfoDescription; 1034bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private AlertDialog mDialog; 1035bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private Button mOkButton; 1036bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private int mPid; 1037bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1038bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1039bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Last "committed" value of the bugreport name. 1040bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * <p> 1041bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Once initially set, it's only updated when user clicks the OK button. 1042bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1043bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private String mSavedName; 1044bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1045bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1046bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Last value of the bugreport name as entered by the user. 1047bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * <p> 1048bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Every time it's changed the equivalent system property is changed as well, but if the 1049bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * user clicks CANCEL, the old value (stored on {@code mSavedName} is restored. 1050bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * <p> 1051bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * This logic handles the corner-case scenario where {@code dumpstate} finishes after the 1052bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * user changed the name but didn't clicked OK yet (for example, because the user is typing 1053bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * the description). The only drawback is that if the user changes the name while 1054bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * {@code dumpstate} is running but clicks CANCEL after it finishes, then the final name 1055bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * will be the one that has been canceled. But when {@code dumpstate} finishes the {code 1056bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * name} UI is disabled and the old name restored anyways, so the user will be "alerted" of 1057bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * such drawback. 1058bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1059bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme private String mTempName; 1060bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1061bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1062bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Sets its internal state and displays the dialog. 1063bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1064d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void initialize(Context context, int pid, String name, String title, 1065bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme String description) { 1066bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // First initializes singleton. 1067bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (mDialog == null) { 1068bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme @SuppressLint("InflateParams") 1069bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // It's ok pass null ViewRoot on AlertDialogs. 1070bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final View view = View.inflate(context, R.layout.dialog_bugreport_info, null); 1071bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1072bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoName = (EditText) view.findViewById(R.id.name); 1073bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoTitle = (EditText) view.findViewById(R.id.title); 1074bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoDescription = (EditText) view.findViewById(R.id.description); 1075bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1076bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoName.setOnFocusChangeListener(new OnFocusChangeListener() { 1077bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1078bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme @Override 1079bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme public void onFocusChange(View v, boolean hasFocus) { 1080bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (hasFocus) { 1081bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return; 1082bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1083bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme sanitizeName(); 1084bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1085bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme }); 1086bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1087bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mDialog = new AlertDialog.Builder(context) 1088bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .setView(view) 1089bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .setTitle(context.getString(R.string.bugreport_info_dialog_title)) 1090bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .setCancelable(false) 1091bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .setPositiveButton(context.getString(com.android.internal.R.string.ok), 1092bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme null) 1093bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .setNegativeButton(context.getString(com.android.internal.R.string.cancel), 1094bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme new DialogInterface.OnClickListener() 1095bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme { 1096bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme @Override 1097bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme public void onClick(DialogInterface dialog, int id) 1098bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme { 1099bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (!mTempName.equals(mSavedName)) { 1100bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // Must restore dumpstate's name since it was changed 1101bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // before user clicked OK. 1102bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme setBugreportNameProperty(mPid, mSavedName); 1103bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1104bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1105bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme }) 1106bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme .create(); 1107bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1108bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mDialog.getWindow().setAttributes( 1109bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme new WindowManager.LayoutParams( 1110bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG)); 1111bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1112bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1113bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1114bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // Then set fields. 1115bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mSavedName = mTempName = name; 1116bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mPid = pid; 1117bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (!TextUtils.isEmpty(name)) { 1118bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoName.setText(name); 1119bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1120bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (!TextUtils.isEmpty(title)) { 1121bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoTitle.setText(title); 1122bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1123bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (!TextUtils.isEmpty(description)) { 1124bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoDescription.setText(description); 1125bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1126bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1127bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // And finally display it. 1128bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mDialog.show(); 1129bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1130bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // TODO: in a traditional AlertDialog, when the positive button is clicked the 1131bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // dialog is always closed, but we need to validate the name first, so we need to 1132bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // get a reference to it, which is only available after it's displayed. 1133bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // It would be cleaner to use a regular dialog instead, but let's keep this 1134bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // workaround for now and change it later, when we add another button to take 1135bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // extra screenshots. 1136bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (mOkButton == null) { 1137bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mOkButton = mDialog.getButton(DialogInterface.BUTTON_POSITIVE); 1138bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mOkButton.setOnClickListener(new View.OnClickListener() { 1139bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1140bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme @Override 1141bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme public void onClick(View view) { 1142bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme sanitizeName(); 1143bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final String name = mInfoName.getText().toString(); 1144bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final String title = mInfoTitle.getText().toString(); 1145bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final String description = mInfoDescription.getText().toString(); 1146bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1147bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme updateBugreportInfo(mPid, name, title, description); 1148bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mDialog.dismiss(); 1149bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1150bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme }); 1151bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1152bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1153bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1154bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1155bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Sanitizes the user-provided value for the {@code name} field, automatically replacing 1156bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * invalid characters if necessary. 1157bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1158d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void sanitizeName() { 1159bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme String name = mInfoName.getText().toString(); 1160bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (name.equals(mTempName)) { 1161bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (DEBUG) Log.v(TAG, "name didn't change, no need to sanitize: " + name); 1162bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return; 1163bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1164bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final StringBuilder safeName = new StringBuilder(name.length()); 1165bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme boolean changed = false; 1166bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme for (int i = 0; i < name.length(); i++) { 1167bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme final char c = name.charAt(i); 1168bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (isValid(c)) { 1169bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme safeName.append(c); 1170bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } else { 1171bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme changed = true; 1172bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme safeName.append('_'); 1173bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1174bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1175bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (changed) { 1176bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme Log.v(TAG, "changed invalid name '" + name + "' to '" + safeName + "'"); 1177bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme name = safeName.toString(); 1178bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoName.setText(name); 1179bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1180bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mTempName = name; 1181bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1182bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // Must update system property for the cases where dumpstate finishes 1183bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // while the user is still entering other fields (like title or 1184bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme // description) 1185bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme setBugreportNameProperty(mPid, name); 1186bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1187bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1188bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1189bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Notifies the dialog that the bugreport has finished so it disables the {@code name} 1190bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * field. 1191bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * <p>Once the bugreport is finished dumpstate has already generated the final files, so 1192bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * changing the name would have no effect. 1193bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1194d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme private void onBugreportFinished(int pid) { 1195bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme if (mInfoName != null) { 1196bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoName.setEnabled(false); 1197bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mInfoName.setText(mSavedName); 1198bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1199bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1200bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1201bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 1202bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 120369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 120446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Information about a bugreport process while its in progress. 120569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 120669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme private static final class BugreportInfo { 1207719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme private final Context context; 1208719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme 120969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 121046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * {@code pid} of the {@code dumpstate} process generating the bugreport. 121169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 121269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme final int pid; 121369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 121469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 121546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Name of the bugreport, will be used to rename the final files. 121669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * <p> 121746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Initial value is the bugreport filename reported by {@code dumpstate}, but user can 121869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * change it later to a more meaningful name. 121969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 1220719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme String name; 122169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 122269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 1223bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * User-provided, one-line summary of the bug; when set, will be used as the subject 1224bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * of the {@link Intent#ACTION_SEND_MULTIPLE} intent. 1225bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1226bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme String title; 1227bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1228bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 1229bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * User-provided, detailed description of the bugreport; when set, will be added to the body 1230bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * of the {@link Intent#ACTION_SEND_MULTIPLE} intent. 1231bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 1232bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme String description; 1233bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1234bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 123546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Maximum progress of the bugreport generation. 123669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 1237719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme int max; 123869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 123969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 124046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Current progress of the bugreport generation. 124169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 124269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme int progress; 124369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 124469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 124569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * Time of the last progress update. 124669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 124769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme long lastUpdate = System.currentTimeMillis(); 124869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 124946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 125046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Path of the main bugreport file. 125146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 125246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme File bugreportFile; 125346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 125446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 1255d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Path of the screenshot files. 125646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 1257d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme List<File> screenshotFiles = new ArrayList<>(1); 125846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 125946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 126046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Whether dumpstate sent an intent informing it has finished. 126146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 126246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme boolean finished; 126346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 126446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 1265d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Internal counter used to name screenshot files. 1266d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 1267d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme int screenshotCounter; 1268d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 1269d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 127046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED. 127146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 1272719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme BugreportInfo(Context context, int pid, String name, int max) { 1273719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme this.context = context; 127469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme this.pid = pid; 127569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme this.name = name; 127669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme this.max = max; 127769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 127869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 127946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme /** 128046d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * Constructor for untracked bugreports - typically called upon receiving BUGREPORT_FINISHED 128146d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme * without a previous call to BUGREPORT_STARTED. 128246d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme */ 128346d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme BugreportInfo(Context context, int pid) { 128446d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme this(context, pid, null, 0); 128546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme this.finished = true; 128646d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme } 128746d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme 1288d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 1289d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Gets the name for next screenshot file. 1290d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 1291d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme String getPathNextScreenshot() { 1292d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme screenshotCounter ++; 1293d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return "screenshot-" + pid + "-" + screenshotCounter + ".png"; 1294d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1295d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 1296d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 1297d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Saves the location of a taken screenshot so it can be sent out at the end. 1298d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 1299d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme void addScreenshot(File screenshot) { 1300d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme screenshotFiles.add(screenshot); 1301d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1302d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 1303d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme /** 1304d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme * Rename all screenshots files so that they contain the user-generated name instead of pid. 1305d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme */ 1306d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme void renameScreenshots(File screenshotDir) { 1307d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (TextUtils.isEmpty(name)) { 1308d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme return; 1309d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1310d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final List<File> renamedFiles = new ArrayList<>(screenshotFiles.size()); 1311d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme for (File oldFile : screenshotFiles) { 1312d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final String oldName = oldFile.getName(); 1313d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final String newName = oldName.replace(Integer.toString(pid), name); 1314d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final File newFile; 1315d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme if (!newName.equals(oldName)) { 1316d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme final File renamedFile = new File(screenshotDir, newName); 1317d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme newFile = oldFile.renameTo(renamedFile) ? renamedFile : oldFile; 1318d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } else { 1319d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme Log.w(TAG, "Name didn't change: " + oldName); // Shouldn't happen. 1320d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme newFile = oldFile; 1321d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1322d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme renamedFiles.add(newFile); 1323d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1324d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme screenshotFiles = renamedFiles; 1325d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme } 1326d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme 132769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme String getFormattedLastUpdate() { 1328719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme return DateUtils.formatDateTime(context, lastUpdate, 1329719aaae3c167c2b15525dbe5c7db514a2c0c8269Felipe Leme DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME); 133069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 133169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 133269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme @Override 133369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public String toString() { 133469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme final float percent = ((float) progress * 100 / max); 133546d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme return "pid: " + pid + ", name: " + name + ", finished: " + finished 1336bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme + "\n\ttitle: " + title + "\n\tdescription: " + description 1337d1e0f12979441733753b538611f6d73e5527c43cFelipe Leme + "\n\tfile: " + bugreportFile + "\n\tscreenshots: " + screenshotFiles 133846d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme + "\n\tprogress: " + progress + "/" + max + "(" + percent + ")" 133946d47911ea8bbbe0b8d7a6029b80da6b1eb94393Felipe Leme + "\n\tlast_update: " + getFormattedLastUpdate(); 134069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 134169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 1342b9238b37838d653c38ce4e712421adb61978fc22Felipe Leme} 1343