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