1 /*
2  * Copyright (C) 2008 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.phone;
18 
19 import android.content.Context;
20 import android.os.AsyncResult;
21 import android.os.Bundle;
22 import android.os.Handler;
23 import android.os.Message;
24 import android.os.PersistableBundle;
25 import android.telephony.CarrierConfigManager;
26 import android.telephony.TelephonyManager;
27 import android.text.Editable;
28 import android.text.Spannable;
29 import android.text.TextUtils;
30 import android.text.TextWatcher;
31 import android.text.method.DialerKeyListener;
32 import android.util.Log;
33 import android.view.KeyEvent;
34 import android.view.View;
35 import android.widget.Button;
36 import android.widget.EditText;
37 import android.widget.LinearLayout;
38 import android.widget.TextView;
39 
40 import com.android.internal.telephony.Phone;
41 import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
42 
43 /**
44  * "SIM network unlock" PIN entry screen.
45  *
46  * @see PhoneGlobals.EVENT_SIM_NETWORK_LOCKED
47  *
48  * TODO: This UI should be part of the lock screen, not the
49  * phone app (see bug 1804111).
50  */
51 public class IccNetworkDepersonalizationPanel extends IccPanel {
52 
53     /**
54      * Tracks whether there is an instance of the network depersonalization dialog showing or not.
55      * Ensures only a single instance of the dialog is visible.
56      */
57     private static boolean [] sShowingDialog =
58             new boolean[TelephonyManager.getDefault().getSupportedModemCount()];
59 
60     //debug constants
61     private static final boolean DBG = false;
62 
63     //events
64     private static final int EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT = 100;
65 
66     private Phone mPhone;
67     private int mPersoSubtype;
68     private static IccNetworkDepersonalizationPanel [] sNdpPanel =
69             new IccNetworkDepersonalizationPanel[
70                     TelephonyManager.getDefault().getSupportedModemCount()];
71 
72     //UI elements
73     private EditText     mPinEntry;
74     private LinearLayout mEntryPanel;
75     private LinearLayout mStatusPanel;
76     private TextView     mPersoSubtypeText;
77     private PersoSubState mPersoSubState;
78     private TextView     mStatusText;
79 
80     private Button       mUnlockButton;
81     private Button       mDismissButton;
82 
83     enum statusType {
84         ENTRY,
85         IN_PROGRESS,
86         ERROR,
87         SUCCESS
88     }
89 
90     /**
91      * Shows the network depersonalization dialog, but only if it is not already visible.
92      */
showDialog(Phone phone, int subType)93     public static void showDialog(Phone phone, int subType) {
94         int phoneId = phone == null ? 0: phone.getPhoneId();
95         if (phoneId >= sShowingDialog.length) {
96             Log.e(TAG, "[IccNetworkDepersonalizationPanel] showDialog; invalid phoneId" + phoneId);
97             return;
98         }
99         if (sShowingDialog[phoneId]) {
100             Log.i(TAG, "[IccNetworkDepersonalizationPanel] - showDialog; skipped already shown.");
101             return;
102         }
103         Log.i(TAG, "[IccNetworkDepersonalizationPanel] - showDialog; showing dialog.");
104         sShowingDialog[phoneId] = true;
105         sNdpPanel[phoneId] = new IccNetworkDepersonalizationPanel(PhoneGlobals.getInstance(),
106                 phone, subType);
107         sNdpPanel[phoneId].show();
108     }
109 
dialogDismiss(int phoneId)110     public static void dialogDismiss(int phoneId) {
111         if (phoneId >= sShowingDialog.length) {
112             Log.e(TAG, "[IccNetworkDepersonalizationPanel] - dismiss; invalid phoneId " + phoneId);
113             return;
114         }
115         if (sNdpPanel[phoneId] != null && sShowingDialog[phoneId]) {
116             sNdpPanel[phoneId].dismiss();
117         }
118     }
119 
120     //private textwatcher to control text entry.
121     private TextWatcher mPinEntryWatcher = new TextWatcher() {
122         public void beforeTextChanged(CharSequence buffer, int start, int olen, int nlen) {
123         }
124 
125         public void onTextChanged(CharSequence buffer, int start, int olen, int nlen) {
126         }
127 
128         public void afterTextChanged(Editable buffer) {
129             if (SpecialCharSequenceMgr.handleChars(
130                     getContext(), buffer.toString())) {
131                 mPinEntry.getText().clear();
132             }
133         }
134     };
135 
136     //handler for unlock function results
137     private Handler mHandler = new Handler() {
138         public void handleMessage(Message msg) {
139             if (msg.what == EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT) {
140                 AsyncResult res = (AsyncResult) msg.obj;
141                 if (res.exception != null) {
142                     if (DBG) log("network depersonalization request failure.");
143                     displayStatus(statusType.ERROR.name());
144                     postDelayed(new Runnable() {
145                         public void run() {
146                             hideAlert();
147                             mPinEntry.getText().clear();
148                             mPinEntry.requestFocus();
149                         }
150                     }, 3000);
151                 } else {
152                     if (DBG) log("network depersonalization success.");
153                     displayStatus(statusType.SUCCESS.name());
154                     postDelayed(new Runnable() {
155                         public void run() {
156                             dismiss();
157                         }
158                     }, 3000);
159                 }
160             }
161         }
162     };
163 
164 
165     //constructor
IccNetworkDepersonalizationPanel(Context context)166     public IccNetworkDepersonalizationPanel(Context context) {
167         super(context);
168         mPhone = PhoneGlobals.getPhone();
169         mPersoSubtype = PersoSubState.PERSOSUBSTATE_SIM_NETWORK.ordinal();
170     }
171 
172     //constructor
IccNetworkDepersonalizationPanel(Context context, Phone phone, int subtype)173     public IccNetworkDepersonalizationPanel(Context context, Phone phone,
174             int subtype) {
175         super(context);
176         mPhone = phone == null ? PhoneGlobals.getPhone() : phone;
177         mPersoSubtype = subtype;
178     }
179 
180     @Override
onCreate(Bundle icicle)181     protected void onCreate(Bundle icicle) {
182         super.onCreate(icicle);
183         setContentView(R.layout.sim_ndp);
184 
185         // PIN entry text field
186         mPinEntry = (EditText) findViewById(R.id.pin_entry);
187         mPinEntry.setKeyListener(DialerKeyListener.getInstance());
188         mPinEntry.setOnClickListener(mUnlockListener);
189 
190         // Attach the textwatcher
191         CharSequence text = mPinEntry.getText();
192         Spannable span = (Spannable) text;
193         span.setSpan(mPinEntryWatcher, 0, text.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
194 
195         mEntryPanel = (LinearLayout) findViewById(R.id.entry_panel);
196         mPersoSubtypeText = (TextView) findViewById(R.id.perso_subtype_text);
197         displayStatus(statusType.ENTRY.name());
198 
199         mUnlockButton = (Button) findViewById(R.id.ndp_unlock);
200         mUnlockButton.setOnClickListener(mUnlockListener);
201 
202         // The "Dismiss" button is present in some (but not all) products,
203         // based on the "KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL" variable.
204         mDismissButton = (Button) findViewById(R.id.ndp_dismiss);
205         PersistableBundle carrierConfig = PhoneGlobals.getInstance().getCarrierConfig();
206         if (carrierConfig.getBoolean(
207                 CarrierConfigManager.KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL)) {
208             if (DBG) log("Enabling 'Dismiss' button...");
209             mDismissButton.setVisibility(View.VISIBLE);
210             mDismissButton.setOnClickListener(mDismissListener);
211         } else {
212             if (DBG) log("Removing 'Dismiss' button...");
213             mDismissButton.setVisibility(View.GONE);
214         }
215 
216         //status panel is used since we're having problems with the alert dialog.
217         mStatusPanel = (LinearLayout) findViewById(R.id.status_panel);
218         mStatusText = (TextView) findViewById(R.id.status_text);
219     }
220 
221     @Override
onStart()222     protected void onStart() {
223         super.onStart();
224     }
225 
226     @Override
onStop()227     public void onStop() {
228         super.onStop();
229         Log.i(TAG, "[IccNetworkDepersonalizationPanel] - showDialog; hiding dialog.");
230         int phoneId = mPhone == null ? 0 : mPhone.getPhoneId();
231         if (phoneId >= sShowingDialog.length) {
232             Log.e(TAG, "[IccNetworkDepersonalizationPanel] - onStop; invalid phoneId " + phoneId);
233             return;
234         }
235         sShowingDialog[phoneId] = false;
236     }
237 
238     //Mirrors IccPinUnlockPanel.onKeyDown().
onKeyDown(int keyCode, KeyEvent event)239     public boolean onKeyDown(int keyCode, KeyEvent event) {
240         if (keyCode == KeyEvent.KEYCODE_BACK) {
241             return true;
242         }
243 
244         return super.onKeyDown(keyCode, event);
245     }
246 
247     View.OnClickListener mUnlockListener = new View.OnClickListener() {
248         public void onClick(View v) {
249             String pin = mPinEntry.getText().toString();
250 
251             if (TextUtils.isEmpty(pin)) {
252                 return;
253             }
254 
255             log("Requesting De-Personalization for subtype " + mPersoSubtype);
256 
257             try {
258                 mPhone.getIccCard().supplySimDepersonalization(mPersoSubState,pin,
259                         Message.obtain(mHandler, EVENT_ICC_NTWRK_DEPERSONALIZATION_RESULT));
260             } catch (NullPointerException ex) {
261                 log("NullPointerException @supplySimDepersonalization" + ex);
262             }
263             displayStatus(statusType.IN_PROGRESS.name());
264         }
265     };
266 
displayStatus(String type)267     private void displayStatus(String type) {
268         int label = 0;
269 
270         mPersoSubState = PersoSubState.values()[mPersoSubtype];
271         log("displayStatus mPersoSubState: " +mPersoSubState.name() +"type: " +type);
272 
273         label = getContext().getResources().getIdentifier(mPersoSubState.name()
274                 + "_" + type, "string", "android");
275 
276         if (label == 0) {
277             log ("Unable to get the PersoSubType string");
278             return;
279         }
280 
281         if(!PersoSubState.isPersoLocked(mPersoSubState)) {
282             log ("Unsupported Perso Subtype :" + mPersoSubState.name());
283             return;
284         }
285 
286         if (type == statusType.ENTRY.name()) {
287             String displayText = getContext().getString(label);
288             mPersoSubtypeText.setText(displayText);
289         } else {
290             mStatusText.setText(label);
291             mEntryPanel.setVisibility(View.GONE);
292             mStatusPanel.setVisibility(View.VISIBLE);
293         }
294     }
295 
hideAlert()296     private void hideAlert() {
297         mEntryPanel.setVisibility(View.VISIBLE);
298         mStatusPanel.setVisibility(View.GONE);
299     }
300 
301     View.OnClickListener mDismissListener = new View.OnClickListener() {
302         public void onClick(View v) {
303             if (DBG) log("mDismissListener: skipping depersonalization...");
304             dismiss();
305         }
306     };
307 
log(String msg)308     private void log(String msg) {
309         Log.d(TAG, "[IccNetworkDepersonalizationPanel] " + msg);
310     }
311 }
312