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.PendingIntent;
     20 import android.app.StatusBarManager;
     21 import android.content.BroadcastReceiver;
     22 import android.content.ComponentName;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.content.pm.PackageManager;
     27 import android.content.res.Resources;
     28 import android.net.Uri;
     29 import android.os.IBinder;
     30 import android.os.RemoteException;
     31 import android.os.Binder;
     32 import android.os.Handler;
     33 import android.os.SystemClock;
     34 import android.util.Slog;
     35 
     36 import com.android.internal.statusbar.IStatusBar;
     37 import com.android.internal.statusbar.IStatusBarService;
     38 import com.android.internal.statusbar.StatusBarIcon;
     39 import com.android.internal.statusbar.StatusBarIconList;
     40 import com.android.internal.statusbar.StatusBarNotification;
     41 
     42 import java.io.FileDescriptor;
     43 import java.io.PrintWriter;
     44 import java.util.ArrayList;
     45 import java.util.HashMap;
     46 import java.util.List;
     47 import java.util.Map;
     48 
     49 
     50 /**
     51  * A note on locking:  We rely on the fact that calls onto mBar are oneway or
     52  * if they are local, that they just enqueue messages to not deadlock.
     53  */
     54 public class StatusBarManagerService extends IStatusBarService.Stub
     55 {
     56     static final String TAG = "StatusBarManagerService";
     57     static final boolean SPEW = false;
     58 
     59     final Context mContext;
     60     Handler mHandler = new Handler();
     61     NotificationCallbacks mNotificationCallbacks;
     62     volatile IStatusBar mBar;
     63     StatusBarIconList mIcons = new StatusBarIconList();
     64     HashMap<IBinder,StatusBarNotification> mNotifications
     65             = new HashMap<IBinder,StatusBarNotification>();
     66 
     67     // for disabling the status bar
     68     ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
     69     int mDisabled = 0;
     70 
     71     private class DisableRecord implements IBinder.DeathRecipient {
     72         String pkg;
     73         int what;
     74         IBinder token;
     75 
     76         public void binderDied() {
     77             Slog.i(TAG, "binder died for pkg=" + pkg);
     78             disable(0, token, pkg);
     79             token.unlinkToDeath(this, 0);
     80         }
     81     }
     82 
     83     public interface NotificationCallbacks {
     84         void onSetDisabled(int status);
     85         void onClearAll();
     86         void onNotificationClick(String pkg, String tag, int id);
     87         void onPanelRevealed();
     88         void onNotificationError(String pkg, String tag, int id,
     89                 int uid, int initialPid, String message);
     90     }
     91 
     92     /**
     93      * Construct the service, add the status bar view to the window manager
     94      */
     95     public StatusBarManagerService(Context context) {
     96         mContext = context;
     97 
     98         final Resources res = context.getResources();
     99         mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));
    100     }
    101 
    102     public void setNotificationCallbacks(NotificationCallbacks listener) {
    103         mNotificationCallbacks = listener;
    104     }
    105 
    106     // ================================================================================
    107     // Constructing the view
    108     // ================================================================================
    109 
    110     public void systemReady() {
    111     }
    112 
    113     public void systemReady2() {
    114         ComponentName cn = ComponentName.unflattenFromString(
    115                 mContext.getString(com.android.internal.R.string.config_statusBarComponent));
    116         Intent intent = new Intent();
    117         intent.setComponent(cn);
    118         Slog.i(TAG, "Starting service: " + cn);
    119         mContext.startService(intent);
    120     }
    121 
    122     // ================================================================================
    123     // From IStatusBarService
    124     // ================================================================================
    125     public void expand() {
    126         enforceExpandStatusBar();
    127 
    128         if (mBar != null) {
    129             try {
    130                 mBar.animateExpand();
    131             } catch (RemoteException ex) {
    132             }
    133         }
    134     }
    135 
    136     public void collapse() {
    137         enforceExpandStatusBar();
    138 
    139         if (mBar != null) {
    140             try {
    141                 mBar.animateCollapse();
    142             } catch (RemoteException ex) {
    143             }
    144         }
    145     }
    146 
    147     public void disable(int what, IBinder token, String pkg) {
    148         enforceStatusBar();
    149 
    150         // It's important that the the callback and the call to mBar get done
    151         // in the same order when multiple threads are calling this function
    152         // so they are paired correctly.  The messages on the handler will be
    153         // handled in the order they were enqueued, but will be outside the lock.
    154         synchronized (mDisableRecords) {
    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 
    174     public void setIcon(String slot, String iconPackage, int iconId, int iconLevel) {
    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);
    184             //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
    185             mIcons.setIcon(index, icon);
    186 
    187             if (mBar != null) {
    188                 try {
    189                     mBar.setIcon(index, icon);
    190                 } catch (RemoteException ex) {
    191                 }
    192             }
    193         }
    194     }
    195 
    196     public void setIconVisibility(String slot, boolean visible) {
    197         enforceStatusBar();
    198 
    199         synchronized (mIcons) {
    200             int index = mIcons.getSlotIndex(slot);
    201             if (index < 0) {
    202                 throw new SecurityException("invalid status bar icon slot: " + slot);
    203             }
    204 
    205             StatusBarIcon icon = mIcons.getIcon(index);
    206             if (icon == null) {
    207                 return;
    208             }
    209 
    210             if (icon.visible != visible) {
    211                 icon.visible = visible;
    212 
    213                 if (mBar != null) {
    214                     try {
    215                         mBar.setIcon(index, icon);
    216                     } catch (RemoteException ex) {
    217                     }
    218                 }
    219             }
    220         }
    221     }
    222 
    223     public void removeIcon(String slot) {
    224         enforceStatusBar();
    225 
    226         synchronized (mIcons) {
    227             int index = mIcons.getSlotIndex(slot);
    228             if (index < 0) {
    229                 throw new SecurityException("invalid status bar icon slot: " + slot);
    230             }
    231 
    232             mIcons.removeIcon(index);
    233 
    234             if (mBar != null) {
    235                 try {
    236                     mBar.removeIcon(index);
    237                 } catch (RemoteException ex) {
    238                 }
    239             }
    240         }
    241     }
    242 
    243     private void enforceStatusBar() {
    244         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
    245                 "StatusBarManagerService");
    246     }
    247 
    248     private void enforceExpandStatusBar() {
    249         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR,
    250                 "StatusBarManagerService");
    251     }
    252 
    253     private void enforceStatusBarService() {
    254         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
    255                 "StatusBarManagerService");
    256     }
    257 
    258 
    259     // ================================================================================
    260     // Callbacks from the status bar service.
    261     // ================================================================================
    262     public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
    263             List<IBinder> notificationKeys, List<StatusBarNotification> notifications) {
    264         enforceStatusBarService();
    265 
    266         Slog.i(TAG, "registerStatusBar bar=" + bar);
    267         mBar = bar;
    268         synchronized (mIcons) {
    269             iconList.copyFrom(mIcons);
    270         }
    271         synchronized (mNotifications) {
    272             for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
    273                 notificationKeys.add(e.getKey());
    274                 notifications.add(e.getValue());
    275             }
    276         }
    277     }
    278 
    279     /**
    280      * The status bar service should call this each time the user brings the panel from
    281      * invisible to visible in order to clear the notification light.
    282      */
    283     public void onPanelRevealed() {
    284         enforceStatusBarService();
    285 
    286         // tell the notification manager to turn off the lights.
    287         mNotificationCallbacks.onPanelRevealed();
    288     }
    289 
    290     public void onNotificationClick(String pkg, String tag, int id) {
    291         enforceStatusBarService();
    292 
    293         mNotificationCallbacks.onNotificationClick(pkg, tag, id);
    294     }
    295 
    296     public void onNotificationError(String pkg, String tag, int id,
    297             int uid, int initialPid, String message) {
    298         enforceStatusBarService();
    299 
    300         // WARNING: this will call back into us to do the remove.  Don't hold any locks.
    301         mNotificationCallbacks.onNotificationError(pkg, tag, id, uid, initialPid, message);
    302     }
    303 
    304     public void onClearAllNotifications() {
    305         enforceStatusBarService();
    306 
    307         mNotificationCallbacks.onClearAll();
    308     }
    309 
    310     // ================================================================================
    311     // Callbacks for NotificationManagerService.
    312     // ================================================================================
    313     public IBinder addNotification(StatusBarNotification notification) {
    314         synchronized (mNotifications) {
    315             IBinder key = new Binder();
    316             mNotifications.put(key, notification);
    317             if (mBar != null) {
    318                 try {
    319                     mBar.addNotification(key, notification);
    320                 } catch (RemoteException ex) {
    321                 }
    322             }
    323             return key;
    324         }
    325     }
    326 
    327     public void updateNotification(IBinder key, StatusBarNotification notification) {
    328         synchronized (mNotifications) {
    329             if (!mNotifications.containsKey(key)) {
    330                 throw new IllegalArgumentException("updateNotification key not found: " + key);
    331             }
    332             mNotifications.put(key, notification);
    333             if (mBar != null) {
    334                 try {
    335                     mBar.updateNotification(key, notification);
    336                 } catch (RemoteException ex) {
    337                 }
    338             }
    339         }
    340     }
    341 
    342     public void removeNotification(IBinder key) {
    343         synchronized (mNotifications) {
    344             final StatusBarNotification n = mNotifications.remove(key);
    345             if (n == null) {
    346                 throw new IllegalArgumentException("removeNotification key not found: " + key);
    347             }
    348             if (mBar != null) {
    349                 try {
    350                     mBar.removeNotification(key);
    351                 } catch (RemoteException ex) {
    352                 }
    353             }
    354         }
    355     }
    356 
    357     // ================================================================================
    358     // Can be called from any thread
    359     // ================================================================================
    360 
    361     // lock on mDisableRecords
    362     void manageDisableListLocked(int what, IBinder token, String pkg) {
    363         if (SPEW) {
    364             Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what) + " pkg=" + pkg);
    365         }
    366         // update the list
    367         synchronized (mDisableRecords) {
    368             final int N = mDisableRecords.size();
    369             DisableRecord tok = null;
    370             int i;
    371             for (i=0; i<N; i++) {
    372                 DisableRecord t = mDisableRecords.get(i);
    373                 if (t.token == token) {
    374                     tok = t;
    375                     break;
    376                 }
    377             }
    378             if (what == 0 || !token.isBinderAlive()) {
    379                 if (tok != null) {
    380                     mDisableRecords.remove(i);
    381                     tok.token.unlinkToDeath(tok, 0);
    382                 }
    383             } else {
    384                 if (tok == null) {
    385                     tok = new DisableRecord();
    386                     try {
    387                         token.linkToDeath(tok, 0);
    388                     }
    389                     catch (RemoteException ex) {
    390                         return; // give up
    391                     }
    392                     mDisableRecords.add(tok);
    393                 }
    394                 tok.what = what;
    395                 tok.token = token;
    396                 tok.pkg = pkg;
    397             }
    398         }
    399     }
    400 
    401     // lock on mDisableRecords
    402     int gatherDisableActionsLocked() {
    403         final int N = mDisableRecords.size();
    404         // gather the new net flags
    405         int net = 0;
    406         for (int i=0; i<N; i++) {
    407             net |= mDisableRecords.get(i).what;
    408         }
    409         return net;
    410     }
    411 
    412     // ================================================================================
    413     // Always called from UI thread
    414     // ================================================================================
    415 
    416     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    417         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
    418                 != PackageManager.PERMISSION_GRANTED) {
    419             pw.println("Permission Denial: can't dump StatusBar from from pid="
    420                     + Binder.getCallingPid()
    421                     + ", uid=" + Binder.getCallingUid());
    422             return;
    423         }
    424 
    425         synchronized (mIcons) {
    426             mIcons.dump(pw);
    427         }
    428 
    429         synchronized (mNotifications) {
    430             int i=0;
    431             pw.println("Notification list:");
    432             for (Map.Entry<IBinder,StatusBarNotification> e: mNotifications.entrySet()) {
    433                 pw.printf("  %2d: %s\n", i, e.getValue().toString());
    434                 i++;
    435             }
    436         }
    437 
    438         synchronized (mDisableRecords) {
    439             final int N = mDisableRecords.size();
    440             pw.println("  mDisableRecords.size=" + N
    441                     + " mDisabled=0x" + Integer.toHexString(mDisabled));
    442             for (int i=0; i<N; i++) {
    443                 DisableRecord tok = mDisableRecords.get(i);
    444                 pw.println("    [" + i + "] what=0x" + Integer.toHexString(tok.what)
    445                                 + " pkg=" + tok.pkg + " token=" + tok.token);
    446             }
    447         }
    448     }
    449 
    450     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    451         public void onReceive(Context context, Intent intent) {
    452             String action = intent.getAction();
    453             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
    454                     || Intent.ACTION_SCREEN_OFF.equals(action)) {
    455                 collapse();
    456             }
    457             /*
    458             else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
    459                 updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
    460                         intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
    461                         intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
    462                         intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
    463             }
    464             else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
    465                 updateResources();
    466             }
    467             */
    468         }
    469     };
    470 
    471 }
    472