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