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