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