StatusBarManagerService.java revision 92b389da2a60ac51e804031494fea177fc1c47be
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 IBinder mImeToken = null; 78 private int mCurrentUserId; 79 80 private class DisableRecord implements IBinder.DeathRecipient { 81 int userId; 82 String pkg; 83 int what; 84 IBinder token; 85 86 public void binderDied() { 87 Slog.i(TAG, "binder died for pkg=" + pkg); 88 disableInternal(userId, 0, token, pkg); 89 token.unlinkToDeath(this, 0); 90 } 91 } 92 93 /** 94 * Construct the service, add the status bar view to the window manager 95 */ 96 public StatusBarManagerService(Context context, WindowManagerService windowManager) { 97 mContext = context; 98 mWindowManager = windowManager; 99 mWindowManager.setOnHardKeyboardStatusChangeListener(this); 100 101 final Resources res = context.getResources(); 102 mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons)); 103 104 LocalServices.addService(StatusBarManagerInternal.class, mInternalService); 105 } 106 107 /** 108 * Private API used by NotificationManagerService. 109 */ 110 private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() { 111 @Override 112 public void setNotificationDelegate(NotificationDelegate delegate) { 113 synchronized (mNotifications) { 114 mNotificationDelegate = delegate; 115 } 116 } 117 118 @Override 119 public IBinder addNotification(StatusBarNotification notification) { 120 synchronized (mNotifications) { 121 IBinder key = new Binder(); 122 mNotifications.put(key, notification); 123 if (mBar != null) { 124 try { 125 mBar.addNotification(key, notification); 126 } catch (RemoteException ex) { 127 } 128 } 129 return key; 130 } 131 } 132 133 @Override 134 public void updateNotification(IBinder key, StatusBarNotification notification) { 135 synchronized (mNotifications) { 136 if (!mNotifications.containsKey(key)) { 137 throw new IllegalArgumentException("updateNotification key not found: " + key); 138 } 139 mNotifications.put(key, notification); 140 if (mBar != null) { 141 try { 142 mBar.updateNotification(key, notification); 143 } catch (RemoteException ex) { 144 } 145 } 146 } 147 } 148 149 @Override 150 public void removeNotification(IBinder 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 enforceStatusBar(); 351 352 if (SPEW) { 353 Slog.d(TAG, "swetImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition); 354 } 355 356 synchronized(mLock) { 357 // In case of IME change, we need to call up setImeWindowStatus() regardless of 358 // mImeWindowVis because mImeWindowVis may not have been set to false when the 359 // previous IME was destroyed. 360 mImeWindowVis = vis; 361 mImeBackDisposition = backDisposition; 362 mImeToken = token; 363 mHandler.post(new Runnable() { 364 public void run() { 365 if (mBar != null) { 366 try { 367 mBar.setImeWindowStatus(token, vis, backDisposition); 368 } catch (RemoteException ex) { 369 } 370 } 371 } 372 }); 373 } 374 } 375 376 @Override 377 public void setSystemUiVisibility(int vis, int mask) { 378 // also allows calls from window manager which is in this process. 379 enforceStatusBarService(); 380 381 if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")"); 382 383 synchronized (mLock) { 384 updateUiVisibilityLocked(vis, mask); 385 disableLocked( 386 mCurrentUserId, 387 vis & StatusBarManager.DISABLE_MASK, 388 mSysUiVisToken, 389 "WindowManager.LayoutParams"); 390 } 391 } 392 393 private void updateUiVisibilityLocked(final int vis, final int mask) { 394 if (mSystemUiVisibility != vis) { 395 mSystemUiVisibility = vis; 396 mHandler.post(new Runnable() { 397 public void run() { 398 if (mBar != null) { 399 try { 400 mBar.setSystemUiVisibility(vis, mask); 401 } catch (RemoteException ex) { 402 } 403 } 404 } 405 }); 406 } 407 } 408 409 @Override 410 public void setHardKeyboardEnabled(final boolean enabled) { 411 mHandler.post(new Runnable() { 412 public void run() { 413 mWindowManager.setHardKeyboardEnabled(enabled); 414 } 415 }); 416 } 417 418 @Override 419 public void onHardKeyboardStatusChange(final boolean available, final boolean enabled) { 420 mHandler.post(new Runnable() { 421 public void run() { 422 if (mBar != null) { 423 try { 424 mBar.setHardKeyboardStatus(available, enabled); 425 } catch (RemoteException ex) { 426 } 427 } 428 } 429 }); 430 } 431 432 @Override 433 public void toggleRecentApps() { 434 if (mBar != null) { 435 try { 436 mBar.toggleRecentApps(); 437 } catch (RemoteException ex) {} 438 } 439 } 440 441 @Override 442 public void preloadRecentApps() { 443 if (mBar != null) { 444 try { 445 mBar.preloadRecentApps(); 446 } catch (RemoteException ex) {} 447 } 448 } 449 450 @Override 451 public void cancelPreloadRecentApps() { 452 if (mBar != null) { 453 try { 454 mBar.cancelPreloadRecentApps(); 455 } catch (RemoteException ex) {} 456 } 457 } 458 459 @Override 460 public void setCurrentUser(int newUserId) { 461 if (SPEW) Slog.d(TAG, "Setting current user to user " + newUserId); 462 mCurrentUserId = newUserId; 463 } 464 465 @Override 466 public void setWindowState(int window, int state) { 467 if (mBar != null) { 468 try { 469 mBar.setWindowState(window, state); 470 } catch (RemoteException ex) {} 471 } 472 } 473 474 private void enforceStatusBar() { 475 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR, 476 "StatusBarManagerService"); 477 } 478 479 private void enforceExpandStatusBar() { 480 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR, 481 "StatusBarManagerService"); 482 } 483 484 private void enforceStatusBarService() { 485 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE, 486 "StatusBarManagerService"); 487 } 488 489 // ================================================================================ 490 // Callbacks from the status bar service. 491 // ================================================================================ 492 @Override 493 public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList, 494 List<IBinder> notificationKeys, List<StatusBarNotification> notifications, 495 int switches[], List<IBinder> binders) { 496 enforceStatusBarService(); 497 498 Slog.i(TAG, "registerStatusBar bar=" + bar); 499 mBar = bar; 500 synchronized (mIcons) { 501 iconList.copyFrom(mIcons); 502 } 503 synchronized (mNotifications) { 504 for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) { 505 notificationKeys.add(e.getKey()); 506 notifications.add(e.getValue()); 507 } 508 } 509 synchronized (mLock) { 510 switches[0] = gatherDisableActionsLocked(mCurrentUserId); 511 switches[1] = mSystemUiVisibility; 512 switches[2] = mMenuVisible ? 1 : 0; 513 switches[3] = mImeWindowVis; 514 switches[4] = mImeBackDisposition; 515 binders.add(mImeToken); 516 } 517 switches[5] = mWindowManager.isHardKeyboardAvailable() ? 1 : 0; 518 switches[6] = mWindowManager.isHardKeyboardEnabled() ? 1 : 0; 519 } 520 521 /** 522 * The status bar service should call this each time the user brings the panel from 523 * invisible to visible in order to clear the notification light. 524 */ 525 @Override 526 public void onPanelRevealed() { 527 enforceStatusBarService(); 528 long identity = Binder.clearCallingIdentity(); 529 try { 530 // tell the notification manager to turn off the lights. 531 mNotificationDelegate.onPanelRevealed(); 532 } finally { 533 Binder.restoreCallingIdentity(identity); 534 } 535 } 536 537 @Override 538 public void onPanelHidden() throws RemoteException { 539 enforceStatusBarService(); 540 long identity = Binder.clearCallingIdentity(); 541 try { 542 mNotificationDelegate.onPanelHidden(); 543 } finally { 544 Binder.restoreCallingIdentity(identity); 545 } 546 } 547 548 @Override 549 public void onNotificationClick(String pkg, String tag, int id, int userId) { 550 enforceStatusBarService(); 551 final int callingUid = Binder.getCallingUid(); 552 final int callingPid = Binder.getCallingPid(); 553 long identity = Binder.clearCallingIdentity(); 554 try { 555 mNotificationDelegate.onNotificationClick(callingUid, callingPid, pkg, tag, id, userId); 556 } finally { 557 Binder.restoreCallingIdentity(identity); 558 } 559 } 560 561 @Override 562 public void onNotificationError(String pkg, String tag, int id, 563 int uid, int initialPid, String message, int userId) { 564 enforceStatusBarService(); 565 final int callingUid = Binder.getCallingUid(); 566 final int callingPid = Binder.getCallingPid(); 567 long identity = Binder.clearCallingIdentity(); 568 try { 569 // WARNING: this will call back into us to do the remove. Don't hold any locks. 570 mNotificationDelegate.onNotificationError(callingUid, callingPid, 571 pkg, tag, id, uid, initialPid, message, userId); 572 } finally { 573 Binder.restoreCallingIdentity(identity); 574 } 575 } 576 577 @Override 578 public void onNotificationClear(String pkg, String tag, int id, int userId) { 579 enforceStatusBarService(); 580 final int callingUid = Binder.getCallingUid(); 581 final int callingPid = Binder.getCallingPid(); 582 long identity = Binder.clearCallingIdentity(); 583 try { 584 mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId); 585 } finally { 586 Binder.restoreCallingIdentity(identity); 587 } 588 } 589 590 @Override 591 public void onNotificationVisibilityChanged( 592 String[] newlyVisibleKeys, String[] noLongerVisibleKeys) throws RemoteException { 593 enforceStatusBarService(); 594 long identity = Binder.clearCallingIdentity(); 595 try { 596 mNotificationDelegate.onNotificationVisibilityChanged( 597 newlyVisibleKeys, noLongerVisibleKeys); 598 } finally { 599 Binder.restoreCallingIdentity(identity); 600 } 601 } 602 603 @Override 604 public void onClearAllNotifications(int userId) { 605 enforceStatusBarService(); 606 final int callingUid = Binder.getCallingUid(); 607 final int callingPid = Binder.getCallingPid(); 608 long identity = Binder.clearCallingIdentity(); 609 try { 610 mNotificationDelegate.onClearAll(callingUid, callingPid, userId); 611 } finally { 612 Binder.restoreCallingIdentity(identity); 613 } 614 } 615 616 617 // ================================================================================ 618 // Can be called from any thread 619 // ================================================================================ 620 621 // lock on mDisableRecords 622 void manageDisableListLocked(int userId, int what, IBinder token, String pkg) { 623 if (SPEW) { 624 Slog.d(TAG, "manageDisableList userId=" + userId 625 + " what=0x" + Integer.toHexString(what) + " pkg=" + pkg); 626 } 627 // update the list 628 final int N = mDisableRecords.size(); 629 DisableRecord tok = null; 630 int i; 631 for (i=0; i<N; i++) { 632 DisableRecord t = mDisableRecords.get(i); 633 if (t.token == token && t.userId == userId) { 634 tok = t; 635 break; 636 } 637 } 638 if (what == 0 || !token.isBinderAlive()) { 639 if (tok != null) { 640 mDisableRecords.remove(i); 641 tok.token.unlinkToDeath(tok, 0); 642 } 643 } else { 644 if (tok == null) { 645 tok = new DisableRecord(); 646 tok.userId = userId; 647 try { 648 token.linkToDeath(tok, 0); 649 } 650 catch (RemoteException ex) { 651 return; // give up 652 } 653 mDisableRecords.add(tok); 654 } 655 tok.what = what; 656 tok.token = token; 657 tok.pkg = pkg; 658 } 659 } 660 661 // lock on mDisableRecords 662 int gatherDisableActionsLocked(int userId) { 663 final int N = mDisableRecords.size(); 664 // gather the new net flags 665 int net = 0; 666 for (int i=0; i<N; i++) { 667 final DisableRecord rec = mDisableRecords.get(i); 668 if (rec.userId == userId) { 669 net |= rec.what; 670 } 671 } 672 return net; 673 } 674 675 // ================================================================================ 676 // Always called from UI thread 677 // ================================================================================ 678 679 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 680 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 681 != PackageManager.PERMISSION_GRANTED) { 682 pw.println("Permission Denial: can't dump StatusBar from from pid=" 683 + Binder.getCallingPid() 684 + ", uid=" + Binder.getCallingUid()); 685 return; 686 } 687 688 synchronized (mIcons) { 689 mIcons.dump(pw); 690 } 691 692 synchronized (mNotifications) { 693 int i=0; 694 pw.println("Notification list:"); 695 for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) { 696 pw.printf(" %2d: %s\n", i, e.getValue().toString()); 697 i++; 698 } 699 } 700 701 synchronized (mLock) { 702 pw.println(" mDisabled=0x" + Integer.toHexString(mDisabled)); 703 final int N = mDisableRecords.size(); 704 pw.println(" mDisableRecords.size=" + N); 705 for (int i=0; i<N; i++) { 706 DisableRecord tok = mDisableRecords.get(i); 707 pw.println(" [" + i + "] userId=" + tok.userId 708 + " what=0x" + Integer.toHexString(tok.what) 709 + " pkg=" + tok.pkg 710 + " token=" + tok.token); 711 } 712 } 713 } 714} 715