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