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