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