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.util.Log; 30 31 import java.io.File; 32 import java.util.ArrayList; 33 import java.util.Collection; 34 35 public class TraceService extends IntentService { 36 37 protected static String INTENT_ACTION_FORCE_STOP_TRACING = "com.android.traceur.FORCE_STOP_TRACING"; 38 private static String INTENT_ACTION_STOP_TRACING = "com.android.traceur.STOP_TRACING"; 39 private static String INTENT_ACTION_START_TRACING = "com.android.traceur.START_TRACING"; 40 41 private static String INTENT_EXTRA_TAGS= "tags"; 42 private static String INTENT_EXTRA_BUFFER = "buffer"; 43 private static String INTENT_EXTRA_APPS = "apps"; 44 private static String INTENT_EXTRA_LONG_TRACE = "long_trace"; 45 private static String INTENT_EXTRA_LONG_TRACE_SIZE = "long_trace_size"; 46 private static String INTENT_EXTRA_LONG_TRACE_DURATION = "long_trace_duration"; 47 48 private static int TRACE_NOTIFICATION = 1; 49 private static int SAVING_TRACE_NOTIFICATION = 2; 50 private static int FORCE_STOP_SAVING_TRACE_NOTIFICATION = 3; 51 startTracing(final Context context, Collection<String> tags, int bufferSizeKb, boolean apps, boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes)52 public static void startTracing(final Context context, 53 Collection<String> tags, int bufferSizeKb, boolean apps, 54 boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes) { 55 Intent intent = new Intent(context, TraceService.class); 56 intent.setAction(INTENT_ACTION_START_TRACING); 57 intent.putExtra(INTENT_EXTRA_TAGS, new ArrayList(tags)); 58 intent.putExtra(INTENT_EXTRA_BUFFER, bufferSizeKb); 59 intent.putExtra(INTENT_EXTRA_APPS, apps); 60 intent.putExtra(INTENT_EXTRA_LONG_TRACE, longTrace); 61 intent.putExtra(INTENT_EXTRA_LONG_TRACE_SIZE, maxLongTraceSizeMb); 62 intent.putExtra(INTENT_EXTRA_LONG_TRACE_DURATION, maxLongTraceDurationMinutes); 63 context.startForegroundService(intent); 64 } 65 stopTracing(final Context context)66 public static void stopTracing(final Context context) { 67 Intent intent = new Intent(context, TraceService.class); 68 intent.setAction(INTENT_ACTION_STOP_TRACING); 69 context.startForegroundService(intent); 70 } 71 TraceService()72 public TraceService() { 73 this("TraceService"); 74 } 75 TraceService(String name)76 protected TraceService(String name) { 77 super(name); 78 setIntentRedelivery(true); 79 } 80 81 @Override onHandleIntent(Intent intent)82 public void onHandleIntent(Intent intent) { 83 Context context = getApplicationContext(); 84 85 if (intent.getAction().equals(INTENT_ACTION_START_TRACING)) { 86 startTracingInternal(intent.getStringArrayListExtra(INTENT_EXTRA_TAGS), 87 intent.getIntExtra(INTENT_EXTRA_BUFFER, 88 Integer.parseInt(context.getString(R.string.default_buffer_size))), 89 intent.getBooleanExtra(INTENT_EXTRA_APPS, false), 90 intent.getBooleanExtra(INTENT_EXTRA_LONG_TRACE, false), 91 intent.getIntExtra(INTENT_EXTRA_LONG_TRACE_SIZE, 92 Integer.parseInt(context.getString(R.string.default_long_trace_size))), 93 intent.getIntExtra(INTENT_EXTRA_LONG_TRACE_DURATION, 94 Integer.parseInt(context.getString(R.string.default_long_trace_duration)))); 95 } else if (intent.getAction().equals(INTENT_ACTION_STOP_TRACING)) { 96 stopTracingInternal(TraceUtils.getOutputFilename(), false); 97 } else if (intent.getAction().equals(INTENT_ACTION_FORCE_STOP_TRACING)) { 98 stopTracingInternal(TraceUtils.getOutputFilename(), true); 99 } 100 } 101 startTracingInternal(Collection<String> tags, int bufferSizeKb, boolean appTracing, boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes)102 private void startTracingInternal(Collection<String> tags, int bufferSizeKb, boolean appTracing, 103 boolean longTrace, int maxLongTraceSizeMb, int maxLongTraceDurationMinutes) { 104 Context context = getApplicationContext(); 105 Intent stopIntent = new Intent(Receiver.STOP_ACTION, 106 null, context, Receiver.class); 107 stopIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 108 109 String title = context.getString(R.string.trace_is_being_recorded); 110 String msg = context.getString(R.string.tap_to_stop_tracing); 111 112 Notification.Builder notification = 113 new Notification.Builder(context, Receiver.NOTIFICATION_CHANNEL_TRACING) 114 .setSmallIcon(R.drawable.stat_sys_adb) 115 .setContentTitle(title) 116 .setTicker(title) 117 .setContentText(msg) 118 .setContentIntent( 119 PendingIntent.getBroadcast(context, 0, stopIntent, 0)) 120 .setOngoing(true) 121 .setLocalOnly(true) 122 .setColor(getColor( 123 com.android.internal.R.color.system_notification_accent_color)); 124 125 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { 126 notification.extend(new Notification.TvExtender()); 127 } 128 129 startForeground(TRACE_NOTIFICATION, notification.build()); 130 131 if (TraceUtils.traceStart(tags, bufferSizeKb, appTracing, 132 longTrace, maxLongTraceSizeMb, maxLongTraceDurationMinutes)) { 133 stopForeground(Service.STOP_FOREGROUND_DETACH); 134 } else { 135 // Starting the trace was unsuccessful, so ensure that tracing 136 // is stopped and the preference is reset. 137 TraceUtils.traceStop(); 138 PreferenceManager.getDefaultSharedPreferences(context) 139 .edit().putBoolean(context.getString(R.string.pref_key_tracing_on), 140 false).commit(); 141 QsService.updateTile(); 142 stopForeground(Service.STOP_FOREGROUND_REMOVE); 143 } 144 } 145 stopTracingInternal(String outputFilename, boolean forceStop)146 private void stopTracingInternal(String outputFilename, boolean forceStop) { 147 Context context = getApplicationContext(); 148 NotificationManager notificationManager = 149 getSystemService(NotificationManager.class); 150 151 Notification.Builder notification = 152 new Notification.Builder(this, Receiver.NOTIFICATION_CHANNEL_OTHER) 153 .setSmallIcon(R.drawable.stat_sys_adb) 154 .setContentTitle(getString(R.string.saving_trace)) 155 .setTicker(getString(R.string.saving_trace)) 156 .setLocalOnly(true) 157 .setProgress(1, 0, true) 158 .setColor(getColor( 159 com.android.internal.R.color.system_notification_accent_color)); 160 161 if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { 162 notification.extend(new Notification.TvExtender()); 163 } 164 165 // We want to do the same thing regardless of whether the trace was 166 // stopped via the external signal or within Traceur. However, these 167 // two stopping mechanisms must use different notification IDs so that 168 // one doesn't accidentally remove or override notifications from the 169 // other. 170 int notificationId = forceStop 171 ? FORCE_STOP_SAVING_TRACE_NOTIFICATION : SAVING_TRACE_NOTIFICATION; 172 173 startForeground(notificationId, notification.build()); 174 175 notificationManager.cancel(TRACE_NOTIFICATION); 176 177 File file = TraceUtils.getOutputFile(outputFilename); 178 179 if (TraceUtils.traceDump(file)) { 180 FileSender.postNotification(getApplicationContext(), file); 181 } 182 183 stopForeground(Service.STOP_FOREGROUND_REMOVE); 184 } 185 186 } 187