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