1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
|
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.mobile;
import android.os.Handler;
import android.os.Looper;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.settingslib.Utils;
/**
* Tracks the mobile signal status for the SysUI and Settings.
*
* This class is not threadsafe. All the mobile statuses monitored by this class is stored in
* MobileStatus. Whoever uses this class should only rely on the MobileStatusTracker#Callback
* to get the latest mobile statuses. Do not get mobile statues directly from
* MobileStatusTracker#MobileStatus.
*/
public class MobileStatusTracker {
private static final String TAG = "MobileStatusTracker";
private final TelephonyManager mPhone;
private final SubscriptionInfo mSubscriptionInfo;
private final Callback mCallback;
private final MobileStatus mMobileStatus;
private final SubscriptionDefaults mDefaults;
private final Handler mReceiverHandler;
private final MobileTelephonyCallback mTelephonyCallback;
/**
* MobileStatusTracker constructors
*
* @param phone The TelephonyManager which corresponds to the subscription being monitored.
* @param receiverLooper The Looper on which the callback will be invoked.
* @param info The subscription being monitored.
* @param defaults The wrapper of the SubscriptionManager.
* @param callback The callback to notify any changes of the mobile status, users should only
* use this callback to get the latest mobile status.
*/
public MobileStatusTracker(TelephonyManager phone, Looper receiverLooper,
SubscriptionInfo info, SubscriptionDefaults defaults, Callback callback) {
mPhone = phone;
mReceiverHandler = new Handler(receiverLooper);
mTelephonyCallback = new MobileTelephonyCallback();
mSubscriptionInfo = info;
mDefaults = defaults;
mCallback = callback;
mMobileStatus = new MobileStatus();
updateDataSim();
mReceiverHandler.post(() -> mCallback.onMobileStatusChanged(
/* updateTelephony= */false, new MobileStatus(mMobileStatus)));
}
public MobileTelephonyCallback getTelephonyCallback() {
return mTelephonyCallback;
}
/**
* Config the MobileStatusTracker to start or stop monitoring platform signals.
*/
public void setListening(boolean listening) {
if (listening) {
mPhone.registerTelephonyCallback(mReceiverHandler::post, mTelephonyCallback);
} else {
mPhone.unregisterTelephonyCallback(mTelephonyCallback);
}
}
private void updateDataSim() {
int activeDataSubId = mDefaults.getActiveDataSubId();
if (SubscriptionManager.isValidSubscriptionId(activeDataSubId)) {
mMobileStatus.dataSim = activeDataSubId == mSubscriptionInfo.getSubscriptionId();
} else {
// There doesn't seem to be a data sim selected, however if
// there isn't a MobileSignalController with dataSim set, then
// QS won't get any callbacks and will be blank. Instead
// lets just assume we are the data sim (which will basically
// show one at random) in QS until one is selected. The user
// should pick one soon after, so we shouldn't be in this state
// for long.
mMobileStatus.dataSim = true;
}
}
private void setActivity(int activity) {
mMobileStatus.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT
|| activity == TelephonyManager.DATA_ACTIVITY_IN;
mMobileStatus.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT
|| activity == TelephonyManager.DATA_ACTIVITY_OUT;
}
public class MobileTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.ServiceStateListener,
TelephonyCallback.SignalStrengthsListener,
TelephonyCallback.DataConnectionStateListener,
TelephonyCallback.DataActivityListener,
TelephonyCallback.CarrierNetworkListener,
TelephonyCallback.ActiveDataSubscriptionIdListener,
TelephonyCallback.DisplayInfoListener{
@Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onSignalStrengthsChanged signalStrength=" + signalStrength
+ ((signalStrength == null) ? ""
: (" level=" + signalStrength.getLevel())));
}
mMobileStatus.signalStrength = signalStrength;
mCallback.onMobileStatusChanged(
/* updateTelephony= */true, new MobileStatus(mMobileStatus));
}
@Override
public void onServiceStateChanged(ServiceState state) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onServiceStateChanged voiceState="
+ (state == null ? "" : state.getState())
+ " dataState=" + (state == null ? "" : state.getDataRegistrationState()));
}
mMobileStatus.serviceState = state;
mCallback.onMobileStatusChanged(
/* updateTelephony= */true, new MobileStatus(mMobileStatus));
}
@Override
public void onDataConnectionStateChanged(int state, int networkType) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onDataConnectionStateChanged: state=" + state
+ " type=" + networkType);
}
mMobileStatus.dataState = state;
mCallback.onMobileStatusChanged(
/* updateTelephony= */true, new MobileStatus(mMobileStatus));
}
@Override
public void onDataActivity(int direction) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onDataActivity: direction=" + direction);
}
setActivity(direction);
mCallback.onMobileStatusChanged(
/* updateTelephony= */false, new MobileStatus(mMobileStatus));
}
@Override
public void onCarrierNetworkChange(boolean active) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onCarrierNetworkChange: active=" + active);
}
mMobileStatus.carrierNetworkChangeMode = active;
mCallback.onMobileStatusChanged(
/* updateTelephony= */true, new MobileStatus(mMobileStatus));
}
@Override
public void onActiveDataSubscriptionIdChanged(int subId) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onActiveDataSubscriptionIdChanged: subId=" + subId);
}
updateDataSim();
mCallback.onMobileStatusChanged(
/* updateTelephony= */true, new MobileStatus(mMobileStatus));
}
@Override
public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onDisplayInfoChanged: telephonyDisplayInfo=" + telephonyDisplayInfo);
}
mMobileStatus.telephonyDisplayInfo = telephonyDisplayInfo;
mCallback.onMobileStatusChanged(
/* updateTelephony= */ true, new MobileStatus(mMobileStatus));
}
}
/**
* Wrapper class of the SubscriptionManager, for mock testing purpose
*/
public static class SubscriptionDefaults {
public int getDefaultVoiceSubId() {
return SubscriptionManager.getDefaultVoiceSubscriptionId();
}
public int getDefaultDataSubId() {
return SubscriptionManager.getDefaultDataSubscriptionId();
}
public int getActiveDataSubId() {
return SubscriptionManager.getActiveDataSubscriptionId();
}
}
/**
* Wrapper class which contains all the mobile status tracked by MobileStatusTracker.
*/
public static class MobileStatus {
public boolean activityIn;
public boolean activityOut;
public boolean dataSim;
public boolean carrierNetworkChangeMode;
public int dataState = TelephonyManager.DATA_DISCONNECTED;
public ServiceState serviceState;
public SignalStrength signalStrength;
public TelephonyDisplayInfo telephonyDisplayInfo =
new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
/**
* Empty constructor
*/
public MobileStatus() { }
/**
* Copy constructors
*
* @param status Source MobileStatus
*/
public MobileStatus(MobileStatus status) {
copyFrom(status);
}
protected void copyFrom(MobileStatus status) {
activityIn = status.activityIn;
activityOut = status.activityOut;
dataSim = status.dataSim;
carrierNetworkChangeMode = status.carrierNetworkChangeMode;
dataState = status.dataState;
// We don't do deep copy for the below members since they may be Mockito instances.
serviceState = status.serviceState;
signalStrength = status.signalStrength;
telephonyDisplayInfo = status.telephonyDisplayInfo;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
return builder.append("[activityIn=").append(activityIn).append(',')
.append("activityOut=").append(activityOut).append(',')
.append("dataSim=").append(dataSim).append(',')
.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode).append(',')
.append("dataState=").append(dataState).append(',')
.append("serviceState=").append(serviceState == null ? ""
: "mVoiceRegState=" + serviceState.getState() + "("
+ ServiceState.rilServiceStateToString(serviceState.getState())
+ ")" + ", mDataRegState=" + serviceState.getDataRegState() + "("
+ ServiceState.rilServiceStateToString(
serviceState.getDataRegState()) + ")")
.append(',')
.append("signalStrength=").append(signalStrength == null ? ""
: signalStrength.getLevel()).append(',')
.append("telephonyDisplayInfo=").append(telephonyDisplayInfo == null ? ""
: telephonyDisplayInfo.toString()).append(']').toString();
}
}
/**
* Callback for notifying any changes of the mobile status.
*
* This callback will always be invoked on the receiverLooper which must be specified when
* MobileStatusTracker is constructed.
*/
public interface Callback {
/**
* Notify the mobile status has been updated.
*
* @param updateTelephony Whether needs to update other Telephony related parameters, this
* is only used by SysUI.
* @param mobileStatus Holds the latest mobile statuses
*/
void onMobileStatusChanged(boolean updateTelephony, MobileStatus mobileStatus);
}
}
|