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