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