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