StatusBarManagerService.java revision 71f18fd1b64071e486bafff237b1f87a56d4aead
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.statusbar; 18 19import android.app.StatusBarManager; 20import android.os.Binder; 21import android.os.Handler; 22import android.os.IBinder; 23import android.os.RemoteException; 24import android.os.UserHandle; 25import android.service.notification.StatusBarNotification; 26import android.content.Context; 27import android.content.pm.PackageManager; 28import android.content.res.Resources; 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.server.LocalServices; 36import com.android.server.notification.NotificationDelegate; 37import com.android.server.wm.WindowManagerService; 38 39import java.io.FileDescriptor; 40import java.io.PrintWriter; 41import java.util.ArrayList; 42import java.util.HashMap; 43import java.util.List; 44import java.util.Map; 45 46 47/** 48 * A note on locking: We rely on the fact that calls onto mBar are oneway or 49 * if they are local, that they just enqueue messages to not deadlock. 50 */ 51public class StatusBarManagerService extends IStatusBarService.Stub 52 implements WindowManagerService.OnHardKeyboardStatusChangeListener 53{ 54 private static final String TAG = "StatusBarManagerService"; 55 private static final boolean SPEW = false; 56 57 private final Context mContext; 58 private final WindowManagerService mWindowManager; 59 private Handler mHandler = new Handler(); 60 private NotificationDelegate mNotificationDelegate; 61 private volatile IStatusBar mBar; 62 private StatusBarIconList mIcons = new StatusBarIconList(); 63 private HashMap<String,StatusBarNotification> mNotifications 64 = new HashMap<String,StatusBarNotification>(); 65 66 // for disabling the status bar 67 private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>(); 68 private IBinder mSysUiVisToken = new Binder(); 69 private int mDisabled = 0; 70 71 private Object mLock = new Object(); 72 // encompasses lights-out mode and other flags defined on View 73 private int mSystemUiVisibility = 0; 74 private boolean mMenuVisible = false; 75 private int mImeWindowVis = 0; 76 private int mImeBackDisposition; 77 private boolean mShowImeSwitcher; 78 private IBinder mImeToken = null; 79 private int mCurrentUserId; 80 81 private class DisableRecord implements IBinder.DeathRecipient { 82 int userId; 83 String pkg; 84 int what; 85 IBinder token; 86 87 public void binderDied() { 88 Slog.i(TAG, "binder died for pkg=" + pkg); 89 disableInternal(userId, 0, token, pkg); 90 token.unlinkToDeath(this, 0); 91 } 92 } 93 94 /** 95 * Construct the service, add the status bar view to the window manager 96 */ 97 public StatusBarManagerService(Context context, WindowManagerService windowManager) { 98 mContext = context; 99 mWindowManager = windowManager; 100 mWindowManager.setOnHardKeyboardStatusChangeListener(this); 101 102 final Resources res = context.getResources(); 103 mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons)); 104 105 LocalServices.addService(StatusBarManagerInternal.class, mInternalService); 106 } 107 108 /** 109 * Private API used by NotificationManagerService. 110 */ 111 private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() { 112 @Override 113 public void setNotificationDelegate(NotificationDelegate delegate) { 114 synchronized (mNotifications) { 115 mNotificationDelegate = delegate; 116 } 117 } 118 119 @Override 120 public void addNotification(StatusBarNotification notification) { 121 synchronized (mNotifications) { 122 mNotifications.put(notification.getKey(), notification); 123 if (mBar != null) { 124 try { 125 mBar.addNotification(notification); 126 } catch (RemoteException ex) { 127 } 128 } 129 } 130 } 131 132 @Override 133 public void updateNotification(StatusBarNotification notification) { 134 synchronized (mNotifications) { 135 String key = notification.getKey(); 136 if (!mNotifications.containsKey(key)) { 137 throw new IllegalArgumentException("updateNotification key not found: " + key); 138 } 139 mNotifications.put(notification.getKey(), notification); 140 if (mBar != null) { 141 try { 142 mBar.updateNotification(notification); 143 } catch (RemoteException ex) { 144 } 145 } 146 } 147 } 148 149 @Override 150 public void removeNotification(String key) { 151 synchronized (mNotifications) { 152 final StatusBarNotification n = mNotifications.remove(key); 153 if (n == null) { 154 Slog.e(TAG, "removeNotification key not found: " + key); 155 return; 156 } 157 if (mBar != null) { 158 try { 159 mBar.removeNotification(key); 160 } catch (RemoteException ex) { 161 } 162 } 163 } 164 } 165 }; 166 167 // ================================================================================ 168 // From IStatusBarService 169 // ================================================================================ 170 @Override 171 public void expandNotificationsPanel() { 172 enforceExpandStatusBar(); 173 174 if (mBar != null) { 175 try { 176 mBar.animateExpandNotificationsPanel(); 177 } catch (RemoteException ex) { 178 } 179 } 180 } 181 182 @Override 183 public void collapsePanels() { 184 enforceExpandStatusBar(); 185 186 if (mBar != null) { 187 try { 188 mBar.animateCollapsePanels(); 189 } catch (RemoteException ex) { 190 } 191 } 192 } 193 194 @Override 195 public void expandSettingsPanel() { 196 enforceExpandStatusBar(); 197 198 if (mBar != null) { 199 try { 200 mBar.animateExpandSettingsPanel(); 201 } catch (RemoteException ex) { 202 } 203 } 204 } 205 206 @Override 207 public void disable(int what, IBinder token, String pkg) { 208 if (!mNotificationDelegate.allowDisable(what, token, pkg)) { 209 if (SPEW) Slog.d(TAG, "Blocking disable request from " + pkg); 210 return; 211 } 212 disableInternal(mCurrentUserId, what, token, pkg); 213 } 214 215 private void disableInternal(int userId, int what, IBinder token, String pkg) { 216 enforceStatusBar(); 217 218 synchronized (mLock) { 219 disableLocked(userId, what, token, pkg); 220 } 221 } 222 223 private void disableLocked(int userId, int what, IBinder token, String pkg) { 224 // It's important that the the callback and the call to mBar get done 225 // in the same order when multiple threads are calling this function 226 // so they are paired correctly. The messages on the handler will be 227 // handled in the order they were enqueued, but will be outside the lock. 228 manageDisableListLocked(userId, what, token, pkg); 229 230 // Ensure state for the current user is applied, even if passed a non-current user. 231 final int net = gatherDisableActionsLocked(mCurrentUserId); 232 if (net != mDisabled) { 233 mDisabled = net; 234 mHandler.post(new Runnable() { 235 public void run() { 236 mNotificationDelegate.onSetDisabled(net); 237 } 238 }); 239 if (mBar != null) { 240 try { 241 mBar.disable(net); 242 } catch (RemoteException ex) { 243 } 244 } 245 } 246 } 247 248 @Override 249 public void setIcon(String slot, String iconPackage, int iconId, int iconLevel, 250 String contentDescription) { 251 enforceStatusBar(); 252 253 synchronized (mIcons) { 254 int index = mIcons.getSlotIndex(slot); 255 if (index < 0) { 256 throw new SecurityException("invalid status bar icon slot: " + slot); 257 } 258 259 StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.OWNER, iconId, 260 iconLevel, 0, 261 contentDescription); 262 //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon); 263 mIcons.setIcon(index, icon); 264 265 if (mBar != null) { 266 try { 267 mBar.setIcon(index, icon); 268 } catch (RemoteException ex) { 269 } 270 } 271 } 272 } 273 274 @Override 275 public void setIconVisibility(String slot, boolean visible) { 276 enforceStatusBar(); 277 278 synchronized (mIcons) { 279 int index = mIcons.getSlotIndex(slot); 280 if (index < 0) { 281 throw new SecurityException("invalid status bar icon slot: " + slot); 282 } 283 284 StatusBarIcon icon = mIcons.getIcon(index); 285 if (icon == null) { 286 return; 287 } 288 289 if (icon.visible != visible) { 290 icon.visible = visible; 291 292 if (mBar != null) { 293 try { 294 mBar.setIcon(index, icon); 295 } catch (RemoteException ex) { 296 } 297 } 298 } 299 } 300 } 301 302 @Override 303 public void removeIcon(String slot) { 304 enforceStatusBar(); 305 306 synchronized (mIcons) { 307 int index = mIcons.getSlotIndex(slot); 308 if (index < 0) { 309 throw new SecurityException("invalid status bar icon slot: " + slot); 310 } 311 312 mIcons.removeIcon(index); 313 314 if (mBar != null) { 315 try { 316 mBar.removeIcon(index); 317 } catch (RemoteException ex) { 318 } 319 } 320 } 321 } 322 323 /** 324 * Hide or show the on-screen Menu key. Only call this from the window manager, typically in 325 * response to a window with FLAG_NEEDS_MENU_KEY set. 326 */ 327 @Override 328 public void topAppWindowChanged(final boolean menuVisible) { 329 enforceStatusBar(); 330 331 if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key"); 332 333 synchronized(mLock) { 334 mMenuVisible = menuVisible; 335 mHandler.post(new Runnable() { 336 public void run() { 337 if (mBar != null) { 338 try { 339 mBar.topAppWindowChanged(menuVisible); 340 } catch (RemoteException ex) { 341 } 342 } 343 } 344 }); 345 } 346 } 347 348 @Override 349 public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition, 350 final boolean showImeSwitcher) { 351 enforceStatusBar(); 352 353 if (SPEW) { 354 Slog.d(TAG, "swetImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition); 355 } 356 357 synchronized(mLock) { 358 // In case of IME change, we need to call up setImeWindowStatus() regardless of 359 // mImeWindowVis because mImeWindowVis may not have been set to false when the 360 // previous IME was destroyed. 361 mImeWindowVis = vis; 362 mImeBackDisposition = backDisposition; 363 mImeToken = token; 364 mShowImeSwitcher = showImeSwitcher; 365 mHandler.post(new Runnable() { 366 public void run() { 367 if (mBar != null) { 368 try { 369 mBar.setImeWindowStatus(token, vis, backDisposition, showImeSwitcher); 370 } catch (RemoteException ex) { 371 } 372 } 373 } 374 }); 375 } 376 } 377 378 @Override 379 public void setSystemUiVisibility(int vis, int mask) { 380 // also allows calls from window manager which is in this process. 381 enforceStatusBarService(); 382 383 if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")"); 384 385 synchronized (mLock) { 386 updateUiVisibilityLocked(vis, mask); 387 disableLocked( 388 mCurrentUserId, 389 vis & StatusBarManager.DISABLE_MASK, 390 mSysUiVisToken, 391 "WindowManager.LayoutParams"); 392 } 393 } 394 395 private void updateUiVisibilityLocked(final int vis, final int mask) { 396 if (mSystemUiVisibility != vis) { 397 mSystemUiVisibility = vis; 398 mHandler.post(new Runnable() { 399 public void run() { 400 if (mBar != null) { 401 try { 402 mBar.setSystemUiVisibility(vis, mask); 403 } catch (RemoteException ex) { 404 } 405 } 406 } 407 }); 408 } 409 } 410 411 @Override 412 public void setHardKeyboardEnabled(final boolean enabled) { 413 mHandler.post(new Runnable() { 414 public void run() { 415 mWindowManager.setHardKeyboardEnabled(enabled); 416 } 417 }); 418 } 419 420 @Override 421 public void onHardKeyboardStatusChange(final boolean available, final boolean enabled) { 422 mHandler.post(new Runnable() { 423 public void run() { 424 if (mBar != null) { 425 try { 426 mBar.setHardKeyboardStatus(available, enabled); 427 } catch (RemoteException ex) { 428 } 429 } 430 } 431 }); 432 } 433 434 @Override 435 public void toggleRecentApps() { 436 if (mBar != null) { 437 try { 438 mBar.toggleRecentApps(); 439 } catch (RemoteException ex) {} 440 } 441 } 442 443 @Override 444 public void preloadRecentApps() { 445 if (mBar != null) { 446 try { 447 mBar.preloadRecentApps(); 448 } catch (RemoteException ex) {} 449 } 450 } 451 452 @Override 453 public void cancelPreloadRecentApps() { 454 if (mBar != null) { 455 try { 456 mBar.cancelPreloadRecentApps(); 457 } catch (RemoteException ex) {} 458 } 459 } 460 461 @Override 462 public void showRecentApps(boolean triggeredFromAltTab) { 463 if (mBar != null) { 464 try { 465 mBar.showRecentApps(triggeredFromAltTab); 466 } catch (RemoteException ex) {} 467 } 468 } 469 470 @Override 471 public void hideRecentApps(boolean triggeredFromAltTab) { 472 if (mBar != null) { 473 try { 474 mBar.hideRecentApps(triggeredFromAltTab); 475 } catch (RemoteException ex) {} 476 } 477 } 478 479 @Override 480 public void setCurrentUser(int newUserId) { 481 if (SPEW) Slog.d(TAG, "Setting current user to user " + newUserId); 482 mCurrentUserId = newUserId; 483 } 484 485 @Override 486 public void setWindowState(int window, int state) { 487 if (mBar != null) { 488 try { 489 mBar.setWindowState(window, state); 490 } catch (RemoteException ex) {} 491 } 492 } 493 494 private void enforceStatusBar() { 495 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR, 496 "StatusBarManagerService"); 497 } 498 499 private void enforceExpandStatusBar() { 500 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR, 501 "StatusBarManagerService"); 502 } 503 504 private void enforceStatusBarService() { 505 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 506 "StatusBarManagerService"); 507 } 508 509 // ================================================================================ 510 // Callbacks from the status bar service. 511 // ================================================================================ 512 @Override 513 public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList, 514 List<StatusBarNotification> notifications, int switches[], List<IBinder> binders) { 515 enforceStatusBarService(); 516 517 Slog.i(TAG, "registerStatusBar bar=" + bar); 518 mBar = bar; 519 synchronized (mIcons) { 520 iconList.copyFrom(mIcons); 521 } 522 synchronized (mNotifications) { 523 for (StatusBarNotification sbn : mNotifications.values()) { 524 notifications.add(sbn); 525 } 526 } 527 synchronized (mLock) { 528 switches[0] = gatherDisableActionsLocked(mCurrentUserId); 529 switches[1] = mSystemUiVisibility; 530 switches[2] = mMenuVisible ? 1 : 0; 531 switches[3] = mImeWindowVis; 532 switches[4] = mImeBackDisposition; 533 switches[7] = mShowImeSwitcher ? 1 : 0; 534 binders.add(mImeToken); 535 } 536 switches[5] = mWindowManager.isHardKeyboardAvailable() ? 1 : 0; 537 switches[6] = mWindowManager.isHardKeyboardEnabled() ? 1 : 0; 538 } 539 540 /** 541 * The status bar service should call this each time the user brings the panel from 542 * invisible to visible in order to clear the notification light. 543 */ 544 @Override 545 public void onPanelRevealed() { 546 enforceStatusBarService(); 547 long identity = Binder.clearCallingIdentity(); 548 try { 549 // tell the notification manager to turn off the lights. 550 mNotificationDelegate.onPanelRevealed(); 551 } finally { 552 Binder.restoreCallingIdentity(identity); 553 } 554 } 555 556 @Override 557 public void onPanelHidden() throws RemoteException { 558 enforceStatusBarService(); 559 long identity = Binder.clearCallingIdentity(); 560 try { 561 mNotificationDelegate.onPanelHidden(); 562 } finally { 563 Binder.restoreCallingIdentity(identity); 564 } 565 } 566 567 @Override 568 public void onNotificationClick(String key) { 569 enforceStatusBarService(); 570 final int callingUid = Binder.getCallingUid(); 571 final int callingPid = Binder.getCallingPid(); 572 long identity = Binder.clearCallingIdentity(); 573 try { 574 mNotificationDelegate.onNotificationClick(callingUid, callingPid, key); 575 } finally { 576 Binder.restoreCallingIdentity(identity); 577 } 578 } 579 580 @Override 581 public void onNotificationError(String pkg, String tag, int id, 582 int uid, int initialPid, String message, int userId) { 583 enforceStatusBarService(); 584 final int callingUid = Binder.getCallingUid(); 585 final int callingPid = Binder.getCallingPid(); 586 long identity = Binder.clearCallingIdentity(); 587 try { 588 // WARNING: this will call back into us to do the remove. Don't hold any locks. 589 mNotificationDelegate.onNotificationError(callingUid, callingPid, 590 pkg, tag, id, uid, initialPid, message, userId); 591 } finally { 592 Binder.restoreCallingIdentity(identity); 593 } 594 } 595 596 @Override 597 public void onNotificationClear(String pkg, String tag, int id, int userId) { 598 enforceStatusBarService(); 599 final int callingUid = Binder.getCallingUid(); 600 final int callingPid = Binder.getCallingPid(); 601 long identity = Binder.clearCallingIdentity(); 602 try { 603 mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId); 604 } finally { 605 Binder.restoreCallingIdentity(identity); 606 } 607 } 608 609 @Override 610 public void onNotificationVisibilityChanged( 611 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) throws RemoteException { 612 enforceStatusBarService(); 613 long identity = Binder.clearCallingIdentity(); 614 try { 615 mNotificationDelegate.onNotificationVisibilityChanged( 616 newlyVisibleKeys, noLongerVisibleKeys); 617 } finally { 618 Binder.restoreCallingIdentity(identity); 619 } 620 } 621 622 @Override 623 public void onClearAllNotifications(int userId) { 624 enforceStatusBarService(); 625 final int callingUid = Binder.getCallingUid(); 626 final int callingPid = Binder.getCallingPid(); 627 long identity = Binder.clearCallingIdentity(); 628 try { 629 mNotificationDelegate.onClearAll(callingUid, callingPid, userId); 630 } finally { 631 Binder.restoreCallingIdentity(identity); 632 } 633 } 634 635 636 // ================================================================================ 637 // Can be called from any thread 638 // ================================================================================ 639 640 // lock on mDisableRecords 641 void manageDisableListLocked(int userId, int what, IBinder token, String pkg) { 642 if (SPEW) { 643 Slog.d(TAG, "manageDisableList userId=" + userId 644 + " what=0x" + Integer.toHexString(what) + " pkg=" + pkg); 645 } 646 // update the list 647 final int N = mDisableRecords.size(); 648 DisableRecord tok = null; 649 int i; 650 for (i=0; i<N; i++) { 651 DisableRecord t = mDisableRecords.get(i); 652 if (t.token == token && t.userId == userId) { 653 tok = t; 654 break; 655 } 656 } 657 if (what == 0 || !token.isBinderAlive()) { 658 if (tok != null) { 659 mDisableRecords.remove(i); 660 tok.token.unlinkToDeath(tok, 0); 661 } 662 } else { 663 if (tok == null) { 664 tok = new DisableRecord(); 665 tok.userId = userId; 666 try { 667 token.linkToDeath(tok, 0); 668 } 669 catch (RemoteException ex) { 670 return; // give up 671 } 672 mDisableRecords.add(tok); 673 } 674 tok.what = what; 675 tok.token = token; 676 tok.pkg = pkg; 677 } 678 } 679 680 // lock on mDisableRecords 681 int gatherDisableActionsLocked(int userId) { 682 final int N = mDisableRecords.size(); 683 // gather the new net flags 684 int net = 0; 685 for (int i=0; i<N; i++) { 686 final DisableRecord rec = mDisableRecords.get(i); 687 if (rec.userId == userId) { 688 net |= rec.what; 689 } 690 } 691 return net; 692 } 693 694 // ================================================================================ 695 // Always called from UI thread 696 // ================================================================================ 697 698 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 699 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 700 != PackageManager.PERMISSION_GRANTED) { 701 pw.println("Permission Denial: can't dump StatusBar from from pid=" 702 + Binder.getCallingPid() 703 + ", uid=" + Binder.getCallingUid()); 704 return; 705 } 706 707 synchronized (mIcons) { 708 mIcons.dump(pw); 709 } 710 711 synchronized (mNotifications) { 712 int i=0; 713 pw.println("Notification list:"); 714 for (Map.Entry<String,StatusBarNotification> e: mNotifications.entrySet()) { 715 pw.printf(" %2d: %s\n", i, e.getValue().toString()); 716 i++; 717 } 718 } 719 720 synchronized (mLock) { 721 pw.println(" mDisabled=0x" + Integer.toHexString(mDisabled)); 722 final int N = mDisableRecords.size(); 723 pw.println(" mDisableRecords.size=" + N); 724 for (int i=0; i<N; i++) { 725 DisableRecord tok = mDisableRecords.get(i); 726 pw.println(" [" + i + "] userId=" + tok.userId 727 + " what=0x" + Integer.toHexString(tok.what) 728 + " pkg=" + tok.pkg 729 + " token=" + tok.token); 730 } 731 } 732 } 733} 734