1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.traceur; 18 19 20 import android.app.IntentService; 21 import android.app.Notification; 22 import android.app.NotificationManager; 23 import android.app.PendingIntent; 24 import android.app.Service; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.PackageManager; 28 import android.preference.PreferenceManager; 29 import android.text.format.DateUtils; 30 import android.util.Log; 31 32 import java.io.File; 33 import java.util.ArrayList; 34 import java.util.Collection; 35 36 public class TraceService extends IntentService { 37 38 protected static String INTENT_ACTION_FORCE_STOP_TRACING = "com.android.traceur.FORCE_STOP_TRACING"; 39 private static String INTENT_ACTION_STOP_TRACING = "com.android.traceur.STOP_TRACING"; 40 private static String INTENT_ACTION_START_TRACING = "com.android.traceur.START_TRACING"; 41 42 private static String INTENT_EXTRA_TAGS= "tags"; 43 private static String INTENT_EXTRA_BUFFER = "buffer"; 44 private static String INTENT_EXTRA_APPS = "apps"; 45 private static String INTENT_EXTRA_LONG_TRACE = "long_trace"; 46 private static String INTENT_EXTRA_LONG_TRACE_SIZE = "long_trace_size"; 47 private static String INTENT_EXTRA_LONG_TRACE_DURATION = "long_trace_duration"; 48 49 private static int TRACE_NOTIFICATION = 1; 50 private static int SAVING_TRACE_NOTIFICATION = 2; 51 52 private static final int MIN_KEEP_COUNT = 3; 53 private static final long MIN_KEEP_AGE = 4 * DateUtils.WEEK_IN_MILLIS; 54 startTracing(final Context context, Collection<String> tags, int bufferSizeKb, boolean apps, boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes)55 public static void startTracing(final Context context, 56 Collection<String> tags, int bufferSizeKb, boolean apps, 57 boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes) { 58 Intent intent = new Intent(context, TraceService.class); 59 intent.setAction(INTENT_ACTION_START_TRACING); 60 intent.putExtra(INTENT_EXTRA_TAGS, new ArrayList(tags)); 61 intent.putExtra(INTENT_EXTRA_BUFFER, bufferSizeKb); 62 intent.putExtra(INTENT_EXTRA_APPS, apps); 63 intent.putExtra(INTENT_EXTRA_LONG_TRACE, longTrace); 64 intent.putExtra(INTENT_EXTRA_LONG_TRACE_SIZE, maxLongTraceSizeMb); 65 intent.putExtra(INTENT_EXTRA_LONG_TRACE_DURATION, maxLongTraceDurationMinutes); 66 context.startForegroundService(intent); 67 } 68 stopTracing(final Context context)69 public static void stopTracing(final Context context) { 70 Intent intent = new Intent(context, TraceService.class); 71 intent.setAction(INTENT_ACTION_STOP_TRACING); 72 context.startForegroundService(intent); 73 } 74 TraceService()75 public TraceService() { 76 this("TraceService"); 77 } 78 TraceService(String name)79 protected TraceService(String name) { 80 super(name); 81 setIntentRedelivery(true); 82 } 83 84 @Override onHandleIntent(Intent intent)85 public void onHandleIntent(Intent intent) { 86 Context context = getApplicationContext(); 87 88 if (intent.getAction().equals(INTENT_ACTION_START_TRACING)) { 89 startTracingInternal(intent.getStringArrayListExtra(INTENT_EXTRA_TAGS), 90 intent.getIntExtra(INTENT_EXTRA_BUFFER, 91 Integer.parseInt(context.getString(R.string.default_buffer_size))), 92 intent.getBooleanExtra(INTENT_EXTRA_APPS, false), 93 intent.getBooleanExtra(INTENT_EXTRA_LONG_TRACE, false), 94 intent.getIntExtra(INTENT_EXTRA_LONG_TRACE_SIZE, 95 Integer.parseInt(context.getString(R.string.default_long_trace_size))), 96 intent.getIntExtra(INTENT_EXTRA_LONG_TRACE_DURATION, 97 Integer.parseInt(context.getString(R.string.default_long_trace_duration)))); 98 } else if (intent.getAction().equals(INTENT_ACTION_STOP_TRACING)) { 99 stopTracingInternal(TraceUtils.getOutputFilename(), false); 100 } else if (intent.getAction().equals(INTENT_ACTION_FORCE_STOP_TRACING)) { 101 stopTracingInternal(TraceUtils.getOutputFilename(), true); 102 } 103 } 104 startTracingInternal(Collection<String> tags, int bufferSizeKb, boolean appTracing, boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes)105 private void startTracingInternal(Collection<String> tags, int bufferSizeKb, boolean appTracing, 106 boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes) { 107 Context context = getApplicationContext(); 108 Intent stopIntent = new Intent(Receiver.STOP_ACTION, 109 null, context, Receiver.class); 110 stopIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 111 112 String title = context.getString(R.string.trace_is_being_recorded); 113 String msg = context.getString(R.string.tap_to_stop_tracing); 114 115 Notification.Builder notification = 116 new Notification.Builder(context, Receiver.NOTIFICATION_CHANNEL_TRACING) 117 .setSmallIcon(R.drawable.bugfood_icon) 118 .setContentTitle(title) 119 .setTicker(title) 120 .setContentText(msg) 121 .setContentIntent( 122 PendingIntent.getBroadcast(context, 0, stopIntent, 0)) 123 .setOngoing(true) 124 .setLocalOnly(true) 125 .setColor(getColor( 126 com.android.internal.R.color.system_notification_accent_color)); 127 128 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { 129 notification.extend(new Notification.TvExtender()); 130 } 131 132 startForeground(TRACE_NOTIFICATION, notification.build()); 133 134 if (TraceUtils.traceStart(tags, bufferSizeKb, appTracing, 135 longTrace, maxLongTraceSizeMb, maxLongTraceDurationMinutes)) { 136 stopForeground(Service.STOP_FOREGROUND_DETACH); 137 } else { 138 // Starting the trace was unsuccessful, so ensure that tracing 139 // is stopped and the preference is reset. 140 TraceUtils.traceStop(); 141 PreferenceManager.getDefaultSharedPreferences(context) 142 .edit().putBoolean(context.getString(R.string.pref_key_tracing_on), 143 false).commit(); 144 QsService.updateTile(); 145 stopForeground(Service.STOP_FOREGROUND_REMOVE); 146 } 147 } 148 stopTracingInternal(String outputFilename, boolean forceStop)149 private void stopTracingInternal(String outputFilename, boolean forceStop) { 150 Context context = getApplicationContext(); 151 NotificationManager notificationManager = 152 getSystemService(NotificationManager.class); 153 154 Notification.Builder notification = 155 new Notification.Builder(this, Receiver.NOTIFICATION_CHANNEL_OTHER) 156 .setSmallIcon(R.drawable.bugfood_icon) 157 .setContentTitle(getString(R.string.saving_trace)) 158 .setTicker(getString(R.string.saving_trace)) 159 .setLocalOnly(true) 160 .setProgress(1, 0, true) 161 .setColor(getColor( 162 com.android.internal.R.color.system_notification_accent_color)); 163 164 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { 165 notification.extend(new Notification.TvExtender()); 166 } 167 168 startForeground(SAVING_TRACE_NOTIFICATION, notification.build()); 169 170 notificationManager.cancel(TRACE_NOTIFICATION); 171 172 File file = TraceUtils.getOutputFile(outputFilename); 173 174 if (TraceUtils.traceDump(file)) { 175 FileSender.postNotification(getApplicationContext(), file); 176 } 177 178 stopForeground(Service.STOP_FOREGROUND_REMOVE); 179 180 TraceUtils.cleanupOlderFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE); 181 } 182 183 } 184