• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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 android.bluetooth;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SuppressLint;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ResolveInfo;
28 import android.os.Build;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.os.RemoteException;
34 import android.os.UserHandle;
35 import android.util.CloseGuard;
36 import android.util.Log;
37 
38 import java.util.List;
39 /**
40  * Connector for Bluetooth profile proxies to bind manager service and
41  * profile services
42  * @param <T> The Bluetooth profile interface for this connection.
43  * @hide
44  */
45 @SuppressLint("AndroidFrameworkBluetoothPermission")
46 public abstract class BluetoothProfileConnector<T> {
47     private final CloseGuard mCloseGuard = new CloseGuard();
48     private final int mProfileId;
49     private BluetoothProfile.ServiceListener mServiceListener;
50     private final BluetoothProfile mProfileProxy;
51     private Context mContext;
52     private final String mProfileName;
53     private final String mServiceName;
54     private volatile T mService;
55 
56     // -3 match with UserHandle.USER_CURRENT_OR_SELF
57     private static final UserHandle USER_HANDLE_CURRENT_OR_SELF = UserHandle.of(-3);
58 
59     private static final int MESSAGE_SERVICE_CONNECTED = 100;
60     private static final int MESSAGE_SERVICE_DISCONNECTED = 101;
61 
62     private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
63             new IBluetoothStateChangeCallback.Stub() {
64         public void onBluetoothStateChange(boolean up) {
65             if (up) {
66                 doBind();
67             } else {
68                 doUnbind();
69             }
70         }
71     };
72 
resolveSystemService(@onNull Intent intent, @NonNull PackageManager pm)73     private @Nullable ComponentName resolveSystemService(@NonNull Intent intent,
74             @NonNull PackageManager pm) {
75         List<ResolveInfo> results = pm.queryIntentServices(intent,
76                 PackageManager.ResolveInfoFlags.of(0));
77         if (results == null) {
78             return null;
79         }
80         ComponentName comp = null;
81         for (int i = 0; i < results.size(); i++) {
82             ResolveInfo ri = results.get(i);
83             if ((ri.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
84                 continue;
85             }
86             ComponentName foundComp = new ComponentName(ri.serviceInfo.applicationInfo.packageName,
87                     ri.serviceInfo.name);
88             if (comp != null) {
89                 throw new IllegalStateException("Multiple system services handle " + intent
90                         + ": " + comp + ", " + foundComp);
91             }
92             comp = foundComp;
93         }
94         return comp;
95     }
96 
97     private final IBluetoothProfileServiceConnection mConnection =
98             new IBluetoothProfileServiceConnection.Stub() {
99         @Override
100         public void onServiceConnected(ComponentName className, IBinder service) {
101             logDebug("Proxy object connected");
102             mService = getServiceInterface(service);
103             mHandler.sendMessage(mHandler.obtainMessage(
104                     MESSAGE_SERVICE_CONNECTED));
105         }
106 
107         @Override
108         public void onServiceDisconnected(ComponentName className) {
109             logDebug("Proxy object disconnected");
110             doUnbind();
111             mHandler.sendMessage(mHandler.obtainMessage(
112                     MESSAGE_SERVICE_DISCONNECTED));
113         }
114     };
115 
BluetoothProfileConnector(BluetoothProfile profile, int profileId, String profileName, String serviceName)116     BluetoothProfileConnector(BluetoothProfile profile, int profileId, String profileName,
117             String serviceName) {
118         mProfileId = profileId;
119         mProfileProxy = profile;
120         mProfileName = profileName;
121         mServiceName = serviceName;
122     }
123 
124     /** {@hide} */
125     @Override
finalize()126     public void finalize() {
127         mCloseGuard.warnIfOpen();
128         doUnbind();
129     }
130 
doBind()131     private boolean doBind() {
132         synchronized (mConnection) {
133             if (mService == null) {
134                 logDebug("Binding service...");
135                 mCloseGuard.open("doUnbind");
136                 try {
137                     return BluetoothAdapter.getDefaultAdapter().getBluetoothManager()
138                             .bindBluetoothProfileService(mProfileId, mServiceName, mConnection);
139                 } catch (RemoteException re) {
140                     logError("Failed to bind service. " + re);
141                     return false;
142                 }
143             }
144         }
145         return true;
146     }
147 
doUnbind()148     private void doUnbind() {
149         synchronized (mConnection) {
150             if (mService != null) {
151                 logDebug("Unbinding service...");
152                 mCloseGuard.close();
153                 try {
154                     BluetoothAdapter.getDefaultAdapter().getBluetoothManager()
155                             .unbindBluetoothProfileService(mProfileId, mConnection);
156                 } catch (RemoteException re) {
157                     logError("Unable to unbind service: " + re);
158                 } finally {
159                     mService = null;
160                 }
161             }
162         }
163     }
164 
connect(Context context, BluetoothProfile.ServiceListener listener)165     void connect(Context context, BluetoothProfile.ServiceListener listener) {
166         mContext = context;
167         mServiceListener = listener;
168         IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
169 
170         // Preserve legacy compatibility where apps were depending on
171         // registerStateChangeCallback() performing a permissions check which
172         // has been relaxed in modern platform versions
173         if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
174                 && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
175                         != PackageManager.PERMISSION_GRANTED) {
176             throw new SecurityException("Need BLUETOOTH permission");
177         }
178 
179         if (mgr != null) {
180             try {
181                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
182             } catch (RemoteException re) {
183                 logError("Failed to register state change callback. " + re);
184             }
185         }
186         doBind();
187     }
188 
disconnect()189     void disconnect() {
190         if (mServiceListener != null) {
191             BluetoothProfile.ServiceListener listener = mServiceListener;
192             mServiceListener = null;
193             listener.onServiceDisconnected(mProfileId);
194         }
195         IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
196         if (mgr != null) {
197             try {
198                 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
199             } catch (RemoteException re) {
200                 logError("Failed to unregister state change callback" + re);
201             }
202         }
203         doUnbind();
204     }
205 
getService()206     T getService() {
207         return mService;
208     }
209 
210     /**
211      * This abstract function is used to implement method to get the
212      * connected Bluetooth service interface.
213      * @param service the connected binder service.
214      * @return T the binder interface of {@code service}.
215      * @hide
216      */
getServiceInterface(IBinder service)217     public abstract T getServiceInterface(IBinder service);
218 
logDebug(String log)219     private void logDebug(String log) {
220         Log.d(mProfileName, log);
221     }
222 
logError(String log)223     private void logError(String log) {
224         Log.e(mProfileName, log);
225     }
226 
227     @SuppressLint("AndroidFrameworkBluetoothPermission")
228     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
229         @Override
230         public void handleMessage(Message msg) {
231             switch (msg.what) {
232                 case MESSAGE_SERVICE_CONNECTED: {
233                     if (mServiceListener != null) {
234                         mServiceListener.onServiceConnected(mProfileId, mProfileProxy);
235                     }
236                     break;
237                 }
238                 case MESSAGE_SERVICE_DISCONNECTED: {
239                     if (mServiceListener != null) {
240                         mServiceListener.onServiceDisconnected(mProfileId);
241                     }
242                     break;
243                 }
244             }
245         }
246     };
247 }
248