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, int mask) {
    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, mask);
    303             disableLocked(vis & StatusBarManager.DISABLE_MASK, mSysUiVisToken,
    304                     "WindowManager.LayoutParams");
    305         }
    306     }
    307 
    308     private void updateUiVisibilityLocked(final int vis, final int mask) {
    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, mask);
    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     @Override
    356     public void preloadRecentApps() {
    357         if (mBar != null) {
    358             try {
    359                 mBar.preloadRecentApps();
    360             } catch (RemoteException ex) {}
    361         }
    362     }
    363 
    364     @Override
    365     public void cancelPreloadRecentApps() {
    366         if (mBar != null) {
    367             try {
    368                 mBar.cancelPreloadRecentApps();
    369             } catch (RemoteException ex) {}
    370         }
    371     }
    372 
    373     private void enforceStatusBar() {
    374         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
    375                 "StatusBarManagerService");
    376     }
    377 
    378     private void enforceExpandStatusBar() {
    379         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR,
    380                 "StatusBarManagerService");
    381     }
    382 
    383     private void enforceStatusBarService() {
    384         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
    385                 "StatusBarManagerService");
    386     }
    387 
    388     // ================================================================================
    389     // Callbacks from the status bar service.
    390     // ================================================================================
    391     public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
    392             List<IBinder> notificationKeys, List<StatusBarNotification> notifications,
    393             int switches[], List<IBinder> binders) {
    394         enforceStatusBarService();
    395 
    396         Slog.i(TAG, "registerStatusBar bar=" + bar);
    397         mBar = bar;
    398         synchronized (mIcons) {
    399             iconList.copyFrom(mIcons);
    400         }
    401         synchronized (mNotifications) {
    402             for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
    403                 notificationKeys.add(e.getKey());
    404                 notifications.add(e.getValue());
    405             }
    406         }
    407         synchronized (mLock) {
    408             switches[0] = gatherDisableActionsLocked();
    409             switches[1] = mSystemUiVisibility;
    410             switches[2] = mMenuVisible ? 1 : 0;
    411             switches[3] = mImeWindowVis;
    412             switches[4] = mImeBackDisposition;
    413             binders.add(mImeToken);
    414         }
    415         switches[5] = mWindowManager.isHardKeyboardAvailable() ? 1 : 0;
    416         switches[6] = mWindowManager.isHardKeyboardEnabled() ? 1 : 0;
    417     }
    418 
    419     /**
    420      * The status bar service should call this each time the user brings the panel from
    421      * invisible to visible in order to clear the notification light.
    422      */
    423     public void onPanelRevealed() {
    424         enforceStatusBarService();
    425 
    426         // tell the notification manager to turn off the lights.
    427         mNotificationCallbacks.onPanelRevealed();
    428     }
    429 
    430     public void onNotificationClick(String pkg, String tag, int id) {
    431         enforceStatusBarService();
    432 
    433         mNotificationCallbacks.onNotificationClick(pkg, tag, id);
    434     }
    435 
    436     public void onNotificationError(String pkg, String tag, int id,
    437             int uid, int initialPid, String message) {
    438         enforceStatusBarService();
    439 
    440         // WARNING: this will call back into us to do the remove.  Don't hold any locks.
    441         mNotificationCallbacks.onNotificationError(pkg, tag, id, uid, initialPid, message);
    442     }
    443 
    444     public void onNotificationClear(String pkg, String tag, int id) {
    445         enforceStatusBarService();
    446 
    447         mNotificationCallbacks.onNotificationClear(pkg, tag, id);
    448     }
    449 
    450     public void onClearAllNotifications() {
    451         enforceStatusBarService();
    452 
    453         mNotificationCallbacks.onClearAll();
    454     }
    455 
    456     // ================================================================================
    457     // Callbacks for NotificationManagerService.
    458     // ================================================================================
    459     public IBinder addNotification(StatusBarNotification notification) {
    460         synchronized (mNotifications) {
    461             IBinder key = new Binder();
    462             mNotifications.put(key, notification);
    463             if (mBar != null) {
    464                 try {
    465                     mBar.addNotification(key, notification);
    466                 } catch (RemoteException ex) {
    467                 }
    468             }
    469             return key;
    470         }
    471     }
    472 
    473     public void updateNotification(IBinder key, StatusBarNotification notification) {
    474         synchronized (mNotifications) {
    475             if (!mNotifications.containsKey(key)) {
    476                 throw new IllegalArgumentException("updateNotification key not found: " + key);
    477             }
    478             mNotifications.put(key, notification);
    479             if (mBar != null) {
    480                 try {
    481                     mBar.updateNotification(key, notification);
    482                 } catch (RemoteException ex) {
    483                 }
    484             }
    485         }
    486     }
    487 
    488     public void removeNotification(IBinder key) {
    489         synchronized (mNotifications) {
    490             final StatusBarNotification n = mNotifications.remove(key);
    491             if (n == null) {
    492                 Slog.e(TAG, "removeNotification key not found: " + key);
    493                 return;
    494             }
    495             if (mBar != null) {
    496                 try {
    497                     mBar.removeNotification(key);
    498                 } catch (RemoteException ex) {
    499                 }
    500             }
    501         }
    502     }
    503 
    504     // ================================================================================
    505     // Can be called from any thread
    506     // ================================================================================
    507 
    508     // lock on mDisableRecords
    509     void manageDisableListLocked(int what, IBinder token, String pkg) {
    510         if (SPEW) {
    511             Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what) + " pkg=" + pkg);
    512         }
    513         // update the list
    514         final int N = mDisableRecords.size();
    515         DisableRecord tok = null;
    516         int i;
    517         for (i=0; i<N; i++) {
    518             DisableRecord t = mDisableRecords.get(i);
    519             if (t.token == token) {
    520                 tok = t;
    521                 break;
    522             }
    523         }
    524         if (what == 0 || !token.isBinderAlive()) {
    525             if (tok != null) {
    526                 mDisableRecords.remove(i);
    527                 tok.token.unlinkToDeath(tok, 0);
    528             }
    529         } else {
    530             if (tok == null) {
    531                 tok = new DisableRecord();
    532                 try {
    533                     token.linkToDeath(tok, 0);
    534                 }
    535                 catch (RemoteException ex) {
    536                     return; // give up
    537                 }
    538                 mDisableRecords.add(tok);
    539             }
    540             tok.what = what;
    541             tok.token = token;
    542             tok.pkg = pkg;
    543         }
    544     }
    545 
    546     // lock on mDisableRecords
    547     int gatherDisableActionsLocked() {
    548         final int N = mDisableRecords.size();
    549         // gather the new net flags
    550         int net = 0;
    551         for (int i=0; i<N; i++) {
    552             net |= mDisableRecords.get(i).what;
    553         }
    554         return net;
    555     }
    556 
    557     // ================================================================================
    558     // Always called from UI thread
    559     // ================================================================================
    560 
    561     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    562         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
    563                 != PackageManager.PERMISSION_GRANTED) {
    564             pw.println("Permission Denial: can't dump StatusBar from from pid="
    565                     + Binder.getCallingPid()
    566                     + ", uid=" + Binder.getCallingUid());
    567             return;
    568         }
    569 
    570         synchronized (mIcons) {
    571             mIcons.dump(pw);
    572         }
    573 
    574         synchronized (mNotifications) {
    575             int i=0;
    576             pw.println("Notification list:");
    577             for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
    578                 pw.printf("  %2d: %s\n", i, e.getValue().toString());
    579                 i++;
    580             }
    581         }
    582 
    583         synchronized (mLock) {
    584             final int N = mDisableRecords.size();
    585             pw.println("  mDisableRecords.size=" + N
    586                     + " mDisabled=0x" + Integer.toHexString(mDisabled));
    587             for (int i=0; i<N; i++) {
    588                 DisableRecord tok = mDisableRecords.get(i);
    589                 pw.println("    [" + i + "] what=0x" + Integer.toHexString(tok.what)
    590                                 + " pkg=" + tok.pkg + " token=" + tok.token);
    591             }
    592         }
    593     }
    594 
    595     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    596         public void onReceive(Context context, Intent intent) {
    597             String action = intent.getAction();
    598             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
    599                     || Intent.ACTION_SCREEN_OFF.equals(action)) {
    600                 collapse();
    601             }
    602             /*
    603             else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
    604                 updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
    605                         intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
    606                         intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
    607                         intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
    608             }
    609             else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
    610                 updateResources();
    611             }
    612             */
    613         }
    614     };
    615 
    616 }
    617