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