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