1 /*
2  * Copyright (C) 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 
18 package com.android.internal.telephony;
19 
20 import android.app.PendingIntent;
21 import android.bluetooth.BluetoothAdapter;
22 import android.bluetooth.BluetoothDevice;
23 import android.bluetooth.BluetoothMapClient;
24 import android.bluetooth.BluetoothProfile;
25 import android.content.Context;
26 import android.net.Uri;
27 import android.telecom.PhoneAccount;
28 import android.telephony.SmsManager;
29 import android.telephony.SubscriptionInfo;
30 import android.util.Log;
31 
32 
33 /**
34  * BtSmsInterfaceManager to provide a mechanism for sending SMS over Bluetooth
35  */
36 public class BtSmsInterfaceManager {
37 
38     private static final String LOG_TAG = "BtSmsInterfaceManager";
39 
40     /**
41      * Sends text through connected Bluetooth device
42      */
sendText(Context context, String destAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, SubscriptionInfo info)43     public void sendText(Context context, String destAddr, String text, PendingIntent sentIntent,
44             PendingIntent deliveryIntent, SubscriptionInfo info) {
45         BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
46         if (btAdapter == null) {
47             // No bluetooth service on this platform?
48             sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_NO_BLUETOOTH_SERVICE);
49             return;
50         }
51         BluetoothDevice device = btAdapter.getRemoteDevice(info.getIccId());
52         if (device == null) {
53             Log.d(LOG_TAG, "Bluetooth device addr invalid: " + info.getIccId());
54             sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS);
55             return;
56         }
57         if (btAdapter.getProfileProxy(context.getApplicationContext(),
58                 new MapMessageSender(destAddr, text, device, sentIntent, deliveryIntent),
59                 BluetoothProfile.MAP_CLIENT)) {
60             return;
61         }
62         throw new RuntimeException("Can't send message through BluetoothMapClient");
63     }
64 
sendErrorInPendingIntent(PendingIntent intent, int errorCode)65     private void sendErrorInPendingIntent(PendingIntent intent, int errorCode) {
66         if (intent == null) {
67             return;
68         }
69         try {
70             intent.send(errorCode);
71         } catch (PendingIntent.CanceledException e) {
72             // PendingIntent is cancelled. ignore sending this error code back to
73             // caller.
74             Log.d(LOG_TAG, "PendingIntent.CanceledException: " + e.getMessage());
75         }
76     }
77 
78     private class MapMessageSender implements BluetoothProfile.ServiceListener {
79 
80         final Uri[] mDestAddr;
81         private String mMessage;
82         final BluetoothDevice mDevice;
83         final PendingIntent mSentIntent;
84         final PendingIntent mDeliveryIntent;
85 
MapMessageSender(final String destAddr, final String message, final BluetoothDevice device, final PendingIntent sentIntent, final PendingIntent deliveryIntent)86         MapMessageSender(final String destAddr, final String message, final BluetoothDevice device,
87                 final PendingIntent sentIntent, final PendingIntent deliveryIntent) {
88             super();
89             mDestAddr = new Uri[]{new Uri.Builder()
90                     .appendPath(destAddr)
91                     .scheme(PhoneAccount.SCHEME_TEL)
92                     .build()};
93             mMessage = message;
94             mDevice = device;
95             mSentIntent = sentIntent;
96             mDeliveryIntent = deliveryIntent;
97         }
98 
99         @Override
onServiceConnected(int profile, BluetoothProfile proxy)100         public void onServiceConnected(int profile, BluetoothProfile proxy) {
101             Log.d(LOG_TAG, "Service connected");
102             if (profile != BluetoothProfile.MAP_CLIENT) {
103                 return;
104             }
105             BluetoothMapClient mapProfile = (BluetoothMapClient) proxy;
106             if (mMessage != null) {
107                 Log.d(LOG_TAG, "Sending message thru bluetooth");
108                 mapProfile.sendMessage(mDevice, mDestAddr, mMessage, mSentIntent, mDeliveryIntent);
109                 mMessage = null;
110             }
111             BluetoothAdapter.getDefaultAdapter()
112                     .closeProfileProxy(BluetoothProfile.MAP_CLIENT, mapProfile);
113         }
114 
115         @Override
onServiceDisconnected(int profile)116         public void onServiceDisconnected(int profile) {
117             if (mMessage != null) {
118                 Log.d(LOG_TAG, "Bluetooth disconnected before sending the message");
119                 sendErrorInPendingIntent(mSentIntent, SmsManager.RESULT_BLUETOOTH_DISCONNECTED);
120                 mMessage = null;
121             }
122         }
123     }
124 }
125