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