Home | History | Annotate | Download | only in deviceinfo
      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 }