• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.systemui.car.bluetooth;
18 
19 import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
20 
21 import android.bluetooth.BluetoothAdapter;
22 import android.bluetooth.BluetoothDevice;
23 import android.bluetooth.BluetoothHeadsetClient;
24 import android.bluetooth.BluetoothHeadsetClient.NetworkServiceState;
25 import android.bluetooth.BluetoothProfile;
26 import android.bluetooth.BluetoothProfile.ServiceListener;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.telephony.SignalStrength;
32 import android.util.Log;
33 import android.util.TypedValue;
34 import android.view.View;
35 import android.widget.ImageView;
36 
37 import com.android.settingslib.graph.SignalDrawable;
38 import com.android.systemui.Dependency;
39 import com.android.systemui.R;
40 import com.android.systemui.statusbar.ScalingDrawableWrapper;
41 import com.android.systemui.statusbar.policy.BluetoothController;
42 
43 /**
44  * Controller that monitors signal strength for a device that is connected via bluetooth.
45  */
46 public class ConnectedDeviceSignalController extends BroadcastReceiver implements
47         BluetoothController.Callback {
48     private static final String TAG = "DeviceSignalCtlr";
49 
50     /**
51      * The value that indicates if a network is unavailable. This value is according ot the
52      * Bluetooth HFP 1.5 spec, which indicates this value is one of two: 0 or 1. These stand
53      * for network unavailable and available respectively.
54      */
55     private static final int NETWORK_UNAVAILABLE = 0;
56     private static final int NETWORK_UNAVAILABLE_ICON_ID = R.drawable.stat_sys_signal_null;
57 
58     /**
59      * All possible signal strength icons. According to the Bluetooth HFP 1.5 specification,
60      * signal strength is indicated by a value from 1-5, where these values represent the following:
61      *
62      * <p>0%% - 0, 1-25%% - 1, 26-50%% - 2, 51-75%% - 3, 76-99%% - 4, 100%% - 5
63      *
64      * <p>As a result, these are treated as an index into this array for the corresponding icon.
65      * Note that the icon is the same for 0 and 1.
66      */
67     private static final int[] SIGNAL_STRENGTH_ICONS = {
68             0,
69             0,
70             1,
71             2,
72             3,
73             4,
74     };
75 
76     private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
77     private final Context mContext;
78     private final BluetoothController mController;
79 
80     private final View mSignalsView;
81     private final ImageView mNetworkSignalView;
82 
83     private final float mIconScaleFactor;
84     private final SignalDrawable mSignalDrawable;
85 
86     private BluetoothHeadsetClient mBluetoothHeadsetClient;
87     private final ServiceListener mHfpServiceListener = new ServiceListener() {
88         @Override
89         public void onServiceConnected(int profile, BluetoothProfile proxy) {
90             if (profile == BluetoothProfile.HEADSET_CLIENT) {
91                 mBluetoothHeadsetClient = (BluetoothHeadsetClient) proxy;
92             }
93         }
94 
95         @Override
96         public void onServiceDisconnected(int profile) {
97             if (profile == BluetoothProfile.HEADSET_CLIENT) {
98                 mBluetoothHeadsetClient = null;
99             }
100         }
101     };
102 
ConnectedDeviceSignalController(Context context, View signalsView)103     public ConnectedDeviceSignalController(Context context, View signalsView) {
104         mContext = context;
105         mController = Dependency.get(BluetoothController.class);
106 
107         mSignalsView = signalsView;
108         mNetworkSignalView = (ImageView)
109                 mSignalsView.findViewById(R.id.connected_device_network_signal);
110 
111         TypedValue typedValue = new TypedValue();
112         context.getResources().getValue(R.dimen.status_bar_icon_scale_factor, typedValue, true);
113         mIconScaleFactor = typedValue.getFloat();
114         mSignalDrawable = new SignalDrawable(mNetworkSignalView.getContext());
115         mNetworkSignalView.setImageDrawable(
116                 new ScalingDrawableWrapper(mSignalDrawable, mIconScaleFactor));
117 
118         if (mAdapter == null) {
119             return;
120         }
121 
122         mAdapter.getProfileProxy(context.getApplicationContext(), mHfpServiceListener,
123                 BluetoothProfile.HEADSET_CLIENT);
124     }
125 
126     /** Starts listening for bluetooth broadcast messages. */
startListening()127     public void startListening() {
128         IntentFilter filter = new IntentFilter();
129         filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
130         filter.addAction(BluetoothHeadsetClient.ACTION_NETWORK_SERVICE_STATE_CHANGED);
131         mContext.registerReceiver(this, filter);
132 
133         mController.addCallback(this);
134     }
135 
136     /** Stops listening for bluetooth broadcast messages. */
stopListening()137     public void stopListening() {
138         mContext.unregisterReceiver(this);
139         mController.removeCallback(this);
140     }
141 
142     @Override
onBluetoothDevicesChanged()143     public void onBluetoothDevicesChanged() {
144         // Nothing to do here because this Controller is not displaying a list of possible
145         // bluetooth devices.
146     }
147 
148     @Override
onBluetoothStateChange(boolean enabled)149     public void onBluetoothStateChange(boolean enabled) {
150         if (DEBUG) {
151             Log.d(TAG, "onBluetoothStateChange(). enabled: " + enabled);
152         }
153 
154         // Only need to handle the case if bluetooth has been disabled, in which case the
155         // signal indicators are hidden. If bluetooth has been enabled, then this class should
156         // receive updates to the connection state via onReceive().
157         if (!enabled) {
158             mNetworkSignalView.setVisibility(View.GONE);
159             mSignalsView.setVisibility(View.GONE);
160         }
161     }
162 
163     @Override
onReceive(Context context, Intent intent)164     public void onReceive(Context context, Intent intent) {
165         String action = intent.getAction();
166 
167         if (DEBUG) {
168             Log.d(TAG, "onReceive(). action: " + action);
169         }
170 
171         if (BluetoothHeadsetClient.ACTION_NETWORK_SERVICE_STATE_CHANGED.equals(action)) {
172             if (DEBUG) {
173                 Log.d(TAG, "Received ACTION_NETWORK_SERVICE_STATE_CHANGED");
174             }
175 
176             processActionNetworkServiceStateEvent(intent);
177         } else if (BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
178             int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
179 
180             if (DEBUG) {
181                 int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
182                 Log.d(TAG, "ACTION_CONNECTION_STATE_CHANGED event: "
183                         + oldState + " -> " + newState);
184             }
185             BluetoothDevice device =
186                     (BluetoothDevice) intent.getExtra(BluetoothDevice.EXTRA_DEVICE);
187             updateViewVisibility(device, newState);
188         }
189     }
190 
191     /**
192      * Processes an {@link Intent} that had an action of
193      * {@link BluetoothHeadsetClient#ACTION_NETWORK_SERVICE_STATE_CHANGED}.
194      */
processActionNetworkServiceStateEvent(Intent intent)195     private void processActionNetworkServiceStateEvent(Intent intent) {
196         NetworkServiceState state = (NetworkServiceState) intent.getExtra(
197                 BluetoothHeadsetClient.EXTRA_NETWORK_SERVICE_STATE, null);
198 
199         if (!state.isServiceAvailable()) {
200             setNetworkSignalIcon(NETWORK_UNAVAILABLE_ICON_ID);
201         }
202 
203         setNetworkSignalIcon(SIGNAL_STRENGTH_ICONS[state.getSignalStrength()]);
204     }
205 
setNetworkSignalIcon(int level)206     private void setNetworkSignalIcon(int level) {
207         // Setting the icon on a child view of mSignalView, so toggle this container visible.
208         mSignalsView.setVisibility(View.VISIBLE);
209 
210         mSignalDrawable.setLevel(SignalDrawable.getState(level,
211                 SignalStrength.NUM_SIGNAL_STRENGTH_BINS, false));
212         mNetworkSignalView.setVisibility(View.VISIBLE);
213     }
214 
updateViewVisibility(BluetoothDevice device, int newState)215     private void updateViewVisibility(BluetoothDevice device, int newState) {
216         if (newState == BluetoothProfile.STATE_CONNECTED) {
217             if (DEBUG) {
218                 Log.d(TAG, "Device connected");
219             }
220 
221             if (mBluetoothHeadsetClient == null || device == null) {
222                 return;
223             }
224 
225             // Check if network signal strength information is available and immediately update.
226             NetworkServiceState state = mBluetoothHeadsetClient.getNetworkServiceState(device);
227             if (state == null) {
228                 return;
229             }
230 
231             int signalStrength = state.getSignalStrength();
232             if (DEBUG) {
233                 Log.d(TAG, "NetworkServiceState getSignalStrength(): " + signalStrength);
234             }
235             setNetworkSignalIcon(SIGNAL_STRENGTH_ICONS[signalStrength]);
236         } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
237             if (DEBUG) {
238                 Log.d(TAG, "Device disconnected");
239             }
240 
241             mNetworkSignalView.setVisibility(View.GONE);
242             mSignalsView.setVisibility(View.GONE);
243         }
244     }
245 }
246