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 mSecurityType = getCarSoftApConfig().getSecurityType(); 78 syncPassword(); 79 } 80 81 @SuppressWarnings("MissingSuperCall") // TODO: Fix me 82 @Override onStartInternal()83 protected void onStartInternal() { 84 int newSecurityType = mSharedPreferences.getInt( 85 WifiTetherSecurityPreferenceController.KEY_SECURITY_TYPE, 86 /* defaultValue= */ SHARED_SECURITY_TYPE_UNSET); 87 if (mSecurityType != newSecurityType && newSecurityType != SHARED_SECURITY_TYPE_UNSET) { 88 // Security type has been changed - update ap configuration 89 mSecurityType = newSecurityType; 90 syncPassword(); 91 updateApSecurity(mPassword); 92 } 93 } 94 95 @Override handlePreferenceChanged(ValidatedEditTextPreference preference, Object newValue)96 protected boolean handlePreferenceChanged(ValidatedEditTextPreference preference, 97 Object newValue) { 98 if (!newValue.toString().equals(mPassword)) { 99 mPassword = newValue.toString(); 100 updateApSecurity(mPassword); 101 refreshUi(); 102 } 103 return true; 104 } 105 106 @Override updateState(ValidatedEditTextPreference preference)107 protected void updateState(ValidatedEditTextPreference preference) { 108 super.updateState(preference); 109 updatePasswordDisplay(); 110 if (TextUtils.isEmpty(mPassword)) { 111 preference.setSummaryInputType(InputType.TYPE_CLASS_TEXT); 112 } else { 113 preference.setSummaryInputType( 114 InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); 115 } 116 } 117 118 @Override getSummary()119 protected String getSummary() { 120 return mPassword; 121 } 122 123 @Override getDefaultSummary()124 protected String getDefaultSummary() { 125 return getContext().getString(R.string.default_password_summary); 126 } 127 syncPassword()128 private void syncPassword() { 129 mPassword = getSyncedPassword(); 130 refreshUi(); 131 } 132 getSyncedPassword()133 private String getSyncedPassword() { 134 if (mSecurityType == SoftApConfiguration.SECURITY_TYPE_OPEN) { 135 return null; 136 } 137 138 String passphrase = getCarSoftApConfig().getPassphrase(); 139 if (!TextUtils.isEmpty(passphrase)) { 140 return passphrase; 141 } 142 143 if (!TextUtils.isEmpty( 144 mSharedPreferences.getString(KEY_SAVED_PASSWORD, /* defaultValue= */ null))) { 145 return mSharedPreferences.getString(KEY_SAVED_PASSWORD, /* defaultValue= */ null); 146 } 147 148 return generateRandomPassword(); 149 } 150 generateRandomPassword()151 private static String generateRandomPassword() { 152 String randomUUID = UUID.randomUUID().toString(); 153 // First 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx 154 return randomUUID.substring(0, 8) + randomUUID.substring(9, 13); 155 } 156 157 /** 158 * If the password and/or security type has changed, update the ap configuration 159 */ updateApSecurity(String password)160 private void updateApSecurity(String password) { 161 String passwordOrNullIfOpen; 162 if (mSecurityType == SoftApConfiguration.SECURITY_TYPE_OPEN) { 163 passwordOrNullIfOpen = null; 164 Log.w(TAG, "Setting password on an open network!"); 165 } else { 166 passwordOrNullIfOpen = password; 167 } 168 SoftApConfiguration config = new SoftApConfiguration.Builder(getCarSoftApConfig()) 169 .setPassphrase(passwordOrNullIfOpen, mSecurityType) 170 .build(); 171 setCarSoftApConfig(config); 172 173 if (!TextUtils.isEmpty(password)) { 174 mSharedPreferences.edit().putString(KEY_SAVED_PASSWORD, password).commit(); 175 } 176 } 177 updatePasswordDisplay()178 private void updatePasswordDisplay() { 179 getPreference().setText(mPassword); 180 getPreference().setVisible(mSecurityType != SoftApConfiguration.SECURITY_TYPE_OPEN); 181 getPreference().setSummary(getSummary()); 182 } 183 184 } 185