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.annotation.NonNull;
20 import android.content.Context;
21 import android.os.Handler;
22 import android.util.ArraySet;
23 import android.util.Log;
24 
25 import com.android.settingslib.mobile.TelephonyIcons;
26 import com.android.systemui.dagger.SysUISingleton;
27 import com.android.systemui.res.R;
28 import com.android.systemui.statusbar.connectivity.IconState;
29 import com.android.systemui.statusbar.connectivity.NetworkController;
30 import com.android.systemui.statusbar.connectivity.SignalCallback;
31 import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
32 import com.android.systemui.statusbar.policy.SecurityController;
33 import com.android.systemui.tuner.TunerService;
34 import com.android.systemui.tuner.TunerService.Tunable;
35 import com.android.systemui.util.CarrierConfigTracker;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Objects;
40 
41 import javax.inject.Inject;
42 
43 /** Controls the signal policies for icons shown in the statusbar. **/
44 @SysUISingleton
45 public class StatusBarSignalPolicy implements SignalCallback,
46         SecurityController.SecurityControllerCallback, Tunable {
47     private static final String TAG = "StatusBarSignalPolicy";
48     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
49 
50     private final String mSlotAirplane;
51     private final String mSlotMobile;
52     private final String mSlotEthernet;
53     private final String mSlotVpn;
54     private final String mSlotNoCalling;
55     private final String mSlotCallStrength;
56 
57     private final Context mContext;
58     private final StatusBarIconController mIconController;
59     private final NetworkController mNetworkController;
60     private final SecurityController mSecurityController;
61     private final Handler mHandler = Handler.getMain();
62     private final CarrierConfigTracker mCarrierConfigTracker;
63     private final TunerService mTunerService;
64 
65     private boolean mHideAirplane;
66     private boolean mHideMobile;
67     private boolean mHideEthernet;
68     private boolean mActivityEnabled;
69 
70     // Track as little state as possible, and only for padding purposes
71     private boolean mIsAirplaneMode = false;
72 
73     private ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>();
74     private boolean mInitialized;
75 
76     @Inject
StatusBarSignalPolicy( Context context, StatusBarIconController iconController, CarrierConfigTracker carrierConfigTracker, NetworkController networkController, SecurityController securityController, TunerService tunerService )77     public StatusBarSignalPolicy(
78             Context context,
79             StatusBarIconController iconController,
80             CarrierConfigTracker carrierConfigTracker,
81             NetworkController networkController,
82             SecurityController securityController,
83             TunerService tunerService
84     ) {
85         mContext = context;
86 
87         mIconController = iconController;
88         mCarrierConfigTracker = carrierConfigTracker;
89         mNetworkController = networkController;
90         mSecurityController = securityController;
91         mTunerService = tunerService;
92 
93         mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
94         mSlotMobile   = mContext.getString(com.android.internal.R.string.status_bar_mobile);
95         mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet);
96         mSlotVpn      = mContext.getString(com.android.internal.R.string.status_bar_vpn);
97         mSlotNoCalling = mContext.getString(com.android.internal.R.string.status_bar_no_calling);
98         mSlotCallStrength =
99                 mContext.getString(com.android.internal.R.string.status_bar_call_strength);
100         mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
101     }
102 
103     /** Call to initialize and register this class with the system. */
init()104     public void init() {
105         if (mInitialized) {
106             return;
107         }
108         mInitialized = true;
109         mTunerService.addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
110         mNetworkController.addCallback(this);
111         mSecurityController.addCallback(this);
112     }
113 
destroy()114     public void destroy() {
115         mTunerService.removeTunable(this);
116         mNetworkController.removeCallback(this);
117         mSecurityController.removeCallback(this);
118     }
119 
updateVpn()120     private void updateVpn() {
121         boolean vpnVisible = mSecurityController.isVpnEnabled();
122         int vpnIconId = currentVpnIconId(
123                 mSecurityController.isVpnBranded(),
124                 mSecurityController.isVpnValidated());
125 
126         mIconController.setIcon(mSlotVpn, vpnIconId,
127                 mContext.getResources().getString(R.string.accessibility_vpn_on));
128         mIconController.setIconVisibility(mSlotVpn, vpnVisible);
129     }
130 
currentVpnIconId(boolean isBranded, boolean isValidated)131     private int currentVpnIconId(boolean isBranded, boolean isValidated) {
132         if (isBranded) {
133             return isValidated
134                     ? R.drawable.stat_sys_branded_vpn
135                     : R.drawable.stat_sys_no_internet_branded_vpn;
136         } else {
137             return isValidated
138                     ? R.drawable.stat_sys_vpn_ic
139                     : R.drawable.stat_sys_no_internet_vpn_ic;
140         }
141     }
142 
143     /**
144      * From SecurityController
145      */
146     @Override
onStateChanged()147     public void onStateChanged() {
148         mHandler.post(this::updateVpn);
149     }
150 
151     @Override
onTuningChanged(String key, String newValue)152     public void onTuningChanged(String key, String newValue) {
153         if (!StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
154             return;
155         }
156         ArraySet<String> hideList = StatusBarIconController.getIconHideList(mContext, newValue);
157         boolean hideAirplane = hideList.contains(mSlotAirplane);
158         boolean hideMobile = hideList.contains(mSlotMobile);
159         boolean hideEthernet = hideList.contains(mSlotEthernet);
160 
161         if (hideAirplane != mHideAirplane || hideMobile != mHideMobile
162                 || hideEthernet != mHideEthernet) {
163             mHideAirplane = hideAirplane;
164             mHideMobile = hideMobile;
165             mHideEthernet = hideEthernet;
166             // Re-register to get new callbacks.
167             mNetworkController.removeCallback(this);
168             mNetworkController.addCallback(this);
169         }
170     }
171 
172     @Override
setCallIndicator(@onNull IconState statusIcon, int subId)173     public void setCallIndicator(@NonNull IconState statusIcon, int subId) {
174         if (DEBUG) {
175             Log.d(TAG, "setCallIndicator: "
176                     + "statusIcon = " + statusIcon + ","
177                     + "subId = " + subId);
178         }
179         CallIndicatorIconState state = getNoCallingState(subId);
180         if (state == null) {
181             return;
182         }
183         if (statusIcon.icon == R.drawable.ic_shade_no_calling_sms) {
184             state.isNoCalling = statusIcon.visible;
185             state.noCallingDescription = statusIcon.contentDescription;
186         } else {
187             state.callStrengthResId = statusIcon.icon;
188             state.callStrengthDescription = statusIcon.contentDescription;
189         }
190         if (mCarrierConfigTracker.getCallStrengthConfig(subId)) {
191             mIconController.setCallStrengthIcons(mSlotCallStrength,
192                     CallIndicatorIconState.copyStates(mCallIndicatorStates));
193         } else {
194             mIconController.removeIcon(mSlotCallStrength, subId);
195         }
196         mIconController.setNoCallingIcons(mSlotNoCalling,
197                 CallIndicatorIconState.copyStates(mCallIndicatorStates));
198     }
199 
getNoCallingState(int subId)200     private CallIndicatorIconState getNoCallingState(int subId) {
201         for (CallIndicatorIconState state : mCallIndicatorStates) {
202             if (state.subId == subId) {
203                 return state;
204             }
205         }
206         Log.e(TAG, "Unexpected subscription " + subId);
207         return null;
208     }
209 
210     @Override
setEthernetIndicators(IconState state)211     public void setEthernetIndicators(IconState state) {
212         int resId = state.icon;
213         String description = state.contentDescription;
214 
215         if (resId > 0) {
216             mIconController.setIcon(mSlotEthernet, resId, description);
217             mIconController.setIconVisibility(mSlotEthernet, true);
218         } else {
219             mIconController.setIconVisibility(mSlotEthernet, false);
220         }
221     }
222 
223     @Override
setIsAirplaneMode(IconState icon)224     public void setIsAirplaneMode(IconState icon) {
225         if (DEBUG) {
226             Log.d(TAG, "setIsAirplaneMode: "
227                     + "icon = " + (icon == null ? "" : icon.toString()));
228         }
229         mIsAirplaneMode = icon.visible && !mHideAirplane;
230         int resId = icon.icon;
231         String description = icon.contentDescription;
232 
233         if (mIsAirplaneMode && resId > 0) {
234             mIconController.setIcon(mSlotAirplane, resId, description);
235             mIconController.setIconVisibility(mSlotAirplane, true);
236         } else {
237             mIconController.setIconVisibility(mSlotAirplane, false);
238         }
239     }
240 
241     /**
242      * Stores the statusbar state for no Calling & SMS.
243      */
244     public static class CallIndicatorIconState {
245         public boolean isNoCalling;
246         public int noCallingResId;
247         public int callStrengthResId;
248         public int subId;
249         public String noCallingDescription;
250         public String callStrengthDescription;
251 
CallIndicatorIconState(int subId)252         private CallIndicatorIconState(int subId) {
253             this.subId = subId;
254             this.noCallingResId = R.drawable.ic_shade_no_calling_sms;
255             this.callStrengthResId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
256         }
257 
258         @Override
equals(Object o)259         public boolean equals(Object o) {
260             // Skipping reference equality bc this should be more of a value type
261             if (o == null || getClass() != o.getClass()) {
262                 return false;
263             }
264             CallIndicatorIconState that = (CallIndicatorIconState) o;
265             return  isNoCalling == that.isNoCalling
266                     && noCallingResId == that.noCallingResId
267                     && callStrengthResId == that.callStrengthResId
268                     && subId == that.subId
269                     && noCallingDescription == that.noCallingDescription
270                     && callStrengthDescription == that.callStrengthDescription;
271 
272         }
273 
274         @Override
hashCode()275         public int hashCode() {
276             return Objects.hash(isNoCalling, noCallingResId,
277                     callStrengthResId, subId, noCallingDescription, callStrengthDescription);
278         }
279 
copyTo(CallIndicatorIconState other)280         private void copyTo(CallIndicatorIconState other) {
281             other.isNoCalling = isNoCalling;
282             other.noCallingResId = noCallingResId;
283             other.callStrengthResId = callStrengthResId;
284             other.subId = subId;
285             other.noCallingDescription = noCallingDescription;
286             other.callStrengthDescription = callStrengthDescription;
287         }
288 
copyStates( List<CallIndicatorIconState> inStates)289         private static List<CallIndicatorIconState> copyStates(
290                 List<CallIndicatorIconState> inStates) {
291             ArrayList<CallIndicatorIconState> outStates = new ArrayList<>();
292             for (CallIndicatorIconState state : inStates) {
293                 CallIndicatorIconState copy = new CallIndicatorIconState(state.subId);
294                 state.copyTo(copy);
295                 outStates.add(copy);
296             }
297             return outStates;
298         }
299     }
300 }
301