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