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 
17 package com.android.tv.settings.about;
18 
19 import android.bluetooth.BluetoothAdapter;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.net.ConnectivityManager;
25 import android.net.LinkProperties;
26 import android.net.wifi.WifiInfo;
27 import android.net.wifi.WifiManager;
28 import android.os.Build;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.Message;
32 import android.os.SystemClock;
33 import android.os.SystemProperties;
34 import android.os.UserManager;
35 import android.support.annotation.Nullable;
36 import android.support.v17.preference.LeanbackPreferenceFragment;
37 import android.support.v7.preference.Preference;
38 import android.text.TextUtils;
39 import android.text.format.DateUtils;
40 
41 import com.android.internal.util.ArrayUtils;
42 import com.android.tv.settings.R;
43 
44 import java.lang.ref.WeakReference;
45 import java.net.InetAddress;
46 import java.util.Iterator;
47 
48 public 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 
MyHandler(StatusFragment activity)97         public MyHandler(StatusFragment activity) {
98             mStatus = new WeakReference<>(activity);
99         }
100 
101         @Override
handleMessage(Message msg)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 
newInstance()121     public static StatusFragment newInstance() {
122         return new StatusFragment();
123     }
124 
125     @Override
onCreate(Bundle savedInstanceState)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
onCreatePreferences(Bundle savedInstanceState, String rootKey)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 
removePreference(@ullable Preference preference)183     private void removePreference(@Nullable Preference preference) {
184         if (preference != null) {
185             getPreferenceScreen().removePreference(preference);
186         }
187     }
188 
hasBluetooth()189     private boolean hasBluetooth() {
190         return BluetoothAdapter.getDefaultAdapter() != null;
191     }
192 
hasWimax()193     private boolean hasWimax() {
194         return  mCM.getNetworkInfo(ConnectivityManager.TYPE_WIMAX) != null;
195     }
196 
197     @Override
onStart()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
onStop()206     public void onStop() {
207         super.onStop();
208         getActivity().unregisterReceiver(mConnectivityReceiver);
209         mHandler.removeMessages(EVENT_UPDATE_STATS);
210     }
211 
setWimaxStatus()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 
setWifiStatus()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 
setIpAddressStatus()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 
setBtStatus()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 
updateConnectivity()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      */
getDefaultIpAddresses(ConnectivityManager cm)262     private static String getDefaultIpAddresses(ConnectivityManager cm) {
263         LinkProperties prop = cm.getActiveLinkProperties();
264         return formatIpAddresses(prop);
265     }
266 
formatIpAddresses(LinkProperties prop)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 
updateTimes()281     void updateTimes() {
282         mUptime.setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime()));
283     }
284 
285 }
286