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