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