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