• 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.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 
binderDied()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 {
onSetDisabled(int status)92         void onSetDisabled(int status);
onClearAll()93         void onClearAll();
onNotificationClick(String pkg, String tag, int id)94         void onNotificationClick(String pkg, String tag, int id);
onNotificationClear(String pkg, String tag, int id)95         void onNotificationClear(String pkg, String tag, int id);
onPanelRevealed()96         void onPanelRevealed();
onNotificationError(String pkg, String tag, int id, int uid, int initialPid, String message)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      */
StatusBarManagerService(Context context, WindowManagerService windowManager)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 
setNotificationCallbacks(NotificationCallbacks listener)113     public void setNotificationCallbacks(NotificationCallbacks listener) {
114         mNotificationCallbacks = listener;
115     }
116 
117     // ================================================================================
118     // From IStatusBarService
119     // ================================================================================
expand()120     public void expand() {
121         enforceExpandStatusBar();
122 
123         if (mBar != null) {
124             try {
125                 mBar.animateExpand();
126             } catch (RemoteException ex) {
127             }
128         }
129     }
130 
collapse()131     public void collapse() {
132         enforceExpandStatusBar();
133 
134         if (mBar != null) {
135             try {
136                 mBar.animateCollapse();
137             } catch (RemoteException ex) {
138             }
139         }
140     }
141 
disable(int what, IBinder token, String pkg)142     public void disable(int what, IBinder token, String pkg) {
143         enforceStatusBar();
144 
145         synchronized (mLock) {
146             disableLocked(what, token, pkg);
147         }
148     }
149 
disableLocked(int what, IBinder token, String pkg)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 
setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription)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 
setIconVisibility(String slot, boolean visible)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 
removeIcon(String slot)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      */
topAppWindowChanged(final boolean menuVisible)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 
setImeWindowStatus(final IBinder token, final int vis, final int backDisposition)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 
setSystemUiVisibility(int vis, int mask)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 
updateUiVisibilityLocked(final int vis, final int mask)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 
setHardKeyboardEnabled(final boolean enabled)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
onHardKeyboardStatusChange(final boolean available, final boolean enabled)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
toggleRecentApps()347     public void toggleRecentApps() {
348         if (mBar != null) {
349             try {
350                 mBar.toggleRecentApps();
351             } catch (RemoteException ex) {}
352         }
353     }
354 
355     @Override
preloadRecentApps()356     public void preloadRecentApps() {
357         if (mBar != null) {
358             try {
359                 mBar.preloadRecentApps();
360             } catch (RemoteException ex) {}
361         }
362     }
363 
364     @Override
cancelPreloadRecentApps()365     public void cancelPreloadRecentApps() {
366         if (mBar != null) {
367             try {
368                 mBar.cancelPreloadRecentApps();
369             } catch (RemoteException ex) {}
370         }
371     }
372 
enforceStatusBar()373     private void enforceStatusBar() {
374         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
375                 "StatusBarManagerService");
376     }
377 
enforceExpandStatusBar()378     private void enforceExpandStatusBar() {
379         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR,
380                 "StatusBarManagerService");
381     }
382 
enforceStatusBarService()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     // ================================================================================
registerStatusBar(IStatusBar bar, StatusBarIconList iconList, List<IBinder> notificationKeys, List<StatusBarNotification> notifications, int switches[], List<IBinder> binders)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      */
onPanelRevealed()423     public void onPanelRevealed() {
424         enforceStatusBarService();
425 
426         // tell the notification manager to turn off the lights.
427         mNotificationCallbacks.onPanelRevealed();
428     }
429 
onNotificationClick(String pkg, String tag, int id)430     public void onNotificationClick(String pkg, String tag, int id) {
431         enforceStatusBarService();
432 
433         mNotificationCallbacks.onNotificationClick(pkg, tag, id);
434     }
435 
onNotificationError(String pkg, String tag, int id, int uid, int initialPid, String message)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 
onNotificationClear(String pkg, String tag, int id)444     public void onNotificationClear(String pkg, String tag, int id) {
445         enforceStatusBarService();
446 
447         mNotificationCallbacks.onNotificationClear(pkg, tag, id);
448     }
449 
onClearAllNotifications()450     public void onClearAllNotifications() {
451         enforceStatusBarService();
452 
453         mNotificationCallbacks.onClearAll();
454     }
455 
456     // ================================================================================
457     // Callbacks for NotificationManagerService.
458     // ================================================================================
addNotification(StatusBarNotification notification)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 
updateNotification(IBinder key, StatusBarNotification notification)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 
removeNotification(IBinder key)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
manageDisableListLocked(int what, IBinder token, String pkg)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
gatherDisableActionsLocked()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 
dump(FileDescriptor fd, PrintWriter pw, String[] args)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