1 /*
2  * Copyright 2022 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.bluetooth;
18 
19 import android.annotation.NonNull;
20 import android.annotation.RequiresPermission;
21 import android.bluetooth.BluetoothAdapter;
22 import android.bluetooth.BluetoothDevice;
23 import android.bluetooth.le.PeriodicAdvertisingCallback;
24 import android.bluetooth.le.PeriodicAdvertisingManager;
25 import android.bluetooth.le.ScanResult;
26 import android.content.ContentProviderClient;
27 import android.content.ContentResolver;
28 import android.content.ContentValues;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.res.AssetFileDescriptor;
32 import android.database.Cursor;
33 import android.media.session.MediaController;
34 import android.media.session.MediaSessionManager;
35 import android.net.Uri;
36 import android.os.Bundle;
37 import android.os.CancellationSignal;
38 import android.os.Handler;
39 import android.os.HandlerThread;
40 import android.os.Looper;
41 import android.os.Message;
42 import android.os.ParcelFileDescriptor;
43 import android.provider.Telephony;
44 import android.util.Log;
45 
46 import com.android.bluetooth.bass_client.BassClientPeriodicAdvertisingManager;
47 import com.android.bluetooth.gatt.AppAdvertiseStats;
48 import com.android.bluetooth.gatt.ContextMap;
49 import com.android.bluetooth.gatt.GattService;
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.obex.HeaderSet;
52 
53 import java.io.FileNotFoundException;
54 import java.io.IOException;
55 import java.io.InputStream;
56 import java.io.OutputStream;
57 import java.util.List;
58 import java.util.Set;
59 
60 /** Proxy class for method calls to help with unit testing */
61 public class BluetoothMethodProxy {
62     private static final String TAG = BluetoothMethodProxy.class.getSimpleName();
63     private static final Object INSTANCE_LOCK = new Object();
64     private static BluetoothMethodProxy sInstance;
65 
BluetoothMethodProxy()66     private BluetoothMethodProxy() {}
67 
68     /**
69      * Get the singleton instance of proxy
70      *
71      * @return the singleton instance, guaranteed not null
72      */
getInstance()73     public static BluetoothMethodProxy getInstance() {
74         synchronized (INSTANCE_LOCK) {
75             if (sInstance == null) {
76                 sInstance = new BluetoothMethodProxy();
77             }
78         }
79         return sInstance;
80     }
81 
82     /**
83      * Allow unit tests to substitute BluetoothPbapMethodCallProxy with a test instance
84      *
85      * @param proxy a test instance of the BluetoothPbapMethodCallProxy
86      */
87     @VisibleForTesting
setInstanceForTesting(BluetoothMethodProxy proxy)88     public static void setInstanceForTesting(BluetoothMethodProxy proxy) {
89         Utils.enforceInstrumentationTestMode();
90         synchronized (INSTANCE_LOCK) {
91             Log.d(TAG, "setInstanceForTesting(), set to " + proxy);
92             sInstance = proxy;
93         }
94     }
95 
96     /** Proxies {@link ContentResolver#query(Uri, String[], String, String[], String)}. */
contentResolverQuery( ContentResolver contentResolver, final Uri contentUri, final String[] projection, final String selection, final String[] selectionArgs, final String sortOrder)97     public Cursor contentResolverQuery(
98             ContentResolver contentResolver,
99             final Uri contentUri,
100             final String[] projection,
101             final String selection,
102             final String[] selectionArgs,
103             final String sortOrder) {
104         return contentResolver.query(contentUri, projection, selection, selectionArgs, sortOrder);
105     }
106 
107     /** Proxies {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}. */
contentResolverQuery( ContentResolver contentResolver, final Uri contentUri, final String[] projection, final Bundle queryArgs, final CancellationSignal cancellationSignal)108     public Cursor contentResolverQuery(
109             ContentResolver contentResolver,
110             final Uri contentUri,
111             final String[] projection,
112             final Bundle queryArgs,
113             final CancellationSignal cancellationSignal) {
114         return contentResolver.query(contentUri, projection, queryArgs, cancellationSignal);
115     }
116 
117     /** Proxies {@link ContentResolver#insert(Uri, ContentValues)}. */
contentResolverInsert( ContentResolver contentResolver, final Uri contentUri, final ContentValues contentValues)118     public Uri contentResolverInsert(
119             ContentResolver contentResolver,
120             final Uri contentUri,
121             final ContentValues contentValues) {
122         return contentResolver.insert(contentUri, contentValues);
123     }
124 
125     /** Proxies {@link ContentResolver#update(Uri, ContentValues, String, String[])}. */
contentResolverUpdate( ContentResolver contentResolver, final Uri contentUri, final ContentValues contentValues, String where, String[] selectionArgs)126     public int contentResolverUpdate(
127             ContentResolver contentResolver,
128             final Uri contentUri,
129             final ContentValues contentValues,
130             String where,
131             String[] selectionArgs) {
132         return contentResolver.update(contentUri, contentValues, where, selectionArgs);
133     }
134 
135     /** Proxies {@link ContentResolver#delete(Uri, String, String[])}. */
contentResolverDelete( ContentResolver contentResolver, final Uri url, final String where, final String[] selectionArgs)136     public int contentResolverDelete(
137             ContentResolver contentResolver,
138             final Uri url,
139             final String where,
140             final String[] selectionArgs) {
141         return contentResolver.delete(url, where, selectionArgs);
142     }
143 
144     /** Proxies {@link BluetoothAdapter#isEnabled()}. */
bluetoothAdapterIsEnabled(BluetoothAdapter adapter)145     public boolean bluetoothAdapterIsEnabled(BluetoothAdapter adapter) {
146         return adapter.isEnabled();
147     }
148 
149     /**
150      * Proxies {@link BluetoothAdapter#getRemoteLeDevice(String, int)} on default Bluetooth Adapter.
151      */
getDefaultAdapterRemoteLeDevice(String address, int addressType)152     public BluetoothDevice getDefaultAdapterRemoteLeDevice(String address, int addressType) {
153         return BluetoothAdapter.getDefaultAdapter().getRemoteLeDevice(address, addressType);
154     }
155 
156     /** Proxies {@link ContentResolver#openFileDescriptor(Uri, String)}. */
contentResolverOpenFileDescriptor( ContentResolver contentResolver, final Uri uri, final String mode)157     public ParcelFileDescriptor contentResolverOpenFileDescriptor(
158             ContentResolver contentResolver, final Uri uri, final String mode)
159             throws FileNotFoundException {
160         return contentResolver.openFileDescriptor(uri, mode);
161     }
162 
163     /** Proxies {@link ContentResolver#openAssetFileDescriptor(Uri, String)}. */
contentResolverOpenAssetFileDescriptor( ContentResolver contentResolver, final Uri uri, final String mode)164     public AssetFileDescriptor contentResolverOpenAssetFileDescriptor(
165             ContentResolver contentResolver, final Uri uri, final String mode)
166             throws FileNotFoundException {
167         return contentResolver.openAssetFileDescriptor(uri, mode);
168     }
169 
170     /** Proxies {@link ContentResolver#openInputStream(Uri)}. */
contentResolverOpenInputStream( ContentResolver contentResolver, final Uri uri)171     public InputStream contentResolverOpenInputStream(
172             ContentResolver contentResolver, final Uri uri) throws FileNotFoundException {
173         return contentResolver.openInputStream(uri);
174     }
175 
176     /** Proxies {@link ContentResolver#acquireUnstableContentProviderClient(String)}. */
contentResolverAcquireUnstableContentProviderClient( ContentResolver contentResolver, @NonNull String name)177     public ContentProviderClient contentResolverAcquireUnstableContentProviderClient(
178             ContentResolver contentResolver, @NonNull String name) {
179         return contentResolver.acquireUnstableContentProviderClient(name);
180     }
181 
182     /** Proxies {@link ContentResolver#openOutputStream(Uri)}. */
contentResolverOpenOutputStream(ContentResolver contentResolver, Uri uri)183     public OutputStream contentResolverOpenOutputStream(ContentResolver contentResolver, Uri uri)
184             throws FileNotFoundException {
185         return contentResolver.openOutputStream(uri);
186     }
187 
188     /** Proxies {@link Context#sendBroadcast(Intent)}. */
contextSendBroadcast(Context context, @RequiresPermission Intent intent)189     public void contextSendBroadcast(Context context, @RequiresPermission Intent intent) {
190         context.sendBroadcast(intent);
191     }
192 
193     /** Proxies {@link Handler#sendEmptyMessage(int)}}. */
handlerSendEmptyMessage(Handler handler, final int what)194     public boolean handlerSendEmptyMessage(Handler handler, final int what) {
195         return handler.sendEmptyMessage(what);
196     }
197 
198     /** Proxies {@link Handler#sendMessageDelayed(Message, long)}. */
handlerSendMessageDelayed( Handler handler, final int what, final long delayMillis)199     public boolean handlerSendMessageDelayed(
200             Handler handler, final int what, final long delayMillis) {
201         return handler.sendMessageDelayed(handler.obtainMessage(what), delayMillis);
202     }
203 
204     /** Proxies {@link HeaderSet#getHeader}. */
getHeader(HeaderSet headerSet, int headerId)205     public Object getHeader(HeaderSet headerSet, int headerId) throws IOException {
206         return headerSet.getHeader(headerId);
207     }
208 
209     /** Proxies {@link Context#getSystemService(Class)}. */
getSystemService(Context context, Class<T> serviceClass)210     public <T> T getSystemService(Context context, Class<T> serviceClass) {
211         return context.getSystemService(serviceClass);
212     }
213 
214     /** Proxies {@link Telephony.Threads#getOrCreateThreadId(Context, Set <String>)}. */
telephonyGetOrCreateThreadId(Context context, Set<String> recipients)215     public long telephonyGetOrCreateThreadId(Context context, Set<String> recipients) {
216         return Telephony.Threads.getOrCreateThreadId(context, recipients);
217     }
218 
219     /**
220      * Proxies {@link
221      * BassClientPeriodicAdvertisingManager#initializePeriodicAdvertisingManagerOnDefaultAdapter}.
222      */
initializePeriodicAdvertisingManagerOnDefaultAdapter()223     public boolean initializePeriodicAdvertisingManagerOnDefaultAdapter() {
224         return BassClientPeriodicAdvertisingManager
225                 .initializePeriodicAdvertisingManagerOnDefaultAdapter();
226     }
227 
228     /**
229      * Proxies {@link PeriodicAdvertisingManager#registerSync(ScanResult, int, int,
230      * PeriodicAdvertisingCallback, Handler)}.
231      */
periodicAdvertisingManagerRegisterSync( PeriodicAdvertisingManager manager, ScanResult scanResult, int skip, int timeout, PeriodicAdvertisingCallback callback, Handler handler)232     public void periodicAdvertisingManagerRegisterSync(
233             PeriodicAdvertisingManager manager,
234             ScanResult scanResult,
235             int skip,
236             int timeout,
237             PeriodicAdvertisingCallback callback,
238             Handler handler) {
239         manager.registerSync(scanResult, skip, timeout, callback, handler);
240     }
241 
242     /** Proxies {@link PeriodicAdvertisingManager#unregisterSync(PeriodicAdvertisingCallback)}. */
periodicAdvertisingManagerUnregisterSync( PeriodicAdvertisingManager manager, PeriodicAdvertisingCallback callback)243     public void periodicAdvertisingManagerUnregisterSync(
244             PeriodicAdvertisingManager manager, PeriodicAdvertisingCallback callback) {
245         manager.unregisterSync(callback);
246     }
247 
248     /** Proxies {@link PeriodicAdvertisingManager#transferSync}. */
periodicAdvertisingManagerTransferSync( PeriodicAdvertisingManager manager, BluetoothDevice bda, int serviceData, int syncHandle)249     public void periodicAdvertisingManagerTransferSync(
250             PeriodicAdvertisingManager manager,
251             BluetoothDevice bda,
252             int serviceData,
253             int syncHandle) {
254         manager.transferSync(bda, serviceData, syncHandle);
255     }
256 
257     /** Proxies {@link PeriodicAdvertisingManager#transferSetInfo}. */
periodicAdvertisingManagerTransferSetInfo( PeriodicAdvertisingManager manager, BluetoothDevice bda, int serviceData, int advHandle, PeriodicAdvertisingCallback callback)258     public void periodicAdvertisingManagerTransferSetInfo(
259             PeriodicAdvertisingManager manager,
260             BluetoothDevice bda,
261             int serviceData,
262             int advHandle,
263             PeriodicAdvertisingCallback callback) {
264         manager.transferSetInfo(bda, serviceData, advHandle, callback);
265     }
266 
267     /** Proxies {@link AppAdvertiseStats}. */
createAppAdvertiseStats( int id, String name, ContextMap map, GattService service)268     public AppAdvertiseStats createAppAdvertiseStats(
269             int id, String name, ContextMap map, GattService service) {
270         return new AppAdvertiseStats(id, name, map, service);
271     }
272 
273     /** Proxies {@link Thread#start()}. */
threadStart(Thread thread)274     public void threadStart(Thread thread) {
275         thread.start();
276     }
277 
278     /** Proxies {@link HandlerThread#getLooper()}. */
handlerThreadGetLooper(HandlerThread handlerThread)279     public Looper handlerThreadGetLooper(HandlerThread handlerThread) {
280         return handlerThread.getLooper();
281     }
282 
283     /** Peoziws {@link MediaSessionManager#getActiveSessions} */
mediaSessionManagerGetActiveSessions( MediaSessionManager manager)284     public @NonNull List<MediaController> mediaSessionManagerGetActiveSessions(
285             MediaSessionManager manager) {
286         return manager.getActiveSessions(null);
287     }
288 }
289