Bug 1550291 - Android Q: Use the overlay permission or a foreground notification to start the crash handler; r=VladBaicu

Android Q doesn't allow starting Activities from background.
https://developer.android.com/preview/privacy/background-activity-starts

We can either piggy-back the SYSTEM_ALERT_WINDOW permission given by the user
for the Tab Queue functionality or use a foreground notification from where
users could start `CrashReporterActivity`.

Differential Revision: https://phabricator.services.mozilla.com/D33029

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Petru Lingurar 2019-06-10 15:52:58 +00:00
Родитель c9f1eb41c0
Коммит 70755e0b8c
5 изменённых файлов: 98 добавлений и 11 удалений

Просмотреть файл

@ -23,4 +23,5 @@
<item type="id" name="action_button"/>
<item type="id" name="page_progress"/>
<item type="id" name="mediaControlNotification"/>
<item type="id" name="crashReporterNotification"/>
</resources>

Просмотреть файл

@ -1,23 +1,92 @@
package org.mozilla.gecko;
import android.app.IntentService;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.Nullable;
import android.os.IBinder;
import android.provider.Settings;
import org.mozilla.gecko.notifications.NotificationHelper;
public class CrashHandlerService extends IntentService {
public CrashHandlerService() {
super("Crash Handler");
}
public class CrashHandlerService extends Service {
private static final String ACTION_STOP = "action_stop";
// Build.VERSION_CODES.Q placeholder. While Android Q is in Beta it shares API 28 with Android P.
private static final int ANDROID_Q = 29;
@Override
protected void onHandleIntent(@Nullable Intent intent) {
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_STOP.equals(intent.getAction())) {
dismissNotification();
} else {
intent.setClass(this, CrashReporterActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (AppConstants.Versions.feature29Plus) {
startCrashHandling(intent);
} else {
startActivity(intent);
// Avoid ANR due to background limitations on Oreo+
System.exit(0);
}
}
return Service.START_NOT_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* Call this for any necessary cleanup like removing the foreground notification shown on Android Q+.
*/
public static void reportingStarted(final Context context) {
if (AppConstants.Versions.feature29Plus) {
final Intent intent = new Intent(context, CrashHandlerService.class);
intent.setAction(ACTION_STOP);
context.startService(intent);
}
}
@TargetApi(ANDROID_Q)
private Notification getActivityNotification(final Context context, final Intent activityIntent) {
final Intent dismissNotificationIntent = new Intent(ACTION_STOP, null, this, this.getClass());
final PendingIntent dismissNotification = PendingIntent.getService(this, 0, dismissNotificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
final PendingIntent startReporterActivity = PendingIntent.getActivity(this, 0, activityIntent, 0);
final String notificationChannelId = NotificationHelper.getInstance(context)
.getNotificationChannel(NotificationHelper.Channel.CRASH_HANDLER).getId();
return new Notification.Builder(this, notificationChannelId)
.setSmallIcon(R.drawable.ic_status_logo)
.setContentTitle(getString(R.string.crash_notification_title))
.setContentText(getString(R.string.crash_notification_message))
.setDefaults(Notification.DEFAULT_ALL)
.setContentIntent(startReporterActivity)
.addAction(0, getString(R.string.crash_notification_negative_button_text), dismissNotification)
.build();
}
@TargetApi(ANDROID_Q)
private void dismissNotification() {
stopForeground(Service.STOP_FOREGROUND_REMOVE);
}
@TargetApi(ANDROID_Q)
private void startCrashHandling(final Intent activityIntent) {
// Piggy-back the SYSTEM_ALERT_WINDOW permission given by the user for the Tab Queue functionality.
// Otherwise fallback to display a foreground notification, this being the only way we can
// start an activity from background.
// https://developer.android.com/preview/privacy/background-activity-starts#conditions-allow-activity-starts
if (Settings.canDrawOverlays(this)) {
startActivity(activityIntent);
} else {
final Notification notification = getActivityNotification(this, activityIntent);
startForeground(R.id.mediaControlNotification, notification);
}
}
}

Просмотреть файл

@ -139,6 +139,8 @@ public class CrashReporterActivity extends AppCompatActivity
@Override
@SuppressLint("WrongThread") // We don't have a worker thread for the TelemetryDispatcher
public void onCreate(Bundle savedInstanceState) {
informReporterStarted();
super.onCreate(savedInstanceState);
// mHandler is created here so runnables can be run on the main thread
mHandler = new Handler();
@ -596,4 +598,11 @@ public class CrashReporterActivity extends AppCompatActivity
private String unescape(String string) {
return string.replaceAll("\\\\\\\\", "\\").replaceAll("\\\\n", "\n").replaceAll("\\\\t", "\t");
}
/**
* Inform other parts of the app that user started the crash reporting process.
*/
private void informReporterStarted() {
CrashHandlerService.reportingStarted(this);
}
}

Просмотреть файл

@ -68,6 +68,11 @@
<!ENTITY sending_crash_report "Sending crash report\u2026">
<!ENTITY crash_close_label "Close">
<!ENTITY crash_restart_label "Restart &brandShortName;">
<!-- Localization note (crash_notification_title, crash_notification_message, crash_notification_negative_button_text)
Text displayed in a system notification to allow starting the Crash Reporter (Android Q and later).-->
<!ENTITY crash_notification_title "&brandShortName; crashed">
<!ENTITY crash_notification_message "Tap to report to &vendorShortName;">
<!ENTITY crash_notification_negative_button_text "Ignore">
<!ENTITY url_bar_default_text2 "Search or enter address">

Просмотреть файл

@ -71,6 +71,9 @@
<string name="sending_crash_report">&sending_crash_report;</string>
<string name="crash_close_label">&crash_close_label;</string>
<string name="crash_restart_label">&crash_restart_label;</string>
<string name="crash_notification_title">&crash_notification_title;</string>
<string name="crash_notification_message">&crash_notification_message;</string>
<string name="crash_notification_negative_button_text">&crash_notification_negative_button_text;</string>
<string name="url_bar_default_text">&url_bar_default_text2;</string>
<string name="url_bar_qrcode_text">&url_bar_qrcode_text2;</string>