• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.Nullable;
20 import android.app.ActivityTaskManager;
21 import android.app.AlarmManager;
22 import android.app.AlarmManager.AlarmClockInfo;
23 import android.app.IActivityManager;
24 import android.app.SynchronousUserSwitchObserver;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.content.SharedPreferences;
30 import android.content.res.Resources;
31 import android.media.AudioManager;
32 import android.os.Handler;
33 import android.os.RemoteException;
34 import android.os.UserHandle;
35 import android.os.UserManager;
36 import android.provider.Settings.Global;
37 import android.service.notification.ZenModeConfig;
38 import android.telecom.TelecomManager;
39 import android.text.format.DateFormat;
40 import android.util.Log;
41 import android.view.View;
42 
43 import androidx.lifecycle.Observer;
44 
45 import com.android.systemui.R;
46 import com.android.systemui.broadcast.BroadcastDispatcher;
47 import com.android.systemui.dagger.qualifiers.DisplayId;
48 import com.android.systemui.dagger.qualifiers.Main;
49 import com.android.systemui.dagger.qualifiers.UiBackground;
50 import com.android.systemui.privacy.PrivacyItem;
51 import com.android.systemui.privacy.PrivacyItemController;
52 import com.android.systemui.privacy.PrivacyType;
53 import com.android.systemui.qs.tiles.DndTile;
54 import com.android.systemui.qs.tiles.RotationLockTile;
55 import com.android.systemui.screenrecord.RecordingController;
56 import com.android.systemui.statusbar.CommandQueue;
57 import com.android.systemui.statusbar.policy.BluetoothController;
58 import com.android.systemui.statusbar.policy.CastController;
59 import com.android.systemui.statusbar.policy.CastController.CastDevice;
60 import com.android.systemui.statusbar.policy.DataSaverController;
61 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
62 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
63 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
64 import com.android.systemui.statusbar.policy.HotspotController;
65 import com.android.systemui.statusbar.policy.KeyguardStateController;
66 import com.android.systemui.statusbar.policy.LocationController;
67 import com.android.systemui.statusbar.policy.NextAlarmController;
68 import com.android.systemui.statusbar.policy.RotationLockController;
69 import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
70 import com.android.systemui.statusbar.policy.SensorPrivacyController;
71 import com.android.systemui.statusbar.policy.UserInfoController;
72 import com.android.systemui.statusbar.policy.ZenModeController;
73 import com.android.systemui.util.RingerModeTracker;
74 import com.android.systemui.util.time.DateFormatUtil;
75 
76 import java.io.PrintWriter;
77 import java.io.StringWriter;
78 import java.util.List;
79 import java.util.Locale;
80 import java.util.concurrent.Executor;
81 
82 import javax.inject.Inject;
83 
84 /**
85  * This class contains all of the policy about which icons are installed in the status bar at boot
86  * time. It goes through the normal API for icons, even though it probably strictly doesn't need to.
87  */
88 public class PhoneStatusBarPolicy
89         implements BluetoothController.Callback,
90                 CommandQueue.Callbacks,
91                 RotationLockControllerCallback,
92                 Listener,
93                 ZenModeController.Callback,
94                 DeviceProvisionedListener,
95                 KeyguardStateController.Callback,
96                 PrivacyItemController.Callback,
97                 LocationController.LocationChangeCallback,
98                 RecordingController.RecordingStateChangeCallback {
99     private static final String TAG = "PhoneStatusBarPolicy";
100     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
101 
102     static final int LOCATION_STATUS_ICON_ID = PrivacyType.TYPE_LOCATION.getIconId();
103 
104     private final String mSlotCast;
105     private final String mSlotHotspot;
106     private final String mSlotBluetooth;
107     private final String mSlotTty;
108     private final String mSlotZen;
109     private final String mSlotVolume;
110     private final String mSlotAlarmClock;
111     private final String mSlotManagedProfile;
112     private final String mSlotRotate;
113     private final String mSlotHeadset;
114     private final String mSlotDataSaver;
115     private final String mSlotLocation;
116     private final String mSlotMicrophone;
117     private final String mSlotCamera;
118     private final String mSlotSensorsOff;
119     private final String mSlotScreenRecord;
120     private final int mDisplayId;
121     private final SharedPreferences mSharedPreferences;
122     private final DateFormatUtil mDateFormatUtil;
123     private final TelecomManager mTelecomManager;
124 
125     private final Handler mHandler = new Handler();
126     private final CastController mCast;
127     private final HotspotController mHotspot;
128     private final NextAlarmController mNextAlarmController;
129     private final AlarmManager mAlarmManager;
130     private final UserInfoController mUserInfoController;
131     private final IActivityManager mIActivityManager;
132     private final UserManager mUserManager;
133     private final StatusBarIconController mIconController;
134     private final CommandQueue mCommandQueue;
135     private final BroadcastDispatcher mBroadcastDispatcher;
136     private final Resources mResources;
137     private final RotationLockController mRotationLockController;
138     private final DataSaverController mDataSaver;
139     private final ZenModeController mZenController;
140     private final DeviceProvisionedController mProvisionedController;
141     private final KeyguardStateController mKeyguardStateController;
142     private final LocationController mLocationController;
143     private final PrivacyItemController mPrivacyItemController;
144     private final Executor mUiBgExecutor;
145     private final SensorPrivacyController mSensorPrivacyController;
146     private final RecordingController mRecordingController;
147     private final RingerModeTracker mRingerModeTracker;
148 
149     private boolean mZenVisible;
150     private boolean mVolumeVisible;
151     private boolean mCurrentUserSetup;
152 
153     private boolean mManagedProfileIconVisible = false;
154 
155     private BluetoothController mBluetooth;
156     private AlarmManager.AlarmClockInfo mNextAlarm;
157 
158     @Inject
PhoneStatusBarPolicy(StatusBarIconController iconController, CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher, @UiBackground Executor uiBgExecutor, @Main Resources resources, CastController castController, HotspotController hotspotController, BluetoothController bluetoothController, NextAlarmController nextAlarmController, UserInfoController userInfoController, RotationLockController rotationLockController, DataSaverController dataSaverController, ZenModeController zenModeController, DeviceProvisionedController deviceProvisionedController, KeyguardStateController keyguardStateController, LocationController locationController, SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager, AlarmManager alarmManager, UserManager userManager, RecordingController recordingController, @Nullable TelecomManager telecomManager, @DisplayId int displayId, @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil, RingerModeTracker ringerModeTracker, PrivacyItemController privacyItemController)159     public PhoneStatusBarPolicy(StatusBarIconController iconController,
160             CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher,
161             @UiBackground Executor uiBgExecutor, @Main Resources resources,
162             CastController castController, HotspotController hotspotController,
163             BluetoothController bluetoothController, NextAlarmController nextAlarmController,
164             UserInfoController userInfoController, RotationLockController rotationLockController,
165             DataSaverController dataSaverController, ZenModeController zenModeController,
166             DeviceProvisionedController deviceProvisionedController,
167             KeyguardStateController keyguardStateController,
168             LocationController locationController,
169             SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager,
170             AlarmManager alarmManager, UserManager userManager,
171             RecordingController recordingController,
172             @Nullable TelecomManager telecomManager, @DisplayId int displayId,
173             @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil,
174             RingerModeTracker ringerModeTracker,
175             PrivacyItemController privacyItemController) {
176         mIconController = iconController;
177         mCommandQueue = commandQueue;
178         mBroadcastDispatcher = broadcastDispatcher;
179         mResources = resources;
180         mCast = castController;
181         mHotspot = hotspotController;
182         mBluetooth = bluetoothController;
183         mNextAlarmController = nextAlarmController;
184         mAlarmManager = alarmManager;
185         mUserInfoController = userInfoController;
186         mIActivityManager = iActivityManager;
187         mUserManager = userManager;
188         mRotationLockController = rotationLockController;
189         mDataSaver = dataSaverController;
190         mZenController = zenModeController;
191         mProvisionedController = deviceProvisionedController;
192         mKeyguardStateController = keyguardStateController;
193         mLocationController = locationController;
194         mPrivacyItemController = privacyItemController;
195         mSensorPrivacyController = sensorPrivacyController;
196         mRecordingController = recordingController;
197         mUiBgExecutor = uiBgExecutor;
198         mTelecomManager = telecomManager;
199         mRingerModeTracker = ringerModeTracker;
200 
201         mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast);
202         mSlotHotspot = resources.getString(com.android.internal.R.string.status_bar_hotspot);
203         mSlotBluetooth = resources.getString(com.android.internal.R.string.status_bar_bluetooth);
204         mSlotTty = resources.getString(com.android.internal.R.string.status_bar_tty);
205         mSlotZen = resources.getString(com.android.internal.R.string.status_bar_zen);
206         mSlotVolume = resources.getString(com.android.internal.R.string.status_bar_volume);
207         mSlotAlarmClock = resources.getString(com.android.internal.R.string.status_bar_alarm_clock);
208         mSlotManagedProfile = resources.getString(
209                 com.android.internal.R.string.status_bar_managed_profile);
210         mSlotRotate = resources.getString(com.android.internal.R.string.status_bar_rotate);
211         mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset);
212         mSlotDataSaver = resources.getString(com.android.internal.R.string.status_bar_data_saver);
213         mSlotLocation = resources.getString(com.android.internal.R.string.status_bar_location);
214         mSlotMicrophone = resources.getString(com.android.internal.R.string.status_bar_microphone);
215         mSlotCamera = resources.getString(com.android.internal.R.string.status_bar_camera);
216         mSlotSensorsOff = resources.getString(com.android.internal.R.string.status_bar_sensors_off);
217         mSlotScreenRecord = resources.getString(
218                 com.android.internal.R.string.status_bar_screen_record);
219 
220         mDisplayId = displayId;
221         mSharedPreferences = sharedPreferences;
222         mDateFormatUtil = dateFormatUtil;
223     }
224 
225     /** Initialize the object after construction. */
init()226     public void init() {
227         // listen for broadcasts
228         IntentFilter filter = new IntentFilter();
229 
230         filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
231         filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
232         filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
233         filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
234         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
235         filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
236         mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
237         Observer<Integer> observer = ringer -> mHandler.post(this::updateVolumeZen);
238 
239         mRingerModeTracker.getRingerMode().observeForever(observer);
240         mRingerModeTracker.getRingerModeInternal().observeForever(observer);
241 
242         // listen for user / profile change.
243         try {
244             mIActivityManager.registerUserSwitchObserver(mUserSwitchListener, TAG);
245         } catch (RemoteException e) {
246             // Ignore
247         }
248 
249         // TTY status
250         updateTTY();
251 
252         // bluetooth status
253         updateBluetooth();
254 
255         // Alarm clock
256         mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);
257         mIconController.setIconVisibility(mSlotAlarmClock, false);
258 
259         // zen
260         mIconController.setIcon(mSlotZen, R.drawable.stat_sys_dnd, null);
261         mIconController.setIconVisibility(mSlotZen, false);
262 
263         // volume
264         mIconController.setIcon(mSlotVolume, R.drawable.stat_sys_ringer_vibrate, null);
265         mIconController.setIconVisibility(mSlotVolume, false);
266         updateVolumeZen();
267 
268         // cast
269         mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, null);
270         mIconController.setIconVisibility(mSlotCast, false);
271 
272         // hotspot
273         mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot,
274                 mResources.getString(R.string.accessibility_status_bar_hotspot));
275         mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled());
276 
277         // managed profile
278         mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status,
279                 mResources.getString(R.string.accessibility_managed_profile));
280         mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible);
281 
282         // data saver
283         mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver,
284                 mResources.getString(R.string.accessibility_data_saver_on));
285         mIconController.setIconVisibility(mSlotDataSaver, false);
286 
287 
288         // privacy items
289         String microphoneString = mResources.getString(PrivacyType.TYPE_MICROPHONE.getNameId());
290         String microphoneDesc = mResources.getString(
291                 R.string.ongoing_privacy_chip_content_multiple_apps, microphoneString);
292         mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(),
293                 microphoneDesc);
294         mIconController.setIconVisibility(mSlotMicrophone, false);
295 
296         String cameraString = mResources.getString(PrivacyType.TYPE_CAMERA.getNameId());
297         String cameraDesc = mResources.getString(
298                 R.string.ongoing_privacy_chip_content_multiple_apps, cameraString);
299         mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(),
300                 cameraDesc);
301         mIconController.setIconVisibility(mSlotCamera, false);
302 
303         mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
304                 mResources.getString(R.string.accessibility_location_active));
305         mIconController.setIconVisibility(mSlotLocation, false);
306 
307         // sensors off
308         mIconController.setIcon(mSlotSensorsOff, R.drawable.stat_sys_sensors_off,
309                 mResources.getString(R.string.accessibility_sensors_off_active));
310         mIconController.setIconVisibility(mSlotSensorsOff,
311                 mSensorPrivacyController.isSensorPrivacyEnabled());
312 
313         // screen record
314         mIconController.setIcon(mSlotScreenRecord, R.drawable.stat_sys_screen_record, null);
315         mIconController.setIconVisibility(mSlotScreenRecord, false);
316 
317         mRotationLockController.addCallback(this);
318         mBluetooth.addCallback(this);
319         mProvisionedController.addCallback(this);
320         mZenController.addCallback(this);
321         mCast.addCallback(mCastCallback);
322         mHotspot.addCallback(mHotspotCallback);
323         mNextAlarmController.addCallback(mNextAlarmCallback);
324         mDataSaver.addCallback(this);
325         mKeyguardStateController.addCallback(this);
326         mPrivacyItemController.addCallback(this);
327         mSensorPrivacyController.addCallback(mSensorPrivacyListener);
328         mLocationController.addCallback(this);
329         mRecordingController.addCallback(this);
330 
331         mCommandQueue.addCallback(this);
332     }
333 
334     @Override
onZenChanged(int zen)335     public void onZenChanged(int zen) {
336         updateVolumeZen();
337     }
338 
339     @Override
onConfigChanged(ZenModeConfig config)340     public void onConfigChanged(ZenModeConfig config) {
341         updateVolumeZen();
342     }
343 
updateAlarm()344     private void updateAlarm() {
345         final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
346         final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
347         int zen = mZenController.getZen();
348         final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
349         mIconController.setIcon(mSlotAlarmClock, zenNone ? R.drawable.stat_sys_alarm_dim
350                 : R.drawable.stat_sys_alarm, buildAlarmContentDescription());
351         mIconController.setIconVisibility(mSlotAlarmClock, mCurrentUserSetup && hasAlarm);
352     }
353 
buildAlarmContentDescription()354     private String buildAlarmContentDescription() {
355         if (mNextAlarm == null) {
356             return mResources.getString(R.string.status_bar_alarm);
357         }
358 
359         String skeleton = mDateFormatUtil.is24HourFormat() ? "EHm" : "Ehma";
360         String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
361         String dateString = DateFormat.format(pattern, mNextAlarm.getTriggerTime()).toString();
362 
363         return mResources.getString(R.string.accessibility_quick_settings_alarm, dateString);
364     }
365 
updateVolumeZen()366     private final void updateVolumeZen() {
367         boolean zenVisible = false;
368         int zenIconId = 0;
369         String zenDescription = null;
370 
371         boolean volumeVisible = false;
372         int volumeIconId = 0;
373         String volumeDescription = null;
374         int zen = mZenController.getZen();
375 
376         if (DndTile.isVisible(mSharedPreferences) || DndTile.isCombinedIcon(mSharedPreferences)) {
377             zenVisible = zen != Global.ZEN_MODE_OFF;
378             zenIconId = R.drawable.stat_sys_dnd;
379             zenDescription = mResources.getString(R.string.quick_settings_dnd_label);
380         } else if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
381             zenVisible = true;
382             zenIconId = R.drawable.stat_sys_dnd;
383             zenDescription = mResources.getString(R.string.interruption_level_none);
384         } else if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
385             zenVisible = true;
386             zenIconId = R.drawable.stat_sys_dnd;
387             zenDescription = mResources.getString(R.string.interruption_level_priority);
388         }
389 
390         if (!ZenModeConfig.isZenOverridingRinger(zen, mZenController.getConsolidatedPolicy())) {
391             final Integer ringerModeInternal =
392                     mRingerModeTracker.getRingerModeInternal().getValue();
393             if (ringerModeInternal != null) {
394                 if (ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
395                     volumeVisible = true;
396                     volumeIconId = R.drawable.stat_sys_ringer_vibrate;
397                     volumeDescription = mResources.getString(R.string.accessibility_ringer_vibrate);
398                 } else if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) {
399                     volumeVisible = true;
400                     volumeIconId = R.drawable.stat_sys_ringer_silent;
401                     volumeDescription = mResources.getString(R.string.accessibility_ringer_silent);
402                 }
403             }
404         }
405 
406         if (zenVisible) {
407             mIconController.setIcon(mSlotZen, zenIconId, zenDescription);
408         }
409         if (zenVisible != mZenVisible) {
410             mIconController.setIconVisibility(mSlotZen, zenVisible);
411             mZenVisible = zenVisible;
412         }
413 
414         if (volumeVisible) {
415             mIconController.setIcon(mSlotVolume, volumeIconId, volumeDescription);
416         }
417         if (volumeVisible != mVolumeVisible) {
418             mIconController.setIconVisibility(mSlotVolume, volumeVisible);
419             mVolumeVisible = volumeVisible;
420         }
421         updateAlarm();
422     }
423 
424     @Override
onBluetoothDevicesChanged()425     public void onBluetoothDevicesChanged() {
426         updateBluetooth();
427     }
428 
429     @Override
onBluetoothStateChange(boolean enabled)430     public void onBluetoothStateChange(boolean enabled) {
431         updateBluetooth();
432     }
433 
updateBluetooth()434     private final void updateBluetooth() {
435         int iconId = R.drawable.stat_sys_data_bluetooth_connected;
436         String contentDescription =
437                 mResources.getString(R.string.accessibility_quick_settings_bluetooth_on);
438         boolean bluetoothVisible = false;
439         if (mBluetooth != null) {
440             if (mBluetooth.isBluetoothConnected()
441                     && (mBluetooth.isBluetoothAudioActive()
442                     || !mBluetooth.isBluetoothAudioProfileOnly())) {
443                 contentDescription = mResources.getString(
444                         R.string.accessibility_bluetooth_connected);
445                 bluetoothVisible = mBluetooth.isBluetoothEnabled();
446             }
447         }
448 
449         mIconController.setIcon(mSlotBluetooth, iconId, contentDescription);
450         mIconController.setIconVisibility(mSlotBluetooth, bluetoothVisible);
451     }
452 
updateTTY()453     private final void updateTTY() {
454         if (mTelecomManager == null) {
455             updateTTY(TelecomManager.TTY_MODE_OFF);
456         } else {
457             updateTTY(mTelecomManager.getCurrentTtyMode());
458         }
459     }
460 
updateTTY(int currentTtyMode)461     private final void updateTTY(int currentTtyMode) {
462         boolean enabled = currentTtyMode != TelecomManager.TTY_MODE_OFF;
463 
464         if (DEBUG) Log.v(TAG, "updateTTY: enabled: " + enabled);
465 
466         if (enabled) {
467             // TTY is on
468             if (DEBUG) Log.v(TAG, "updateTTY: set TTY on");
469             mIconController.setIcon(mSlotTty, R.drawable.stat_sys_tty_mode,
470                     mResources.getString(R.string.accessibility_tty_enabled));
471             mIconController.setIconVisibility(mSlotTty, true);
472         } else {
473             // TTY is off
474             if (DEBUG) Log.v(TAG, "updateTTY: set TTY off");
475             mIconController.setIconVisibility(mSlotTty, false);
476         }
477     }
478 
updateCast()479     private void updateCast() {
480         boolean isCasting = false;
481         for (CastDevice device : mCast.getCastDevices()) {
482             if (device.state == CastDevice.STATE_CONNECTING
483                     || device.state == CastDevice.STATE_CONNECTED) {
484                 isCasting = true;
485                 break;
486             }
487         }
488         if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting);
489         mHandler.removeCallbacks(mRemoveCastIconRunnable);
490         if (isCasting && !mRecordingController.isRecording()) { // screen record has its own icon
491             mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast,
492                     mResources.getString(R.string.accessibility_casting));
493             mIconController.setIconVisibility(mSlotCast, true);
494         } else {
495             // don't turn off the screen-record icon for a few seconds, just to make sure the user
496             // has seen it
497             if (DEBUG) Log.v(TAG, "updateCast: hiding icon in 3 sec...");
498             mHandler.postDelayed(mRemoveCastIconRunnable, 3000);
499         }
500     }
501 
updateManagedProfile()502     private void updateManagedProfile() {
503         // getLastResumedActivityUserId needds to acquire the AM lock, which may be contended in
504         // some cases. Since it doesn't really matter here whether it's updated in this frame
505         // or in the next one, we call this method from our UI offload thread.
506         mUiBgExecutor.execute(() -> {
507             final int userId;
508             try {
509                 userId = ActivityTaskManager.getService().getLastResumedActivityUserId();
510                 boolean isManagedProfile = mUserManager.isManagedProfile(userId);
511                 mHandler.post(() -> {
512                     final boolean showIcon;
513                     if (isManagedProfile && (!mKeyguardStateController.isShowing()
514                             || mKeyguardStateController.isOccluded())) {
515                         showIcon = true;
516                         mIconController.setIcon(mSlotManagedProfile,
517                                 R.drawable.stat_sys_managed_profile_status,
518                                 mResources.getString(R.string.accessibility_managed_profile));
519                     } else {
520                         showIcon = false;
521                     }
522                     if (mManagedProfileIconVisible != showIcon) {
523                         mIconController.setIconVisibility(mSlotManagedProfile, showIcon);
524                         mManagedProfileIconVisible = showIcon;
525                     }
526                 });
527             } catch (RemoteException e) {
528                 Log.w(TAG, "updateManagedProfile: ", e);
529             }
530         });
531     }
532 
533     private final SynchronousUserSwitchObserver mUserSwitchListener =
534             new SynchronousUserSwitchObserver() {
535                 @Override
536                 public void onUserSwitching(int newUserId) throws RemoteException {
537                     mHandler.post(() -> mUserInfoController.reloadUserInfo());
538                 }
539 
540                 @Override
541                 public void onUserSwitchComplete(int newUserId) throws RemoteException {
542                     mHandler.post(() -> {
543                         updateAlarm();
544                         updateManagedProfile();
545                     });
546                 }
547             };
548 
549     private final HotspotController.Callback mHotspotCallback = new HotspotController.Callback() {
550         @Override
551         public void onHotspotChanged(boolean enabled, int numDevices) {
552             mIconController.setIconVisibility(mSlotHotspot, enabled);
553         }
554     };
555 
556     private final CastController.Callback mCastCallback = new CastController.Callback() {
557         @Override
558         public void onCastDevicesChanged() {
559             updateCast();
560         }
561     };
562 
563     private final NextAlarmController.NextAlarmChangeCallback mNextAlarmCallback =
564             new NextAlarmController.NextAlarmChangeCallback() {
565                 @Override
566                 public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
567                     mNextAlarm = nextAlarm;
568                     updateAlarm();
569                 }
570             };
571 
572     private final SensorPrivacyController.OnSensorPrivacyChangedListener mSensorPrivacyListener =
573             new SensorPrivacyController.OnSensorPrivacyChangedListener() {
574                 @Override
575                 public void onSensorPrivacyChanged(boolean enabled) {
576                     mHandler.post(() -> {
577                         mIconController.setIconVisibility(mSlotSensorsOff, enabled);
578                     });
579                 }
580             };
581 
582     @Override
appTransitionStarting(int displayId, long startTime, long duration, boolean forced)583     public void appTransitionStarting(int displayId, long startTime, long duration,
584             boolean forced) {
585         if (mDisplayId == displayId) {
586             updateManagedProfile();
587         }
588     }
589 
590     @Override
onKeyguardShowingChanged()591     public void onKeyguardShowingChanged() {
592         updateManagedProfile();
593     }
594 
595     @Override
onUserSetupChanged()596     public void onUserSetupChanged() {
597         boolean userSetup = mProvisionedController.isUserSetup(
598                 mProvisionedController.getCurrentUser());
599         if (mCurrentUserSetup == userSetup) return;
600         mCurrentUserSetup = userSetup;
601         updateAlarm();
602     }
603 
604     @Override
onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible)605     public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
606         boolean portrait = RotationLockTile.isCurrentOrientationLockPortrait(
607                 mRotationLockController, mResources);
608         if (rotationLocked) {
609             if (portrait) {
610                 mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_portrait,
611                         mResources.getString(R.string.accessibility_rotation_lock_on_portrait));
612             } else {
613                 mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_landscape,
614                         mResources.getString(R.string.accessibility_rotation_lock_on_landscape));
615             }
616             mIconController.setIconVisibility(mSlotRotate, true);
617         } else {
618             mIconController.setIconVisibility(mSlotRotate, false);
619         }
620     }
621 
updateHeadsetPlug(Intent intent)622     private void updateHeadsetPlug(Intent intent) {
623         boolean connected = intent.getIntExtra("state", 0) != 0;
624         boolean hasMic = intent.getIntExtra("microphone", 0) != 0;
625         if (connected) {
626             String contentDescription = mResources.getString(hasMic
627                     ? R.string.accessibility_status_bar_headset
628                     : R.string.accessibility_status_bar_headphones);
629             mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.stat_sys_headset_mic
630                     : R.drawable.stat_sys_headset, contentDescription);
631             mIconController.setIconVisibility(mSlotHeadset, true);
632         } else {
633             mIconController.setIconVisibility(mSlotHeadset, false);
634         }
635     }
636 
637     @Override
onDataSaverChanged(boolean isDataSaving)638     public void onDataSaverChanged(boolean isDataSaving) {
639         mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
640     }
641 
642     @Override  // PrivacyItemController.Callback
onPrivacyItemsChanged(List<PrivacyItem> privacyItems)643     public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
644         updatePrivacyItems(privacyItems);
645     }
646 
updatePrivacyItems(List<PrivacyItem> items)647     private void updatePrivacyItems(List<PrivacyItem> items) {
648         boolean showCamera = false;
649         boolean showMicrophone = false;
650         boolean showLocation = false;
651         for (PrivacyItem item : items) {
652             if (item == null /* b/124234367 */) {
653                 if (DEBUG) {
654                     Log.e(TAG, "updatePrivacyItems - null item found");
655                     StringWriter out = new StringWriter();
656                     mPrivacyItemController.dump(null, new PrintWriter(out), null);
657                     Log.e(TAG, out.toString());
658                 }
659                 continue;
660             }
661             switch (item.getPrivacyType()) {
662                 case TYPE_CAMERA:
663                     showCamera = true;
664                     break;
665                 case TYPE_LOCATION:
666                     showLocation = true;
667                     break;
668                 case TYPE_MICROPHONE:
669                     showMicrophone = true;
670                     break;
671             }
672         }
673 
674         mIconController.setIconVisibility(mSlotCamera, showCamera);
675         mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
676         if (mPrivacyItemController.getAllIndicatorsAvailable()) {
677             mIconController.setIconVisibility(mSlotLocation, showLocation);
678         }
679     }
680 
681     @Override
onLocationActiveChanged(boolean active)682     public void onLocationActiveChanged(boolean active) {
683         if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController();
684     }
685 
686     // Updates the status view based on the current state of location requests.
updateLocationFromController()687     private void updateLocationFromController() {
688         if (mLocationController.isLocationActive()) {
689             mIconController.setIconVisibility(mSlotLocation, true);
690         } else {
691             mIconController.setIconVisibility(mSlotLocation, false);
692         }
693     }
694 
695     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
696         @Override
697         public void onReceive(Context context, Intent intent) {
698             String action = intent.getAction();
699             switch (action) {
700                 case Intent.ACTION_SIM_STATE_CHANGED:
701                     // Avoid rebroadcast because SysUI is direct boot aware.
702                     if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
703                         break;
704                     }
705                     break;
706                 case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:
707                     updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
708                             TelecomManager.TTY_MODE_OFF));
709                     break;
710                 case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
711                 case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
712                 case Intent.ACTION_MANAGED_PROFILE_REMOVED:
713                     updateManagedProfile();
714                     break;
715                 case AudioManager.ACTION_HEADSET_PLUG:
716                     updateHeadsetPlug(intent);
717                     break;
718             }
719         }
720     };
721 
722     private Runnable mRemoveCastIconRunnable = new Runnable() {
723         @Override
724         public void run() {
725             if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW");
726             mIconController.setIconVisibility(mSlotCast, false);
727         }
728     };
729 
730     // Screen Recording
731     @Override
onCountdown(long millisUntilFinished)732     public void onCountdown(long millisUntilFinished) {
733         if (DEBUG) Log.d(TAG, "screenrecord: countdown " + millisUntilFinished);
734         int countdown = (int) Math.floorDiv(millisUntilFinished + 500, 1000);
735         int resourceId = R.drawable.stat_sys_screen_record;
736         String description = Integer.toString(countdown);
737         switch (countdown) {
738             case 1:
739                 resourceId = R.drawable.stat_sys_screen_record_1;
740                 break;
741             case 2:
742                 resourceId = R.drawable.stat_sys_screen_record_2;
743                 break;
744             case 3:
745                 resourceId = R.drawable.stat_sys_screen_record_3;
746                 break;
747         }
748         mIconController.setIcon(mSlotScreenRecord, resourceId, description);
749         mIconController.setIconVisibility(mSlotScreenRecord, true);
750         // Set as assertive so talkback will announce the countdown
751         mIconController.setIconAccessibilityLiveRegion(mSlotScreenRecord,
752                 View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE);
753     }
754 
755     @Override
onCountdownEnd()756     public void onCountdownEnd() {
757         if (DEBUG) Log.d(TAG, "screenrecord: hiding icon during countdown");
758         mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
759         // Reset talkback priority
760         mHandler.post(() -> mIconController.setIconAccessibilityLiveRegion(mSlotScreenRecord,
761                 View.ACCESSIBILITY_LIVE_REGION_NONE));
762     }
763 
764     @Override
onRecordingStart()765     public void onRecordingStart() {
766         if (DEBUG) Log.d(TAG, "screenrecord: showing icon");
767         mIconController.setIcon(mSlotScreenRecord,
768                 R.drawable.stat_sys_screen_record,
769                 mResources.getString(R.string.screenrecord_ongoing_screen_only));
770         mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, true));
771     }
772 
773     @Override
onRecordingEnd()774     public void onRecordingEnd() {
775         // Ensure this is on the main thread
776         if (DEBUG) Log.d(TAG, "screenrecord: hiding icon");
777         mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
778     }
779 }
780