Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2008 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.systemui.statusbar.phone;
     18 
     19 import android.app.ActivityManager;
     20 import android.app.ActivityManager.StackId;
     21 import android.app.ActivityManager.StackInfo;
     22 import android.app.AlarmManager;
     23 import android.app.AlarmManager.AlarmClockInfo;
     24 import android.app.AppGlobals;
     25 import android.app.Notification;
     26 import android.app.Notification.Action;
     27 import android.app.NotificationManager;
     28 import android.app.PendingIntent;
     29 import android.app.SynchronousUserSwitchObserver;
     30 import android.content.BroadcastReceiver;
     31 import android.content.ComponentName;
     32 import android.content.Context;
     33 import android.content.Intent;
     34 import android.content.IntentFilter;
     35 import android.content.pm.ApplicationInfo;
     36 import android.content.pm.IPackageManager;
     37 import android.content.pm.PackageManager;
     38 import android.content.pm.UserInfo;
     39 import android.graphics.drawable.Icon;
     40 import android.media.AudioManager;
     41 import android.net.Uri;
     42 import android.os.Bundle;
     43 import android.os.Handler;
     44 import android.os.RemoteException;
     45 import android.os.UserHandle;
     46 import android.os.UserManager;
     47 import android.provider.Settings;
     48 import android.provider.Settings.Global;
     49 import android.service.notification.StatusBarNotification;
     50 import android.telecom.TelecomManager;
     51 import android.util.ArraySet;
     52 import android.util.Log;
     53 import android.util.Pair;
     54 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
     55 import com.android.internal.telephony.IccCardConstants;
     56 import com.android.internal.telephony.TelephonyIntents;
     57 import com.android.systemui.Dependency;
     58 import com.android.systemui.DockedStackExistsListener;
     59 import com.android.systemui.R;
     60 import com.android.systemui.SysUiServiceProvider;
     61 import com.android.systemui.UiOffloadThread;
     62 import com.android.systemui.qs.tiles.DndTile;
     63 import com.android.systemui.qs.tiles.RotationLockTile;
     64 import com.android.systemui.recents.misc.SystemServicesProxy;
     65 import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
     66 import com.android.systemui.statusbar.CommandQueue;
     67 import com.android.systemui.statusbar.CommandQueue.Callbacks;
     68 import com.android.systemui.statusbar.policy.BluetoothController;
     69 import com.android.systemui.statusbar.policy.BluetoothController.Callback;
     70 import com.android.systemui.statusbar.policy.CastController;
     71 import com.android.systemui.statusbar.policy.CastController.CastDevice;
     72 import com.android.systemui.statusbar.policy.DataSaverController;
     73 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
     74 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
     75 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
     76 import com.android.systemui.statusbar.policy.HotspotController;
     77 import com.android.systemui.statusbar.policy.KeyguardMonitor;
     78 import com.android.systemui.statusbar.policy.LocationController;
     79 import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
     80 import com.android.systemui.statusbar.policy.NextAlarmController;
     81 import com.android.systemui.statusbar.policy.RotationLockController;
     82 import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
     83 import com.android.systemui.statusbar.policy.UserInfoController;
     84 import com.android.systemui.statusbar.policy.ZenModeController;
     85 import com.android.systemui.util.NotificationChannels;
     86 
     87 import java.util.List;
     88 
     89 /**
     90  * This class contains all of the policy about which icons are installed in the status
     91  * bar at boot time.  It goes through the normal API for icons, even though it probably
     92  * strictly doesn't need to.
     93  */
     94 public class PhoneStatusBarPolicy implements Callback, Callbacks,
     95         RotationLockControllerCallback, Listener, LocationChangeCallback,
     96         ZenModeController.Callback, DeviceProvisionedListener, KeyguardMonitor.Callback {
     97     private static final String TAG = "PhoneStatusBarPolicy";
     98     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     99 
    100     public static final int LOCATION_STATUS_ICON_ID = R.drawable.stat_sys_location;
    101     public static final int NUM_TASKS_FOR_INSTANT_APP_INFO = 5;
    102 
    103     private final String mSlotCast;
    104     private final String mSlotHotspot;
    105     private final String mSlotBluetooth;
    106     private final String mSlotTty;
    107     private final String mSlotZen;
    108     private final String mSlotVolume;
    109     private final String mSlotAlarmClock;
    110     private final String mSlotManagedProfile;
    111     private final String mSlotRotate;
    112     private final String mSlotHeadset;
    113     private final String mSlotDataSaver;
    114     private final String mSlotLocation;
    115 
    116     private final Context mContext;
    117     private final Handler mHandler = new Handler();
    118     private final CastController mCast;
    119     private final HotspotController mHotspot;
    120     private final NextAlarmController mNextAlarm;
    121     private final AlarmManager mAlarmManager;
    122     private final UserInfoController mUserInfoController;
    123     private final UserManager mUserManager;
    124     private final StatusBarIconController mIconController;
    125     private final RotationLockController mRotationLockController;
    126     private final DataSaverController mDataSaver;
    127     private final ZenModeController mZenController;
    128     private final DeviceProvisionedController mProvisionedController;
    129     private final KeyguardMonitor mKeyguardMonitor;
    130     private final LocationController mLocationController;
    131     private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>();
    132     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
    133 
    134     // Assume it's all good unless we hear otherwise.  We don't always seem
    135     // to get broadcasts that it *is* there.
    136     IccCardConstants.State mSimState = IccCardConstants.State.READY;
    137 
    138     private boolean mZenVisible;
    139     private boolean mVolumeVisible;
    140     private boolean mCurrentUserSetup;
    141     private boolean mDockedStackExists;
    142 
    143     private boolean mManagedProfileIconVisible = false;
    144     private boolean mManagedProfileInQuietMode = false;
    145 
    146     private BluetoothController mBluetooth;
    147 
    148     public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController) {
    149         mContext = context;
    150         mIconController = iconController;
    151         mCast = Dependency.get(CastController.class);
    152         mHotspot = Dependency.get(HotspotController.class);
    153         mBluetooth = Dependency.get(BluetoothController.class);
    154         mNextAlarm = Dependency.get(NextAlarmController.class);
    155         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    156         mUserInfoController = Dependency.get(UserInfoController.class);
    157         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
    158         mRotationLockController = Dependency.get(RotationLockController.class);
    159         mDataSaver = Dependency.get(DataSaverController.class);
    160         mZenController = Dependency.get(ZenModeController.class);
    161         mProvisionedController = Dependency.get(DeviceProvisionedController.class);
    162         mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
    163         mLocationController = Dependency.get(LocationController.class);
    164 
    165         mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
    166         mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
    167         mSlotBluetooth = context.getString(com.android.internal.R.string.status_bar_bluetooth);
    168         mSlotTty = context.getString(com.android.internal.R.string.status_bar_tty);
    169         mSlotZen = context.getString(com.android.internal.R.string.status_bar_zen);
    170         mSlotVolume = context.getString(com.android.internal.R.string.status_bar_volume);
    171         mSlotAlarmClock = context.getString(com.android.internal.R.string.status_bar_alarm_clock);
    172         mSlotManagedProfile = context.getString(
    173                 com.android.internal.R.string.status_bar_managed_profile);
    174         mSlotRotate = context.getString(com.android.internal.R.string.status_bar_rotate);
    175         mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
    176         mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
    177         mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
    178 
    179         // listen for broadcasts
    180         IntentFilter filter = new IntentFilter();
    181         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
    182         filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
    183         filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
    184         filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
    185         filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
    186         filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
    187         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
    188         filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
    189         mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
    190 
    191         // listen for user / profile change.
    192         try {
    193             ActivityManager.getService().registerUserSwitchObserver(mUserSwitchListener, TAG);
    194         } catch (RemoteException e) {
    195             // Ignore
    196         }
    197 
    198         // TTY status
    199         mIconController.setIcon(mSlotTty, R.drawable.stat_sys_tty_mode, null);
    200         mIconController.setIconVisibility(mSlotTty, false);
    201 
    202         // bluetooth status
    203         updateBluetooth();
    204 
    205         // Alarm clock
    206         mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);
    207         mIconController.setIconVisibility(mSlotAlarmClock, false);
    208 
    209         // zen
    210         mIconController.setIcon(mSlotZen, R.drawable.stat_sys_zen_important, null);
    211         mIconController.setIconVisibility(mSlotZen, false);
    212 
    213         // volume
    214         mIconController.setIcon(mSlotVolume, R.drawable.stat_sys_ringer_vibrate, null);
    215         mIconController.setIconVisibility(mSlotVolume, false);
    216         updateVolumeZen();
    217 
    218         // cast
    219         mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, null);
    220         mIconController.setIconVisibility(mSlotCast, false);
    221 
    222         // hotspot
    223         mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot,
    224                 mContext.getString(R.string.accessibility_status_bar_hotspot));
    225         mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled());
    226 
    227         // managed profile
    228         mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status,
    229                 mContext.getString(R.string.accessibility_managed_profile));
    230         mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible);
    231 
    232         // data saver
    233         mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver,
    234                 context.getString(R.string.accessibility_data_saver_on));
    235         mIconController.setIconVisibility(mSlotDataSaver, false);
    236 
    237         mRotationLockController.addCallback(this);
    238         mBluetooth.addCallback(this);
    239         mProvisionedController.addCallback(this);
    240         mZenController.addCallback(this);
    241         mCast.addCallback(mCastCallback);
    242         mHotspot.addCallback(mHotspotCallback);
    243         mNextAlarm.addCallback(mNextAlarmCallback);
    244         mDataSaver.addCallback(this);
    245         mKeyguardMonitor.addCallback(this);
    246         mLocationController.addCallback(this);
    247 
    248         SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
    249         SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskListener);
    250 
    251         // Clear out all old notifications on startup (only present in the case where sysui dies)
    252         NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
    253         for (StatusBarNotification notification : noMan.getActiveNotifications()) {
    254             if (notification.getId() == SystemMessage.NOTE_INSTANT_APPS) {
    255                 noMan.cancel(notification.getTag(), notification.getId());
    256             }
    257         }
    258         DockedStackExistsListener.register(exists -> {
    259             mDockedStackExists = exists;
    260             updateForegroundInstantApps();
    261         });
    262     }
    263 
    264     public void destroy() {
    265         mRotationLockController.removeCallback(this);
    266         mBluetooth.removeCallback(this);
    267         mProvisionedController.removeCallback(this);
    268         mZenController.removeCallback(this);
    269         mCast.removeCallback(mCastCallback);
    270         mHotspot.removeCallback(mHotspotCallback);
    271         mNextAlarm.removeCallback(mNextAlarmCallback);
    272         mDataSaver.removeCallback(this);
    273         mKeyguardMonitor.removeCallback(this);
    274         mLocationController.removeCallback(this);
    275         SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
    276         mContext.unregisterReceiver(mIntentReceiver);
    277 
    278         NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
    279         mCurrentNotifs.forEach(v -> noMan.cancelAsUser(v.first, SystemMessage.NOTE_INSTANT_APPS,
    280                 new UserHandle(v.second)));
    281     }
    282 
    283     @Override
    284     public void onZenChanged(int zen) {
    285         updateVolumeZen();
    286     }
    287 
    288     @Override
    289     public void onLocationActiveChanged(boolean active) {
    290         updateLocation();
    291     }
    292 
    293     // Updates the status view based on the current state of location requests.
    294     private void updateLocation() {
    295         if (mLocationController.isLocationActive()) {
    296             mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
    297                     mContext.getString(R.string.accessibility_location_active));
    298         } else {
    299             mIconController.removeIcon(mSlotLocation);
    300         }
    301     }
    302 
    303     private void updateAlarm() {
    304         final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
    305         final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
    306         int zen = mZenController.getZen();
    307         final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
    308         mIconController.setIcon(mSlotAlarmClock, zenNone ? R.drawable.stat_sys_alarm_dim
    309                 : R.drawable.stat_sys_alarm, null);
    310         mIconController.setIconVisibility(mSlotAlarmClock, mCurrentUserSetup && hasAlarm);
    311     }
    312 
    313     private final void updateSimState(Intent intent) {
    314         String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
    315         if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
    316             mSimState = IccCardConstants.State.ABSENT;
    317         } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) {
    318             mSimState = IccCardConstants.State.CARD_IO_ERROR;
    319         } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED.equals(stateExtra)) {
    320             mSimState = IccCardConstants.State.CARD_RESTRICTED;
    321         } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
    322             mSimState = IccCardConstants.State.READY;
    323         } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
    324             final String lockedReason =
    325                     intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
    326             if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
    327                 mSimState = IccCardConstants.State.PIN_REQUIRED;
    328             } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
    329                 mSimState = IccCardConstants.State.PUK_REQUIRED;
    330             } else {
    331                 mSimState = IccCardConstants.State.NETWORK_LOCKED;
    332             }
    333         } else {
    334             mSimState = IccCardConstants.State.UNKNOWN;
    335         }
    336     }
    337 
    338     private final void updateVolumeZen() {
    339         AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
    340 
    341         boolean zenVisible = false;
    342         int zenIconId = 0;
    343         String zenDescription = null;
    344 
    345         boolean volumeVisible = false;
    346         int volumeIconId = 0;
    347         String volumeDescription = null;
    348         int zen = mZenController.getZen();
    349 
    350         if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) {
    351             zenVisible = zen != Global.ZEN_MODE_OFF;
    352             zenIconId = zen == Global.ZEN_MODE_NO_INTERRUPTIONS
    353                     ? R.drawable.stat_sys_dnd_total_silence : R.drawable.stat_sys_dnd;
    354             zenDescription = mContext.getString(R.string.quick_settings_dnd_label);
    355         } else if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
    356             zenVisible = true;
    357             zenIconId = R.drawable.stat_sys_zen_none;
    358             zenDescription = mContext.getString(R.string.interruption_level_none);
    359         } else if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
    360             zenVisible = true;
    361             zenIconId = R.drawable.stat_sys_zen_important;
    362             zenDescription = mContext.getString(R.string.interruption_level_priority);
    363         }
    364 
    365         if (DndTile.isVisible(mContext) && !DndTile.isCombinedIcon(mContext)
    366                 && audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
    367             volumeVisible = true;
    368             volumeIconId = R.drawable.stat_sys_ringer_silent;
    369             volumeDescription = mContext.getString(R.string.accessibility_ringer_silent);
    370         } else if (zen != Global.ZEN_MODE_NO_INTERRUPTIONS && zen != Global.ZEN_MODE_ALARMS &&
    371                 audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
    372             volumeVisible = true;
    373             volumeIconId = R.drawable.stat_sys_ringer_vibrate;
    374             volumeDescription = mContext.getString(R.string.accessibility_ringer_vibrate);
    375         }
    376 
    377         if (zenVisible) {
    378             mIconController.setIcon(mSlotZen, zenIconId, zenDescription);
    379         }
    380         if (zenVisible != mZenVisible) {
    381             mIconController.setIconVisibility(mSlotZen, zenVisible);
    382             mZenVisible = zenVisible;
    383         }
    384 
    385         if (volumeVisible) {
    386             mIconController.setIcon(mSlotVolume, volumeIconId, volumeDescription);
    387         }
    388         if (volumeVisible != mVolumeVisible) {
    389             mIconController.setIconVisibility(mSlotVolume, volumeVisible);
    390             mVolumeVisible = volumeVisible;
    391         }
    392         updateAlarm();
    393     }
    394 
    395     @Override
    396     public void onBluetoothDevicesChanged() {
    397         updateBluetooth();
    398     }
    399 
    400     @Override
    401     public void onBluetoothStateChange(boolean enabled) {
    402         updateBluetooth();
    403     }
    404 
    405     private final void updateBluetooth() {
    406         int iconId = R.drawable.stat_sys_data_bluetooth;
    407         String contentDescription =
    408                 mContext.getString(R.string.accessibility_quick_settings_bluetooth_on);
    409         boolean bluetoothEnabled = false;
    410         if (mBluetooth != null) {
    411             bluetoothEnabled = mBluetooth.isBluetoothEnabled();
    412             if (mBluetooth.isBluetoothConnected()) {
    413                 iconId = R.drawable.stat_sys_data_bluetooth_connected;
    414                 contentDescription = mContext.getString(R.string.accessibility_bluetooth_connected);
    415             }
    416         }
    417 
    418         mIconController.setIcon(mSlotBluetooth, iconId, contentDescription);
    419         mIconController.setIconVisibility(mSlotBluetooth, bluetoothEnabled);
    420     }
    421 
    422     private final void updateTTY(Intent intent) {
    423         int currentTtyMode = intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
    424                 TelecomManager.TTY_MODE_OFF);
    425         boolean enabled = currentTtyMode != TelecomManager.TTY_MODE_OFF;
    426 
    427         if (DEBUG) Log.v(TAG, "updateTTY: enabled: " + enabled);
    428 
    429         if (enabled) {
    430             // TTY is on
    431             if (DEBUG) Log.v(TAG, "updateTTY: set TTY on");
    432             mIconController.setIcon(mSlotTty, R.drawable.stat_sys_tty_mode,
    433                     mContext.getString(R.string.accessibility_tty_enabled));
    434             mIconController.setIconVisibility(mSlotTty, true);
    435         } else {
    436             // TTY is off
    437             if (DEBUG) Log.v(TAG, "updateTTY: set TTY off");
    438             mIconController.setIconVisibility(mSlotTty, false);
    439         }
    440     }
    441 
    442     private void updateCast() {
    443         boolean isCasting = false;
    444         for (CastDevice device : mCast.getCastDevices()) {
    445             if (device.state == CastDevice.STATE_CONNECTING
    446                     || device.state == CastDevice.STATE_CONNECTED) {
    447                 isCasting = true;
    448                 break;
    449             }
    450         }
    451         if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting);
    452         mHandler.removeCallbacks(mRemoveCastIconRunnable);
    453         if (isCasting) {
    454             mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast,
    455                     mContext.getString(R.string.accessibility_casting));
    456             mIconController.setIconVisibility(mSlotCast, true);
    457         } else {
    458             // don't turn off the screen-record icon for a few seconds, just to make sure the user
    459             // has seen it
    460             if (DEBUG) Log.v(TAG, "updateCast: hiding icon in 3 sec...");
    461             mHandler.postDelayed(mRemoveCastIconRunnable, 3000);
    462         }
    463     }
    464 
    465     private void updateQuietState() {
    466         mManagedProfileInQuietMode = false;
    467         int currentUserId = ActivityManager.getCurrentUser();
    468         for (UserInfo ui : mUserManager.getEnabledProfiles(currentUserId)) {
    469             if (ui.isManagedProfile() && ui.isQuietModeEnabled()) {
    470                 mManagedProfileInQuietMode = true;
    471                 return;
    472             }
    473         }
    474     }
    475 
    476     private void updateManagedProfile() {
    477         // getLastResumedActivityUserId needds to acquire the AM lock, which may be contended in
    478         // some cases. Since it doesn't really matter here whether it's updated in this frame
    479         // or in the next one, we call this method from our UI offload thread.
    480         mUiOffloadThread.submit(() -> {
    481             final int userId;
    482             try {
    483                 userId = ActivityManager.getService().getLastResumedActivityUserId();
    484                 boolean isManagedProfile = mUserManager.isManagedProfile(userId);
    485                 mHandler.post(() -> {
    486                     final boolean showIcon;
    487                     if (isManagedProfile && !mKeyguardMonitor.isShowing()) {
    488                         showIcon = true;
    489                         mIconController.setIcon(mSlotManagedProfile,
    490                                 R.drawable.stat_sys_managed_profile_status,
    491                                 mContext.getString(R.string.accessibility_managed_profile));
    492                     } else if (mManagedProfileInQuietMode) {
    493                         showIcon = true;
    494                         mIconController.setIcon(mSlotManagedProfile,
    495                                 R.drawable.stat_sys_managed_profile_status_off,
    496                                 mContext.getString(R.string.accessibility_managed_profile));
    497                     } else {
    498                         showIcon = false;
    499                     }
    500                     if (mManagedProfileIconVisible != showIcon) {
    501                         mIconController.setIconVisibility(mSlotManagedProfile, showIcon);
    502                         mManagedProfileIconVisible = showIcon;
    503                     }
    504                 });
    505             } catch (RemoteException e) {
    506                 Log.w(TAG, "updateManagedProfile: ", e);
    507             }
    508         });
    509     }
    510 
    511     private void updateForegroundInstantApps() {
    512         NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
    513         ArraySet<Pair<String, Integer>> notifs = new ArraySet<>(mCurrentNotifs);
    514         IPackageManager pm = AppGlobals.getPackageManager();
    515         mCurrentNotifs.clear();
    516         mUiOffloadThread.submit(() -> {
    517             try {
    518                 int focusedId = ActivityManager.getService().getFocusedStackId();
    519                 if (focusedId == StackId.FULLSCREEN_WORKSPACE_STACK_ID) {
    520                     checkStack(StackId.FULLSCREEN_WORKSPACE_STACK_ID, notifs, noMan, pm);
    521                 }
    522                 if (mDockedStackExists) {
    523                     checkStack(StackId.DOCKED_STACK_ID, notifs, noMan, pm);
    524                 }
    525             } catch (RemoteException e) {
    526                 e.rethrowFromSystemServer();
    527             }
    528             // Cancel all the leftover notifications that don't have a foreground process anymore.
    529             notifs.forEach(v -> noMan.cancelAsUser(v.first, SystemMessage.NOTE_INSTANT_APPS,
    530                     new UserHandle(v.second)));
    531         });
    532     }
    533 
    534     private void checkStack(int stackId, ArraySet<Pair<String, Integer>> notifs,
    535             NotificationManager noMan, IPackageManager pm) {
    536         try {
    537             StackInfo info = ActivityManager.getService().getStackInfo(stackId);
    538             if (info == null || info.topActivity == null) return;
    539             String pkg = info.topActivity.getPackageName();
    540             if (!hasNotif(notifs, pkg, info.userId)) {
    541                 // TODO: Optimize by not always needing to get application info.
    542                 // Maybe cache non-ephemeral packages?
    543                 ApplicationInfo appInfo = pm.getApplicationInfo(pkg,
    544                         PackageManager.MATCH_UNINSTALLED_PACKAGES, info.userId);
    545                 if (appInfo.isInstantApp()) {
    546                     postEphemeralNotif(pkg, info.userId, appInfo, noMan, info.taskIds[info.taskIds.length - 1]);
    547                 }
    548             }
    549         } catch (RemoteException e) {
    550             e.rethrowFromSystemServer();
    551         }
    552     }
    553 
    554     private void postEphemeralNotif(String pkg, int userId, ApplicationInfo appInfo,
    555             NotificationManager noMan, int taskId) {
    556         final Bundle extras = new Bundle();
    557         extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
    558                 mContext.getString(R.string.instant_apps));
    559         mCurrentNotifs.add(new Pair<>(pkg, userId));
    560         String message = mContext.getString(R.string.instant_apps_message);
    561         PendingIntent appInfoAction = PendingIntent.getActivity(mContext, 0,
    562                 new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
    563                         .setData(Uri.fromParts("package", pkg, null)), 0);
    564         Action action = new Notification.Action.Builder(null, mContext.getString(R.string.app_info),
    565                 appInfoAction).build();
    566 
    567         Intent browserIntent = getTaskIntent(taskId, userId);
    568         Notification.Builder builder = new Notification.Builder(mContext, NotificationChannels.GENERAL);
    569         if (browserIntent != null) {
    570             // Make sure that this doesn't resolve back to an instant app
    571             browserIntent.setComponent(null)
    572                     .setPackage(null)
    573                     .addFlags(Intent.FLAG_IGNORE_EPHEMERAL)
    574                     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    575 
    576             PendingIntent pendingIntent = PendingIntent.getActivity(mContext,
    577                     0 /* requestCode */, browserIntent, 0 /* flags */);
    578             ComponentName aiaComponent = null;
    579             try {
    580                 aiaComponent = AppGlobals.getPackageManager().getInstantAppInstallerComponent();
    581             } catch (RemoteException e) {
    582                 e.rethrowFromSystemServer();
    583             }
    584             Intent goToWebIntent = new Intent()
    585                     .setComponent(aiaComponent)
    586                     .setAction(Intent.ACTION_VIEW)
    587                     .addCategory(Intent.CATEGORY_BROWSABLE)
    588                     .addCategory("unique:" + System.currentTimeMillis())
    589                     .putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName)
    590                     .putExtra(Intent.EXTRA_VERSION_CODE, appInfo.versionCode)
    591                     .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent);
    592 
    593             PendingIntent webPendingIntent = PendingIntent.getActivity(mContext, 0, goToWebIntent, 0);
    594             Action webAction = new Notification.Action.Builder(null, mContext.getString(R.string.go_to_web),
    595                     webPendingIntent).build();
    596             builder.addAction(webAction);
    597         }
    598 
    599         noMan.notifyAsUser(pkg, SystemMessage.NOTE_INSTANT_APPS, builder
    600                         .addExtras(extras)
    601                         .addAction(action)
    602                         .setContentIntent(appInfoAction)
    603                         .setColor(mContext.getColor(R.color.instant_apps_color))
    604                         .setContentTitle(appInfo.loadLabel(mContext.getPackageManager()))
    605                         .setLargeIcon(Icon.createWithResource(pkg, appInfo.icon))
    606                         .setSmallIcon(Icon.createWithResource(mContext.getPackageName(),
    607                                 R.drawable.instant_icon))
    608                         .setContentText(message)
    609                         .setOngoing(true)
    610                         .build(),
    611                 new UserHandle(userId));
    612     }
    613 
    614     private Intent getTaskIntent(int taskId, int userId) {
    615         List<ActivityManager.RecentTaskInfo> tasks = mContext.getSystemService(ActivityManager.class)
    616                 .getRecentTasksForUser(NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId);
    617         for (int i = 0; i < tasks.size(); i++) {
    618             if (tasks.get(i).id == taskId) {
    619                 return tasks.get(i).baseIntent;
    620             }
    621         }
    622         return null;
    623     }
    624 
    625     private boolean hasNotif(ArraySet<Pair<String, Integer>> notifs, String pkg, int userId) {
    626         Pair<String, Integer> key = new Pair<>(pkg, userId);
    627         if (notifs.remove(key)) {
    628             mCurrentNotifs.add(key);
    629             return true;
    630         }
    631         return false;
    632     }
    633 
    634     private final SynchronousUserSwitchObserver mUserSwitchListener =
    635             new SynchronousUserSwitchObserver() {
    636                 @Override
    637                 public void onUserSwitching(int newUserId) throws RemoteException {
    638                     mHandler.post(() -> mUserInfoController.reloadUserInfo());
    639                 }
    640 
    641                 @Override
    642                 public void onUserSwitchComplete(int newUserId) throws RemoteException {
    643                     mHandler.post(() -> {
    644                         updateAlarm();
    645                         updateQuietState();
    646                         updateManagedProfile();
    647                         updateForegroundInstantApps();
    648                     });
    649                 }
    650             };
    651 
    652     private final HotspotController.Callback mHotspotCallback = new HotspotController.Callback() {
    653         @Override
    654         public void onHotspotChanged(boolean enabled) {
    655             mIconController.setIconVisibility(mSlotHotspot, enabled);
    656         }
    657     };
    658 
    659     private final CastController.Callback mCastCallback = new CastController.Callback() {
    660         @Override
    661         public void onCastDevicesChanged() {
    662             updateCast();
    663         }
    664     };
    665 
    666     private final NextAlarmController.NextAlarmChangeCallback mNextAlarmCallback =
    667             new NextAlarmController.NextAlarmChangeCallback() {
    668                 @Override
    669                 public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
    670                     updateAlarm();
    671                 }
    672             };
    673 
    674     @Override
    675     public void appTransitionStarting(long startTime, long duration, boolean forced) {
    676         updateManagedProfile();
    677         updateForegroundInstantApps();
    678     }
    679 
    680     @Override
    681     public void onKeyguardShowingChanged() {
    682         updateManagedProfile();
    683         updateForegroundInstantApps();
    684     }
    685 
    686     @Override
    687     public void onUserSetupChanged() {
    688         boolean userSetup = mProvisionedController.isUserSetup(
    689                 mProvisionedController.getCurrentUser());
    690         if (mCurrentUserSetup == userSetup) return;
    691         mCurrentUserSetup = userSetup;
    692         updateAlarm();
    693         updateQuietState();
    694     }
    695 
    696     @Override
    697     public void preloadRecentApps() {
    698         updateForegroundInstantApps();
    699     }
    700 
    701     @Override
    702     public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
    703         boolean portrait = RotationLockTile.isCurrentOrientationLockPortrait(
    704                 mRotationLockController, mContext);
    705         if (rotationLocked) {
    706             if (portrait) {
    707                 mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_portrait,
    708                         mContext.getString(R.string.accessibility_rotation_lock_on_portrait));
    709             } else {
    710                 mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_landscape,
    711                         mContext.getString(R.string.accessibility_rotation_lock_on_landscape));
    712             }
    713             mIconController.setIconVisibility(mSlotRotate, true);
    714         } else {
    715             mIconController.setIconVisibility(mSlotRotate, false);
    716         }
    717     }
    718 
    719     private void updateHeadsetPlug(Intent intent) {
    720         boolean connected = intent.getIntExtra("state", 0) != 0;
    721         boolean hasMic = intent.getIntExtra("microphone", 0) != 0;
    722         if (connected) {
    723             String contentDescription = mContext.getString(hasMic
    724                     ? R.string.accessibility_status_bar_headset
    725                     : R.string.accessibility_status_bar_headphones);
    726             mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.ic_headset_mic
    727                     : R.drawable.ic_headset, contentDescription);
    728             mIconController.setIconVisibility(mSlotHeadset, true);
    729         } else {
    730             mIconController.setIconVisibility(mSlotHeadset, false);
    731         }
    732     }
    733 
    734     @Override
    735     public void onDataSaverChanged(boolean isDataSaving) {
    736         mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
    737     }
    738 
    739     private final TaskStackListener mTaskListener = new TaskStackListener() {
    740         @Override
    741         public void onTaskStackChanged() {
    742             // Listen for changes to stacks and then check which instant apps are foreground.
    743             updateForegroundInstantApps();
    744         }
    745     };
    746 
    747     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
    748         @Override
    749         public void onReceive(Context context, Intent intent) {
    750             String action = intent.getAction();
    751             if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
    752                     action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
    753                 updateVolumeZen();
    754             } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
    755                 updateSimState(intent);
    756             } else if (action.equals(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED)) {
    757                 updateTTY(intent);
    758             } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) ||
    759                     action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) ||
    760                     action.equals(Intent.ACTION_MANAGED_PROFILE_REMOVED)) {
    761                 updateQuietState();
    762                 updateManagedProfile();
    763             } else if (action.equals(AudioManager.ACTION_HEADSET_PLUG)) {
    764                 updateHeadsetPlug(intent);
    765             }
    766         }
    767     };
    768 
    769     private Runnable mRemoveCastIconRunnable = new Runnable() {
    770         @Override
    771         public void run() {
    772             if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW");
    773             mIconController.setIconVisibility(mSlotCast, false);
    774         }
    775     };
    776 }
    777