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