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