summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--framework/java/android/bluetooth/BluetoothAdapter.java32
-rw-r--r--framework/java/android/bluetooth/BluetoothGatt.java1
-rw-r--r--framework/java/android/bluetooth/IBluetoothManager.aidl5
-rw-r--r--service/java/com/android/server/bluetooth/BluetoothManagerService.java92
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 +