Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 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.content.Context; |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 20 | import android.os.Handler; |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 21 | import android.telephony.SubscriptionInfo; |
| 22 | import android.util.ArraySet; |
| 23 | import android.util.Log; |
Gus Prevas | ab33679 | 2018-11-14 13:52:20 -0500 | [diff] [blame] | 24 | |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 25 | import com.android.systemui.Dependency; |
| 26 | import com.android.systemui.R; |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 27 | import com.android.systemui.statusbar.policy.NetworkController; |
| 28 | import com.android.systemui.statusbar.policy.NetworkController.IconState; |
| 29 | import com.android.systemui.statusbar.policy.NetworkControllerImpl; |
| 30 | import com.android.systemui.statusbar.policy.SecurityController; |
Luca Stefani | 4e41142 | 2018-08-31 22:05:08 +0200 | [diff] [blame] | 31 | import com.android.systemui.tuner.TunerService; |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 32 | import com.android.systemui.tuner.TunerService.Tunable; |
Gus Prevas | ab33679 | 2018-11-14 13:52:20 -0500 | [diff] [blame] | 33 | |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 34 | import java.util.ArrayList; |
| 35 | import java.util.List; |
| 36 | import java.util.Objects; |
| 37 | |
| 38 | |
| 39 | public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback, |
| 40 | SecurityController.SecurityControllerCallback, Tunable { |
| 41 | private static final String TAG = "StatusBarSignalPolicy"; |
| 42 | |
| 43 | private final String mSlotAirplane; |
| 44 | private final String mSlotMobile; |
| 45 | private final String mSlotWifi; |
| 46 | private final String mSlotEthernet; |
| 47 | private final String mSlotVpn; |
| 48 | |
| 49 | private final Context mContext; |
| 50 | private final StatusBarIconController mIconController; |
| 51 | private final NetworkController mNetworkController; |
| 52 | private final SecurityController mSecurityController; |
| 53 | private final Handler mHandler = Handler.getMain(); |
| 54 | |
| 55 | private boolean mBlockAirplane; |
| 56 | private boolean mBlockMobile; |
| 57 | private boolean mBlockWifi; |
| 58 | private boolean mBlockEthernet; |
| 59 | private boolean mActivityEnabled; |
| 60 | private boolean mForceBlockWifi; |
| 61 | |
| 62 | // Track as little state as possible, and only for padding purposes |
| 63 | private boolean mIsAirplaneMode = false; |
| 64 | private boolean mWifiVisible = false; |
| 65 | |
| 66 | private ArrayList<MobileIconState> mMobileStates = new ArrayList<MobileIconState>(); |
| 67 | private WifiIconState mWifiIconState = new WifiIconState(); |
| 68 | |
| 69 | public StatusBarSignalPolicy(Context context, StatusBarIconController iconController) { |
| 70 | mContext = context; |
| 71 | |
| 72 | mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); |
| 73 | mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); |
| 74 | mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi); |
| 75 | mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet); |
| 76 | mSlotVpn = mContext.getString(com.android.internal.R.string.status_bar_vpn); |
Evan Laird | 6fd0417 | 2018-04-23 11:37:04 -0400 | [diff] [blame] | 77 | mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity); |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 78 | |
| 79 | mIconController = iconController; |
| 80 | mNetworkController = Dependency.get(NetworkController.class); |
| 81 | mSecurityController = Dependency.get(SecurityController.class); |
| 82 | |
Luca Stefani | 4e41142 | 2018-08-31 22:05:08 +0200 | [diff] [blame] | 83 | Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST); |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 84 | mNetworkController.addCallback(this); |
| 85 | mSecurityController.addCallback(this); |
| 86 | } |
| 87 | |
| 88 | public void destroy() { |
Luca Stefani | 4e41142 | 2018-08-31 22:05:08 +0200 | [diff] [blame] | 89 | Dependency.get(TunerService.class).removeTunable(this); |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 90 | mNetworkController.removeCallback(this); |
| 91 | mSecurityController.removeCallback(this); |
| 92 | } |
| 93 | |
| 94 | private void updateVpn() { |
| 95 | boolean vpnVisible = mSecurityController.isVpnEnabled(); |
| 96 | int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded()); |
| 97 | |
Evan Laird | c5b79d9 | 2018-10-29 14:08:14 -0400 | [diff] [blame] | 98 | mIconController.setIcon(mSlotVpn, vpnIconId, |
| 99 | mContext.getResources().getString(R.string.accessibility_vpn_on)); |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 100 | mIconController.setIconVisibility(mSlotVpn, vpnVisible); |
| 101 | } |
| 102 | |
| 103 | private int currentVpnIconId(boolean isBranded) { |
| 104 | return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic; |
| 105 | } |
| 106 | |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 107 | /** |
| 108 | * From SecurityController |
| 109 | */ |
| 110 | @Override |
| 111 | public void onStateChanged() { |
| 112 | mHandler.post(this::updateVpn); |
| 113 | } |
| 114 | |
| 115 | @Override |
| 116 | public void onTuningChanged(String key, String newValue) { |
| 117 | if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) { |
| 118 | return; |
| 119 | } |
| 120 | ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(newValue); |
| 121 | boolean blockAirplane = blockList.contains(mSlotAirplane); |
| 122 | boolean blockMobile = blockList.contains(mSlotMobile); |
| 123 | boolean blockWifi = blockList.contains(mSlotWifi); |
| 124 | boolean blockEthernet = blockList.contains(mSlotEthernet); |
| 125 | |
| 126 | if (blockAirplane != mBlockAirplane || blockMobile != mBlockMobile |
| 127 | || blockEthernet != mBlockEthernet || blockWifi != mBlockWifi) { |
| 128 | mBlockAirplane = blockAirplane; |
| 129 | mBlockMobile = blockMobile; |
| 130 | mBlockEthernet = blockEthernet; |
| 131 | mBlockWifi = blockWifi || mForceBlockWifi; |
| 132 | // Re-register to get new callbacks. |
| 133 | mNetworkController.removeCallback(this); |
Luca Stefani | 4e41142 | 2018-08-31 22:05:08 +0200 | [diff] [blame] | 134 | mNetworkController.addCallback(this); |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 135 | } |
| 136 | } |
| 137 | |
| 138 | @Override |
| 139 | public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, |
| 140 | boolean activityIn, boolean activityOut, String description, boolean isTransient, |
| 141 | String statusLabel) { |
| 142 | |
| 143 | boolean visible = statusIcon.visible && !mBlockWifi; |
| 144 | boolean in = activityIn && mActivityEnabled && visible; |
| 145 | boolean out = activityOut && mActivityEnabled && visible; |
| 146 | |
Evan Laird | af56a33 | 2018-05-23 19:52:34 -0400 | [diff] [blame] | 147 | WifiIconState newState = mWifiIconState.copy(); |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 148 | |
Evan Laird | af56a33 | 2018-05-23 19:52:34 -0400 | [diff] [blame] | 149 | newState.visible = visible; |
| 150 | newState.resId = statusIcon.icon; |
| 151 | newState.activityIn = in; |
| 152 | newState.activityOut = out; |
| 153 | newState.slot = mSlotWifi; |
| 154 | newState.airplaneSpacerVisible = mIsAirplaneMode; |
| 155 | newState.contentDescription = statusIcon.contentDescription; |
| 156 | |
| 157 | MobileIconState first = getFirstMobileState(); |
| 158 | newState.signalSpacerVisible = first != null && first.typeId != 0; |
| 159 | |
| 160 | updateWifiIconWithState(newState); |
| 161 | mWifiIconState = newState; |
| 162 | } |
| 163 | |
| 164 | private void updateShowWifiSignalSpacer(WifiIconState state) { |
| 165 | MobileIconState first = getFirstMobileState(); |
| 166 | state.signalSpacerVisible = first != null && first.typeId != 0; |
| 167 | } |
| 168 | |
| 169 | private void updateWifiIconWithState(WifiIconState state) { |
| 170 | if (state.visible && state.resId > 0) { |
| 171 | mIconController.setSignalIcon(mSlotWifi, state); |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 172 | mIconController.setIconVisibility(mSlotWifi, true); |
| 173 | } else { |
| 174 | mIconController.setIconVisibility(mSlotWifi, false); |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | @Override |
| 179 | public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, |
Evan Laird | 83c87e5 | 2019-09-24 19:14:05 -0400 | [diff] [blame] | 180 | int qsType, boolean activityIn, boolean activityOut, |
| 181 | CharSequence typeContentDescription, |
| 182 | CharSequence typeContentDescriptionHtml, CharSequence description, |
| 183 | boolean isWide, int subId, boolean roaming) { |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 184 | MobileIconState state = getState(subId); |
| 185 | if (state == null) { |
| 186 | return; |
| 187 | } |
| 188 | |
Evan Laird | af56a33 | 2018-05-23 19:52:34 -0400 | [diff] [blame] | 189 | // Visibility of the data type indicator changed |
| 190 | boolean typeChanged = statusType != state.typeId && (statusType == 0 || state.typeId == 0); |
Amin Shaikh | 4c53c8a | 2019-06-06 18:22:07 +0000 | [diff] [blame] | 191 | |
| 192 | state.visible = statusIcon.visible && !mBlockMobile; |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 193 | state.strengthId = statusIcon.icon; |
| 194 | state.typeId = statusType; |
| 195 | state.contentDescription = statusIcon.contentDescription; |
| 196 | state.typeContentDescription = typeContentDescription; |
| 197 | state.roaming = roaming; |
| 198 | state.activityIn = activityIn && mActivityEnabled; |
| 199 | state.activityOut = activityOut && mActivityEnabled; |
| 200 | |
| 201 | // Always send a copy to maintain value type semantics |
| 202 | mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates)); |
Evan Laird | af56a33 | 2018-05-23 19:52:34 -0400 | [diff] [blame] | 203 | |
| 204 | if (typeChanged) { |
| 205 | WifiIconState wifiCopy = mWifiIconState.copy(); |
| 206 | updateShowWifiSignalSpacer(wifiCopy); |
| 207 | if (!Objects.equals(wifiCopy, mWifiIconState)) { |
| 208 | updateWifiIconWithState(wifiCopy); |
| 209 | mWifiIconState = wifiCopy; |
| 210 | } |
| 211 | } |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 212 | } |
| 213 | |
| 214 | private MobileIconState getState(int subId) { |
| 215 | for (MobileIconState state : mMobileStates) { |
| 216 | if (state.subId == subId) { |
| 217 | return state; |
| 218 | } |
| 219 | } |
| 220 | Log.e(TAG, "Unexpected subscription " + subId); |
| 221 | return null; |
| 222 | } |
| 223 | |
Evan Laird | af56a33 | 2018-05-23 19:52:34 -0400 | [diff] [blame] | 224 | private MobileIconState getFirstMobileState() { |
| 225 | if (mMobileStates.size() > 0) { |
| 226 | return mMobileStates.get(0); |
| 227 | } |
| 228 | |
| 229 | return null; |
| 230 | } |
| 231 | |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 232 | |
| 233 | /** |
| 234 | * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators |
| 235 | * so we don't have to update the icon manager at this point, just remove the old ones |
| 236 | * @param subs list of mobile subscriptions, displayed as mobile data indicators (max 8) |
| 237 | */ |
| 238 | @Override |
| 239 | public void setSubs(List<SubscriptionInfo> subs) { |
| 240 | if (hasCorrectSubs(subs)) { |
| 241 | return; |
| 242 | } |
| 243 | |
| 244 | mIconController.removeAllIconsForSlot(mSlotMobile); |
| 245 | mMobileStates.clear(); |
| 246 | final int n = subs.size(); |
| 247 | for (int i = 0; i < n; i++) { |
| 248 | mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId())); |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | private boolean hasCorrectSubs(List<SubscriptionInfo> subs) { |
| 253 | final int N = subs.size(); |
| 254 | if (N != mMobileStates.size()) { |
| 255 | return false; |
| 256 | } |
| 257 | for (int i = 0; i < N; i++) { |
| 258 | if (mMobileStates.get(i).subId != subs.get(i).getSubscriptionId()) { |
| 259 | return false; |
| 260 | } |
| 261 | } |
| 262 | return true; |
| 263 | } |
| 264 | |
| 265 | @Override |
| 266 | public void setNoSims(boolean show, boolean simDetected) { |
| 267 | // Noop yay! |
| 268 | } |
| 269 | |
| 270 | |
| 271 | @Override |
| 272 | public void setEthernetIndicators(IconState state) { |
| 273 | boolean visible = state.visible && !mBlockEthernet; |
| 274 | int resId = state.icon; |
| 275 | String description = state.contentDescription; |
| 276 | |
| 277 | if (resId > 0) { |
| 278 | mIconController.setIcon(mSlotEthernet, resId, description); |
| 279 | mIconController.setIconVisibility(mSlotEthernet, true); |
| 280 | } else { |
| 281 | mIconController.setIconVisibility(mSlotEthernet, false); |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | @Override |
| 286 | public void setIsAirplaneMode(IconState icon) { |
| 287 | mIsAirplaneMode = icon.visible && !mBlockAirplane; |
| 288 | int resId = icon.icon; |
| 289 | String description = icon.contentDescription; |
| 290 | |
| 291 | if (mIsAirplaneMode && resId > 0) { |
| 292 | mIconController.setIcon(mSlotAirplane, resId, description); |
| 293 | mIconController.setIconVisibility(mSlotAirplane, true); |
| 294 | } else { |
| 295 | mIconController.setIconVisibility(mSlotAirplane, false); |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | @Override |
| 300 | public void setMobileDataEnabled(boolean enabled) { |
| 301 | // Don't care. |
| 302 | } |
| 303 | |
| 304 | private static abstract class SignalIconState { |
| 305 | public boolean visible; |
| 306 | public boolean activityOut; |
| 307 | public boolean activityIn; |
| 308 | public String slot; |
| 309 | public String contentDescription; |
| 310 | |
| 311 | @Override |
| 312 | public boolean equals(Object o) { |
| 313 | // Skipping reference equality bc this should be more of a value type |
| 314 | if (o == null || getClass() != o.getClass()) { |
| 315 | return false; |
| 316 | } |
| 317 | SignalIconState that = (SignalIconState) o; |
| 318 | return visible == that.visible && |
| 319 | activityOut == that.activityOut && |
| 320 | activityIn == that.activityIn && |
| 321 | Objects.equals(contentDescription, that.contentDescription) && |
| 322 | Objects.equals(slot, that.slot); |
| 323 | } |
| 324 | |
| 325 | @Override |
| 326 | public int hashCode() { |
| 327 | return Objects.hash(visible, activityOut, slot); |
| 328 | } |
| 329 | |
| 330 | protected void copyTo(SignalIconState other) { |
| 331 | other.visible = visible; |
| 332 | other.activityIn = activityIn; |
| 333 | other.activityOut = activityOut; |
| 334 | other.slot = slot; |
| 335 | other.contentDescription = contentDescription; |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | public static class WifiIconState extends SignalIconState{ |
| 340 | public int resId; |
| 341 | public boolean airplaneSpacerVisible; |
| 342 | public boolean signalSpacerVisible; |
| 343 | |
| 344 | @Override |
| 345 | public boolean equals(Object o) { |
| 346 | // Skipping reference equality bc this should be more of a value type |
| 347 | if (o == null || getClass() != o.getClass()) { |
| 348 | return false; |
| 349 | } |
| 350 | if (!super.equals(o)) { |
| 351 | return false; |
| 352 | } |
| 353 | WifiIconState that = (WifiIconState) o; |
| 354 | return resId == that.resId && |
| 355 | airplaneSpacerVisible == that.airplaneSpacerVisible && |
| 356 | signalSpacerVisible == that.signalSpacerVisible; |
| 357 | } |
| 358 | |
| 359 | public void copyTo(WifiIconState other) { |
| 360 | super.copyTo(other); |
| 361 | other.resId = resId; |
| 362 | other.airplaneSpacerVisible = airplaneSpacerVisible; |
| 363 | other.signalSpacerVisible = signalSpacerVisible; |
| 364 | } |
| 365 | |
| 366 | public WifiIconState copy() { |
| 367 | WifiIconState newState = new WifiIconState(); |
| 368 | copyTo(newState); |
| 369 | return newState; |
| 370 | } |
| 371 | |
| 372 | @Override |
| 373 | public int hashCode() { |
| 374 | return Objects.hash(super.hashCode(), |
| 375 | resId, airplaneSpacerVisible, signalSpacerVisible); |
| 376 | } |
| 377 | |
| 378 | @Override public String toString() { |
| 379 | return "WifiIconState(resId=" + resId + ", visible=" + visible + ")"; |
| 380 | } |
| 381 | } |
| 382 | |
| 383 | /** |
| 384 | * A little different. This one delegates to SignalDrawable instead of a specific resId |
| 385 | */ |
| 386 | public static class MobileIconState extends SignalIconState { |
| 387 | public int subId; |
| 388 | public int strengthId; |
| 389 | public int typeId; |
| 390 | public boolean roaming; |
| 391 | public boolean needsLeadingPadding; |
Evan Laird | 83c87e5 | 2019-09-24 19:14:05 -0400 | [diff] [blame] | 392 | public CharSequence typeContentDescription; |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 393 | |
| 394 | private MobileIconState(int subId) { |
| 395 | super(); |
| 396 | this.subId = subId; |
| 397 | } |
| 398 | |
| 399 | @Override |
| 400 | public boolean equals(Object o) { |
| 401 | if (o == null || getClass() != o.getClass()) { |
| 402 | return false; |
| 403 | } |
| 404 | if (!super.equals(o)) { |
| 405 | return false; |
| 406 | } |
| 407 | MobileIconState that = (MobileIconState) o; |
| 408 | return subId == that.subId && |
| 409 | strengthId == that.strengthId && |
| 410 | typeId == that.typeId && |
| 411 | roaming == that.roaming && |
| 412 | needsLeadingPadding == that.needsLeadingPadding && |
| 413 | Objects.equals(typeContentDescription, that.typeContentDescription); |
| 414 | } |
| 415 | |
| 416 | @Override |
| 417 | public int hashCode() { |
| 418 | |
| 419 | return Objects |
| 420 | .hash(super.hashCode(), subId, strengthId, typeId, roaming, needsLeadingPadding, |
| 421 | typeContentDescription); |
| 422 | } |
| 423 | |
Evan Laird | 56fb9f8 | 2018-04-24 21:11:15 -0400 | [diff] [blame] | 424 | public MobileIconState copy() { |
| 425 | MobileIconState copy = new MobileIconState(this.subId); |
| 426 | copyTo(copy); |
| 427 | return copy; |
| 428 | } |
| 429 | |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 430 | public void copyTo(MobileIconState other) { |
| 431 | super.copyTo(other); |
| 432 | other.subId = subId; |
| 433 | other.strengthId = strengthId; |
| 434 | other.typeId = typeId; |
| 435 | other.roaming = roaming; |
| 436 | other.needsLeadingPadding = needsLeadingPadding; |
| 437 | other.typeContentDescription = typeContentDescription; |
| 438 | } |
| 439 | |
| 440 | private static List<MobileIconState> copyStates(List<MobileIconState> inStates) { |
| 441 | ArrayList<MobileIconState> outStates = new ArrayList<>(); |
| 442 | for (MobileIconState state : inStates) { |
| 443 | MobileIconState copy = new MobileIconState(state.subId); |
| 444 | state.copyTo(copy); |
| 445 | outStates.add(copy); |
| 446 | } |
| 447 | |
| 448 | return outStates; |
| 449 | } |
| 450 | |
| 451 | @Override public String toString() { |
| 452 | return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming=" |
Evan Laird | 6dedbe6 | 2018-04-05 16:22:36 -0400 | [diff] [blame] | 453 | + roaming + ", typeId=" + typeId + ", visible=" + visible + ")"; |
Evan Laird | e1d13c9 | 2018-03-20 16:58:01 -0400 | [diff] [blame] | 454 | } |
| 455 | } |
| 456 | } |