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.ActivityThread;
20import android.app.StatusBarManager;
21import android.content.ComponentName;
22import android.content.Context;
23import android.graphics.Rect;
24import android.os.Binder;
25import android.os.Bundle;
26import android.os.Handler;
27import android.os.IBinder;
28import android.os.PowerManager;
29import android.os.Process;
30import android.os.RemoteException;
31import android.os.ResultReceiver;
32import android.os.ShellCallback;
33import android.os.UserHandle;
34import android.text.TextUtils;
35import android.util.ArrayMap;
36import android.util.Slog;
37
38import com.android.internal.R;
39import com.android.internal.statusbar.IStatusBar;
40import com.android.internal.statusbar.IStatusBarService;
41import com.android.internal.statusbar.NotificationVisibility;
42import com.android.internal.statusbar.StatusBarIcon;
43import com.android.internal.util.DumpUtils;
44import com.android.server.LocalServices;
45import com.android.server.notification.NotificationDelegate;
46import com.android.server.power.ShutdownThread;
47import com.android.server.statusbar.StatusBarManagerInternal.GlobalActionsListener;
48import com.android.server.wm.WindowManagerService;
49
50import java.io.FileDescriptor;
51import java.io.PrintWriter;
52import java.util.ArrayList;
53import java.util.List;
54
55
56/**
57 * A note on locking:  We rely on the fact that calls onto mBar are oneway or
58 * if they are local, that they just enqueue messages to not deadlock.
59 */
60public class StatusBarManagerService extends IStatusBarService.Stub {
61    private static final String TAG = "StatusBarManagerService";
62    private static final boolean SPEW = false;
63
64    private final Context mContext;
65
66    private final WindowManagerService mWindowManager;
67    private Handler mHandler = new Handler();
68    private NotificationDelegate mNotificationDelegate;
69    private volatile IStatusBar mBar;
70    private ArrayMap<String, StatusBarIcon> mIcons = new ArrayMap<>();
71
72    // for disabling the status bar
73    private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
74    private GlobalActionsListener mGlobalActionListener;
75    private IBinder mSysUiVisToken = new Binder();
76    private int mDisabled1 = 0;
77    private int mDisabled2 = 0;
78
79    private final Object mLock = new Object();
80    // encompasses lights-out mode and other flags defined on View
81    private int mSystemUiVisibility = 0;
82    private int mFullscreenStackSysUiVisibility;
83    private int mDockedStackSysUiVisibility;
84    private final Rect mFullscreenStackBounds = new Rect();
85    private final Rect mDockedStackBounds = new Rect();
86    private boolean mMenuVisible = false;
87    private int mImeWindowVis = 0;
88    private int mImeBackDisposition;
89    private boolean mShowImeSwitcher;
90    private IBinder mImeToken = null;
91    private int mCurrentUserId;
92
93    private class DisableRecord implements IBinder.DeathRecipient {
94        int userId;
95        String pkg;
96        int what1;
97        int what2;
98        IBinder token;
99
100        public void binderDied() {
101            Slog.i(TAG, "binder died for pkg=" + pkg);
102            disableForUser(0, token, pkg, userId);
103            disable2ForUser(0, token, pkg, userId);
104            token.unlinkToDeath(this, 0);
105        }
106    }
107
108    /**
109     * Construct the service, add the status bar view to the window manager
110     */
111    public StatusBarManagerService(Context context, WindowManagerService windowManager) {
112        mContext = context;
113        mWindowManager = windowManager;
114
115        LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
116    }
117
118    /**
119     * Private API used by NotificationManagerService.
120     */
121    private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() {
122        private boolean mNotificationLightOn;
123
124        @Override
125        public void setNotificationDelegate(NotificationDelegate delegate) {
126            mNotificationDelegate = delegate;
127        }
128
129        @Override
130        public void showScreenPinningRequest(int taskId) {
131            if (mBar != null) {
132                try {
133                    mBar.showScreenPinningRequest(taskId);
134                } catch (RemoteException e) {
135                }
136            }
137        }
138
139        @Override
140        public void showAssistDisclosure() {
141            if (mBar != null) {
142                try {
143                    mBar.showAssistDisclosure();
144                } catch (RemoteException e) {
145                }
146            }
147        }
148
149        @Override
150        public void startAssist(Bundle args) {
151            if (mBar != null) {
152                try {
153                    mBar.startAssist(args);
154                } catch (RemoteException e) {
155                }
156            }
157        }
158
159        @Override
160        public void onCameraLaunchGestureDetected(int source) {
161            if (mBar != null) {
162                try {
163                    mBar.onCameraLaunchGestureDetected(source);
164                } catch (RemoteException e) {
165                }
166            }
167        }
168
169        @Override
170        public void topAppWindowChanged(boolean menuVisible) {
171            StatusBarManagerService.this.topAppWindowChanged(menuVisible);
172        }
173
174        @Override
175        public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
176                int mask,
177                Rect fullscreenBounds, Rect dockedBounds, String cause) {
178            StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
179                    dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
180        }
181
182        @Override
183        public void toggleSplitScreen() {
184            enforceStatusBarService();
185            if (mBar != null) {
186                try {
187                    mBar.toggleSplitScreen();
188                } catch (RemoteException ex) {}
189            }
190        }
191
192        public void appTransitionFinished() {
193            enforceStatusBarService();
194            if (mBar != null) {
195                try {
196                    mBar.appTransitionFinished();
197                } catch (RemoteException ex) {}
198            }
199        }
200
201        @Override
202        public void toggleRecentApps() {
203            if (mBar != null) {
204                try {
205                    mBar.toggleRecentApps();
206                } catch (RemoteException ex) {}
207            }
208        }
209
210        @Override
211        public void setCurrentUser(int newUserId) {
212            if (SPEW) Slog.d(TAG, "Setting current user to user " + newUserId);
213            mCurrentUserId = newUserId;
214        }
215
216
217        @Override
218        public void preloadRecentApps() {
219            if (mBar != null) {
220                try {
221                    mBar.preloadRecentApps();
222                } catch (RemoteException ex) {}
223            }
224        }
225
226        @Override
227        public void cancelPreloadRecentApps() {
228            if (mBar != null) {
229                try {
230                    mBar.cancelPreloadRecentApps();
231                } catch (RemoteException ex) {}
232            }
233        }
234
235        @Override
236        public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
237            if (mBar != null) {
238                try {
239                    mBar.showRecentApps(triggeredFromAltTab, fromHome);
240                } catch (RemoteException ex) {}
241            }
242        }
243
244        @Override
245        public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
246            if (mBar != null) {
247                try {
248                    mBar.hideRecentApps(triggeredFromAltTab, triggeredFromHomeKey);
249                } catch (RemoteException ex) {}
250            }
251        }
252
253        @Override
254        public void dismissKeyboardShortcutsMenu() {
255            if (mBar != null) {
256                try {
257                    mBar.dismissKeyboardShortcutsMenu();
258                } catch (RemoteException ex) {}
259            }
260        }
261
262        @Override
263        public void toggleKeyboardShortcutsMenu(int deviceId) {
264            if (mBar != null) {
265                try {
266                    mBar.toggleKeyboardShortcutsMenu(deviceId);
267                } catch (RemoteException ex) {}
268            }
269        }
270
271        @Override
272        public void showPictureInPictureMenu() {
273            if (mBar != null) {
274                try {
275                    mBar.showPictureInPictureMenu();
276                } catch (RemoteException ex) {}
277            }
278        }
279
280        @Override
281        public void setWindowState(int window, int state) {
282            if (mBar != null) {
283                try {
284                    mBar.setWindowState(window, state);
285                } catch (RemoteException ex) {}
286            }
287        }
288
289        @Override
290        public void appTransitionPending() {
291            if (mBar != null) {
292                try {
293                    mBar.appTransitionPending();
294                } catch (RemoteException ex) {}
295            }
296        }
297
298        @Override
299        public void appTransitionCancelled() {
300            if (mBar != null) {
301                try {
302                    mBar.appTransitionCancelled();
303                } catch (RemoteException ex) {}
304            }
305        }
306
307        @Override
308        public void appTransitionStarting(long statusBarAnimationsStartTime,
309                long statusBarAnimationsDuration) {
310            if (mBar != null) {
311                try {
312                    mBar.appTransitionStarting(
313                            statusBarAnimationsStartTime, statusBarAnimationsDuration);
314                } catch (RemoteException ex) {}
315            }
316        }
317
318        @Override
319        public void setGlobalActionsListener(GlobalActionsListener listener) {
320            mGlobalActionListener = listener;
321            mGlobalActionListener.onStatusBarConnectedChanged(mBar != null);
322        }
323
324        @Override
325        public void showGlobalActions() {
326            if (mBar != null) {
327                try {
328                    mBar.showGlobalActionsMenu();
329                } catch (RemoteException ex) {}
330            }
331        }
332
333        @Override
334        public void setTopAppHidesStatusBar(boolean hidesStatusBar) {
335            if (mBar != null) {
336                try {
337                    mBar.setTopAppHidesStatusBar(hidesStatusBar);
338                } catch (RemoteException ex) {}
339            }
340        }
341
342        @Override
343        public boolean showShutdownUi(boolean isReboot, String reason) {
344            if (!mContext.getResources().getBoolean(R.bool.config_showSysuiShutdown)) {
345                return false;
346            }
347            if (mBar != null) {
348                try {
349                    mBar.showShutdownUi(isReboot, reason);
350                    return true;
351                } catch (RemoteException ex) {}
352            }
353            return false;
354        }
355    };
356
357    // ================================================================================
358    // From IStatusBarService
359    // ================================================================================
360    @Override
361    public void expandNotificationsPanel() {
362        enforceExpandStatusBar();
363
364        if (mBar != null) {
365            try {
366                mBar.animateExpandNotificationsPanel();
367            } catch (RemoteException ex) {
368            }
369        }
370    }
371
372    @Override
373    public void collapsePanels() {
374        enforceExpandStatusBar();
375
376        if (mBar != null) {
377            try {
378                mBar.animateCollapsePanels();
379            } catch (RemoteException ex) {
380            }
381        }
382    }
383
384    @Override
385    public void togglePanel() {
386        enforceExpandStatusBar();
387
388        if (mBar != null) {
389            try {
390                mBar.togglePanel();
391            } catch (RemoteException ex) {
392            }
393        }
394    }
395
396    @Override
397    public void expandSettingsPanel(String subPanel) {
398        enforceExpandStatusBar();
399
400        if (mBar != null) {
401            try {
402                mBar.animateExpandSettingsPanel(subPanel);
403            } catch (RemoteException ex) {
404            }
405        }
406    }
407
408    public void addTile(ComponentName component) {
409        enforceStatusBarOrShell();
410
411        if (mBar != null) {
412            try {
413                mBar.addQsTile(component);
414            } catch (RemoteException ex) {
415            }
416        }
417    }
418
419    public void remTile(ComponentName component) {
420        enforceStatusBarOrShell();
421
422        if (mBar != null) {
423            try {
424                mBar.remQsTile(component);
425            } catch (RemoteException ex) {
426            }
427        }
428    }
429
430    public void clickTile(ComponentName component) {
431        enforceStatusBarOrShell();
432
433        if (mBar != null) {
434            try {
435                mBar.clickQsTile(component);
436            } catch (RemoteException ex) {
437            }
438        }
439    }
440
441    @Override
442    public void handleSystemKey(int key) throws RemoteException {
443        enforceExpandStatusBar();
444
445        if (mBar != null) {
446            try {
447                mBar.handleSystemKey(key);
448            } catch (RemoteException ex) {
449            }
450        }
451    }
452
453    @Override
454    public void disable(int what, IBinder token, String pkg) {
455        disableForUser(what, token, pkg, mCurrentUserId);
456    }
457
458    @Override
459    public void disableForUser(int what, IBinder token, String pkg, int userId) {
460        enforceStatusBar();
461
462        synchronized (mLock) {
463            disableLocked(userId, what, token, pkg, 1);
464        }
465    }
466
467    /**
468     * Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags.
469     * To re-enable everything, pass {@link #DISABLE_NONE}.
470     *
471     * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
472     */
473    @Override
474    public void disable2(int what, IBinder token, String pkg) {
475        disable2ForUser(what, token, pkg, mCurrentUserId);
476    }
477
478    /**
479     * Disable additional status bar features for a given user. Pass the bitwise-or of the
480     * DISABLE2_* flags. To re-enable everything, pass {@link #DISABLE_NONE}.
481     *
482     * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
483     */
484    @Override
485    public void disable2ForUser(int what, IBinder token, String pkg, int userId) {
486        enforceStatusBar();
487
488        synchronized (mLock) {
489            disableLocked(userId, what, token, pkg, 2);
490        }
491    }
492
493    private void disableLocked(int userId, int what, IBinder token, String pkg, int whichFlag) {
494        // It's important that the the callback and the call to mBar get done
495        // in the same order when multiple threads are calling this function
496        // so they are paired correctly.  The messages on the handler will be
497        // handled in the order they were enqueued, but will be outside the lock.
498        manageDisableListLocked(userId, what, token, pkg, whichFlag);
499
500        // Ensure state for the current user is applied, even if passed a non-current user.
501        final int net1 = gatherDisableActionsLocked(mCurrentUserId, 1);
502        final int net2 = gatherDisableActionsLocked(mCurrentUserId, 2);
503        if (net1 != mDisabled1 || net2 != mDisabled2) {
504            mDisabled1 = net1;
505            mDisabled2 = net2;
506            mHandler.post(new Runnable() {
507                    public void run() {
508                        mNotificationDelegate.onSetDisabled(net1);
509                    }
510                });
511            if (mBar != null) {
512                try {
513                    mBar.disable(net1, net2);
514                } catch (RemoteException ex) {
515                }
516            }
517        }
518    }
519
520    @Override
521    public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
522            String contentDescription) {
523        enforceStatusBar();
524
525        synchronized (mIcons) {
526            StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.SYSTEM, iconId,
527                    iconLevel, 0, contentDescription);
528            //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon);
529            mIcons.put(slot, icon);
530
531            if (mBar != null) {
532                try {
533                    mBar.setIcon(slot, icon);
534                } catch (RemoteException ex) {
535                }
536            }
537        }
538    }
539
540    @Override
541    public void setIconVisibility(String slot, boolean visibility) {
542        enforceStatusBar();
543
544        synchronized (mIcons) {
545            StatusBarIcon icon = mIcons.get(slot);
546            if (icon == null) {
547                return;
548            }
549            if (icon.visible != visibility) {
550                icon.visible = visibility;
551
552                if (mBar != null) {
553                    try {
554                        mBar.setIcon(slot, icon);
555                    } catch (RemoteException ex) {
556                    }
557                }
558            }
559        }
560    }
561
562    @Override
563    public void removeIcon(String slot) {
564        enforceStatusBar();
565
566        synchronized (mIcons) {
567            mIcons.remove(slot);
568
569            if (mBar != null) {
570                try {
571                    mBar.removeIcon(slot);
572                } catch (RemoteException ex) {
573                }
574            }
575        }
576    }
577
578    /**
579     * Hide or show the on-screen Menu key. Only call this from the window manager, typically in
580     * response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set
581     * to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}.
582     */
583    private void topAppWindowChanged(final boolean menuVisible) {
584        enforceStatusBar();
585
586        if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key");
587
588        synchronized(mLock) {
589            mMenuVisible = menuVisible;
590            mHandler.post(new Runnable() {
591                public void run() {
592                    if (mBar != null) {
593                        try {
594                            mBar.topAppWindowChanged(menuVisible);
595                        } catch (RemoteException ex) {
596                        }
597                    }
598                }
599            });
600        }
601    }
602
603    @Override
604    public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition,
605            final boolean showImeSwitcher) {
606        enforceStatusBar();
607
608        if (SPEW) {
609            Slog.d(TAG, "swetImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition);
610        }
611
612        synchronized(mLock) {
613            // In case of IME change, we need to call up setImeWindowStatus() regardless of
614            // mImeWindowVis because mImeWindowVis may not have been set to false when the
615            // previous IME was destroyed.
616            mImeWindowVis = vis;
617            mImeBackDisposition = backDisposition;
618            mImeToken = token;
619            mShowImeSwitcher = showImeSwitcher;
620            mHandler.post(new Runnable() {
621                public void run() {
622                    if (mBar != null) {
623                        try {
624                            mBar.setImeWindowStatus(token, vis, backDisposition, showImeSwitcher);
625                        } catch (RemoteException ex) {
626                        }
627                    }
628                }
629            });
630        }
631    }
632
633    @Override
634    public void setSystemUiVisibility(int vis, int mask, String cause) {
635        setSystemUiVisibility(vis, 0, 0, mask, mFullscreenStackBounds, mDockedStackBounds, cause);
636    }
637
638    private void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
639            Rect fullscreenBounds, Rect dockedBounds, String cause) {
640        // also allows calls from window manager which is in this process.
641        enforceStatusBarService();
642
643        if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");
644
645        synchronized (mLock) {
646            updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask,
647                    fullscreenBounds, dockedBounds);
648            disableLocked(
649                    mCurrentUserId,
650                    vis & StatusBarManager.DISABLE_MASK,
651                    mSysUiVisToken,
652                    cause, 1);
653        }
654    }
655
656    private void updateUiVisibilityLocked(final int vis,
657            final int fullscreenStackVis, final int dockedStackVis, final int mask,
658            final Rect fullscreenBounds, final Rect dockedBounds) {
659        if (mSystemUiVisibility != vis
660                || mFullscreenStackSysUiVisibility != fullscreenStackVis
661                || mDockedStackSysUiVisibility != dockedStackVis
662                || !mFullscreenStackBounds.equals(fullscreenBounds)
663                || !mDockedStackBounds.equals(dockedBounds)) {
664            mSystemUiVisibility = vis;
665            mFullscreenStackSysUiVisibility = fullscreenStackVis;
666            mDockedStackSysUiVisibility = dockedStackVis;
667            mFullscreenStackBounds.set(fullscreenBounds);
668            mDockedStackBounds.set(dockedBounds);
669            mHandler.post(new Runnable() {
670                    public void run() {
671                        if (mBar != null) {
672                            try {
673                                mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis,
674                                        mask, fullscreenBounds, dockedBounds);
675                            } catch (RemoteException ex) {
676                            }
677                        }
678                    }
679                });
680        }
681    }
682
683    private void enforceStatusBarOrShell() {
684        if (Binder.getCallingUid() == Process.SHELL_UID) {
685            return;
686        }
687        enforceStatusBar();
688    }
689
690    private void enforceStatusBar() {
691        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
692                "StatusBarManagerService");
693    }
694
695    private void enforceExpandStatusBar() {
696        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.EXPAND_STATUS_BAR,
697                "StatusBarManagerService");
698    }
699
700    private void enforceStatusBarService() {
701        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
702                "StatusBarManagerService");
703    }
704
705    // ================================================================================
706    // Callbacks from the status bar service.
707    // ================================================================================
708    @Override
709    public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
710            List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
711            Rect fullscreenStackBounds, Rect dockedStackBounds) {
712        enforceStatusBarService();
713
714        Slog.i(TAG, "registerStatusBar bar=" + bar);
715        mBar = bar;
716        try {
717            mBar.asBinder().linkToDeath(new DeathRecipient() {
718                @Override
719                public void binderDied() {
720                    mBar = null;
721                    notifyBarAttachChanged();
722                }
723            }, 0);
724        } catch (RemoteException e) {
725        }
726        notifyBarAttachChanged();
727        synchronized (mIcons) {
728            for (String slot : mIcons.keySet()) {
729                iconSlots.add(slot);
730                iconList.add(mIcons.get(slot));
731            }
732        }
733        synchronized (mLock) {
734            switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1);
735            switches[1] = mSystemUiVisibility;
736            switches[2] = mMenuVisible ? 1 : 0;
737            switches[3] = mImeWindowVis;
738            switches[4] = mImeBackDisposition;
739            switches[5] = mShowImeSwitcher ? 1 : 0;
740            switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
741            switches[7] = mFullscreenStackSysUiVisibility;
742            switches[8] = mDockedStackSysUiVisibility;
743            binders.add(mImeToken);
744            fullscreenStackBounds.set(mFullscreenStackBounds);
745            dockedStackBounds.set(mDockedStackBounds);
746        }
747    }
748
749    private void notifyBarAttachChanged() {
750        mHandler.post(() -> {
751            if (mGlobalActionListener == null) return;
752            mGlobalActionListener.onStatusBarConnectedChanged(mBar != null);
753        });
754    }
755
756    /**
757     * @param clearNotificationEffects whether to consider notifications as "shown" and stop
758     *     LED, vibration, and ringing
759     */
760    @Override
761    public void onPanelRevealed(boolean clearNotificationEffects, int numItems) {
762        enforceStatusBarService();
763        long identity = Binder.clearCallingIdentity();
764        try {
765            mNotificationDelegate.onPanelRevealed(clearNotificationEffects, numItems);
766        } finally {
767            Binder.restoreCallingIdentity(identity);
768        }
769    }
770
771    @Override
772    public void clearNotificationEffects() throws RemoteException {
773        enforceStatusBarService();
774        long identity = Binder.clearCallingIdentity();
775        try {
776            mNotificationDelegate.clearEffects();
777        } finally {
778            Binder.restoreCallingIdentity(identity);
779        }
780    }
781
782    @Override
783    public void onPanelHidden() throws RemoteException {
784        enforceStatusBarService();
785        long identity = Binder.clearCallingIdentity();
786        try {
787            mNotificationDelegate.onPanelHidden();
788        } finally {
789            Binder.restoreCallingIdentity(identity);
790        }
791    }
792
793    /**
794     * Allows the status bar to shutdown the device.
795     */
796    @Override
797    public void shutdown() {
798        enforceStatusBarService();
799        long identity = Binder.clearCallingIdentity();
800        try {
801            // ShutdownThread displays UI, so give it a UI context.
802            mHandler.post(() ->
803                    ShutdownThread.shutdown(getUiContext(),
804                        PowerManager.SHUTDOWN_USER_REQUESTED, false));
805        } finally {
806            Binder.restoreCallingIdentity(identity);
807        }
808    }
809
810    /**
811     * Allows the status bar to reboot the device.
812     */
813    @Override
814    public void reboot(boolean safeMode) {
815        enforceStatusBarService();
816        long identity = Binder.clearCallingIdentity();
817        try {
818            mHandler.post(() -> {
819                // ShutdownThread displays UI, so give it a UI context.
820                if (safeMode) {
821                    ShutdownThread.rebootSafeMode(getUiContext(), true);
822                } else {
823                    ShutdownThread.reboot(getUiContext(),
824                            PowerManager.SHUTDOWN_USER_REQUESTED, false);
825                }
826            });
827        } finally {
828            Binder.restoreCallingIdentity(identity);
829        }
830    }
831
832    @Override
833    public void onGlobalActionsShown() {
834        enforceStatusBarService();
835        long identity = Binder.clearCallingIdentity();
836        try {
837            if (mGlobalActionListener == null) return;
838            mGlobalActionListener.onGlobalActionsShown();
839        } finally {
840            Binder.restoreCallingIdentity(identity);
841        }
842    }
843
844    @Override
845    public void onGlobalActionsHidden() {
846        enforceStatusBarService();
847        long identity = Binder.clearCallingIdentity();
848        try {
849            if (mGlobalActionListener == null) return;
850            mGlobalActionListener.onGlobalActionsDismissed();
851        } finally {
852            Binder.restoreCallingIdentity(identity);
853        }
854    }
855
856    @Override
857    public void onNotificationClick(String key) {
858        enforceStatusBarService();
859        final int callingUid = Binder.getCallingUid();
860        final int callingPid = Binder.getCallingPid();
861        long identity = Binder.clearCallingIdentity();
862        try {
863            mNotificationDelegate.onNotificationClick(callingUid, callingPid, key);
864        } finally {
865            Binder.restoreCallingIdentity(identity);
866        }
867    }
868
869    @Override
870    public void onNotificationActionClick(String key, int actionIndex) {
871        enforceStatusBarService();
872        final int callingUid = Binder.getCallingUid();
873        final int callingPid = Binder.getCallingPid();
874        long identity = Binder.clearCallingIdentity();
875        try {
876            mNotificationDelegate.onNotificationActionClick(callingUid, callingPid, key,
877                    actionIndex);
878        } finally {
879            Binder.restoreCallingIdentity(identity);
880        }
881    }
882
883    @Override
884    public void onNotificationError(String pkg, String tag, int id,
885            int uid, int initialPid, String message, int userId) {
886        enforceStatusBarService();
887        final int callingUid = Binder.getCallingUid();
888        final int callingPid = Binder.getCallingPid();
889        long identity = Binder.clearCallingIdentity();
890        try {
891            // WARNING: this will call back into us to do the remove.  Don't hold any locks.
892            mNotificationDelegate.onNotificationError(callingUid, callingPid,
893                    pkg, tag, id, uid, initialPid, message, userId);
894        } finally {
895            Binder.restoreCallingIdentity(identity);
896        }
897    }
898
899    @Override
900    public void onNotificationClear(String pkg, String tag, int id, int userId) {
901        enforceStatusBarService();
902        final int callingUid = Binder.getCallingUid();
903        final int callingPid = Binder.getCallingPid();
904        long identity = Binder.clearCallingIdentity();
905        try {
906            mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId);
907        } finally {
908            Binder.restoreCallingIdentity(identity);
909        }
910    }
911
912    @Override
913    public void onNotificationVisibilityChanged(
914            NotificationVisibility[] newlyVisibleKeys, NotificationVisibility[] noLongerVisibleKeys)
915            throws RemoteException {
916        enforceStatusBarService();
917        long identity = Binder.clearCallingIdentity();
918        try {
919            mNotificationDelegate.onNotificationVisibilityChanged(
920                    newlyVisibleKeys, noLongerVisibleKeys);
921        } finally {
922            Binder.restoreCallingIdentity(identity);
923        }
924    }
925
926    @Override
927    public void onNotificationExpansionChanged(String key, boolean userAction,
928            boolean expanded) throws RemoteException {
929        enforceStatusBarService();
930        long identity = Binder.clearCallingIdentity();
931        try {
932            mNotificationDelegate.onNotificationExpansionChanged(
933                    key, userAction, expanded);
934        } finally {
935            Binder.restoreCallingIdentity(identity);
936        }
937    }
938
939    @Override
940    public void onClearAllNotifications(int userId) {
941        enforceStatusBarService();
942        final int callingUid = Binder.getCallingUid();
943        final int callingPid = Binder.getCallingPid();
944        long identity = Binder.clearCallingIdentity();
945        try {
946            mNotificationDelegate.onClearAll(callingUid, callingPid, userId);
947        } finally {
948            Binder.restoreCallingIdentity(identity);
949        }
950    }
951
952    @Override
953    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
954            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
955        (new StatusBarShellCommand(this)).exec(
956                this, in, out, err, args, callback, resultReceiver);
957    }
958
959    public String[] getStatusBarIcons() {
960        return mContext.getResources().getStringArray(R.array.config_statusBarIcons);
961    }
962
963    // ================================================================================
964    // Can be called from any thread
965    // ================================================================================
966
967    // lock on mDisableRecords
968    void manageDisableListLocked(int userId, int what, IBinder token, String pkg, int which) {
969        if (SPEW) {
970            Slog.d(TAG, "manageDisableList userId=" + userId
971                    + " what=0x" + Integer.toHexString(what) + " pkg=" + pkg);
972        }
973        // update the list
974        final int N = mDisableRecords.size();
975        DisableRecord tok = null;
976        int i;
977        for (i=0; i<N; i++) {
978            DisableRecord t = mDisableRecords.get(i);
979            if (t.token == token && t.userId == userId) {
980                tok = t;
981                break;
982            }
983        }
984        if (what == 0 || !token.isBinderAlive()) {
985            if (tok != null) {
986                mDisableRecords.remove(i);
987                tok.token.unlinkToDeath(tok, 0);
988            }
989        } else {
990            if (tok == null) {
991                tok = new DisableRecord();
992                tok.userId = userId;
993                try {
994                    token.linkToDeath(tok, 0);
995                }
996                catch (RemoteException ex) {
997                    return; // give up
998                }
999                mDisableRecords.add(tok);
1000            }
1001            if (which == 1) {
1002                tok.what1 = what;
1003            } else {
1004                tok.what2 = what;
1005            }
1006            tok.token = token;
1007            tok.pkg = pkg;
1008        }
1009    }
1010
1011    // lock on mDisableRecords
1012    int gatherDisableActionsLocked(int userId, int which) {
1013        final int N = mDisableRecords.size();
1014        // gather the new net flags
1015        int net = 0;
1016        for (int i=0; i<N; i++) {
1017            final DisableRecord rec = mDisableRecords.get(i);
1018            if (rec.userId == userId) {
1019                net |= (which == 1) ? rec.what1 : rec.what2;
1020            }
1021        }
1022        return net;
1023    }
1024
1025    // ================================================================================
1026    // Always called from UI thread
1027    // ================================================================================
1028
1029    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1030        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
1031
1032        synchronized (mLock) {
1033            pw.println("  mDisabled1=0x" + Integer.toHexString(mDisabled1));
1034            pw.println("  mDisabled2=0x" + Integer.toHexString(mDisabled2));
1035            final int N = mDisableRecords.size();
1036            pw.println("  mDisableRecords.size=" + N);
1037            for (int i=0; i<N; i++) {
1038                DisableRecord tok = mDisableRecords.get(i);
1039                pw.println("    [" + i + "] userId=" + tok.userId
1040                                + " what1=0x" + Integer.toHexString(tok.what1)
1041                                + " what2=0x" + Integer.toHexString(tok.what2)
1042                                + " pkg=" + tok.pkg
1043                                + " token=" + tok.token);
1044            }
1045            pw.println("  mCurrentUserId=" + mCurrentUserId);
1046            pw.println("  mIcons=");
1047            for (String slot : mIcons.keySet()) {
1048                pw.println("    ");
1049                pw.print(slot);
1050                pw.print(" -> ");
1051                final StatusBarIcon icon = mIcons.get(slot);
1052                pw.print(icon);
1053                if (!TextUtils.isEmpty(icon.contentDescription)) {
1054                    pw.print(" \"");
1055                    pw.print(icon.contentDescription);
1056                    pw.print("\"");
1057                }
1058                pw.println();
1059            }
1060        }
1061    }
1062
1063    private static final Context getUiContext() {
1064        return ActivityThread.currentActivityThread().getSystemUiContext();
1065    }
1066}
1067