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