StatusBarManagerService.java revision 4120375d46091df8527bb701882e056fbb0e6b06
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 17package com.android.server; 18 19import android.app.StatusBarManager; 20import android.content.BroadcastReceiver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.pm.PackageManager; 24import android.content.res.Resources; 25import android.os.Binder; 26import android.os.Handler; 27import android.os.IBinder; 28import android.os.RemoteException; 29import android.util.Slog; 30 31import com.android.internal.statusbar.IStatusBar; 32import com.android.internal.statusbar.IStatusBarService; 33import com.android.internal.statusbar.StatusBarIcon; 34import com.android.internal.statusbar.StatusBarIconList; 35import com.android.internal.statusbar.StatusBarNotification; 36import com.android.server.wm.WindowManagerService; 37 38import java.io.FileDescriptor; 39import java.io.PrintWriter; 40import java.util.ArrayList; 41import java.util.HashMap; 42import java.util.List; 43import java.util.Map; 44 45 46/** 47 * A note on locking: We rely on the fact that calls onto mBar are oneway or 48 * if they are local, that they just enqueue messages to not deadlock. 49 */ 50public class StatusBarManagerService extends IStatusBarService.Stub 51 implements WindowManagerService.OnHardKeyboardStatusChangeListener 52{ 53 static final String TAG = "StatusBarManagerService"; 54 static final boolean SPEW = false; 55 56 final Context mContext; 57 final WindowManagerService mWindowManager; 58 Handler mHandler = new Handler(); 59 NotificationCallbacks mNotificationCallbacks; 60 volatile IStatusBar mBar; 61 StatusBarIconList mIcons = new StatusBarIconList(); 62 HashMap<IBinder,StatusBarNotification> mNotifications 63 = new HashMap<IBinder,StatusBarNotification>(); 64 65 // for disabling the status bar 66 ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>(); 67 IBinder mSysUiVisToken = new Binder(); 68 int mDisabled = 0; 69 70 Object mLock = new Object(); 71 // encompasses lights-out mode and other flags defined on View 72 int mSystemUiVisibility = 0; 73 boolean mMenuVisible = false; 74 int mImeWindowVis = 0; 75 int mImeBackDisposition; 76 IBinder mImeToken = null; 77 78 private class DisableRecord implements IBinder.DeathRecipient { 79 String pkg; 80 int what; 81 IBinder token; 82 83 public void binderDied() { 84 Slog.i(TAG, "binder died for pkg=" + pkg); 85 disable(0, token, pkg); 86 token.unlinkToDeath(this, 0); 87 } 88 } 89 90 public interface NotificationCallbacks { 91 void onSetDisabled(int status); 92 void onClearAll(); 93 void onNotificationClick(String pkg, String tag, int id); 94 void onNotificationClear(String pkg, String tag, int id); 95 void onPanelRevealed(); 96 void onNotificationError(String pkg, String tag, int id, 97 int uid, int initialPid, String message); 98 } 99 100 /** 101 * Construct the service, add the status bar view to the window manager 102 */ 103 public StatusBarManagerService(Context context, WindowManagerService windowManager) { 104 mContext = context; 105 mWindowManager = windowManager; 106 mWindowManager.setOnHardKeyboardStatusChangeListener(this); 107 108 final Resources res = context.getResources(); 109 mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons)); 110 } 111 112 public void setNotificationCallbacks(NotificationCallbacks listener) { 113 mNotificationCallbacks = listener; 114 } 115 116 // ================================================================================ 117 // From IStatusBarService 118 // ================================================================================ 119 public void expand() { 120 enforceExpandStatusBar(); 121 122 if (mBar != null) { 123 try { 124 mBar.animateExpand(); 125 } catch (RemoteException ex) { 126 } 127 } 128 } 129 130 public void collapse() { 131 enforceExpandStatusBar(); 132 133 if (mBar != null) { 134 try { 135 mBar.animateCollapse(); 136 } catch (RemoteException ex) { 137 } 138 } 139 } 140 141 public void disable(int what, IBinder token, String pkg) { 142 enforceStatusBar(); 143 144 synchronized (mLock) { 145 disableLocked(what, token, pkg); 146 } 147 } 148 149 private void disableLocked(int what, IBinder token, String pkg) { 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 manageDisableListLocked(what, token, pkg); 155 final int net = gatherDisableActionsLocked(); 156 if (net != mDisabled) { 157 mDisabled = net; 158 mHandler.post(new Runnable() { 159 public void run() { 160 mNotificationCallbacks.onSetDisabled(net); 161 } 162 }); 163 if (mBar != null) { 164 try { 165 mBar.disable(net); 166 } catch (RemoteException ex) { 167 } 168 } 169 } 170 } 171 172 public void setIcon(String slot, String iconPackage, int iconId, int iconLevel, 173 String contentDescription) { 174 enforceStatusBar(); 175 176 synchronized (mIcons) { 177 int index = mIcons.getSlotIndex(slot); 178 if (index < 0) { 179 throw new SecurityException("invalid status bar icon slot: " + slot); 180 } 181 182 StatusBarIcon icon = new StatusBarIcon(iconPackage, iconId, iconLevel, 0, 183 contentDescription); 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 /** 244 * Hide or show the on-screen Menu key. Only call this from the window manager, typically in 245 * response to a window with FLAG_NEEDS_MENU_KEY set. 246 */ 247 public void topAppWindowChanged(final boolean menuVisible) { 248 enforceStatusBar(); 249 250 if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key"); 251 252 synchronized(mLock) { 253 mMenuVisible = menuVisible; 254 mHandler.post(new Runnable() { 255 public void run() { 256 if (mBar != null) { 257 try { 258 mBar.topAppWindowChanged(menuVisible); 259 } catch (RemoteException ex) { 260 } 261 } 262 } 263 }); 264 } 265 } 266 267 public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition) { 268 enforceStatusBar(); 269 270 if (SPEW) { 271 Slog.d(TAG, "swetImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition); 272 } 273 274 synchronized(mLock) { 275 // In case of IME change, we need to call up setImeWindowStatus() regardless of 276 // mImeWindowVis because mImeWindowVis may not have been set to false when the 277 // previous IME was destroyed. 278 mImeWindowVis = vis; 279 mImeBackDisposition = backDisposition; 280 mImeToken = token; 281 mHandler.post(new Runnable() { 282 public void run() { 283 if (mBar != null) { 284 try { 285 mBar.setImeWindowStatus(token, vis, backDisposition); 286 } catch (RemoteException ex) { 287 } 288 } 289 } 290 }); 291 } 292 } 293 294 public void setSystemUiVisibility(int vis, int mask) { 295 // also allows calls from window manager which is in this process. 296 enforceStatusBarService(); 297 298 if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")"); 299 300 synchronized (mLock) { 301 updateUiVisibilityLocked(vis, mask); 302 disableLocked(vis & StatusBarManager.DISABLE_MASK, mSysUiVisToken, 303 "WindowManager.LayoutParams"); 304 } 305 } 306 307 private void updateUiVisibilityLocked(final int vis, final int mask) { 308 if (mSystemUiVisibility != vis) { 309 mSystemUiVisibility = vis; 310 mHandler.post(new Runnable() { 311 public void run() { 312 if (mBar != null) { 313 try { 314 mBar.setSystemUiVisibility(vis, mask); 315 } catch (RemoteException ex) { 316 } 317 } 318 } 319 }); 320 } 321 } 322 323 public void setHardKeyboardEnabled(final boolean enabled) { 324 mHandler.post(new Runnable() { 325 public void run() { 326 mWindowManager.setHardKeyboardEnabled(enabled); 327 } 328 }); 329 } 330 331 @Override 332 public void onHardKeyboardStatusChange(final boolean available, final boolean enabled) { 333 mHandler.post(new Runnable() { 334 public void run() { 335 if (mBar != null) { 336 try { 337 mBar.setHardKeyboardStatus(available, enabled); 338 } catch (RemoteException ex) { 339 } 340 } 341 } 342 }); 343 } 344 345 @Override 346 public void toggleRecentApps() { 347 if (mBar != null) { 348 try { 349 mBar.toggleRecentApps(); 350 } catch (RemoteException ex) {} 351 } 352 } 353 354 @Override 355 public void preloadRecentApps() { 356 if (mBar != null) { 357 try { 358 mBar.preloadRecentApps(); 359 } catch (RemoteException ex) {} 360 } 361 } 362 363 @Override 364 public void cancelPreloadRecentApps() { 365 if (mBar != null) { 366 try { 367 mBar.cancelPreloadRecentApps(); 368 } catch (RemoteException ex) {} 369 } 370 } 371 372 private void enforceStatusBar() { 373 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR, 374 "StatusBarManagerService"); 375 } 376 377 private void enforceExpandStatusBar() { 378 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR, 379 "StatusBarManagerService"); 380 } 381 382 private void enforceStatusBarService() { 383 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 384 "StatusBarManagerService"); 385 } 386 387 // ================================================================================ 388 // Callbacks from the status bar service. 389 // ================================================================================ 390 public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList, 391 List<IBinder> notificationKeys, List<StatusBarNotification> notifications, 392 int switches[], List<IBinder> binders) { 393 enforceStatusBarService(); 394 395 Slog.i(TAG, "registerStatusBar bar=" + bar); 396 mBar = bar; 397 synchronized (mIcons) { 398 iconList.copyFrom(mIcons); 399 } 400 synchronized (mNotifications) { 401 for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) { 402 notificationKeys.add(e.getKey()); 403 notifications.add(e.getValue()); 404 } 405 } 406 synchronized (mLock) { 407 switches[0] = gatherDisableActionsLocked(); 408 switches[1] = mSystemUiVisibility; 409 switches[2] = mMenuVisible ? 1 : 0; 410 switches[3] = mImeWindowVis; 411 switches[4] = mImeBackDisposition; 412 binders.add(mImeToken); 413 } 414 switches[5] = mWindowManager.isHardKeyboardAvailable() ? 1 : 0; 415 switches[6] = mWindowManager.isHardKeyboardEnabled() ? 1 : 0; 416 } 417 418 /** 419 * The status bar service should call this each time the user brings the panel from 420 * invisible to visible in order to clear the notification light. 421 */ 422 public void onPanelRevealed() { 423 enforceStatusBarService(); 424 425 // tell the notification manager to turn off the lights. 426 mNotificationCallbacks.onPanelRevealed(); 427 } 428 429 public void onNotificationClick(String pkg, String tag, int id) { 430 enforceStatusBarService(); 431 432 mNotificationCallbacks.onNotificationClick(pkg, tag, id); 433 } 434 435 public void onNotificationError(String pkg, String tag, int id, 436 int uid, int initialPid, String message) { 437 enforceStatusBarService(); 438 439 // WARNING: this will call back into us to do the remove. Don't hold any locks. 440 mNotificationCallbacks.onNotificationError(pkg, tag, id, uid, initialPid, message); 441 } 442 443 public void onNotificationClear(String pkg, String tag, int id) { 444 enforceStatusBarService(); 445 446 mNotificationCallbacks.onNotificationClear(pkg, tag, id); 447 } 448 449 public void onClearAllNotifications() { 450 enforceStatusBarService(); 451 452 mNotificationCallbacks.onClearAll(); 453 } 454 455 // ================================================================================ 456 // Callbacks for NotificationManagerService. 457 // ================================================================================ 458 public IBinder addNotification(StatusBarNotification notification) { 459 synchronized (mNotifications) { 460 IBinder key = new Binder(); 461 mNotifications.put(key, notification); 462 if (mBar != null) { 463 try { 464 mBar.addNotification(key, notification); 465 } catch (RemoteException ex) { 466 } 467 } 468 return key; 469 } 470 } 471 472 public void updateNotification(IBinder key, StatusBarNotification notification) { 473 synchronized (mNotifications) { 474 if (!mNotifications.containsKey(key)) { 475 throw new IllegalArgumentException("updateNotification key not found: " + key); 476 } 477 mNotifications.put(key, notification); 478 if (mBar != null) { 479 try { 480 mBar.updateNotification(key, notification); 481 } catch (RemoteException ex) { 482 } 483 } 484 } 485 } 486 487 public void removeNotification(IBinder key) { 488 synchronized (mNotifications) { 489 final StatusBarNotification n = mNotifications.remove(key); 490 if (n == null) { 491 Slog.e(TAG, "removeNotification key not found: " + key); 492 return; 493 } 494 if (mBar != null) { 495 try { 496 mBar.removeNotification(key); 497 } catch (RemoteException ex) { 498 } 499 } 500 } 501 } 502 503 // ================================================================================ 504 // Can be called from any thread 505 // ================================================================================ 506 507 // lock on mDisableRecords 508 void manageDisableListLocked(int what, IBinder token, String pkg) { 509 if (SPEW) { 510 Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what) + " pkg=" + pkg); 511 } 512 // update the list 513 final int N = mDisableRecords.size(); 514 DisableRecord tok = null; 515 int i; 516 for (i=0; i<N; i++) { 517 DisableRecord t = mDisableRecords.get(i); 518 if (t.token == token) { 519 tok = t; 520 break; 521 } 522 } 523 if (what == 0 || !token.isBinderAlive()) { 524 if (tok != null) { 525 mDisableRecords.remove(i); 526 tok.token.unlinkToDeath(tok, 0); 527 } 528 } else { 529 if (tok == null) { 530 tok = new DisableRecord(); 531 try { 532 token.linkToDeath(tok, 0); 533 } 534 catch (RemoteException ex) { 535 return; // give up 536 } 537 mDisableRecords.add(tok); 538 } 539 tok.what = what; 540 tok.token = token; 541 tok.pkg = pkg; 542 } 543 } 544 545 // lock on mDisableRecords 546 int gatherDisableActionsLocked() { 547 final int N = mDisableRecords.size(); 548 // gather the new net flags 549 int net = 0; 550 for (int i=0; i<N; i++) { 551 net |= mDisableRecords.get(i).what; 552 } 553 return net; 554 } 555 556 // ================================================================================ 557 // Always called from UI thread 558 // ================================================================================ 559 560 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 561 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 562 != PackageManager.PERMISSION_GRANTED) { 563 pw.println("Permission Denial: can't dump StatusBar from from pid=" 564 + Binder.getCallingPid() 565 + ", uid=" + Binder.getCallingUid()); 566 return; 567 } 568 569 synchronized (mIcons) { 570 mIcons.dump(pw); 571 } 572 573 synchronized (mNotifications) { 574 int i=0; 575 pw.println("Notification list:"); 576 for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) { 577 pw.printf(" %2d: %s\n", i, e.getValue().toString()); 578 i++; 579 } 580 } 581 582 synchronized (mLock) { 583 final int N = mDisableRecords.size(); 584 pw.println(" mDisableRecords.size=" + N 585 + " mDisabled=0x" + Integer.toHexString(mDisabled)); 586 for (int i=0; i<N; i++) { 587 DisableRecord tok = mDisableRecords.get(i); 588 pw.println(" [" + i + "] what=0x" + Integer.toHexString(tok.what) 589 + " pkg=" + tok.pkg + " token=" + tok.token); 590 } 591 } 592 } 593 594 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 595 public void onReceive(Context context, Intent intent) { 596 String action = intent.getAction(); 597 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) 598 || Intent.ACTION_SCREEN_OFF.equals(action)) { 599 collapse(); 600 } 601 /* 602 else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) { 603 updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false), 604 intent.getStringExtra(Telephony.Intents.EXTRA_SPN), 605 intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false), 606 intent.getStringExtra(Telephony.Intents.EXTRA_PLMN)); 607 } 608 else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { 609 updateResources(); 610 } 611 */ 612 } 613 }; 614 615} 616