1/* 2 * Copyright (C) 2015 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 17package com.android.tv.settings.about; 18 19import android.bluetooth.BluetoothAdapter; 20import android.content.BroadcastReceiver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.net.ConnectivityManager; 25import android.net.LinkProperties; 26import android.net.wifi.WifiInfo; 27import android.net.wifi.WifiManager; 28import android.os.Build; 29import android.os.Bundle; 30import android.os.Handler; 31import android.os.Message; 32import android.os.SystemClock; 33import android.os.SystemProperties; 34import android.os.UserManager; 35import android.support.annotation.Nullable; 36import android.support.v17.preference.LeanbackPreferenceFragment; 37import android.support.v7.preference.Preference; 38import android.text.TextUtils; 39import android.text.format.DateUtils; 40 41import com.android.internal.util.ArrayUtils; 42import com.android.tv.settings.R; 43 44import java.lang.ref.WeakReference; 45import java.net.InetAddress; 46import java.util.Iterator; 47 48public class StatusFragment extends LeanbackPreferenceFragment { 49 50 private static final String KEY_BATTERY_STATUS = "battery_status"; 51 private static final String KEY_BATTERY_LEVEL = "battery_level"; 52 private static final String KEY_IP_ADDRESS = "wifi_ip_address"; 53 private static final String KEY_WIFI_MAC_ADDRESS = "wifi_mac_address"; 54 private static final String KEY_BT_ADDRESS = "bt_address"; 55 private static final String KEY_SERIAL_NUMBER = "serial_number"; 56 private static final String KEY_WIMAX_MAC_ADDRESS = "wimax_mac_address"; 57 private static final String KEY_SIM_STATUS = "sim_status"; 58 private static final String KEY_IMEI_INFO = "imei_info"; 59 60 // Broadcasts to listen to for connectivity changes. 61 private static final String[] CONNECTIVITY_INTENTS = { 62 BluetoothAdapter.ACTION_STATE_CHANGED, 63 ConnectivityManager.CONNECTIVITY_ACTION, 64 WifiManager.LINK_CONFIGURATION_CHANGED_ACTION, 65 WifiManager.NETWORK_STATE_CHANGED_ACTION, 66 }; 67 68 private static final int EVENT_UPDATE_STATS = 500; 69 70 private static final int EVENT_UPDATE_CONNECTIVITY = 600; 71 72 private ConnectivityManager mCM; 73 private WifiManager mWifiManager; 74 75 private Preference mUptime; 76 private Preference mBtAddress; 77 private Preference mIpAddress; 78 private Preference mWifiMacAddress; 79 private Preference mWimaxMacAddress; 80 81 private IntentFilter mConnectivityIntentFilter; 82 private final BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() { 83 @Override 84 public void onReceive(Context context, Intent intent) { 85 String action = intent.getAction(); 86 if (ArrayUtils.contains(CONNECTIVITY_INTENTS, action)) { 87 mHandler.sendEmptyMessage(EVENT_UPDATE_CONNECTIVITY); 88 } 89 } 90 }; 91 92 private Handler mHandler; 93 94 private static class MyHandler extends Handler { 95 private WeakReference<StatusFragment> mStatus; 96 97 public MyHandler(StatusFragment activity) { 98 mStatus = new WeakReference<>(activity); 99 } 100 101 @Override 102 public void handleMessage(Message msg) { 103 StatusFragment status = mStatus.get(); 104 if (status == null) { 105 return; 106 } 107 108 switch (msg.what) { 109 case EVENT_UPDATE_STATS: 110 status.updateTimes(); 111 sendEmptyMessageDelayed(EVENT_UPDATE_STATS, 1000); 112 break; 113 114 case EVENT_UPDATE_CONNECTIVITY: 115 status.updateConnectivity(); 116 break; 117 } 118 } 119 } 120 121 public static StatusFragment newInstance() { 122 return new StatusFragment(); 123 } 124 125 @Override 126 public void onCreate(Bundle savedInstanceState) { 127 mHandler = new MyHandler(this); 128 129 mCM = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); 130 mWifiManager = (WifiManager) getActivity().getSystemService(Context.WIFI_SERVICE); 131 132 super.onCreate(savedInstanceState); 133 } 134 135 @Override 136 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 137 setPreferencesFromResource(R.xml.device_info_status, null); 138 139 // TODO: detect if we have a battery or not 140 removePreference(findPreference(KEY_BATTERY_LEVEL)); 141 removePreference(findPreference(KEY_BATTERY_STATUS)); 142 mBtAddress = findPreference(KEY_BT_ADDRESS); 143 mWifiMacAddress = findPreference(KEY_WIFI_MAC_ADDRESS); 144 mWimaxMacAddress = findPreference(KEY_WIMAX_MAC_ADDRESS); 145 mIpAddress = findPreference(KEY_IP_ADDRESS); 146 mUptime = findPreference("up_time"); 147 148 if (!hasBluetooth()) { 149 getPreferenceScreen().removePreference(mBtAddress); 150 mBtAddress = null; 151 } 152 153 if (!hasWimax()) { 154 getPreferenceScreen().removePreference(mWimaxMacAddress); 155 mWimaxMacAddress = null; 156 } 157 158 mConnectivityIntentFilter = new IntentFilter(); 159 for (String intent: CONNECTIVITY_INTENTS) { 160 mConnectivityIntentFilter.addAction(intent); 161 } 162 163 updateConnectivity(); 164 165 final Preference serialPref = findPreference(KEY_SERIAL_NUMBER); 166 String serial = Build.SERIAL; 167 if (!TextUtils.isEmpty(serial)) { 168 serialPref.setSummary(serial); 169 } else { 170 removePreference(serialPref); 171 } 172 173 // Remove SimStatus and Imei for Secondary user as it access Phone b/19165700 174 // Also remove on Wi-Fi only devices. 175 //TODO: the bug above will surface in split system user mode. 176 if (!UserManager.get(getActivity()).isAdminUser() 177 || AboutFragment.isWifiOnly(getActivity())) { 178 removePreference(findPreference(KEY_SIM_STATUS)); 179 removePreference(findPreference(KEY_IMEI_INFO)); 180 } 181 } 182 183 private void removePreference(@Nullable Preference preference) { 184 if (preference != null) { 185 getPreferenceScreen().removePreference(preference); 186 } 187 } 188 189 private boolean hasBluetooth() { 190 return BluetoothAdapter.getDefaultAdapter() != null; 191 } 192 193 private boolean hasWimax() { 194 return mCM.getNetworkInfo(ConnectivityManager.TYPE_WIMAX) != null; 195 } 196 197 @Override 198 public void onStart() { 199 super.onStart(); 200 getActivity().registerReceiver(mConnectivityReceiver, mConnectivityIntentFilter, 201 android.Manifest.permission.CHANGE_NETWORK_STATE, null); 202 mHandler.sendEmptyMessage(EVENT_UPDATE_STATS); 203 } 204 205 @Override 206 public void onStop() { 207 super.onStop(); 208 getActivity().unregisterReceiver(mConnectivityReceiver); 209 mHandler.removeMessages(EVENT_UPDATE_STATS); 210 } 211 212 private void setWimaxStatus() { 213 if (mWimaxMacAddress != null) { 214 String macAddress = SystemProperties.get("net.wimax.mac.address", 215 getString(R.string.status_unavailable)); 216 mWimaxMacAddress.setSummary(macAddress); 217 } 218 } 219 220 private void setWifiStatus() { 221 WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); 222 String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress(); 223 mWifiMacAddress.setSummary(!TextUtils.isEmpty(macAddress) ? 224 macAddress : getString(R.string.status_unavailable)); 225 } 226 227 private void setIpAddressStatus() { 228 String ipAddress = getDefaultIpAddresses(mCM); 229 if (ipAddress != null) { 230 mIpAddress.setSummary(ipAddress); 231 } else { 232 mIpAddress.setSummary(R.string.status_unavailable); 233 } 234 } 235 236 private void setBtStatus() { 237 BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter(); 238 if (bluetooth != null && mBtAddress != null) { 239 String address = bluetooth.isEnabled() ? bluetooth.getAddress() : null; 240 if (!TextUtils.isEmpty(address)) { 241 // Convert the address to lowercase for consistency with the wifi MAC address. 242 mBtAddress.setSummary(address.toLowerCase()); 243 } else { 244 mBtAddress.setSummary(R.string.status_unavailable); 245 } 246 } 247 } 248 249 void updateConnectivity() { 250 setWimaxStatus(); 251 setWifiStatus(); 252 setBtStatus(); 253 setIpAddressStatus(); 254 } 255 256 /** 257 * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style 258 * addresses. 259 * @param cm ConnectivityManager 260 * @return the formatted and newline-separated IP addresses, or null if none. 261 */ 262 private static String getDefaultIpAddresses(ConnectivityManager cm) { 263 LinkProperties prop = cm.getActiveLinkProperties(); 264 return formatIpAddresses(prop); 265 } 266 267 private static String formatIpAddresses(LinkProperties prop) { 268 if (prop == null) return null; 269 Iterator<InetAddress> iter = prop.getAllAddresses().iterator(); 270 // If there are no entries, return null 271 if (!iter.hasNext()) return null; 272 // Concatenate all available addresses, comma separated 273 String addresses = ""; 274 while (iter.hasNext()) { 275 addresses += iter.next().getHostAddress(); 276 if (iter.hasNext()) addresses += "\n"; 277 } 278 return addresses; 279 } 280 281 void updateTimes() { 282 mUptime.setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime())); 283 } 284 285} 286