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