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