1 /*
2  * Copyright (C) 2019 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.car.settings.wifi;
18 
19 import android.car.drivingstate.CarUxRestrictions;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.SharedPreferences;
25 import android.net.wifi.WifiConfiguration;
26 import android.text.InputType;
27 import android.text.TextUtils;
28 
29 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
30 
31 import com.android.car.settings.R;
32 import com.android.car.settings.common.FragmentController;
33 import com.android.car.settings.common.ValidatedEditTextPreference;
34 
35 import java.util.UUID;
36 
37 /**
38  * Controls Wifi Hotspot password configuration.
39  *
40  * <p>Note: This controller uses {@link ValidatedEditTextPreference} as opposed to
41  * PasswordEditTextPreference because the input is not obscured by default, and the user is setting
42  * their own password, as opposed to entering password for authentication.
43  */
44 public class WifiTetherPasswordPreferenceController extends
45         WifiTetherBasePreferenceController<ValidatedEditTextPreference> {
46 
47     protected static final String SHARED_PREFERENCE_PATH =
48             "com.android.car.settings.wifi.WifiTetherPreferenceController";
49     protected static final String KEY_SAVED_PASSWORD =
50             "com.android.car.settings.wifi.SAVED_PASSWORD";
51 
52     private static final int HOTSPOT_PASSWORD_MIN_LENGTH = 8;
53     private static final int HOTSPOT_PASSWORD_MAX_LENGTH = 63;
54     private static final ValidatedEditTextPreference.Validator PASSWORD_VALIDATOR =
55             value -> value.length() >= HOTSPOT_PASSWORD_MIN_LENGTH
56                     && value.length() <= HOTSPOT_PASSWORD_MAX_LENGTH;
57 
58     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
59         @Override
60         public void onReceive(Context context, Intent intent) {
61             mSecurityType = intent.getIntExtra(
62                     WifiTetherSecurityPreferenceController.KEY_SECURITY_TYPE,
63                     /* defaultValue= */ WifiConfiguration.KeyMgmt.NONE);
64             syncPassword();
65         }
66     };
67     private final SharedPreferences mSharedPreferences =
68             getContext().getSharedPreferences(SHARED_PREFERENCE_PATH, Context.MODE_PRIVATE);
69 
70     private String mPassword;
71     private int mSecurityType;
72 
WifiTetherPasswordPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)73     public WifiTetherPasswordPreferenceController(Context context, String preferenceKey,
74             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
75         super(context, preferenceKey, fragmentController, uxRestrictions);
76     }
77 
78     @Override
getPreferenceType()79     protected Class<ValidatedEditTextPreference> getPreferenceType() {
80         return ValidatedEditTextPreference.class;
81     }
82 
83     @Override
onCreateInternal()84     protected void onCreateInternal() {
85         super.onCreateInternal();
86 
87         getPreference().setValidator(PASSWORD_VALIDATOR);
88         mSecurityType = getCarWifiApConfig().getAuthType();
89         syncPassword();
90     }
91 
92     @Override
onStartInternal()93     protected void onStartInternal() {
94         LocalBroadcastManager.getInstance(getContext()).registerReceiver(mReceiver,
95                 new IntentFilter(
96                         WifiTetherSecurityPreferenceController.ACTION_SECURITY_TYPE_CHANGED));
97     }
98 
99     @Override
onStopInternal()100     protected void onStopInternal() {
101         super.onStopInternal();
102         LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(mReceiver);
103     }
104 
105     @Override
handlePreferenceChanged(ValidatedEditTextPreference preference, Object newValue)106     protected boolean handlePreferenceChanged(ValidatedEditTextPreference preference,
107             Object newValue) {
108         mPassword = newValue.toString();
109         updatePassword(mPassword);
110         refreshUi();
111         return true;
112     }
113 
114     @Override
updateState(ValidatedEditTextPreference preference)115     protected void updateState(ValidatedEditTextPreference preference) {
116         super.updateState(preference);
117         updatePasswordDisplay();
118         if (TextUtils.isEmpty(mPassword)) {
119             preference.setSummaryInputType(InputType.TYPE_CLASS_TEXT);
120         } else {
121             preference.setSummaryInputType(
122                     InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
123         }
124     }
125 
126     @Override
getSummary()127     protected String getSummary() {
128         return mPassword;
129     }
130 
131     @Override
getDefaultSummary()132     protected String getDefaultSummary() {
133         return getContext().getString(R.string.default_password_summary);
134     }
135 
syncPassword()136     private void syncPassword() {
137         mPassword = getSyncedPassword();
138         updatePassword(mPassword);
139         refreshUi();
140     }
141 
getSyncedPassword()142     private String getSyncedPassword() {
143         if (getCarWifiApConfig().getAuthType() == WifiConfiguration.KeyMgmt.NONE) {
144             return null;
145         }
146 
147         if (!TextUtils.isEmpty(getCarWifiApConfig().preSharedKey)) {
148             return getCarWifiApConfig().preSharedKey;
149         }
150 
151         if (!TextUtils.isEmpty(
152                 mSharedPreferences.getString(KEY_SAVED_PASSWORD, /* defaultValue= */ null))) {
153             return mSharedPreferences.getString(KEY_SAVED_PASSWORD, /* defaultValue= */ null);
154         }
155 
156         return generateRandomPassword();
157     }
158 
generateRandomPassword()159     private static String generateRandomPassword() {
160         String randomUUID = UUID.randomUUID().toString();
161         // First 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
162         return randomUUID.substring(0, 8) + randomUUID.substring(9, 13);
163     }
164 
updatePassword(String password)165     private void updatePassword(String password) {
166         WifiConfiguration config = getCarWifiApConfig();
167         config.preSharedKey = password;
168         setCarWifiApConfig(config);
169 
170         if (!TextUtils.isEmpty(password)) {
171             mSharedPreferences.edit().putString(KEY_SAVED_PASSWORD, password).commit();
172         }
173     }
174 
updatePasswordDisplay()175     private void updatePasswordDisplay() {
176         getPreference().setText(mPassword);
177         getPreference().setVisible(mSecurityType != WifiConfiguration.KeyMgmt.NONE);
178         getPreference().setSummary(getSummary());
179     }
180 
181 }
182