• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.keyguard;
18 
19 import java.util.List;
20 import java.util.Locale;
21 
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.content.res.TypedArray;
26 import android.net.ConnectivityManager;
27 import android.telephony.SubscriptionInfo;
28 import android.telephony.SubscriptionManager;
29 import android.text.TextUtils;
30 import android.text.method.SingleLineTransformationMethod;
31 import android.util.AttributeSet;
32 import android.util.Log;
33 import android.view.View;
34 import android.widget.TextView;
35 
36 import com.android.internal.telephony.IccCardConstants;
37 import com.android.internal.telephony.IccCardConstants.State;
38 import com.android.internal.telephony.TelephonyIntents;
39 import com.android.internal.widget.LockPatternUtils;
40 
41 public class CarrierText extends TextView {
42     private static final boolean DEBUG = KeyguardConstants.DEBUG;
43     private static final String TAG = "CarrierText";
44 
45     private static CharSequence mSeparator;
46 
47     private LockPatternUtils mLockPatternUtils;
48     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
49 
50     private KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
51         @Override
52         public void onRefreshCarrierInfo() {
53             updateCarrierText();
54         }
55 
56         public void onScreenTurnedOff(int why) {
57             setSelected(false);
58         };
59 
60         public void onScreenTurnedOn() {
61             setSelected(true);
62         };
63     };
64     /**
65      * The status of this lock screen. Primarily used for widgets on LockScreen.
66      */
67     private static enum StatusMode {
68         Normal, // Normal case (sim card present, it's not locked)
69         NetworkLocked, // SIM card is 'network locked'.
70         SimMissing, // SIM card is missing.
71         SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access
72         SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times
73         SimLocked, // SIM card is currently locked
74         SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure
75         SimNotReady; // SIM is not ready yet. May never be on devices w/o a SIM.
76     }
77 
CarrierText(Context context)78     public CarrierText(Context context) {
79         this(context, null);
80     }
81 
CarrierText(Context context, AttributeSet attrs)82     public CarrierText(Context context, AttributeSet attrs) {
83         super(context, attrs);
84         mLockPatternUtils = new LockPatternUtils(mContext);
85         boolean useAllCaps;
86         TypedArray a = context.getTheme().obtainStyledAttributes(
87                 attrs, R.styleable.CarrierText, 0, 0);
88         try {
89             useAllCaps = a.getBoolean(R.styleable.CarrierText_allCaps, false);
90         } finally {
91             a.recycle();
92         }
93         setTransformationMethod(new CarrierTextTransformationMethod(mContext, useAllCaps));
94     }
95 
updateCarrierText()96     protected void updateCarrierText() {
97         boolean allSimsMissing = true;
98         CharSequence displayText = null;
99 
100         List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false);
101         final int N = subs.size();
102         if (DEBUG) Log.d(TAG, "updateCarrierText(): " + N);
103         for (int i = 0; i < N; i++) {
104             State simState = mKeyguardUpdateMonitor.getSimState(subs.get(i).getSubscriptionId());
105             CharSequence carrierName = subs.get(i).getCarrierName();
106             CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
107             if (DEBUG) Log.d(TAG, "Handling " + simState + " " + carrierName);
108             if (carrierTextForSimState != null) {
109                 allSimsMissing = false;
110                 displayText = concatenate(displayText, carrierTextForSimState);
111             }
112         }
113         if (allSimsMissing) {
114             if (N != 0) {
115                 // Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
116                 // This depends on mPlmn containing the text "Emergency calls only" when the radio
117                 // has some connectivity. Otherwise, it should be null or empty and just show
118                 // "No SIM card"
119                 // Grab the first subscripton, because they all should contain the emergency text,
120                 // described above.
121                 displayText =  makeCarrierStringOnEmergencyCapable(
122                         getContext().getText(R.string.keyguard_missing_sim_message_short),
123                         subs.get(0).getCarrierName());
124             } else {
125                 // We don't have a SubscriptionInfo to get the emergency calls only from.
126                 // Grab it from the old sticky broadcast if possible instead. We can use it
127                 // here because no subscriptions are active, so we don't have
128                 // to worry about MSIM clashing.
129                 CharSequence text =
130                         getContext().getText(com.android.internal.R.string.emergency_calls_only);
131                 Intent i = getContext().registerReceiver(null,
132                         new IntentFilter(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION));
133                 if (i != null) {
134                     String spn = "";
135                     String plmn = "";
136                     if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false)) {
137                         spn = i.getStringExtra(TelephonyIntents.EXTRA_SPN);
138                     }
139                     if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false)) {
140                         plmn = i.getStringExtra(TelephonyIntents.EXTRA_PLMN);
141                     }
142                     if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn);
143                     text = concatenate(plmn, spn);
144                 }
145                 displayText =  makeCarrierStringOnEmergencyCapable(
146                         getContext().getText(R.string.keyguard_missing_sim_message_short), text);
147             }
148         }
149         setText(displayText);
150     }
151 
152     @Override
onFinishInflate()153     protected void onFinishInflate() {
154         super.onFinishInflate();
155         mSeparator = getResources().getString(
156                 com.android.internal.R.string.kg_text_message_separator);
157         final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
158         setSelected(screenOn); // Allow marquee to work.
159     }
160 
161     @Override
onAttachedToWindow()162     protected void onAttachedToWindow() {
163         super.onAttachedToWindow();
164         if (ConnectivityManager.from(mContext).isNetworkSupported(
165                 ConnectivityManager.TYPE_MOBILE)) {
166             mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
167             mKeyguardUpdateMonitor.registerCallback(mCallback);
168         } else {
169             // Don't listen and clear out the text when the device isn't a phone.
170             mKeyguardUpdateMonitor = null;
171             setText("");
172         }
173     }
174 
175     @Override
onDetachedFromWindow()176     protected void onDetachedFromWindow() {
177         super.onDetachedFromWindow();
178         if (mKeyguardUpdateMonitor != null) {
179             mKeyguardUpdateMonitor.removeCallback(mCallback);
180         }
181     }
182 
183     /**
184      * Top-level function for creating carrier text. Makes text based on simState, PLMN
185      * and SPN as well as device capabilities, such as being emergency call capable.
186      *
187      * @param simState
188      * @param text
189      * @param spn
190      * @return Carrier text if not in missing state, null otherwise.
191      */
getCarrierTextForSimState(IccCardConstants.State simState, CharSequence text)192     private CharSequence getCarrierTextForSimState(IccCardConstants.State simState,
193             CharSequence text) {
194         CharSequence carrierText = null;
195         StatusMode status = getStatusForIccState(simState);
196         switch (status) {
197             case Normal:
198                 carrierText = text;
199                 break;
200 
201             case SimNotReady:
202                 // Null is reserved for denoting missing, in this case we have nothing to display.
203                 carrierText = ""; // nothing to display yet.
204                 break;
205 
206             case NetworkLocked:
207                 carrierText = makeCarrierStringOnEmergencyCapable(
208                         mContext.getText(R.string.keyguard_network_locked_message), text);
209                 break;
210 
211             case SimMissing:
212                 carrierText = null;
213                 break;
214 
215             case SimPermDisabled:
216                 carrierText = getContext().getText(
217                         R.string.keyguard_permanent_disabled_sim_message_short);
218                 break;
219 
220             case SimMissingLocked:
221                 carrierText = null;
222                 break;
223 
224             case SimLocked:
225                 carrierText = makeCarrierStringOnEmergencyCapable(
226                         getContext().getText(R.string.keyguard_sim_locked_message),
227                         text);
228                 break;
229 
230             case SimPukLocked:
231                 carrierText = makeCarrierStringOnEmergencyCapable(
232                         getContext().getText(R.string.keyguard_sim_puk_locked_message),
233                         text);
234                 break;
235         }
236 
237         return carrierText;
238     }
239 
240     /*
241      * Add emergencyCallMessage to carrier string only if phone supports emergency calls.
242      */
makeCarrierStringOnEmergencyCapable( CharSequence simMessage, CharSequence emergencyCallMessage)243     private CharSequence makeCarrierStringOnEmergencyCapable(
244             CharSequence simMessage, CharSequence emergencyCallMessage) {
245         if (mLockPatternUtils.isEmergencyCallCapable()) {
246             return concatenate(simMessage, emergencyCallMessage);
247         }
248         return simMessage;
249     }
250 
251     /**
252      * Determine the current status of the lock screen given the SIM state and other stuff.
253      */
getStatusForIccState(IccCardConstants.State simState)254     private StatusMode getStatusForIccState(IccCardConstants.State simState) {
255         // Since reading the SIM may take a while, we assume it is present until told otherwise.
256         if (simState == null) {
257             return StatusMode.Normal;
258         }
259 
260         final boolean missingAndNotProvisioned =
261                 !KeyguardUpdateMonitor.getInstance(mContext).isDeviceProvisioned()
262                 && (simState == IccCardConstants.State.ABSENT ||
263                         simState == IccCardConstants.State.PERM_DISABLED);
264 
265         // Assume we're NETWORK_LOCKED if not provisioned
266         simState = missingAndNotProvisioned ? IccCardConstants.State.NETWORK_LOCKED : simState;
267         switch (simState) {
268             case ABSENT:
269                 return StatusMode.SimMissing;
270             case NETWORK_LOCKED:
271                 return StatusMode.SimMissingLocked;
272             case NOT_READY:
273                 return StatusMode.SimNotReady;
274             case PIN_REQUIRED:
275                 return StatusMode.SimLocked;
276             case PUK_REQUIRED:
277                 return StatusMode.SimPukLocked;
278             case READY:
279                 return StatusMode.Normal;
280             case PERM_DISABLED:
281                 return StatusMode.SimPermDisabled;
282             case UNKNOWN:
283                 return StatusMode.SimMissing;
284         }
285         return StatusMode.SimMissing;
286     }
287 
concatenate(CharSequence plmn, CharSequence spn)288     private static CharSequence concatenate(CharSequence plmn, CharSequence spn) {
289         final boolean plmnValid = !TextUtils.isEmpty(plmn);
290         final boolean spnValid = !TextUtils.isEmpty(spn);
291         if (plmnValid && spnValid) {
292             if (plmn.equals(spn)) {
293                 return plmn;
294             } else {
295                 return new StringBuilder().append(plmn).append(mSeparator).append(spn).toString();
296             }
297         } else if (plmnValid) {
298             return plmn;
299         } else if (spnValid) {
300             return spn;
301         } else {
302             return "";
303         }
304     }
305 
getCarrierHelpTextForSimState(IccCardConstants.State simState, String plmn, String spn)306     private CharSequence getCarrierHelpTextForSimState(IccCardConstants.State simState,
307             String plmn, String spn) {
308         int carrierHelpTextId = 0;
309         StatusMode status = getStatusForIccState(simState);
310         switch (status) {
311             case NetworkLocked:
312                 carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled;
313                 break;
314 
315             case SimMissing:
316                 carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long;
317                 break;
318 
319             case SimPermDisabled:
320                 carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions;
321                 break;
322 
323             case SimMissingLocked:
324                 carrierHelpTextId = R.string.keyguard_missing_sim_instructions;
325                 break;
326 
327             case Normal:
328             case SimLocked:
329             case SimPukLocked:
330                 break;
331         }
332 
333         return mContext.getText(carrierHelpTextId);
334     }
335 
336     private class CarrierTextTransformationMethod extends SingleLineTransformationMethod {
337         private final Locale mLocale;
338         private final boolean mAllCaps;
339 
CarrierTextTransformationMethod(Context context, boolean allCaps)340         public CarrierTextTransformationMethod(Context context, boolean allCaps) {
341             mLocale = context.getResources().getConfiguration().locale;
342             mAllCaps = allCaps;
343         }
344 
345         @Override
getTransformation(CharSequence source, View view)346         public CharSequence getTransformation(CharSequence source, View view) {
347             source = super.getTransformation(source, view);
348 
349             if (mAllCaps && source != null) {
350                 source = source.toString().toUpperCase(mLocale);
351             }
352 
353             return source;
354         }
355     }
356 }
357