2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.systemui.statusbar.phone;
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
24 import android.app.ActivityManager;
25 import android.app.ActivityManager.StackInfo;
26 import android.app.AlarmManager;
27 import android.app.AlarmManager.AlarmClockInfo;
28 import android.app.AppGlobals;
29 import android.app.Notification;
30 import android.app.Notification.Action;
31 import android.app.NotificationManager;
32 import android.app.PendingIntent;
33 import android.app.SynchronousUserSwitchObserver;
34 import android.content.BroadcastReceiver;
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.IntentFilter;
39 import android.content.pm.ApplicationInfo;
40 import android.content.pm.IPackageManager;
41 import android.content.pm.PackageManager;
42 import android.graphics.drawable.Icon;
43 import android.media.AudioManager;
44 import android.net.Uri;
45 import android.os.Bundle;
46 import android.os.Handler;
47 import android.os.RemoteException;
48 import android.os.UserHandle;
49 import android.os.UserManager;
50 import android.provider.Settings;
51 import android.provider.Settings.Global;
52 import android.service.notification.StatusBarNotification;
53 import android.service.notification.ZenModeConfig;
54 import android.telecom.TelecomManager;
55 import android.util.ArraySet;
56 import android.util.Log;
57 import android.util.Pair;
58 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
59 import com.android.internal.telephony.IccCardConstants;
60 import com.android.internal.telephony.TelephonyIntents;
61 import com.android.systemui.Dependency;
62 import com.android.systemui.DockedStackExistsListener;
63 import com.android.systemui.R;
64 import com.android.systemui.SysUiServiceProvider;
65 import com.android.systemui.UiOffloadThread;
66 import com.android.systemui.qs.tiles.DndTile;
67 import com.android.systemui.qs.tiles.RotationLockTile;
68 import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
69 import com.android.systemui.shared.system.ActivityManagerWrapper;
70 import com.android.systemui.statusbar.CommandQueue;
71 import com.android.systemui.statusbar.CommandQueue.Callbacks;
72 import com.android.systemui.statusbar.policy.BluetoothController;
73 import com.android.systemui.statusbar.policy.BluetoothController.Callback;
74 import com.android.systemui.statusbar.policy.CastController;
75 import com.android.systemui.statusbar.policy.CastController.CastDevice;
76 import com.android.systemui.statusbar.policy.DataSaverController;
77 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
78 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
79 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
80 import com.android.systemui.statusbar.policy.HotspotController;
81 import com.android.systemui.statusbar.policy.KeyguardMonitor;
82 import com.android.systemui.statusbar.policy.LocationController;
83 import com.android.systemui.statusbar.policy.LocationController.LocationChangeCallback;
84 import com.android.systemui.statusbar.policy.NextAlarmController;
85 import com.android.systemui.statusbar.policy.RotationLockController;
86 import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
87 import com.android.systemui.statusbar.policy.UserInfoController;
88 import com.android.systemui.statusbar.policy.ZenModeController;
89 import com.android.systemui.util.NotificationChannels;
91 import java.util.List;
94 * This class contains all of the policy about which icons are installed in the status
95 * bar at boot time. It goes through the normal API for icons, even though it probably
96 * strictly doesn't need to.
98 public class PhoneStatusBarPolicy implements Callback, Callbacks,
99 RotationLockControllerCallback, Listener, LocationChangeCallback,
100 ZenModeController.Callback, DeviceProvisionedListener, KeyguardMonitor.Callback {
101 private static final String TAG = "PhoneStatusBarPolicy";
102 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
104 public static final int LOCATION_STATUS_ICON_ID = R.drawable.stat_sys_location;
105 public static final int NUM_TASKS_FOR_INSTANT_APP_INFO = 5;
107 private final String mSlotCast;
108 private final String mSlotHotspot;
109 private final String mSlotBluetooth;
110 private final String mSlotTty;
111 private final String mSlotZen;
112 private final String mSlotVolume;
113 private final String mSlotAlarmClock;
114 private final String mSlotManagedProfile;
115 private final String mSlotRotate;
116 private final String mSlotHeadset;
117 private final String mSlotDataSaver;
118 private final String mSlotLocation;
120 private final Context mContext;
121 private final Handler mHandler = new Handler();
122 private final CastController mCast;
123 private final HotspotController mHotspot;
124 private final NextAlarmController mNextAlarm;
125 private final AlarmManager mAlarmManager;
126 private final UserInfoController mUserInfoController;
127 private final UserManager mUserManager;
128 private final StatusBarIconController mIconController;
129 private final RotationLockController mRotationLockController;
130 private final DataSaverController mDataSaver;
131 private final ZenModeController mZenController;
132 private final DeviceProvisionedController mProvisionedController;
133 private final KeyguardMonitor mKeyguardMonitor;
134 private final LocationController mLocationController;
135 private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>();
136 private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
138 // Assume it's all good unless we hear otherwise. We don't always seem
139 // to get broadcasts that it *is* there.
140 IccCardConstants.State mSimState = IccCardConstants.State.READY;
142 private boolean mZenVisible;
143 private boolean mVolumeVisible;
144 private boolean mCurrentUserSetup;
145 private boolean mDockedStackExists;
147 private boolean mManagedProfileIconVisible = false;
149 private BluetoothController mBluetooth;
151 public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController) {
153 mIconController = iconController;
154 mCast = Dependency.get(CastController.class);
155 mHotspot = Dependency.get(HotspotController.class);
156 mBluetooth = Dependency.get(BluetoothController.class);
157 mNextAlarm = Dependency.get(NextAlarmController.class);
158 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
159 mUserInfoController = Dependency.get(UserInfoController.class);
160 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
161 mRotationLockController = Dependency.get(RotationLockController.class);
162 mDataSaver = Dependency.get(DataSaverController.class);
163 mZenController = Dependency.get(ZenModeController.class);
164 mProvisionedController = Dependency.get(DeviceProvisionedController.class);
165 mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
166 mLocationController = Dependency.get(LocationController.class);
168 mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
169 mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
170 mSlotBluetooth = context.getString(com.android.internal.R.string.status_bar_bluetooth);
171 mSlotTty = context.getString(com.android.internal.R.string.status_bar_tty);
172 mSlotZen = context.getString(com.android.internal.R.string.status_bar_zen);
173 mSlotVolume = context.getString(com.android.internal.R.string.status_bar_volume);
174 mSlotAlarmClock = context.getString(com.android.internal.R.string.status_bar_alarm_clock);
175 mSlotManagedProfile = context.getString(
176 com.android.internal.R.string.status_bar_managed_profile);
177 mSlotRotate = context.getString(com.android.internal.R.string.status_bar_rotate);
178 mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
179 mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
180 mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
182 // listen for broadcasts
183 IntentFilter filter = new IntentFilter();
184 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
185 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
186 filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
187 filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
188 filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
189 filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
190 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
191 filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
192 mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
194 // listen for user / profile change.
196 ActivityManager.getService().registerUserSwitchObserver(mUserSwitchListener, TAG);
197 } catch (RemoteException e) {
208 mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);
209 mIconController.setIconVisibility(mSlotAlarmClock, false);
212 mIconController.setIcon(mSlotZen, R.drawable.stat_sys_zen_important, null);
213 mIconController.setIconVisibility(mSlotZen, false);
216 mIconController.setIcon(mSlotVolume, R.drawable.stat_sys_ringer_vibrate, null);
217 mIconController.setIconVisibility(mSlotVolume, false);
221 mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, null);
222 mIconController.setIconVisibility(mSlotCast, false);
225 mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot,
226 mContext.getString(R.string.accessibility_status_bar_hotspot));
227 mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled());
230 mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status,
231 mContext.getString(R.string.accessibility_managed_profile));
232 mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible);
235 mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver,
236 context.getString(R.string.accessibility_data_saver_on));
237 mIconController.setIconVisibility(mSlotDataSaver, false);
239 mRotationLockController.addCallback(this);
240 mBluetooth.addCallback(this);
241 mProvisionedController.addCallback(this);
242 mZenController.addCallback(this);
243 mCast.addCallback(mCastCallback);
244 mHotspot.addCallback(mHotspotCallback);
245 mNextAlarm.addCallback(mNextAlarmCallback);
246 mDataSaver.addCallback(this);
247 mKeyguardMonitor.addCallback(this);
248 mLocationController.addCallback(this);
250 SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
251 ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
253 // Clear out all old notifications on startup (only present in the case where sysui dies)
254 NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
255 for (StatusBarNotification notification : noMan.getActiveNotifications()) {
256 if (notification.getId() == SystemMessage.NOTE_INSTANT_APPS) {
257 noMan.cancel(notification.getTag(), notification.getId());
260 DockedStackExistsListener.register(exists -> {
261 mDockedStackExists = exists;
262 updateForegroundInstantApps();
266 public void destroy() {
267 mRotationLockController.removeCallback(this);
268 mBluetooth.removeCallback(this);
269 mProvisionedController.removeCallback(this);
270 mZenController.removeCallback(this);
271 mCast.removeCallback(mCastCallback);
272 mHotspot.removeCallback(mHotspotCallback);
273 mNextAlarm.removeCallback(mNextAlarmCallback);
274 mDataSaver.removeCallback(this);
275 mKeyguardMonitor.removeCallback(this);
276 mLocationController.removeCallback(this);
277 SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
278 mContext.unregisterReceiver(mIntentReceiver);
280 NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
281 mCurrentNotifs.forEach(v -> noMan.cancelAsUser(v.first, SystemMessage.NOTE_INSTANT_APPS,
282 new UserHandle(v.second)));
286 public void onZenChanged(int zen) {
291 public void onConfigChanged(ZenModeConfig config) {
296 public void onLocationActiveChanged(boolean active) {
300 // Updates the status view based on the current state of location requests.
301 private void updateLocation() {
302 if (mLocationController.isLocationActive()) {
303 mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
304 mContext.getString(R.string.accessibility_location_active));
306 mIconController.removeAllIconsForSlot(mSlotLocation);
310 private void updateAlarm() {
311 final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
312 final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
313 int zen = mZenController.getZen();
314 final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
315 mIconController.setIcon(mSlotAlarmClock, zenNone ? R.drawable.stat_sys_alarm_dim
316 : R.drawable.stat_sys_alarm, null);
317 mIconController.setIconVisibility(mSlotAlarmClock, mCurrentUserSetup && hasAlarm);
320 private final void updateSimState(Intent intent) {
321 String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
322 if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
323 mSimState = IccCardConstants.State.ABSENT;
324 } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) {
325 mSimState = IccCardConstants.State.CARD_IO_ERROR;
326 } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED.equals(stateExtra)) {
327 mSimState = IccCardConstants.State.CARD_RESTRICTED;
328 } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
329 mSimState = IccCardConstants.State.READY;
330 } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
331 final String lockedReason =
332 intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON);
333 if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
334 mSimState = IccCardConstants.State.PIN_REQUIRED;
335 } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
336 mSimState = IccCardConstants.State.PUK_REQUIRED;
338 mSimState = IccCardConstants.State.NETWORK_LOCKED;
341 mSimState = IccCardConstants.State.UNKNOWN;
345 private final void updateVolumeZen() {
346 AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
348 boolean zenVisible = false;
350 String zenDescription = null;
352 boolean volumeVisible = false;
353 int volumeIconId = 0;
354 String volumeDescription = null;
355 int zen = mZenController.getZen();
357 if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) {
358 zenVisible = zen != Global.ZEN_MODE_OFF;
359 zenIconId = zen == Global.ZEN_MODE_NO_INTERRUPTIONS
360 ? R.drawable.stat_sys_dnd_total_silence : R.drawable.stat_sys_dnd;
361 zenDescription = mContext.getString(R.string.quick_settings_dnd_label);
362 } else if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
364 zenIconId = R.drawable.stat_sys_zen_none;
365 zenDescription = mContext.getString(R.string.interruption_level_none);
366 } else if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
368 zenIconId = R.drawable.stat_sys_zen_important;
369 zenDescription = mContext.getString(R.string.interruption_level_priority);
372 if (!ZenModeConfig.isZenOverridingRinger(zen, mZenController.getConfig())) {
373 if (audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
374 volumeVisible = true;
375 volumeIconId = R.drawable.stat_sys_ringer_vibrate;
376 volumeDescription = mContext.getString(R.string.accessibility_ringer_vibrate);
377 } else if (audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
378 volumeVisible = true;
379 volumeIconId = R.drawable.stat_sys_ringer_silent;
380 volumeDescription = mContext.getString(R.string.accessibility_ringer_silent);
385 mIconController.setIcon(mSlotZen, zenIconId, zenDescription);
387 if (zenVisible != mZenVisible) {
388 mIconController.setIconVisibility(mSlotZen, zenVisible);
389 mZenVisible = zenVisible;
393 mIconController.setIcon(mSlotVolume, volumeIconId, volumeDescription);
395 if (volumeVisible != mVolumeVisible) {
396 mIconController.setIconVisibility(mSlotVolume, volumeVisible);
397 mVolumeVisible = volumeVisible;
403 public void onBluetoothDevicesChanged() {
408 public void onBluetoothStateChange(boolean enabled) {
412 private final void updateBluetooth() {
413 int iconId = R.drawable.stat_sys_data_bluetooth;
414 String contentDescription =
415 mContext.getString(R.string.accessibility_quick_settings_bluetooth_on);
416 boolean bluetoothVisible = false;
417 if (mBluetooth != null) {
418 if (mBluetooth.isBluetoothConnected()) {
419 iconId = R.drawable.stat_sys_data_bluetooth_connected;
420 contentDescription = mContext.getString(R.string.accessibility_bluetooth_connected);
421 bluetoothVisible = mBluetooth.isBluetoothEnabled();
425 mIconController.setIcon(mSlotBluetooth, iconId, contentDescription);
426 mIconController.setIconVisibility(mSlotBluetooth, bluetoothVisible);
429 private final void updateTTY() {
430 TelecomManager telecomManager =
431 (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
432 if (telecomManager == null) {
433 updateTTY(TelecomManager.TTY_MODE_OFF);
435 updateTTY(telecomManager.getCurrentTtyMode());
439 private final void updateTTY(int currentTtyMode) {
440 boolean enabled = currentTtyMode != TelecomManager.TTY_MODE_OFF;
442 if (DEBUG) Log.v(TAG, "updateTTY: enabled: " + enabled);
446 if (DEBUG) Log.v(TAG, "updateTTY: set TTY on");
447 mIconController.setIcon(mSlotTty, R.drawable.stat_sys_tty_mode,
448 mContext.getString(R.string.accessibility_tty_enabled));
449 mIconController.setIconVisibility(mSlotTty, true);
452 if (DEBUG) Log.v(TAG, "updateTTY: set TTY off");
453 mIconController.setIconVisibility(mSlotTty, false);
457 private void updateCast() {
458 boolean isCasting = false;
459 for (CastDevice device : mCast.getCastDevices()) {
460 if (device.state == CastDevice.STATE_CONNECTING
461 || device.state == CastDevice.STATE_CONNECTED) {
466 if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting);
467 mHandler.removeCallbacks(mRemoveCastIconRunnable);
469 mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast,
470 mContext.getString(R.string.accessibility_casting));
471 mIconController.setIconVisibility(mSlotCast, true);
473 // don't turn off the screen-record icon for a few seconds, just to make sure the user
475 if (DEBUG) Log.v(TAG, "updateCast: hiding icon in 3 sec...");
476 mHandler.postDelayed(mRemoveCastIconRunnable, 3000);
480 private void updateManagedProfile() {
481 // getLastResumedActivityUserId needds to acquire the AM lock, which may be contended in
482 // some cases. Since it doesn't really matter here whether it's updated in this frame
483 // or in the next one, we call this method from our UI offload thread.
484 mUiOffloadThread.submit(() -> {
487 userId = ActivityManager.getService().getLastResumedActivityUserId();
488 boolean isManagedProfile = mUserManager.isManagedProfile(userId);
489 mHandler.post(() -> {
490 final boolean showIcon;
491 if (isManagedProfile &&
492 (!mKeyguardMonitor.isShowing() || mKeyguardMonitor.isOccluded())) {
494 mIconController.setIcon(mSlotManagedProfile,
495 R.drawable.stat_sys_managed_profile_status,
496 mContext.getString(R.string.accessibility_managed_profile));
500 if (mManagedProfileIconVisible != showIcon) {
501 mIconController.setIconVisibility(mSlotManagedProfile, showIcon);
502 mManagedProfileIconVisible = showIcon;
505 } catch (RemoteException e) {
506 Log.w(TAG, "updateManagedProfile: ", e);
511 private void updateForegroundInstantApps() {
512 NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
513 ArraySet<Pair<String, Integer>> notifs = new ArraySet<>(mCurrentNotifs);
514 IPackageManager pm = AppGlobals.getPackageManager();
515 mCurrentNotifs.clear();
516 mUiOffloadThread.submit(() -> {
518 final StackInfo focusedStack = ActivityManager.getService().getFocusedStackInfo();
519 if (focusedStack != null) {
520 final int windowingMode =
521 focusedStack.configuration.windowConfiguration.getWindowingMode();
522 if (windowingMode == WINDOWING_MODE_FULLSCREEN
523 || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
524 checkStack(focusedStack, notifs, noMan, pm);
527 if (mDockedStackExists) {
528 checkStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED,
531 } catch (RemoteException e) {
532 e.rethrowFromSystemServer();
534 // Cancel all the leftover notifications that don't have a foreground process anymore.
535 notifs.forEach(v -> noMan.cancelAsUser(v.first, SystemMessage.NOTE_INSTANT_APPS,
536 new UserHandle(v.second)));
540 private void checkStack(int windowingMode, int activityType,
541 ArraySet<Pair<String, Integer>> notifs, NotificationManager noMan, IPackageManager pm) {
543 final StackInfo info =
544 ActivityManager.getService().getStackInfo(windowingMode, activityType);
545 checkStack(info, notifs, noMan, pm);
546 } catch (RemoteException e) {
547 e.rethrowFromSystemServer();
550 private void checkStack(StackInfo info, ArraySet<Pair<String, Integer>> notifs,
551 NotificationManager noMan, IPackageManager pm) {
553 if (info == null || info.topActivity == null) return;
554 String pkg = info.topActivity.getPackageName();
555 if (!hasNotif(notifs, pkg, info.userId)) {
556 // TODO: Optimize by not always needing to get application info.
557 // Maybe cache non-ephemeral packages?
558 ApplicationInfo appInfo = pm.getApplicationInfo(pkg,
559 PackageManager.MATCH_UNINSTALLED_PACKAGES, info.userId);
560 if (appInfo.isInstantApp()) {
561 postEphemeralNotif(pkg, info.userId, appInfo, noMan, info.taskIds[info.taskIds.length - 1]);
564 } catch (RemoteException e) {
565 e.rethrowFromSystemServer();
569 private void postEphemeralNotif(String pkg, int userId, ApplicationInfo appInfo,
570 NotificationManager noMan, int taskId) {
571 final Bundle extras = new Bundle();
572 extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
573 mContext.getString(R.string.instant_apps));
574 mCurrentNotifs.add(new Pair<>(pkg, userId));
575 String message = mContext.getString(R.string.instant_apps_message);
576 PendingIntent appInfoAction = PendingIntent.getActivity(mContext, 0,
577 new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
578 .setData(Uri.fromParts("package", pkg, null)), 0);
579 Action action = new Notification.Action.Builder(null, mContext.getString(R.string.app_info),
580 appInfoAction).build();
582 Intent browserIntent = getTaskIntent(taskId, userId);
583 Notification.Builder builder = new Notification.Builder(mContext, NotificationChannels.GENERAL);
584 if (browserIntent != null && browserIntent.isWebIntent()) {
585 // Make sure that this doesn't resolve back to an instant app
586 browserIntent.setComponent(null)
588 .addFlags(Intent.FLAG_IGNORE_EPHEMERAL)
589 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
591 PendingIntent pendingIntent = PendingIntent.getActivity(mContext,
592 0 /* requestCode */, browserIntent, 0 /* flags */);
593 ComponentName aiaComponent = null;
595 aiaComponent = AppGlobals.getPackageManager().getInstantAppInstallerComponent();
596 } catch (RemoteException e) {
597 e.rethrowFromSystemServer();
599 Intent goToWebIntent = new Intent()
600 .setComponent(aiaComponent)
601 .setAction(Intent.ACTION_VIEW)
602 .addCategory(Intent.CATEGORY_BROWSABLE)
603 .addCategory("unique:" + System.currentTimeMillis())
604 .putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName)
605 .putExtra(Intent.EXTRA_VERSION_CODE, (int) (appInfo.versionCode & 0x7fffffff))
606 .putExtra(Intent.EXTRA_LONG_VERSION_CODE, appInfo.versionCode)
607 .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent)
608 .putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, pendingIntent);
610 PendingIntent webPendingIntent = PendingIntent.getActivity(mContext, 0, goToWebIntent, 0);
611 Action webAction = new Notification.Action.Builder(null, mContext.getString(R.string.go_to_web),
612 webPendingIntent).build();
613 builder.addAction(webAction);
616 noMan.notifyAsUser(pkg, SystemMessage.NOTE_INSTANT_APPS, builder
619 .setContentIntent(appInfoAction)
620 .setColor(mContext.getColor(R.color.instant_apps_color))
621 .setContentTitle(appInfo.loadLabel(mContext.getPackageManager()))
622 .setLargeIcon(Icon.createWithResource(pkg, appInfo.icon))
623 .setSmallIcon(Icon.createWithResource(mContext.getPackageName(),
624 R.drawable.instant_icon))
625 .setContentText(message)
628 new UserHandle(userId));
631 private Intent getTaskIntent(int taskId, int userId) {
633 final List<ActivityManager.RecentTaskInfo> tasks =
634 ActivityManager.getService().getRecentTasks(
635 NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId).getList();
636 for (int i = 0; i < tasks.size(); i++) {
637 if (tasks.get(i).id == taskId) {
638 return tasks.get(i).baseIntent;
641 } catch (RemoteException e) {
647 private boolean hasNotif(ArraySet<Pair<String, Integer>> notifs, String pkg, int userId) {
648 Pair<String, Integer> key = new Pair<>(pkg, userId);
649 if (notifs.remove(key)) {
650 mCurrentNotifs.add(key);
656 private final SynchronousUserSwitchObserver mUserSwitchListener =
657 new SynchronousUserSwitchObserver() {
659 public void onUserSwitching(int newUserId) throws RemoteException {
660 mHandler.post(() -> mUserInfoController.reloadUserInfo());
664 public void onUserSwitchComplete(int newUserId) throws RemoteException {
665 mHandler.post(() -> {
667 updateManagedProfile();
668 updateForegroundInstantApps();
673 private final HotspotController.Callback mHotspotCallback = new HotspotController.Callback() {
675 public void onHotspotChanged(boolean enabled, int numDevices) {
676 mIconController.setIconVisibility(mSlotHotspot, enabled);
680 private final CastController.Callback mCastCallback = new CastController.Callback() {
682 public void onCastDevicesChanged() {
687 private final NextAlarmController.NextAlarmChangeCallback mNextAlarmCallback =
688 new NextAlarmController.NextAlarmChangeCallback() {
690 public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
696 public void appTransitionStarting(long startTime, long duration, boolean forced) {
697 updateManagedProfile();
698 updateForegroundInstantApps();
702 public void onKeyguardShowingChanged() {
703 updateManagedProfile();
704 updateForegroundInstantApps();
708 public void onUserSetupChanged() {
709 boolean userSetup = mProvisionedController.isUserSetup(
710 mProvisionedController.getCurrentUser());
711 if (mCurrentUserSetup == userSetup) return;
712 mCurrentUserSetup = userSetup;
717 public void preloadRecentApps() {
718 updateForegroundInstantApps();
722 public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
723 boolean portrait = RotationLockTile.isCurrentOrientationLockPortrait(
724 mRotationLockController, mContext);
725 if (rotationLocked) {
727 mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_portrait,
728 mContext.getString(R.string.accessibility_rotation_lock_on_portrait));
730 mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_landscape,
731 mContext.getString(R.string.accessibility_rotation_lock_on_landscape));
733 mIconController.setIconVisibility(mSlotRotate, true);
735 mIconController.setIconVisibility(mSlotRotate, false);
739 private void updateHeadsetPlug(Intent intent) {
740 boolean connected = intent.getIntExtra("state", 0) != 0;
741 boolean hasMic = intent.getIntExtra("microphone", 0) != 0;
743 String contentDescription = mContext.getString(hasMic
744 ? R.string.accessibility_status_bar_headset
745 : R.string.accessibility_status_bar_headphones);
746 mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.ic_headset_mic
747 : R.drawable.ic_headset, contentDescription);
748 mIconController.setIconVisibility(mSlotHeadset, true);
750 mIconController.setIconVisibility(mSlotHeadset, false);
755 public void onDataSaverChanged(boolean isDataSaving) {
756 mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
759 private final SysUiTaskStackChangeListener mTaskListener = new SysUiTaskStackChangeListener() {
761 public void onTaskStackChanged() {
762 // Listen for changes to stacks and then check which instant apps are foreground.
763 updateForegroundInstantApps();
767 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
769 public void onReceive(Context context, Intent intent) {
770 String action = intent.getAction();
772 case AudioManager.RINGER_MODE_CHANGED_ACTION:
773 case AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION:
776 case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
777 // Avoid rebroadcast because SysUI is direct boot aware.
778 if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK,
782 updateSimState(intent);
784 case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:
785 updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
786 TelecomManager.TTY_MODE_OFF));
788 case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
789 case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
790 case Intent.ACTION_MANAGED_PROFILE_REMOVED:
791 updateManagedProfile();
793 case AudioManager.ACTION_HEADSET_PLUG:
794 updateHeadsetPlug(intent);
800 private Runnable mRemoveCastIconRunnable = new Runnable() {
803 if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW");
804 mIconController.setIconVisibility(mSlotCast, false);