1 /*
2  * Copyright (C) 2017 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.settings.widget;
18 
19 import android.app.AlertDialog;
20 import android.app.Dialog;
21 import android.app.Fragment;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.content.res.TypedArray;
25 import android.os.Bundle;
26 import android.text.Editable;
27 import android.text.InputType;
28 import android.text.TextUtils;
29 import android.text.TextWatcher;
30 import android.util.Log;
31 import android.view.inputmethod.InputMethodManager;
32 import android.widget.Button;
33 import android.widget.EditText;
34 
35 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
36 import com.android.internal.util.HexDump;
37 import com.android.settings.R;
38 
39 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
40 import java.security.MessageDigest;
41 import java.security.NoSuchAlgorithmException;
42 
43 public class CarrierDemoPasswordDialogFragment extends InstrumentedDialogFragment {
44 
45     private static final String TAG = "CarrierDemoPasswordDF";
46 
47     private MessageDigest mMessageDigest;
48 
CarrierDemoPasswordDialogFragment()49     public CarrierDemoPasswordDialogFragment() {
50         try {
51             mMessageDigest = MessageDigest.getInstance("SHA-256");
52         } catch (NoSuchAlgorithmException e) {
53             Log.e(TAG, "Unable to verify demo mode password", e);
54         }
55     }
56 
57     @Override
onCreateDialog(Bundle savedInstanceState)58     public Dialog onCreateDialog(Bundle savedInstanceState) {
59         final AlertDialog dialog = new AlertDialog.Builder(getContext())
60                 .setPositiveButton(R.string.retail_demo_reset_next,
61                         new DialogInterface.OnClickListener() {
62                     @Override
63                     public void onClick(DialogInterface dialog, int which) {
64                         final Fragment parentFragment = getParentFragment();
65                         if (parentFragment instanceof Callback
66                                 && which == DialogInterface.BUTTON_POSITIVE) {
67                             ((Callback) parentFragment).onPasswordVerified();
68                         }
69                     }
70                 })
71                 .setNegativeButton(android.R.string.cancel, null)
72                 .setMessage(R.string.retail_demo_reset_message)
73                 .setTitle(R.string.retail_demo_reset_title)
74                 .create();
75 
76         final Context context = dialog.getContext();
77         final EditText passwordField = new EditText(context);
78         passwordField.setSingleLine();
79         passwordField.setInputType(InputType.TYPE_CLASS_TEXT
80                 | InputType.TYPE_TEXT_VARIATION_PASSWORD);
81         passwordField.addTextChangedListener(new TextWatcher() {
82             @Override
83             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
84                 // no-op
85             }
86 
87             @Override
88             public void onTextChanged(CharSequence s, int start, int before, int count) {
89                 verifyPassword(dialog, passwordField.getText().toString());
90             }
91 
92             @Override
93             public void afterTextChanged(Editable s) {
94                 // no-op
95             }
96         });
97 
98         dialog.setOnShowListener(new DialogInterface.OnShowListener() {
99             @Override
100             public void onShow(DialogInterface dialogInterface) {
101                 verifyPassword(dialog, passwordField.getText().toString());
102                 passwordField.requestFocus();
103                 final InputMethodManager imm = (InputMethodManager) getContext().getSystemService(
104                         Context.INPUT_METHOD_SERVICE);
105                 imm.showSoftInput(passwordField, InputMethodManager.SHOW_IMPLICIT);
106             }
107         });
108         dialog.setCanceledOnTouchOutside(false);
109 
110         final TypedArray a = context.obtainStyledAttributes(
111                 new int[] { android.R.attr.dialogPreferredPadding });
112         final int sidePadding = a.getDimensionPixelSize(0, 0);
113         dialog.setView(passwordField, sidePadding, 0, sidePadding, 0);
114         a.recycle();
115 
116         return dialog;
117     }
118 
verifyPassword(AlertDialog dialog, String input)119     private void verifyPassword(AlertDialog dialog, String input) {
120         final Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
121         if (mMessageDigest == null || TextUtils.isEmpty(input)) {
122             positiveButton.setEnabled(false);
123             return;
124         }
125         final String passwordHash = getContext().getString(
126                 com.android.internal.R.string.config_carrierDemoModePassword);
127         if (passwordHash == null || TextUtils.isEmpty(passwordHash)) {
128             // This device does not support carrier demo mode.
129             return;
130         }
131         final byte[] inputDigest = mMessageDigest.digest(input.getBytes());
132         final String inputHash = HexDump.toHexString(inputDigest, 0, inputDigest.length, false);
133         positiveButton.setEnabled(TextUtils.equals(passwordHash, inputHash));
134     }
135 
136     @Override
getMetricsCategory()137     public int getMetricsCategory() {
138         return MetricsEvent.CARRIER_DEMO_MODE_PASSWORD;
139     }
140 
141     public interface Callback {
onPasswordVerified()142         void onPasswordVerified();
143     }
144 }
145