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