blob: 68da00fc107b732d832ba134b0866cde43644c81 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.traceur;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.preference.PreferenceManager;
import android.text.format.DateUtils;
import android.util.Log;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
public class TraceService extends IntentService {
protected static String INTENT_ACTION_FORCE_STOP_TRACING = "com.android.traceur.FORCE_STOP_TRACING";
private static String INTENT_ACTION_STOP_TRACING = "com.android.traceur.STOP_TRACING";
private static String INTENT_ACTION_START_TRACING = "com.android.traceur.START_TRACING";
private static String INTENT_EXTRA_TAGS= "tags";
private static String INTENT_EXTRA_BUFFER = "buffer";
private static String INTENT_EXTRA_APPS = "apps";
private static String INTENT_EXTRA_LONG_TRACE = "long_trace";
private static String INTENT_EXTRA_LONG_TRACE_SIZE = "long_trace_size";
private static String INTENT_EXTRA_LONG_TRACE_DURATION = "long_trace_duration";
private static int TRACE_NOTIFICATION = 1;
private static int SAVING_TRACE_NOTIFICATION = 2;
private static int FORCE_STOP_SAVING_TRACE_NOTIFICATION = 3;
private static final int MIN_KEEP_COUNT = 3;
private static final long MIN_KEEP_AGE = 4 * DateUtils.WEEK_IN_MILLIS;
public static void startTracing(final Context context,
Collection<String> tags, int bufferSizeKb, boolean apps,
boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes) {
Intent intent = new Intent(context, TraceService.class);
intent.setAction(INTENT_ACTION_START_TRACING);
intent.putExtra(INTENT_EXTRA_TAGS, new ArrayList(tags));
intent.putExtra(INTENT_EXTRA_BUFFER, bufferSizeKb);
intent.putExtra(INTENT_EXTRA_APPS, apps);
intent.putExtra(INTENT_EXTRA_LONG_TRACE, longTrace);
intent.putExtra(INTENT_EXTRA_LONG_TRACE_SIZE, maxLongTraceSizeMb);
intent.putExtra(INTENT_EXTRA_LONG_TRACE_DURATION, maxLongTraceDurationMinutes);
context.startForegroundService(intent);
}
public static void stopTracing(final Context context) {
Intent intent = new Intent(context, TraceService.class);
intent.setAction(INTENT_ACTION_STOP_TRACING);
context.startForegroundService(intent);
}
public TraceService() {
this("TraceService");
}
protected TraceService(String name) {
super(name);
setIntentRedelivery(true);
}
@Override
public void onHandleIntent(Intent intent) {
Context context = getApplicationContext();
if (intent.getAction().equals(INTENT_ACTION_START_TRACING)) {
startTracingInternal(intent.getStringArrayListExtra(INTENT_EXTRA_TAGS),
intent.getIntExtra(INTENT_EXTRA_BUFFER,
Integer.parseInt(context.getString(R.string.default_buffer_size))),
intent.getBooleanExtra(INTENT_EXTRA_APPS, false),
intent.getBooleanExtra(INTENT_EXTRA_LONG_TRACE, false),
intent.getIntExtra(INTENT_EXTRA_LONG_TRACE_SIZE,
Integer.parseInt(context.getString(R.string.default_long_trace_size))),
intent.getIntExtra(INTENT_EXTRA_LONG_TRACE_DURATION,
Integer.parseInt(context.getString(R.string.default_long_trace_duration))));
} else if (intent.getAction().equals(INTENT_ACTION_STOP_TRACING)) {
stopTracingInternal(TraceUtils.getOutputFilename(), false);
} else if (intent.getAction().equals(INTENT_ACTION_FORCE_STOP_TRACING)) {
stopTracingInternal(TraceUtils.getOutputFilename(), true);
}
}
private void startTracingInternal(Collection<String> tags, int bufferSizeKb, boolean appTracing,
boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes) {
Context context = getApplicationContext();
Intent stopIntent = new Intent(Receiver.STOP_ACTION,
null, context, Receiver.class);
stopIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
String title = context.getString(R.string.trace_is_being_recorded);
String msg = context.getString(R.string.tap_to_stop_tracing);
Notification.Builder notification =
new Notification.Builder(context, Receiver.NOTIFICATION_CHANNEL_TRACING)
.setSmallIcon(R.drawable.bugfood_icon)
.setContentTitle(title)
.setTicker(title)
.setContentText(msg)
.setContentIntent(
PendingIntent.getBroadcast(context, 0, stopIntent, 0))
.setOngoing(true)
.setLocalOnly(true)
.setColor(getColor(
com.android.internal.R.color.system_notification_accent_color));
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
notification.extend(new Notification.TvExtender());
}
startForeground(TRACE_NOTIFICATION, notification.build());
if (TraceUtils.traceStart(tags, bufferSizeKb, appTracing,
longTrace, maxLongTraceSizeMb, maxLongTraceDurationMinutes)) {
stopForeground(Service.STOP_FOREGROUND_DETACH);
} else {
// Starting the trace was unsuccessful, so ensure that tracing
// is stopped and the preference is reset.
TraceUtils.traceStop();
PreferenceManager.getDefaultSharedPreferences(context)
.edit().putBoolean(context.getString(R.string.pref_key_tracing_on),
false).commit();
QsService.updateTile();
stopForeground(Service.STOP_FOREGROUND_REMOVE);
}
}
private void stopTracingInternal(String outputFilename, boolean forceStop) {
Context context = getApplicationContext();
NotificationManager notificationManager =
getSystemService(NotificationManager.class);
Notification.Builder notification =
new Notification.Builder(this, Receiver.NOTIFICATION_CHANNEL_OTHER)
.setSmallIcon(R.drawable.bugfood_icon)
.setContentTitle(getString(R.string.saving_trace))
.setTicker(getString(R.string.saving_trace))
.setLocalOnly(true)
.setProgress(1, 0, true)
.setColor(getColor(
com.android.internal.R.color.system_notification_accent_color));
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
notification.extend(new Notification.TvExtender());
}
// We want to do the same thing regardless of whether the trace was
// stopped via the external signal or within Traceur. However, these
// two stopping mechanisms must use different notification IDs so that
// one doesn't accidentally remove or override notifications from the
// other.
int notificationId = forceStop
? FORCE_STOP_SAVING_TRACE_NOTIFICATION : SAVING_TRACE_NOTIFICATION;
startForeground(notificationId, notification.build());
notificationManager.cancel(TRACE_NOTIFICATION);
File file = TraceUtils.getOutputFile(outputFilename);
if (TraceUtils.traceDump(file)) {
FileSender.postNotification(getApplicationContext(), file);
}
stopForeground(Service.STOP_FOREGROUND_REMOVE);
TraceUtils.cleanupOlderFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE);
}
}