StatusBarManagerService.java revision 1f32c65697c22f423c2888cf4c53da1c95d602c1
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        @Override
146        public void showScreenPinningRequest() {
147            if (mBar != null) {
148                try {
149                    mBar.showScreenPinningRequest();
150                } catch (RemoteException e) {
151                }
152            }
153        }
154    };
155
156    // ================================================================================
157    // From IStatusBarService
158    // ================================================================================
159    @Override
160    public void expandNotificationsPanel() {
161        enforceExpandStatusBar();
162
163        if (mBar != null) {
164            try {
165                mBar.animateExpandNotificationsPanel();
166            } catch (RemoteException ex) {
167            }
168        }
169    }
170
171    @Override
172    public void collapsePanels() {
173        enforceExpandStatusBar();
174
175        if (mBar != null) {
176            try {
177                mBar.animateCollapsePanels();
178            } catch (RemoteException ex) {
179            }
180        }
181    }
182
183    @Override
184    public void expandSettingsPanel() {
185        enforceExpandStatusBar();
186
187        if (mBar != null) {
188            try {
189                mBar.animateExpandSettingsPanel();
190            } catch (RemoteException ex) {
191            }
192        }
193    }
194
195    @Override
196    public void disable(int what, IBinder token, String pkg) {
197        disableInternal(mCurrentUserId, what, token, pkg);
198    }
199
200    private void disableInternal(int userId, int what, IBinder token, String pkg) {
201        enforceStatusBar();
202
203        synchronized (mLock) {
204            disableLocked(userId, what, token, pkg);
205        }
206    }
207
208    private void disableLocked(int userId, int what, IBinder token, String pkg) {
209        // It's important that the the callback and the call to mBar get done
210        // in the same order when multiple threads are calling this function
211        // so they are paired correctly.  The messages on the handler will be
212        // handled in the order they were enqueued, but will be outside the lock.
213        manageDisableListLocked(userId, what, token, pkg);
214
215        // Ensure state for the current user is applied, even if passed a non-current user.
216        final int net = gatherDisableActionsLocked(mCurrentUserId);
217        if (net != mDisabled) {
218            mDisabled = net;
219            mHandler.post(new Runnable() {
220                    public void run() {
221                        mNotificationDelegate.onSetDisabled(net);
222                    }
223                });
224            if (mBar != null) {
225                try {
226                    mBar.disable(net);
227                } catch (RemoteException ex) {
228                }
229            }
230        }
231    }
232
233    @Override
234    public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
235            String contentDescription) {
236        enforceStatusBar();
237
238        synchronized (mIcons) {
239            int index = mIcons.getSlotIndex(slot);
240            if (index < 0) {
241                throw new SecurityException("invalid status bar icon slot: " + slot);
242            }
243
244            StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.OWNER, iconId,
245                    iconLevel, 0,
246                    contentDescription);
247            //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
248            mIcons.setIcon(index, icon);
249
250            if (mBar != null) {
251                try {
252                    mBar.setIcon(index, icon);
253                } catch (RemoteException ex) {
254                }
255            }
256        }
257    }
258
259    @Override
260    public void setIconVisibility(String slot, boolean visible) {
261        enforceStatusBar();
262
263        synchronized (mIcons) {
264            int index = mIcons.getSlotIndex(slot);
265            if (index < 0) {
266                throw new SecurityException("invalid status bar icon slot: " + slot);
267            }
268
269            StatusBarIcon icon = mIcons.getIcon(index);
270            if (icon == null) {
271                return;
272            }
273
274            if (icon.visible != visible) {
275                icon.visible = visible;
276
277                if (mBar != null) {
278                    try {
279                        mBar.setIcon(index, icon);
280                    } catch (RemoteException ex) {
281                    }
282                }
283            }
284        }
285    }
286
287    @Override
288    public void removeIcon(String slot) {
289        enforceStatusBar();
290
291        synchronized (mIcons) {
292            int index = mIcons.getSlotIndex(slot);
293            if (index < 0) {
294                throw new SecurityException("invalid status bar icon slot: " + slot);
295            }
296
297            mIcons.removeIcon(index);
298
299            if (mBar != null) {
300                try {
301                    mBar.removeIcon(index);
302                } catch (RemoteException ex) {
303                }
304            }
305        }
306    }
307
308    /**
309     * Hide or show the on-screen Menu key. Only call this from the window manager, typically in
310     * response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
311     * to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
312     */
313    @Override
314    public void topAppWindowChanged(final boolean menuVisible) {
315        enforceStatusBar();
316
317        if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key");
318
319        synchronized(mLock) {
320            mMenuVisible = menuVisible;
321            mHandler.post(new Runnable() {
322                    public void run() {
323                        if (mBar != null) {
324                            try {
325                                mBar.topAppWindowChanged(menuVisible);
326                            } catch (RemoteException ex) {
327                            }
328                        }
329                    }
330                });
331        }
332    }
333
334    @Override
335    public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition,
336            final boolean showImeSwitcher) {
337        enforceStatusBar();
338
339        if (SPEW) {
340            Slog.d(TAG, "swetImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition);
341        }
342
343        synchronized(mLock) {
344            // In case of IME change, we need to call up setImeWindowStatus() regardless of
345            // mImeWindowVis because mImeWindowVis may not have been set to false when the
346            // previous IME was destroyed.
347            mImeWindowVis = vis;
348            mImeBackDisposition = backDisposition;
349            mImeToken = token;
350            mShowImeSwitcher = showImeSwitcher;
351            mHandler.post(new Runnable() {
352                public void run() {
353                    if (mBar != null) {
354                        try {
355                            mBar.setImeWindowStatus(token, vis, backDisposition, showImeSwitcher);
356                        } catch (RemoteException ex) {
357                        }
358                    }
359                }
360            });
361        }
362    }
363
364    @Override
365    public void setSystemUiVisibility(int vis, int mask, String cause) {
366        // also allows calls from window manager which is in this process.
367        enforceStatusBarService();
368
369        if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
370
371        synchronized (mLock) {
372            updateUiVisibilityLocked(vis, mask);
373            disableLocked(
374                    mCurrentUserId,
375                    vis & StatusBarManager.DISABLE_MASK,
376                    mSysUiVisToken,
377                    cause);
378        }
379    }
380
381    private void updateUiVisibilityLocked(final int vis, final int mask) {
382        if (mSystemUiVisibility != vis) {
383            mSystemUiVisibility = vis;
384            mHandler.post(new Runnable() {
385                    public void run() {
386                        if (mBar != null) {
387                            try {
388                                mBar.setSystemUiVisibility(vis, mask);
389                            } catch (RemoteException ex) {
390                            }
391                        }
392                    }
393                });
394        }
395    }
396
397    @Override
398    public void toggleRecentApps() {
399        if (mBar != null) {
400            try {
401                mBar.toggleRecentApps();
402            } catch (RemoteException ex) {}
403        }
404    }
405
406    @Override
407    public void preloadRecentApps() {
408        if (mBar != null) {
409            try {
410                mBar.preloadRecentApps();
411            } catch (RemoteException ex) {}
412        }
413    }
414
415    @Override
416    public void cancelPreloadRecentApps() {
417        if (mBar != null) {
418            try {
419                mBar.cancelPreloadRecentApps();
420            } catch (RemoteException ex) {}
421        }
422    }
423
424    @Override
425    public void showRecentApps(boolean triggeredFromAltTab) {
426        if (mBar != null) {
427            try {
428                mBar.showRecentApps(triggeredFromAltTab);
429            } catch (RemoteException ex) {}
430        }
431    }
432
433    @Override
434    public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
435        if (mBar != null) {
436            try {
437                mBar.hideRecentApps(triggeredFromAltTab, triggeredFromHomeKey);
438            } catch (RemoteException ex) {}
439        }
440    }
441
442    @Override
443    public void setCurrentUser(int newUserId) {
444        if (SPEW) Slog.d(TAG, "Setting current user to user " + newUserId);
445        mCurrentUserId = newUserId;
446    }
447
448    @Override
449    public void setWindowState(int window, int state) {
450        if (mBar != null) {
451            try {
452                mBar.setWindowState(window, state);
453            } catch (RemoteException ex) {}
454        }
455    }
456
457    private void enforceStatusBar() {
458        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
459                "StatusBarManagerService");
460    }
461
462    private void enforceExpandStatusBar() {
463        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR,
464                "StatusBarManagerService");
465    }
466
467    private void enforceStatusBarService() {
468        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
469                "StatusBarManagerService");
470    }
471
472    // ================================================================================
473    // Callbacks from the status bar service.
474    // ================================================================================
475    @Override
476    public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
477            int switches[], List<IBinder> binders) {
478        enforceStatusBarService();
479
480        Slog.i(TAG, "registerStatusBar bar=" + bar);
481        mBar = bar;
482        synchronized (mIcons) {
483            iconList.copyFrom(mIcons);
484        }
485        synchronized (mLock) {
486            switches[0] = gatherDisableActionsLocked(mCurrentUserId);
487            switches[1] = mSystemUiVisibility;
488            switches[2] = mMenuVisible ? 1 : 0;
489            switches[3] = mImeWindowVis;
490            switches[4] = mImeBackDisposition;
491            switches[5] = mShowImeSwitcher ? 1 : 0;
492            binders.add(mImeToken);
493        }
494    }
495
496    /**
497     * @param clearNotificationEffects whether to consider notifications as "shown" and stop
498     *     LED, vibration, and ringing
499     */
500    @Override
501    public void onPanelRevealed(boolean clearNotificationEffects) {
502        enforceStatusBarService();
503        long identity = Binder.clearCallingIdentity();
504        try {
505            mNotificationDelegate.onPanelRevealed(clearNotificationEffects);
506        } finally {
507            Binder.restoreCallingIdentity(identity);
508        }
509    }
510
511    @Override
512    public void clearNotificationEffects() throws RemoteException {
513        enforceStatusBarService();
514        long identity = Binder.clearCallingIdentity();
515        try {
516            mNotificationDelegate.clearEffects();
517        } finally {
518            Binder.restoreCallingIdentity(identity);
519        }
520    }
521
522    @Override
523    public void onPanelHidden() throws RemoteException {
524        enforceStatusBarService();
525        long identity = Binder.clearCallingIdentity();
526        try {
527            mNotificationDelegate.onPanelHidden();
528        } finally {
529            Binder.restoreCallingIdentity(identity);
530        }
531    }
532
533    @Override
534    public void onNotificationClick(String key) {
535        enforceStatusBarService();
536        final int callingUid = Binder.getCallingUid();
537        final int callingPid = Binder.getCallingPid();
538        long identity = Binder.clearCallingIdentity();
539        try {
540            mNotificationDelegate.onNotificationClick(callingUid, callingPid, key);
541        } finally {
542            Binder.restoreCallingIdentity(identity);
543        }
544    }
545
546    @Override
547    public void onNotificationActionClick(String key, int actionIndex) {
548        enforceStatusBarService();
549        final int callingUid = Binder.getCallingUid();
550        final int callingPid = Binder.getCallingPid();
551        long identity = Binder.clearCallingIdentity();
552        try {
553            mNotificationDelegate.onNotificationActionClick(callingUid, callingPid, key,
554                    actionIndex);
555        } finally {
556            Binder.restoreCallingIdentity(identity);
557        }
558    }
559
560    @Override
561    public void onNotificationError(String pkg, String tag, int id,
562            int uid, int initialPid, String message, int userId) {
563        enforceStatusBarService();
564        final int callingUid = Binder.getCallingUid();
565        final int callingPid = Binder.getCallingPid();
566        long identity = Binder.clearCallingIdentity();
567        try {
568            // WARNING: this will call back into us to do the remove.  Don't hold any locks.
569            mNotificationDelegate.onNotificationError(callingUid, callingPid,
570                    pkg, tag, id, uid, initialPid, message, userId);
571        } finally {
572            Binder.restoreCallingIdentity(identity);
573        }
574    }
575
576    @Override
577    public void onNotificationClear(String pkg, String tag, int id, int userId) {
578        enforceStatusBarService();
579        final int callingUid = Binder.getCallingUid();
580        final int callingPid = Binder.getCallingPid();
581        long identity = Binder.clearCallingIdentity();
582        try {
583            mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId);
584        } finally {
585            Binder.restoreCallingIdentity(identity);
586        }
587    }
588
589    @Override
590    public void onNotificationVisibilityChanged(
591            String[] newlyVisibleKeys, String[] noLongerVisibleKeys) throws RemoteException {
592        enforceStatusBarService();
593        long identity = Binder.clearCallingIdentity();
594        try {
595            mNotificationDelegate.onNotificationVisibilityChanged(
596                    newlyVisibleKeys, noLongerVisibleKeys);
597        } finally {
598            Binder.restoreCallingIdentity(identity);
599        }
600    }
601
602    @Override
603    public void onNotificationExpansionChanged(String key, boolean userAction,
604            boolean expanded) throws RemoteException {
605        enforceStatusBarService();
606        long identity = Binder.clearCallingIdentity();
607        try {
608            mNotificationDelegate.onNotificationExpansionChanged(
609                    key, userAction, expanded);
610        } finally {
611            Binder.restoreCallingIdentity(identity);
612        }
613    }
614
615    @Override
616    public void onClearAllNotifications(int userId) {
617        enforceStatusBarService();
618        final int callingUid = Binder.getCallingUid();
619        final int callingPid = Binder.getCallingPid();
620        long identity = Binder.clearCallingIdentity();
621        try {
622            mNotificationDelegate.onClearAll(callingUid, callingPid, userId);
623        } finally {
624            Binder.restoreCallingIdentity(identity);
625        }
626    }
627
628
629    // ================================================================================
630    // Can be called from any thread
631    // ================================================================================
632
633    // lock on mDisableRecords
634    void manageDisableListLocked(int userId, int what, IBinder token, String pkg) {
635        if (SPEW) {
636            Slog.d(TAG, "manageDisableList userId=" + userId
637                    + " what=0x" + Integer.toHexString(what) + " pkg=" + pkg);
638        }
639        // update the list
640        final int N = mDisableRecords.size();
641        DisableRecord tok = null;
642        int i;
643        for (i=0; i<N; i++) {
644            DisableRecord t = mDisableRecords.get(i);
645            if (t.token == token && t.userId == userId) {
646                tok = t;
647                break;
648            }
649        }
650        if (what == 0 || !token.isBinderAlive()) {
651            if (tok != null) {
652                mDisableRecords.remove(i);
653                tok.token.unlinkToDeath(tok, 0);
654            }
655        } else {
656            if (tok == null) {
657                tok = new DisableRecord();
658                tok.userId = userId;
659                try {
660                    token.linkToDeath(tok, 0);
661                }
662                catch (RemoteException ex) {
663                    return; // give up
664                }
665                mDisableRecords.add(tok);
666            }
667            tok.what = what;
668            tok.token = token;
669            tok.pkg = pkg;
670        }
671    }
672
673    // lock on mDisableRecords
674    int gatherDisableActionsLocked(int userId) {
675        final int N = mDisableRecords.size();
676        // gather the new net flags
677        int net = 0;
678        for (int i=0; i<N; i++) {
679            final DisableRecord rec = mDisableRecords.get(i);
680            if (rec.userId == userId) {
681                net |= rec.what;
682            }
683        }
684        return net;
685    }
686
687    // ================================================================================
688    // Always called from UI thread
689    // ================================================================================
690
691    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
692        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
693                != PackageManager.PERMISSION_GRANTED) {
694            pw.println("Permission Denial: can't dump StatusBar from from pid="
695                    + Binder.getCallingPid()
696                    + ", uid=" + Binder.getCallingUid());
697            return;
698        }
699
700        synchronized (mIcons) {
701            mIcons.dump(pw);
702        }
703
704        synchronized (mLock) {
705            pw.println("  mDisabled=0x" + Integer.toHexString(mDisabled));
706            final int N = mDisableRecords.size();
707            pw.println("  mDisableRecords.size=" + N);
708            for (int i=0; i<N; i++) {
709                DisableRecord tok = mDisableRecords.get(i);
710                pw.println("    [" + i + "] userId=" + tok.userId
711                                + " what=0x" + Integer.toHexString(tok.what)
712                                + " pkg=" + tok.pkg
713                                + " token=" + tok.token);
714            }
715        }
716    }
717}
718