diff options
4 files changed, 110 insertions, 20 deletions
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 4f0cf74f64..bb344a68e7 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -23,6 +23,7 @@ import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.app.ActivityThread; import android.bluetooth.le.BluetoothLeAdvertiser; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; @@ -255,6 +256,29 @@ public final class BluetoothAdapter { "android.bluetooth.adapter.action.REQUEST_ENABLE"; /** + * Activity Action: Show a system activity that allows the user to turn off + * Bluetooth. This is used only if permission review is enabled which is for + * apps targeting API less than 23 require a permission review before any of + * the app's components can run. + * <p>This system activity will return once Bluetooth has completed turning + * off, or the user has decided not to turn Bluetooth off. + * <p>Notification of the result of this activity is posted using the + * {@link android.app.Activity#onActivityResult} callback. The + * <code>resultCode</code> + * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been + * turned off or {@link android.app.Activity#RESULT_CANCELED} if the user + * has rejected the request or an error has occurred. + * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED} + * for global notification whenever Bluetooth is turned on or off. + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REQUEST_DISABLE = + "android.bluetooth.adapter.action.REQUEST_DISABLE"; + + /** * Activity Action: Show a system activity that allows user to enable BLE scans even when * Bluetooth is turned off.<p> * @@ -772,7 +796,7 @@ public final class BluetoothAdapter { return true; } if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -899,7 +923,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -931,7 +955,7 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) public boolean disable() { try { - return mManagerService.disable(true); + return mManagerService.disable(ActivityThread.currentPackageName(), true); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } @@ -949,7 +973,7 @@ public final class BluetoothAdapter { public boolean disable(boolean persist) { try { - return mManagerService.disable(persist); + return mManagerService.disable(ActivityThread.currentPackageName(), persist); } catch (RemoteException e) {Log.e(TAG, "", e);} return false; } diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index 97a32974d2..12ebdac76d 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -1051,6 +1051,7 @@ public final class BluetoothGatt implements BluetoothProfile { /** * @deprecated Use {@link #abortReliableWrite()} */ + @Deprecated public void abortReliableWrite(BluetoothDevice mDevice) { abortReliableWrite(); } diff --git a/framework/java/android/bluetooth/IBluetoothManager.aidl b/framework/java/android/bluetooth/IBluetoothManager.aidl index 2b853a373b..3af4a4d7b5 100644 --- a/framework/java/android/bluetooth/IBluetoothManager.aidl +++ b/framework/java/android/bluetooth/IBluetoothManager.aidl @@ -34,9 +34,9 @@ interface IBluetoothManager void registerStateChangeCallback(in IBluetoothStateChangeCallback callback); void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); - boolean enable(); + boolean enable(String packageName); boolean enableNoAutoConnect(); - boolean disable(boolean persist); + boolean disable( String packageName, boolean persist); int getState(); IBluetoothGatt getBluetoothGatt(); @@ -50,3 +50,4 @@ interface IBluetoothManager int updateBleAppCount(IBinder b, boolean enable); boolean isBleAppPresent(); } + diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java index 52be811fad..6456048485 100644 --- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java @@ -28,6 +28,7 @@ import android.bluetooth.IBluetoothManager; import android.bluetooth.IBluetoothManagerCallback; import android.bluetooth.IBluetoothProfileServiceConnection; import android.bluetooth.IBluetoothStateChangeCallback; +import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -35,10 +36,12 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -151,6 +154,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private final Map <Integer, ProfileServiceConnections> mProfileServices = new HashMap <Integer, ProfileServiceConnections>(); + private final boolean mPermissionReviewRequired; + private void registerForAirplaneMode(IntentFilter filter) { final ContentResolver resolver = mContext.getContentResolver(); final String airplaneModeRadios = Settings.Global.getString(resolver, @@ -244,6 +249,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler = new BluetoothHandler(IoThread.get().getLooper()); mContext = context; + + mPermissionReviewRequired = context.getResources().getBoolean( + com.android.internal.R.bool.config_permissionReviewRequired); + mBluetooth = null; mBluetoothBinder = null; mBluetoothGatt = null; @@ -654,15 +663,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean enable() { - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG,"enable(): not allowed for non-active and non system user"); - return false; + public boolean enable(String packageName) throws RemoteException { + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; + + if (!callerSystem) { + if (!checkIfCallerIsForegroundUser()) { + Slog.w(TAG, "enable(): not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (!isEnabled() && mPermissionReviewRequired + && startConsentUiIfNeeded(packageName, callingUid, + BluetoothAdapter.ACTION_REQUEST_ENABLE)) { + return false; + } } - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permission"); if (DBG) { Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + " mBinding = " + mBinding + " mState = " + mState); @@ -678,14 +698,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } - public boolean disable(boolean persist) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH ADMIN permissicacheNameAndAddresson"); + public boolean disable(String packageName, boolean persist) throws RemoteException { + final int callingUid = Binder.getCallingUid(); + final boolean callerSystem = UserHandle.getAppId(callingUid) == Process.SYSTEM_UID; - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG,"disable(): not allowed for non-active and non system user"); - return false; + if (!callerSystem) { + if (!checkIfCallerIsForegroundUser()) { + Slog.w(TAG, "disable(): not allowed for non-active and non system user"); + return false; + } + + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH ADMIN permission"); + + if (isEnabled() && mPermissionReviewRequired + && startConsentUiIfNeeded(packageName, callingUid, + BluetoothAdapter.ACTION_REQUEST_DISABLE)) { + return false; + } } if (DBG) { @@ -706,6 +736,40 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return true; } + private boolean startConsentUiIfNeeded(String packageName, + int callingUid, String intentAction) throws RemoteException { + try { + // Validate the package only if we are going to use it + ApplicationInfo applicationInfo = mContext.getPackageManager() + .getApplicationInfoAsUser(packageName, + PackageManager.MATCH_DEBUG_TRIAGED_MISSING, + UserHandle.getUserId(callingUid)); + if (applicationInfo.uid != callingUid) { + throw new SecurityException("Package " + callingUid + + " not in uid " + callingUid); + } + + // Legacy apps in permission review mode trigger a user prompt + if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { + Intent intent = new Intent(intentAction); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + try { + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + // Shouldn't happen + Slog.e(TAG, "Intent to handle action " + intentAction + " missing"); + return false; + } + return true; + } + } catch (PackageManager.NameNotFoundException e) { + throw new RemoteException(e.getMessage()); + } + return false; + } + public void unbindAndFinish() { if (DBG) { Slog.d(TAG,"unbindAndFinish(): " + mBluetooth + |