1 /* 2 * Copyright (C) 2018 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.deviceinfo; 18 19 import android.bluetooth.BluetoothAdapter; 20 import android.content.Context; 21 import android.net.wifi.SoftApConfiguration; 22 import android.net.wifi.WifiManager; 23 import android.os.Build; 24 import android.os.Bundle; 25 import android.provider.Settings; 26 import android.text.SpannedString; 27 28 import androidx.annotation.VisibleForTesting; 29 import androidx.preference.Preference; 30 import androidx.preference.PreferenceScreen; 31 32 import com.android.settings.R; 33 import com.android.settings.bluetooth.BluetoothLengthDeviceNameFilter; 34 import com.android.settings.core.BasePreferenceController; 35 import com.android.settings.widget.ValidatedEditTextPreference; 36 import com.android.settings.wifi.tether.WifiDeviceNameTextValidator; 37 import com.android.settingslib.core.lifecycle.LifecycleObserver; 38 import com.android.settingslib.core.lifecycle.events.OnCreate; 39 import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState; 40 41 public class DeviceNamePreferenceController extends BasePreferenceController 42 implements ValidatedEditTextPreference.Validator, 43 Preference.OnPreferenceChangeListener, 44 LifecycleObserver, 45 OnSaveInstanceState, 46 OnCreate { 47 private static final String KEY_PENDING_DEVICE_NAME = "key_pending_device_name"; 48 @VisibleForTesting 49 static final int RES_SHOW_DEVICE_NAME_BOOL = R.bool.config_show_device_name; 50 private String mDeviceName; 51 protected WifiManager mWifiManager; 52 private final BluetoothAdapter mBluetoothAdapter; 53 private final WifiDeviceNameTextValidator mWifiDeviceNameTextValidator; 54 private ValidatedEditTextPreference mPreference; 55 private DeviceNamePreferenceHost mHost; 56 private String mPendingDeviceName; 57 DeviceNamePreferenceController(Context context, String key)58 public DeviceNamePreferenceController(Context context, String key) { 59 super(context, key); 60 61 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 62 mWifiDeviceNameTextValidator = new WifiDeviceNameTextValidator(); 63 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 64 65 initializeDeviceName(); 66 } 67 68 @Override displayPreference(PreferenceScreen screen)69 public void displayPreference(PreferenceScreen screen) { 70 super.displayPreference(screen); 71 mPreference = screen.findPreference(getPreferenceKey()); 72 final CharSequence deviceName = getSummary(); 73 mPreference.setSummary(deviceName); 74 mPreference.setText(deviceName.toString()); 75 mPreference.setValidator(this); 76 } 77 initializeDeviceName()78 private void initializeDeviceName() { 79 mDeviceName = Settings.Global.getString(mContext.getContentResolver(), 80 Settings.Global.DEVICE_NAME); 81 if (mDeviceName == null) { 82 mDeviceName = Build.MODEL; 83 } 84 } 85 86 @Override getSummary()87 public CharSequence getSummary() { 88 return mDeviceName; 89 } 90 91 @Override getAvailabilityStatus()92 public int getAvailabilityStatus() { 93 return mContext.getResources().getBoolean(R.bool.config_show_device_name) 94 ? AVAILABLE 95 : UNSUPPORTED_ON_DEVICE; 96 } 97 98 @Override onPreferenceChange(Preference preference, Object newValue)99 public boolean onPreferenceChange(Preference preference, Object newValue) { 100 mPendingDeviceName = (String) newValue; 101 if (mHost != null) { 102 mHost.showDeviceNameWarningDialog(mPendingDeviceName); 103 } 104 return true; 105 } 106 107 @Override isTextValid(String deviceName)108 public boolean isTextValid(String deviceName) { 109 // BluetoothNameDialogFragment describes BT name filter as a 248 bytes long cap. 110 // Given the restrictions presented by the SSID name filter (32 char), I don't believe it is 111 // possible to construct an SSID that is not a valid Bluetooth name. 112 return mWifiDeviceNameTextValidator.isTextValid(deviceName); 113 } 114 updateDeviceName(boolean update)115 public void updateDeviceName(boolean update) { 116 if (update && mPendingDeviceName != null) { 117 setDeviceName(mPendingDeviceName); 118 } else { 119 mPreference.setText(getSummary().toString()); 120 } 121 } 122 setHost(DeviceNamePreferenceHost host)123 public void setHost(DeviceNamePreferenceHost host) { 124 mHost = host; 125 } 126 127 /** 128 * This method presumes that security/validity checks have already been passed. 129 */ setDeviceName(String deviceName)130 private void setDeviceName(String deviceName) { 131 mDeviceName = deviceName; 132 setSettingsGlobalDeviceName(deviceName); 133 setBluetoothDeviceName(deviceName); 134 setTetherSsidName(deviceName); 135 mPreference.setSummary(getSummary()); 136 } 137 setSettingsGlobalDeviceName(String deviceName)138 private void setSettingsGlobalDeviceName(String deviceName) { 139 Settings.Global.putString(mContext.getContentResolver(), Settings.Global.DEVICE_NAME, 140 deviceName); 141 } 142 setBluetoothDeviceName(String deviceName)143 private void setBluetoothDeviceName(String deviceName) { 144 if (mBluetoothAdapter != null) { 145 mBluetoothAdapter.setName(getFilteredBluetoothString(deviceName)); 146 } 147 } 148 149 /** 150 * Using a UTF8ByteLengthFilter, we can filter a string to be compliant with the Bluetooth spec. 151 * For more information, see {@link com.android.settings.bluetooth.BluetoothNameDialogFragment}. 152 */ getFilteredBluetoothString(final String deviceName)153 private static final String getFilteredBluetoothString(final String deviceName) { 154 CharSequence filteredSequence = new BluetoothLengthDeviceNameFilter().filter(deviceName, 0, 155 deviceName.length(), 156 new SpannedString(""), 157 0, 0); 158 // null -> use the original 159 if (filteredSequence == null) { 160 return deviceName; 161 } 162 return filteredSequence.toString(); 163 } 164 setTetherSsidName(String deviceName)165 private void setTetherSsidName(String deviceName) { 166 final SoftApConfiguration config = mWifiManager.getSoftApConfiguration(); 167 // TODO: If tether is running, turn off the AP and restart it after setting config. 168 mWifiManager.setSoftApConfiguration( 169 new SoftApConfiguration.Builder(config).setSsid(deviceName).build()); 170 } 171 172 @Override onCreate(Bundle savedInstanceState)173 public void onCreate(Bundle savedInstanceState) { 174 if (savedInstanceState != null) { 175 mPendingDeviceName = savedInstanceState.getString(KEY_PENDING_DEVICE_NAME, null); 176 } 177 } 178 179 @Override onSaveInstanceState(Bundle outState)180 public void onSaveInstanceState(Bundle outState) { 181 outState.putString(KEY_PENDING_DEVICE_NAME, mPendingDeviceName); 182 } 183 184 public interface DeviceNamePreferenceHost { showDeviceNameWarningDialog(String deviceName)185 void showDeviceNameWarningDialog(String deviceName); 186 } 187 } 188