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