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