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         @Override
    146         public void showScreenPinningRequest() {
    147             if (mBar != null) {
    148                 try {
    149                     mBar.showScreenPinningRequest();
    150                 } catch (RemoteException e) {
    151                 }
    152             }
    153         }
    154     };
    155 
    156     // ================================================================================
    157     // From IStatusBarService
    158     // ================================================================================
    159     @Override
    160     public void expandNotificationsPanel() {
    161         enforceExpandStatusBar();
    162 
    163         if (mBar != null) {
    164             try {
    165                 mBar.animateExpandNotificationsPanel();
    166             } catch (RemoteException ex) {
    167             }
    168         }
    169     }
    170 
    171     @Override
    172     public void collapsePanels() {
    173         enforceExpandStatusBar();
    174 
    175         if (mBar != null) {
    176             try {
    177                 mBar.animateCollapsePanels();
    178             } catch (RemoteException ex) {
    179             }
    180         }
    181     }
    182 
    183     @Override
    184     public void expandSettingsPanel() {
    185         enforceExpandStatusBar();
    186 
    187         if (mBar != null) {
    188             try {
    189                 mBar.animateExpandSettingsPanel();
    190             } catch (RemoteException ex) {
    191             }
    192         }
    193     }
    194 
    195     @Override
    196     public void disable(int what, IBinder token, String pkg) {
    197         disableInternal(mCurrentUserId, what, token, pkg);
    198     }
    199 
    200     private void disableInternal(int userId, int what, IBinder token, String pkg) {
    201         enforceStatusBar();
    202 
    203         synchronized (mLock) {
    204             disableLocked(userId, what, token, pkg);
    205         }
    206     }
    207 
    208     private void disableLocked(int userId, int what, IBinder token, String pkg) {
    209         // It's important that the the callback and the call to mBar get done
    210         // in the same order when multiple threads are calling this function
    211         // so they are paired correctly.  The messages on the handler will be
    212         // handled in the order they were enqueued, but will be outside the lock.
    213         manageDisableListLocked(userId, what, token, pkg);
    214 
    215         // Ensure state for the current user is applied, even if passed a non-current user.
    216         final int net = gatherDisableActionsLocked(mCurrentUserId);
    217         if (net != mDisabled) {
    218             mDisabled = net;
    219             mHandler.post(new Runnable() {
    220                     public void run() {
    221                         mNotificationDelegate.onSetDisabled(net);
    222                     }
    223                 });
    224             if (mBar != null) {
    225                 try {
    226                     mBar.disable(net);
    227                 } catch (RemoteException ex) {
    228                 }
    229             }
    230         }
    231     }
    232 
    233     @Override
    234     public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
    235             String contentDescription) {
    236         enforceStatusBar();
    237 
    238         synchronized (mIcons) {
    239             int index = mIcons.getSlotIndex(slot);
    240             if (index < 0) {
    241                 throw new SecurityException("invalid status bar icon slot: " + slot);
    242             }
    243 
    244             StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.OWNER, iconId,
    245                     iconLevel, 0,
    246                     contentDescription);
    247             //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
    248             mIcons.setIcon(index, icon);
    249 
    250             if (mBar != null) {
    251                 try {
    252                     mBar.setIcon(index, icon);
    253                 } catch (RemoteException ex) {
    254                 }
    255             }
    256         }
    257     }
    258 
    259     @Override
    260     public void setIconVisibility(String slot, boolean visible) {
    261         enforceStatusBar();
    262 
    263         synchronized (mIcons) {
    264             int index = mIcons.getSlotIndex(slot);
    265             if (index < 0) {
    266                 throw new SecurityException("invalid status bar icon slot: " + slot);
    267             }
    268 
    269             StatusBarIcon icon = mIcons.getIcon(index);
    270             if (icon == null) {
    271                 return;
    272             }
    273 
    274             if (icon.visible != visible) {
    275                 icon.visible = visible;
    276 
    277                 if (mBar != null) {
    278                     try {
    279                         mBar.setIcon(index, icon);
    280                     } catch (RemoteException ex) {
    281                     }
    282                 }
    283             }
    284         }
    285     }
    286 
    287     @Override
    288     public void removeIcon(String slot) {
    289         enforceStatusBar();
    290 
    291         synchronized (mIcons) {
    292             int index = mIcons.getSlotIndex(slot);
    293             if (index < 0) {
    294                 throw new SecurityException("invalid status bar icon slot: " + slot);
    295             }
    296 
    297             mIcons.removeIcon(index);
    298 
    299             if (mBar != null) {
    300                 try {
    301                     mBar.removeIcon(index);
    302                 } catch (RemoteException ex) {
    303                 }
    304             }
    305         }
    306     }
    307 
    308     /**
    309      * Hide or show the on-screen Menu key. Only call this from the window manager, typically in
    310      * response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
    311      * to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
    312      */
    313     @Override
    314     public void topAppWindowChanged(final boolean menuVisible) {
    315         enforceStatusBar();
    316 
    317         if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key");
    318 
    319         synchronized(mLock) {
    320             mMenuVisible = menuVisible;
    321             mHandler.post(new Runnable() {
    322                     public void run() {
    323                         if (mBar != null) {
    324                             try {
    325                                 mBar.topAppWindowChanged(menuVisible);
    326                             } catch (RemoteException ex) {
    327                             }
    328                         }
    329                     }
    330                 });
    331         }
    332     }
    333 
    334     @Override
    335     public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition,
    336             final boolean showImeSwitcher) {
    337         enforceStatusBar();
    338 
    339         if (SPEW) {
    340             Slog.d(TAG, "swetImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition);
    341         }
    342 
    343         synchronized(mLock) {
    344             // In case of IME change, we need to call up setImeWindowStatus() regardless of
    345             // mImeWindowVis because mImeWindowVis may not have been set to false when the
    346             // previous IME was destroyed.
    347             mImeWindowVis = vis;
    348             mImeBackDisposition = backDisposition;
    349             mImeToken = token;
    350             mShowImeSwitcher = showImeSwitcher;
    351             mHandler.post(new Runnable() {
    352                 public void run() {
    353                     if (mBar != null) {
    354                         try {
    355                             mBar.setImeWindowStatus(token, vis, backDisposition, showImeSwitcher);
    356                         } catch (RemoteException ex) {
    357                         }
    358                     }
    359                 }
    360             });
    361         }
    362     }
    363 
    364     @Override
    365     public void setSystemUiVisibility(int vis, int mask, String cause) {
    366         // also allows calls from window manager which is in this process.
    367         enforceStatusBarService();
    368 
    369         if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
    370 
    371         synchronized (mLock) {
    372             updateUiVisibilityLocked(vis, mask);
    373             disableLocked(
    374                     mCurrentUserId,
    375                     vis & StatusBarManager.DISABLE_MASK,
    376                     mSysUiVisToken,
    377                     cause);
    378         }
    379     }
    380 
    381     private void updateUiVisibilityLocked(final int vis, final int mask) {
    382         if (mSystemUiVisibility != vis) {
    383             mSystemUiVisibility = vis;
    384             mHandler.post(new Runnable() {
    385                     public void run() {
    386                         if (mBar != null) {
    387                             try {
    388                                 mBar.setSystemUiVisibility(vis, mask);
    389                             } catch (RemoteException ex) {
    390                             }
    391                         }
    392                     }
    393                 });
    394         }
    395     }
    396 
    397     @Override
    398     public void toggleRecentApps() {
    399         if (mBar != null) {
    400             try {
    401                 mBar.toggleRecentApps();
    402             } catch (RemoteException ex) {}
    403         }
    404     }
    405 
    406     @Override
    407     public void preloadRecentApps() {
    408         if (mBar != null) {
    409             try {
    410                 mBar.preloadRecentApps();
    411             } catch (RemoteException ex) {}
    412         }
    413     }
    414 
    415     @Override
    416     public void cancelPreloadRecentApps() {
    417         if (mBar != null) {
    418             try {
    419                 mBar.cancelPreloadRecentApps();
    420             } catch (RemoteException ex) {}
    421         }
    422     }
    423 
    424     @Override
    425     public void showRecentApps(boolean triggeredFromAltTab) {
    426         if (mBar != null) {
    427             try {
    428                 mBar.showRecentApps(triggeredFromAltTab);
    429             } catch (RemoteException ex) {}
    430         }
    431     }
    432 
    433     @Override
    434     public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
    435         if (mBar != null) {
    436             try {
    437                 mBar.hideRecentApps(triggeredFromAltTab, triggeredFromHomeKey);
    438             } catch (RemoteException ex) {}
    439         }
    440     }
    441 
    442     @Override
    443     public void setCurrentUser(int newUserId) {
    444         if (SPEW) Slog.d(TAG, "Setting current user to user " + newUserId);
    445         mCurrentUserId = newUserId;
    446     }
    447 
    448     @Override
    449     public void setWindowState(int window, int state) {
    450         if (mBar != null) {
    451             try {
    452                 mBar.setWindowState(window, state);
    453             } catch (RemoteException ex) {}
    454         }
    455     }
    456 
    457     private void enforceStatusBar() {
    458         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
    459                 "StatusBarManagerService");
    460     }
    461 
    462     private void enforceExpandStatusBar() {
    463         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR,
    464                 "StatusBarManagerService");
    465     }
    466 
    467     private void enforceStatusBarService() {
    468         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
    469                 "StatusBarManagerService");
    470     }
    471 
    472     // ================================================================================
    473     // Callbacks from the status bar service.
    474     // ================================================================================
    475     @Override
    476     public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
    477             int switches[], List<IBinder> binders) {
    478         enforceStatusBarService();
    479 
    480         Slog.i(TAG, "registerStatusBar bar=" + bar);
    481         mBar = bar;
    482         synchronized (mIcons) {
    483             iconList.copyFrom(mIcons);
    484         }
    485         synchronized (mLock) {
    486             switches[0] = gatherDisableActionsLocked(mCurrentUserId);
    487             switches[1] = mSystemUiVisibility;
    488             switches[2] = mMenuVisible ? 1 : 0;
    489             switches[3] = mImeWindowVis;
    490             switches[4] = mImeBackDisposition;
    491             switches[5] = mShowImeSwitcher ? 1 : 0;
    492             binders.add(mImeToken);
    493         }
    494     }
    495 
    496     /**
    497      * @param clearNotificationEffects whether to consider notifications as "shown" and stop
    498      *     LED, vibration, and ringing
    499      */
    500     @Override
    501     public void onPanelRevealed(boolean clearNotificationEffects) {
    502         enforceStatusBarService();
    503         long identity = Binder.clearCallingIdentity();
    504         try {
    505             mNotificationDelegate.onPanelRevealed(clearNotificationEffects);
    506         } finally {
    507             Binder.restoreCallingIdentity(identity);
    508         }
    509     }
    510 
    511     @Override
    512     public void clearNotificationEffects() throws RemoteException {
    513         enforceStatusBarService();
    514         long identity = Binder.clearCallingIdentity();
    515         try {
    516             mNotificationDelegate.clearEffects();
    517         } finally {
    518             Binder.restoreCallingIdentity(identity);
    519         }
    520     }
    521 
    522     @Override
    523     public void onPanelHidden() throws RemoteException {
    524         enforceStatusBarService();
    525         long identity = Binder.clearCallingIdentity();
    526         try {
    527             mNotificationDelegate.onPanelHidden();
    528         } finally {
    529             Binder.restoreCallingIdentity(identity);
    530         }
    531     }
    532 
    533     @Override
    534     public void onNotificationClick(String key) {
    535         enforceStatusBarService();
    536         final int callingUid = Binder.getCallingUid();
    537         final int callingPid = Binder.getCallingPid();
    538         long identity = Binder.clearCallingIdentity();
    539         try {
    540             mNotificationDelegate.onNotificationClick(callingUid, callingPid, key);
    541         } finally {
    542             Binder.restoreCallingIdentity(identity);
    543         }
    544     }
    545 
    546     @Override
    547     public void onNotificationActionClick(String key, int actionIndex) {
    548         enforceStatusBarService();
    549         final int callingUid = Binder.getCallingUid();
    550         final int callingPid = Binder.getCallingPid();
    551         long identity = Binder.clearCallingIdentity();
    552         try {
    553             mNotificationDelegate.onNotificationActionClick(callingUid, callingPid, key,
    554                     actionIndex);
    555         } finally {
    556             Binder.restoreCallingIdentity(identity);
    557         }
    558     }
    559 
    560     @Override
    561     public void onNotificationError(String pkg, String tag, int id,
    562             int uid, int initialPid, String message, int userId) {
    563         enforceStatusBarService();
    564         final int callingUid = Binder.getCallingUid();
    565         final int callingPid = Binder.getCallingPid();
    566         long identity = Binder.clearCallingIdentity();
    567         try {
    568             // WARNING: this will call back into us to do the remove.  Don't hold any locks.
    569             mNotificationDelegate.onNotificationError(callingUid, callingPid,
    570                     pkg, tag, id, uid, initialPid, message, userId);
    571         } finally {
    572             Binder.restoreCallingIdentity(identity);
    573         }
    574     }
    575 
    576     @Override
    577     public void onNotificationClear(String pkg, String tag, int id, int userId) {
    578         enforceStatusBarService();
    579         final int callingUid = Binder.getCallingUid();
    580         final int callingPid = Binder.getCallingPid();
    581         long identity = Binder.clearCallingIdentity();
    582         try {
    583             mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId);
    584         } finally {
    585             Binder.restoreCallingIdentity(identity);
    586         }
    587     }
    588 
    589     @Override
    590     public void onNotificationVisibilityChanged(
    591             String[] newlyVisibleKeys, String[] noLongerVisibleKeys) throws RemoteException {
    592         enforceStatusBarService();
    593         long identity = Binder.clearCallingIdentity();
    594         try {
    595             mNotificationDelegate.onNotificationVisibilityChanged(
    596                     newlyVisibleKeys, noLongerVisibleKeys);
    597         } finally {
    598             Binder.restoreCallingIdentity(identity);
    599         }
    600     }
    601 
    602     @Override
    603     public void onNotificationExpansionChanged(String key, boolean userAction,
    604             boolean expanded) throws RemoteException {
    605         enforceStatusBarService();
    606         long identity = Binder.clearCallingIdentity();
    607         try {
    608             mNotificationDelegate.onNotificationExpansionChanged(
    609                     key, userAction, expanded);
    610         } finally {
    611             Binder.restoreCallingIdentity(identity);
    612         }
    613     }
    614 
    615     @Override
    616     public void onClearAllNotifications(int userId) {
    617         enforceStatusBarService();
    618         final int callingUid = Binder.getCallingUid();
    619         final int callingPid = Binder.getCallingPid();
    620         long identity = Binder.clearCallingIdentity();
    621         try {
    622             mNotificationDelegate.onClearAll(callingUid, callingPid, userId);
    623         } finally {
    624             Binder.restoreCallingIdentity(identity);
    625         }
    626     }
    627 
    628 
    629     // ================================================================================
    630     // Can be called from any thread
    631     // ================================================================================
    632 
    633     // lock on mDisableRecords
    634     void manageDisableListLocked(int userId, int what, IBinder token, String pkg) {
    635         if (SPEW) {
    636             Slog.d(TAG, "manageDisableList userId=" + userId
    637                     + " what=0x" + Integer.toHexString(what) + " pkg=" + pkg);
    638         }
    639         // update the list
    640         final int N = mDisableRecords.size();
    641         DisableRecord tok = null;
    642         int i;
    643         for (i=0; i<N; i++) {
    644             DisableRecord t = mDisableRecords.get(i);
    645             if (t.token == token && t.userId == userId) {
    646                 tok = t;
    647                 break;
    648             }
    649         }
    650         if (what == 0 || !token.isBinderAlive()) {
    651             if (tok != null) {
    652                 mDisableRecords.remove(i);
    653                 tok.token.unlinkToDeath(tok, 0);
    654             }
    655         } else {
    656             if (tok == null) {
    657                 tok = new DisableRecord();
    658                 tok.userId = userId;
    659                 try {
    660                     token.linkToDeath(tok, 0);
    661                 }
    662                 catch (RemoteException ex) {
    663                     return; // give up
    664                 }
    665                 mDisableRecords.add(tok);
    666             }
    667             tok.what = what;
    668             tok.token = token;
    669             tok.pkg = pkg;
    670         }
    671     }
    672 
    673     // lock on mDisableRecords
    674     int gatherDisableActionsLocked(int userId) {
    675         final int N = mDisableRecords.size();
    676         // gather the new net flags
    677         int net = 0;
    678         for (int i=0; i<N; i++) {
    679             final DisableRecord rec = mDisableRecords.get(i);
    680             if (rec.userId == userId) {
    681                 net |= rec.what;
    682             }
    683         }
    684         return net;
    685     }
    686 
    687     // ================================================================================
    688     // Always called from UI thread
    689     // ================================================================================
    690 
    691     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    692         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
    693                 != PackageManager.PERMISSION_GRANTED) {
    694             pw.println("Permission Denial: can't dump StatusBar from from pid="
    695                     + Binder.getCallingPid()
    696                     + ", uid=" + Binder.getCallingUid());
    697             return;
    698         }
    699 
    700         synchronized (mIcons) {
    701             mIcons.dump(pw);
    702         }
    703 
    704         synchronized (mLock) {
    705             pw.println("  mDisabled=0x" + Integer.toHexString(mDisabled));
    706             final int N = mDisableRecords.size();
    707             pw.println("  mDisableRecords.size=" + N);
    708             for (int i=0; i<N; i++) {
    709                 DisableRecord tok = mDisableRecords.get(i);
    710                 pw.println("    [" + i + "] userId=" + tok.userId
    711                                 + " what=0x" + Integer.toHexString(tok.what)
    712                                 + " pkg=" + tok.pkg
    713                                 + " token=" + tok.token);
    714             }
    715         }
    716     }
    717 }
    718