1 /* 2 * Copyright (C) 2007 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.server; 18 19 import android.app.PendingIntent; 20 import android.app.StatusBarManager; 21 import android.content.BroadcastReceiver; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.pm.PackageManager; 27 import android.content.res.Resources; 28 import android.net.Uri; 29 import android.os.IBinder; 30 import android.os.RemoteException; 31 import android.os.Binder; 32 import android.os.Handler; 33 import android.os.SystemClock; 34 import android.util.Slog; 35 36 import com.android.internal.statusbar.IStatusBar; 37 import com.android.internal.statusbar.IStatusBarService; 38 import com.android.internal.statusbar.StatusBarIcon; 39 import com.android.internal.statusbar.StatusBarIconList; 40 import com.android.internal.statusbar.StatusBarNotification; 41 42 import java.io.FileDescriptor; 43 import java.io.PrintWriter; 44 import java.util.ArrayList; 45 import java.util.HashMap; 46 import java.util.List; 47 import java.util.Map; 48 49 50 /** 51 * A note on locking: We rely on the fact that calls onto mBar are oneway or 52 * if they are local, that they just enqueue messages to not deadlock. 53 */ 54 public class StatusBarManagerService extends IStatusBarService.Stub 55 { 56 static final String TAG = "StatusBarManagerService"; 57 static final boolean SPEW = false; 58 59 final Context mContext; 60 Handler mHandler = new Handler(); 61 NotificationCallbacks mNotificationCallbacks; 62 volatile IStatusBar mBar; 63 StatusBarIconList mIcons = new StatusBarIconList(); 64 HashMap<IBinder,StatusBarNotification> mNotifications 65 = new HashMap<IBinder,StatusBarNotification>(); 66 67 // for disabling the status bar 68 ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>(); 69 int mDisabled = 0; 70 71 private class DisableRecord implements IBinder.DeathRecipient { 72 String pkg; 73 int what; 74 IBinder token; 75 76 public void binderDied() { 77 Slog.i(TAG, "binder died for pkg=" + pkg); 78 disable(0, token, pkg); 79 token.unlinkToDeath(this, 0); 80 } 81 } 82 83 public interface NotificationCallbacks { 84 void onSetDisabled(int status); 85 void onClearAll(); 86 void onNotificationClick(String pkg, String tag, int id); 87 void onPanelRevealed(); 88 void onNotificationError(String pkg, String tag, int id, 89 int uid, int initialPid, String message); 90 } 91 92 /** 93 * Construct the service, add the status bar view to the window manager 94 */ 95 public StatusBarManagerService(Context context) { 96 mContext = context; 97 98 final Resources res = context.getResources(); 99 mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons)); 100 } 101 102 public void setNotificationCallbacks(NotificationCallbacks listener) { 103 mNotificationCallbacks = listener; 104 } 105 106 // ================================================================================ 107 // Constructing the view 108 // ================================================================================ 109 110 public void systemReady() { 111 } 112 113 public void systemReady2() { 114 ComponentName cn = ComponentName.unflattenFromString( 115 mContext.getString(com.android.internal.R.string.config_statusBarComponent)); 116 Intent intent = new Intent(); 117 intent.setComponent(cn); 118 Slog.i(TAG, "Starting service: " + cn); 119 mContext.startService(intent); 120 } 121 122 // ================================================================================ 123 // From IStatusBarService 124 // ================================================================================ 125 public void expand() { 126 enforceExpandStatusBar(); 127 128 if (mBar != null) { 129 try { 130 mBar.animateExpand(); 131 } catch (RemoteException ex) { 132 } 133 } 134 } 135 136 public void collapse() { 137 enforceExpandStatusBar(); 138 139 if (mBar != null) { 140 try { 141 mBar.animateCollapse(); 142 } catch (RemoteException ex) { 143 } 144 } 145 } 146 147 public void disable(int what, IBinder token, String pkg) { 148 enforceStatusBar(); 149 150 // It's important that the the callback and the call to mBar get done 151 // in the same order when multiple threads are calling this function 152 // so they are paired correctly. The messages on the handler will be 153 // handled in the order they were enqueued, but will be outside the lock. 154 synchronized (mDisableRecords) { 155 manageDisableListLocked(what, token, pkg); 156 final int net = gatherDisableActionsLocked(); 157 if (net != mDisabled) { 158 mDisabled = net; 159 mHandler.post(new Runnable() { 160 public void run() { 161 mNotificationCallbacks.onSetDisabled(net); 162 } 163 }); 164 if (mBar != null) { 165 try { 166 mBar.disable(net); 167 } catch (RemoteException ex) { 168 } 169 } 170 } 171 } 172 } 173 174 public void setIcon(String slot, String iconPackage, int iconId, int iconLevel) { 175 enforceStatusBar(); 176 177 synchronized (mIcons) { 178 int index = mIcons.getSlotIndex(slot); 179 if (index < 0) { 180 throw new SecurityException("invalid status bar icon slot: " + slot); 181 } 182 183 StatusBarIcon icon = new StatusBarIcon(iconPackage, iconId, iconLevel); 184 //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon); 185 mIcons.setIcon(index, icon); 186 187 if (mBar != null) { 188 try { 189 mBar.setIcon(index, icon); 190 } catch (RemoteException ex) { 191 } 192 } 193 } 194 } 195 196 public void setIconVisibility(String slot, boolean visible) { 197 enforceStatusBar(); 198 199 synchronized (mIcons) { 200 int index = mIcons.getSlotIndex(slot); 201 if (index < 0) { 202 throw new SecurityException("invalid status bar icon slot: " + slot); 203 } 204 205 StatusBarIcon icon = mIcons.getIcon(index); 206 if (icon == null) { 207 return; 208 } 209 210 if (icon.visible != visible) { 211 icon.visible = visible; 212 213 if (mBar != null) { 214 try { 215 mBar.setIcon(index, icon); 216 } catch (RemoteException ex) { 217 } 218 } 219 } 220 } 221 } 222 223 public void removeIcon(String slot) { 224 enforceStatusBar(); 225 226 synchronized (mIcons) { 227 int index = mIcons.getSlotIndex(slot); 228 if (index < 0) { 229 throw new SecurityException("invalid status bar icon slot: " + slot); 230 } 231 232 mIcons.removeIcon(index); 233 234 if (mBar != null) { 235 try { 236 mBar.removeIcon(index); 237 } catch (RemoteException ex) { 238 } 239 } 240 } 241 } 242 243 private void enforceStatusBar() { 244 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR, 245 "StatusBarManagerService"); 246 } 247 248 private void enforceExpandStatusBar() { 249 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR, 250 "StatusBarManagerService"); 251 } 252 253 private void enforceStatusBarService() { 254 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 255 "StatusBarManagerService"); 256 } 257 258 259 // ================================================================================ 260 // Callbacks from the status bar service. 261 // ================================================================================ 262 public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList, 263 List<IBinder> notificationKeys, List<StatusBarNotification> notifications) { 264 enforceStatusBarService(); 265 266 Slog.i(TAG, "registerStatusBar bar=" + bar); 267 mBar = bar; 268 synchronized (mIcons) { 269 iconList.copyFrom(mIcons); 270 } 271 synchronized (mNotifications) { 272 for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) { 273 notificationKeys.add(e.getKey()); 274 notifications.add(e.getValue()); 275 } 276 } 277 } 278 279 /** 280 * The status bar service should call this each time the user brings the panel from 281 * invisible to visible in order to clear the notification light. 282 */ 283 public void onPanelRevealed() { 284 enforceStatusBarService(); 285 286 // tell the notification manager to turn off the lights. 287 mNotificationCallbacks.onPanelRevealed(); 288 } 289 290 public void onNotificationClick(String pkg, String tag, int id) { 291 enforceStatusBarService(); 292 293 mNotificationCallbacks.onNotificationClick(pkg, tag, id); 294 } 295 296 public void onNotificationError(String pkg, String tag, int id, 297 int uid, int initialPid, String message) { 298 enforceStatusBarService(); 299 300 // WARNING: this will call back into us to do the remove. Don't hold any locks. 301 mNotificationCallbacks.onNotificationError(pkg, tag, id, uid, initialPid, message); 302 } 303 304 public void onClearAllNotifications() { 305 enforceStatusBarService(); 306 307 mNotificationCallbacks.onClearAll(); 308 } 309 310 // ================================================================================ 311 // Callbacks for NotificationManagerService. 312 // ================================================================================ 313 public IBinder addNotification(StatusBarNotification notification) { 314 synchronized (mNotifications) { 315 IBinder key = new Binder(); 316 mNotifications.put(key, notification); 317 if (mBar != null) { 318 try { 319 mBar.addNotification(key, notification); 320 } catch (RemoteException ex) { 321 } 322 } 323 return key; 324 } 325 } 326 327 public void updateNotification(IBinder key, StatusBarNotification notification) { 328 synchronized (mNotifications) { 329 if (!mNotifications.containsKey(key)) { 330 throw new IllegalArgumentException("updateNotification key not found: " + key); 331 } 332 mNotifications.put(key, notification); 333 if (mBar != null) { 334 try { 335 mBar.updateNotification(key, notification); 336 } catch (RemoteException ex) { 337 } 338 } 339 } 340 } 341 342 public void removeNotification(IBinder key) { 343 synchronized (mNotifications) { 344 final StatusBarNotification n = mNotifications.remove(key); 345 if (n == null) { 346 throw new IllegalArgumentException("removeNotification key not found: " + key); 347 } 348 if (mBar != null) { 349 try { 350 mBar.removeNotification(key); 351 } catch (RemoteException ex) { 352 } 353 } 354 } 355 } 356 357 // ================================================================================ 358 // Can be called from any thread 359 // ================================================================================ 360 361 // lock on mDisableRecords 362 void manageDisableListLocked(int what, IBinder token, String pkg) { 363 if (SPEW) { 364 Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what) + " pkg=" + pkg); 365 } 366 // update the list 367 synchronized (mDisableRecords) { 368 final int N = mDisableRecords.size(); 369 DisableRecord tok = null; 370 int i; 371 for (i=0; i<N; i++) { 372 DisableRecord t = mDisableRecords.get(i); 373 if (t.token == token) { 374 tok = t; 375 break; 376 } 377 } 378 if (what == 0 || !token.isBinderAlive()) { 379 if (tok != null) { 380 mDisableRecords.remove(i); 381 tok.token.unlinkToDeath(tok, 0); 382 } 383 } else { 384 if (tok == null) { 385 tok = new DisableRecord(); 386 try { 387 token.linkToDeath(tok, 0); 388 } 389 catch (RemoteException ex) { 390 return; // give up 391 } 392 mDisableRecords.add(tok); 393 } 394 tok.what = what; 395 tok.token = token; 396 tok.pkg = pkg; 397 } 398 } 399 } 400 401 // lock on mDisableRecords 402 int gatherDisableActionsLocked() { 403 final int N = mDisableRecords.size(); 404 // gather the new net flags 405 int net = 0; 406 for (int i=0; i<N; i++) { 407 net |= mDisableRecords.get(i).what; 408 } 409 return net; 410 } 411 412 // ================================================================================ 413 // Always called from UI thread 414 // ================================================================================ 415 416 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 417 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 418 != PackageManager.PERMISSION_GRANTED) { 419 pw.println("Permission Denial: can't dump StatusBar from from pid=" 420 + Binder.getCallingPid() 421 + ", uid=" + Binder.getCallingUid()); 422 return; 423 } 424 425 synchronized (mIcons) { 426 mIcons.dump(pw); 427 } 428 429 synchronized (mNotifications) { 430 int i=0; 431 pw.println("Notification list:"); 432 for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) { 433 pw.printf(" %2d: %s\n", i, e.getValue().toString()); 434 i++; 435 } 436 } 437 438 synchronized (mDisableRecords) { 439 final int N = mDisableRecords.size(); 440 pw.println(" mDisableRecords.size=" + N 441 + " mDisabled=0x" + Integer.toHexString(mDisabled)); 442 for (int i=0; i<N; i++) { 443 DisableRecord tok = mDisableRecords.get(i); 444 pw.println(" [" + i + "] what=0x" + Integer.toHexString(tok.what) 445 + " pkg=" + tok.pkg + " token=" + tok.token); 446 } 447 } 448 } 449 450 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 451 public void onReceive(Context context, Intent intent) { 452 String action = intent.getAction(); 453 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) 454 || Intent.ACTION_SCREEN_OFF.equals(action)) { 455 collapse(); 456 } 457 /* 458 else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) { 459 updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false), 460 intent.getStringExtra(Telephony.Intents.EXTRA_SPN), 461 intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false), 462 intent.getStringExtra(Telephony.Intents.EXTRA_PLMN)); 463 } 464 else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { 465 updateResources(); 466 } 467 */ 468 } 469 }; 470 471 } 472