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     @Override
    403     public void setWindowState(int window, int state) {
    404         if (mBar != null) {
    405             try {
    406                 mBar.setWindowState(window, state);
    407             } catch (RemoteException ex) {}
    408         }
    409     }
    410 
    411     private void enforceStatusBar() {
    412         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
    413                 "StatusBarManagerService");
    414     }
    415 
    416     private void enforceExpandStatusBar() {
    417         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR,
    418                 "StatusBarManagerService");
    419     }
    420 
    421     private void enforceStatusBarService() {
    422         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
    423                 "StatusBarManagerService");
    424     }
    425 
    426     // ================================================================================
    427     // Callbacks from the status bar service.
    428     // ================================================================================
    429     public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
    430             List<IBinder> notificationKeys, List<StatusBarNotification> notifications,
    431             int switches[], List<IBinder> binders) {
    432         enforceStatusBarService();
    433 
    434         Slog.i(TAG, "registerStatusBar bar=" + bar);
    435         mBar = bar;
    436         synchronized (mIcons) {
    437             iconList.copyFrom(mIcons);
    438         }
    439         synchronized (mNotifications) {
    440             for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
    441                 notificationKeys.add(e.getKey());
    442                 notifications.add(e.getValue());
    443             }
    444         }
    445         synchronized (mLock) {
    446             switches[0] = gatherDisableActionsLocked(mCurrentUserId);
    447             switches[1] = mSystemUiVisibility;
    448             switches[2] = mMenuVisible ? 1 : 0;
    449             switches[3] = mImeWindowVis;
    450             switches[4] = mImeBackDisposition;
    451             binders.add(mImeToken);
    452         }
    453         switches[5] = mWindowManager.isHardKeyboardAvailable() ? 1 : 0;
    454         switches[6] = mWindowManager.isHardKeyboardEnabled() ? 1 : 0;
    455     }
    456 
    457     /**
    458      * The status bar service should call this each time the user brings the panel from
    459      * invisible to visible in order to clear the notification light.
    460      */
    461     public void onPanelRevealed() {
    462         enforceStatusBarService();
    463 
    464         // tell the notification manager to turn off the lights.
    465         mNotificationCallbacks.onPanelRevealed();
    466     }
    467 
    468     public void onNotificationClick(String pkg, String tag, int id) {
    469         enforceStatusBarService();
    470 
    471         mNotificationCallbacks.onNotificationClick(pkg, tag, id);
    472     }
    473 
    474     public void onNotificationError(String pkg, String tag, int id,
    475             int uid, int initialPid, String message) {
    476         enforceStatusBarService();
    477 
    478         // WARNING: this will call back into us to do the remove.  Don't hold any locks.
    479         mNotificationCallbacks.onNotificationError(pkg, tag, id, uid, initialPid, message);
    480     }
    481 
    482     public void onNotificationClear(String pkg, String tag, int id) {
    483         enforceStatusBarService();
    484 
    485         mNotificationCallbacks.onNotificationClear(pkg, tag, id);
    486     }
    487 
    488     public void onClearAllNotifications() {
    489         enforceStatusBarService();
    490 
    491         mNotificationCallbacks.onClearAll();
    492     }
    493 
    494     // ================================================================================
    495     // Callbacks for NotificationManagerService.
    496     // ================================================================================
    497     public IBinder addNotification(StatusBarNotification notification) {
    498         synchronized (mNotifications) {
    499             IBinder key = new Binder();
    500             mNotifications.put(key, notification);
    501             if (mBar != null) {
    502                 try {
    503                     mBar.addNotification(key, notification);
    504                 } catch (RemoteException ex) {
    505                 }
    506             }
    507             return key;
    508         }
    509     }
    510 
    511     public void updateNotification(IBinder key, StatusBarNotification notification) {
    512         synchronized (mNotifications) {
    513             if (!mNotifications.containsKey(key)) {
    514                 throw new IllegalArgumentException("updateNotification key not found: " + key);
    515             }
    516             mNotifications.put(key, notification);
    517             if (mBar != null) {
    518                 try {
    519                     mBar.updateNotification(key, notification);
    520                 } catch (RemoteException ex) {
    521                 }
    522             }
    523         }
    524     }
    525 
    526     public void removeNotification(IBinder key) {
    527         synchronized (mNotifications) {
    528             final StatusBarNotification n = mNotifications.remove(key);
    529             if (n == null) {
    530                 Slog.e(TAG, "removeNotification key not found: " + key);
    531                 return;
    532             }
    533             if (mBar != null) {
    534                 try {
    535                     mBar.removeNotification(key);
    536                 } catch (RemoteException ex) {
    537                 }
    538             }
    539         }
    540     }
    541 
    542     // ================================================================================
    543     // Can be called from any thread
    544     // ================================================================================
    545 
    546     // lock on mDisableRecords
    547     void manageDisableListLocked(int userId, int what, IBinder token, String pkg) {
    548         if (SPEW) {
    549             Slog.d(TAG, "manageDisableList userId=" + userId
    550                     + " what=0x" + Integer.toHexString(what) + " pkg=" + pkg);
    551         }
    552         // update the list
    553         final int N = mDisableRecords.size();
    554         DisableRecord tok = null;
    555         int i;
    556         for (i=0; i<N; i++) {
    557             DisableRecord t = mDisableRecords.get(i);
    558             if (t.token == token && t.userId == userId) {
    559                 tok = t;
    560                 break;
    561             }
    562         }
    563         if (what == 0 || !token.isBinderAlive()) {
    564             if (tok != null) {
    565                 mDisableRecords.remove(i);
    566                 tok.token.unlinkToDeath(tok, 0);
    567             }
    568         } else {
    569             if (tok == null) {
    570                 tok = new DisableRecord();
    571                 tok.userId = userId;
    572                 try {
    573                     token.linkToDeath(tok, 0);
    574                 }
    575                 catch (RemoteException ex) {
    576                     return; // give up
    577                 }
    578                 mDisableRecords.add(tok);
    579             }
    580             tok.what = what;
    581             tok.token = token;
    582             tok.pkg = pkg;
    583         }
    584     }
    585 
    586     // lock on mDisableRecords
    587     int gatherDisableActionsLocked(int userId) {
    588         final int N = mDisableRecords.size();
    589         // gather the new net flags
    590         int net = 0;
    591         for (int i=0; i<N; i++) {
    592             final DisableRecord rec = mDisableRecords.get(i);
    593             if (rec.userId == userId) {
    594                 net |= rec.what;
    595             }
    596         }
    597         return net;
    598     }
    599 
    600     // ================================================================================
    601     // Always called from UI thread
    602     // ================================================================================
    603 
    604     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    605         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
    606                 != PackageManager.PERMISSION_GRANTED) {
    607             pw.println("Permission Denial: can't dump StatusBar from from pid="
    608                     + Binder.getCallingPid()
    609                     + ", uid=" + Binder.getCallingUid());
    610             return;
    611         }
    612 
    613         synchronized (mIcons) {
    614             mIcons.dump(pw);
    615         }
    616 
    617         synchronized (mNotifications) {
    618             int i=0;
    619             pw.println("Notification list:");
    620             for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
    621                 pw.printf("  %2d: %s\n", i, e.getValue().toString());
    622                 i++;
    623             }
    624         }
    625 
    626         synchronized (mLock) {
    627             pw.println("  mDisabled=0x" + Integer.toHexString(mDisabled));
    628             final int N = mDisableRecords.size();
    629             pw.println("  mDisableRecords.size=" + N);
    630             for (int i=0; i<N; i++) {
    631                 DisableRecord tok = mDisableRecords.get(i);
    632                 pw.println("    [" + i + "] userId=" + tok.userId
    633                                 + " what=0x" + Integer.toHexString(tok.what)
    634                                 + " pkg=" + tok.pkg
    635                                 + " token=" + tok.token);
    636             }
    637         }
    638     }
    639 
    640     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    641         public void onReceive(Context context, Intent intent) {
    642             String action = intent.getAction();
    643             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
    644                     || Intent.ACTION_SCREEN_OFF.equals(action)) {
    645                 collapsePanels();
    646             }
    647             /*
    648             else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
    649                 updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
    650                         intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
    651                         intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
    652                         intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
    653             }
    654             else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
    655                 updateResources();
    656             }
    657             */
    658         }
    659     };
    660 
    661 }
    662