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