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