• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import com.google.android.collect.Sets;
20 
21 import android.app.Notification;
22 import android.app.NotificationChannel;
23 import android.app.NotificationManager;
24 import android.app.PendingIntent;
25 import android.content.BroadcastReceiver;
26 import android.content.ComponentName;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.SharedPreferences;
30 import android.content.pm.PackageManager;
31 import android.database.ContentObserver;
32 import android.net.Uri;
33 import android.os.Build;
34 import android.os.Handler;
35 import android.os.RemoteException;
36 import android.os.ServiceManager;
37 import android.preference.PreferenceManager;
38 import android.provider.Settings;
39 import android.text.TextUtils;
40 import android.util.Log;
41 
42 import com.android.internal.statusbar.IStatusBarService;
43 
44 import java.util.Set;
45 import java.util.TreeMap;
46 
47 public class Receiver extends BroadcastReceiver {
48 
49     public static final String STOP_ACTION = "com.android.traceur.STOP";
50     public static final String OPEN_ACTION = "com.android.traceur.OPEN";
51 
52     public static final String NOTIFICATION_CHANNEL = "system-tracing";
53 
54     private static final Set<String> ATRACE_TAGS = Sets.newArraySet(
55             "am", "binder_driver", "camera", "dalvik", "freq", "gfx", "hal",
56             "idle", "input", "irq", "res", "sched", "sync", "view", "wm",
57             "workq");
58 
59     /* The user list doesn't include workq, irq, or sync, because the user builds don't have
60      * permissions for them. */
61     private static final Set<String> ATRACE_TAGS_USER = Sets.newArraySet(
62             "am", "binder_driver", "camera", "dalvik", "freq", "gfx", "hal",
63             "idle", "input", "res", "sched", "view", "wm");
64 
65     private static final String TAG = "Traceur";
66 
67     private static ContentObserver mDeveloperOptionsObserver;
68 
69     @Override
onReceive(Context context, Intent intent)70     public void onReceive(Context context, Intent intent) {
71         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
72 
73         if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
74             createNotificationChannel(context);
75             updateDeveloperOptionsWatcher(context,
76                 prefs.getBoolean(context.getString(R.string.pref_key_quick_setting), false));
77             updateTracing(context);
78         } else if (STOP_ACTION.equals(intent.getAction())) {
79             prefs.edit().putBoolean(context.getString(R.string.pref_key_tracing_on), false).apply();
80             updateTracing(context);
81         } else if (OPEN_ACTION.equals(intent.getAction())) {
82             context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
83             context.startActivity(new Intent(context, MainActivity.class)
84                     .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
85         }
86     }
87 
88     /*
89      * Updates the current tracing state based on the current state of preferences.
90      */
updateTracing(Context context)91     public static void updateTracing(Context context) {
92         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
93         boolean prefsTracingOn =
94                 prefs.getBoolean(context.getString(R.string.pref_key_tracing_on), false);
95 
96         if (prefsTracingOn != AtraceUtils.isTracingOn()) {
97             if (prefsTracingOn) {
98                 // Show notification if the tags in preferences are not all actually available.
99                 String activeAvailableTags = getActiveTags(context, prefs, true);
100                 String activeTags = getActiveTags(context, prefs, false);
101                 if (!TextUtils.equals(activeAvailableTags, activeTags)) {
102                     postCategoryNotification(context, prefs);
103                 }
104 
105                 int bufferSize = Integer.parseInt(
106                     prefs.getString(context.getString(R.string.pref_key_buffer_size),
107                         context.getString(R.string.default_buffer_size)));
108 
109                 boolean appTracing = prefs.getBoolean(context.getString(R.string.pref_key_apps), true);
110 
111                 AtraceService.startTracing(context, activeAvailableTags, bufferSize, appTracing);
112             } else {
113                 AtraceService.stopTracing(context);
114             }
115         }
116 
117         // Update the main UI and the QS tile.
118         context.sendBroadcast(new Intent(MainFragment.ACTION_REFRESH_TAGS));
119         QsService.updateTile();
120     }
121 
122     /*
123      * Updates the current Quick Settings tile state based on the current state
124      * of preferences.
125      */
updateQuickSettings(Context context)126     public static void updateQuickSettings(Context context) {
127         boolean quickSettingsEnabled =
128             PreferenceManager.getDefaultSharedPreferences(context)
129               .getBoolean(context.getString(R.string.pref_key_quick_setting), false);
130 
131         ComponentName name = new ComponentName(context, QsService.class);
132         context.getPackageManager().setComponentEnabledSetting(name,
133             quickSettingsEnabled
134                 ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
135                 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
136             PackageManager.DONT_KILL_APP);
137 
138         IStatusBarService statusBarService = IStatusBarService.Stub.asInterface(
139             ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
140 
141         try {
142             if (statusBarService != null) {
143                 if (quickSettingsEnabled) {
144                     statusBarService.addTile(name);
145                 } else {
146                     statusBarService.remTile(name);
147                 }
148             }
149         } catch (RemoteException e) {
150             Log.e(TAG, "Failed to modify QS tile for Traceur.", e);
151         }
152 
153         QsService.updateTile();
154 
155         updateDeveloperOptionsWatcher(context, quickSettingsEnabled);
156     }
157 
158     /*
159      * When Developer Options are turned off, reset the Show Quick Settings Tile
160      * preference to false to hide the tile. The user will need to re-enable the
161      * preference if they decide to turn Developer Options back on again.
162      */
updateDeveloperOptionsWatcher(Context context, boolean quickSettingsEnabled)163     private static void updateDeveloperOptionsWatcher(Context context,
164             boolean quickSettingsEnabled) {
165 
166         Uri settingUri = Settings.Global.getUriFor(
167             Settings.Global.DEVELOPMENT_SETTINGS_ENABLED);
168 
169         if (quickSettingsEnabled) {
170             mDeveloperOptionsObserver =
171                 new ContentObserver(new Handler()) {
172                     @Override
173                     public void onChange(boolean selfChange) {
174                         super.onChange(selfChange);
175 
176                         boolean developerOptionsEnabled = (1 ==
177                             Settings.Global.getInt(context.getContentResolver(),
178                                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED , 0));
179 
180                         if (!developerOptionsEnabled) {
181                             SharedPreferences prefs =
182                                 PreferenceManager.getDefaultSharedPreferences(context);
183                             prefs.edit().putBoolean(
184                                 context.getString(R.string.pref_key_quick_setting), false)
185                                 .apply();
186                             updateQuickSettings(context);
187                         }
188                     }
189                 };
190 
191             context.getContentResolver().registerContentObserver(settingUri,
192                 false, mDeveloperOptionsObserver);
193 
194         } else if (mDeveloperOptionsObserver != null) {
195             context.getContentResolver().unregisterContentObserver(
196                 mDeveloperOptionsObserver);
197             mDeveloperOptionsObserver = null;
198         }
199     }
200 
postCategoryNotification(Context context, SharedPreferences prefs)201     private static void postCategoryNotification(Context context, SharedPreferences prefs) {
202         Intent sendIntent = new Intent(context, MainActivity.class);
203 
204         String title = context.getString(R.string.tracing_categories_unavailable);
205         String msg = getActiveUnavailableTags(context, prefs);
206         final Notification.Builder builder =
207             new Notification.Builder(context, NOTIFICATION_CHANNEL)
208                 .setSmallIcon(R.drawable.stat_sys_adb)
209                 .setContentTitle(title)
210                 .setTicker(title)
211                 .setContentText(msg)
212                 .setContentIntent(PendingIntent.getActivity(
213                         context, 0, sendIntent, PendingIntent.FLAG_ONE_SHOT
214                                 | PendingIntent.FLAG_CANCEL_CURRENT))
215                 .setAutoCancel(true)
216                 .setLocalOnly(true)
217                 .setColor(context.getColor(
218                         com.android.internal.R.color.system_notification_accent_color));
219 
220         context.getSystemService(NotificationManager.class)
221             .notify(Receiver.class.getName(), 0, builder.build());
222     }
223 
createNotificationChannel(Context context)224     private static void createNotificationChannel(Context context) {
225         NotificationChannel channel = new NotificationChannel(
226             NOTIFICATION_CHANNEL, context.getString(R.string.system_tracing),
227             NotificationManager.IMPORTANCE_HIGH);
228         channel.setBypassDnd(true);
229         channel.enableVibration(true);
230         channel.setSound(null, null);
231 
232         NotificationManager notificationManager =
233             context.getSystemService(NotificationManager.class);
234         notificationManager.createNotificationChannel(channel);
235     }
236 
getActiveTags(Context context, SharedPreferences prefs, boolean onlyAvailable)237     public static String getActiveTags(Context context, SharedPreferences prefs, boolean onlyAvailable) {
238         Set<String> tags = prefs.getStringSet(context.getString(R.string.pref_key_tags),
239                 getDefaultTagList());
240         StringBuilder sb = new StringBuilder(10 * tags.size());
241         TreeMap<String, String> available =
242                 onlyAvailable ? AtraceUtils.atraceListCategories() : null;
243 
244         for (String s : tags) {
245             if (onlyAvailable && !available.containsKey(s)) continue;
246             if (sb.length() > 0) {
247                 sb.append(' ');
248             }
249             sb.append(s);
250         }
251         String s = sb.toString();
252         Log.v(TAG, "getActiveTags(onlyAvailable=" + onlyAvailable + ") = \"" + s + "\"");
253         return s;
254     }
255 
getActiveUnavailableTags(Context context, SharedPreferences prefs)256     public static String getActiveUnavailableTags(Context context, SharedPreferences prefs) {
257         Set<String> tags = prefs.getStringSet(context.getString(R.string.pref_key_tags),
258                 getDefaultTagList());
259         StringBuilder sb = new StringBuilder(10 * tags.size());
260         TreeMap<String, String> available = AtraceUtils.atraceListCategories();
261 
262         for (String s : tags) {
263             if (available.containsKey(s)) continue;
264             if (sb.length() > 0) {
265                 sb.append(' ');
266             }
267             sb.append(s);
268         }
269         String s = sb.toString();
270         Log.v(TAG, "getActiveUnavailableTags() = \"" + s + "\"");
271         return s;
272     }
273 
getDefaultTagList()274     public static Set<String> getDefaultTagList() {
275         return Build.TYPE.equals("user") ? ATRACE_TAGS_USER : ATRACE_TAGS;
276     }
277 }
278