diff options
11 files changed, 140 insertions, 35 deletions
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index cd1a3dd02b61..073196cf7c0c 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/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; @@ -252,6 +253,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> * @@ -775,7 +799,7 @@ public final class BluetoothAdapter { try { if (DBG) Log.d(TAG, "Calling enableBLE"); mManagerService.updateBleAppCount(mToken, true); - return mManagerService.enable(); + return mManagerService.enable(ActivityThread.currentPackageName()); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -902,7 +926,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; } @@ -934,7 +958,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; } @@ -952,7 +976,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/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl index 0b81ee8c547e..f39ca8e0e9c8 100644 --- a/core/java/android/bluetooth/IBluetoothManager.aidl +++ b/core/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); IBluetoothGatt getBluetoothGatt(); boolean bindBluetoothProfileService(int profile, IBluetoothProfileServiceConnection proxy); @@ -49,3 +49,4 @@ interface IBluetoothManager int updateBleAppCount(IBinder b, boolean enable); boolean isBleAppPresent(); } + diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 2b39f1876ee3..0201111e4a22 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2502,4 +2502,11 @@ <item>405</item> </integer-array> + <!-- Specifies whether the permissions needed by a legacy app should be + reviewed before any of its components can run. A legacy app is one + with targetSdkVersion < 23, i.e apps using the old permission model. + If review is not required, permissions are reviewed before the app + is installed. --> + <bool name="config_permissionReviewRequired">false</bool> + </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 23cd7e6fb616..23458851acc1 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2616,4 +2616,7 @@ <java-symbol type="array" name="config_emergency_mcc_codes" /> <java-symbol type="drawable" name="emergency_icon" /> + + <java-symbol type="bool" name="config_permissionReviewRequired" /> + </resources> diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 6575a2a86cb9..7875cb86ee78 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -35,10 +35,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 +153,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, @@ -243,6 +247,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler = new BluetoothHandler(IoThread.get().getLooper()); mContext = context; + + mPermissionReviewRequired = Build.PERMISSIONS_REVIEW_REQUIRED + || context.getResources().getBoolean( + com.android.internal.R.bool.config_permissionReviewRequired); + mBluetooth = null; mBluetoothBinder = null; mBluetoothGatt = null; @@ -626,15 +635,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); @@ -650,14 +670,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) { @@ -678,6 +708,31 @@ 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); + mContext.startActivity(intent); + return true; + } + } catch (PackageManager.NameNotFoundException e) { + throw new RemoteException(e.getMessage()); + } + return false; + } + public void unbindAndFinish() { if (DBG) { Slog.d(TAG,"unbindAndFinish(): " + mBluetooth + diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index d9148817881b..2d534171b94d 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -368,7 +368,7 @@ public final class ActiveServices { // we do not start the service and launch a review activity if the calling app // is in the foreground passing it a pending intent to start the service when // review is completed. - if (Build.PERMISSIONS_REVIEW_REQUIRED) { + if (mAm.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) { if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage, callingUid, service, callerFg, userId)) { return null; @@ -891,7 +891,7 @@ public final class ActiveServices { // we schedule binding to the service but do not start its process, then // we launch a review activity to which is passed a callback to invoke // when done to start the bound service's process to completing the binding. - if (Build.PERMISSIONS_REVIEW_REQUIRED) { + if (mAm.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) { if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired( s.packageName, s.userId)) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index fdde15013663..b59f4686998e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1558,6 +1558,8 @@ public final class ActivityManagerService extends ActivityManagerNative // being called for multiwindow assist in a single session. private int mViSessionId = 1000; + final boolean mPermissionReviewRequired; + final class KillHandler extends Handler { static final int KILL_PROCESS_GROUP_MSG = 4000; @@ -2584,6 +2586,9 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass()); + mPermissionReviewRequired = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_permissionReviewRequired); + mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/); mHandlerThread.start(); @@ -10741,7 +10746,7 @@ public final class ActivityManagerService extends ActivityManagerNative // If permissions need a review before any of the app components can run, // we return no provider and launch a review activity if the calling app // is in the foreground. - if (Build.PERMISSIONS_REVIEW_REQUIRED) { + if (mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) { if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) { return null; } diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 32a798b33bcf..5bd6bed75281 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -410,7 +410,8 @@ class ActivityStarter { // If permissions need a review before any of the app components can run, we // launch the review activity and pass a pending intent to start the activity // we are to launching now after the review is completed. - if (Build.PERMISSIONS_REVIEW_REQUIRED && aInfo != null) { + if ((mService.mPermissionReviewRequired + || Build.PERMISSIONS_REVIEW_REQUIRED) && aInfo != null) { if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired( aInfo.packageName, userId)) { IIntentSender target = mService.getIntentSenderLocked( diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 8e33c4f4bc35..2e1958cc7ba8 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -618,7 +618,7 @@ public final class BroadcastQueue { // the broadcast and if the calling app is in the foreground and the broadcast is // explicit we launch the review UI passing it a pending intent to send the skipped // broadcast. - if (Build.PERMISSIONS_REVIEW_REQUIRED) { + if (mService.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) { if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName, filter.owningUserId)) { r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED; @@ -1124,7 +1124,8 @@ public final class BroadcastQueue { // the broadcast and if the calling app is in the foreground and the broadcast is // explicit we launch the review UI passing it a pending intent to send the skipped // broadcast. - if (Build.PERMISSIONS_REVIEW_REQUIRED && !skip) { + if ((mService.mPermissionReviewRequired + || Build.PERMISSIONS_REVIEW_REQUIRED) && !skip) { if (!requestStartTargetPermissionsReviewIfNeededLocked(r, info.activityInfo.packageName, UserHandle.getUserId( info.activityInfo.applicationInfo.uid))) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 85cc12427cef..70560c72bc61 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1119,6 +1119,8 @@ public class PackageManagerService extends IPackageManager.Stub { final @NonNull String mServicesSystemSharedLibraryPackageName; final @NonNull String mSharedSystemSharedLibraryPackageName; + final boolean mPermissionReviewRequired; + private final PackageUsage mPackageUsage = new PackageUsage(); private final CompilerStats mCompilerStats = new CompilerStats(); @@ -2053,6 +2055,10 @@ public class PackageManagerService extends IPackageManager.Stub { } mContext = context; + + mPermissionReviewRequired = context.getResources().getBoolean( + R.bool.config_permissionReviewRequired); + mFactoryTest = factoryTest; mOnlyCore = onlyCore; mMetrics = new DisplayMetrics(); @@ -3965,7 +3971,7 @@ public class PackageManagerService extends IPackageManager.Stub { // their permissions as always granted runtime ones since we need // to keep the review required permission flag per user while an // install permission's state is shared across all users. - if (Build.PERMISSIONS_REVIEW_REQUIRED + if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) { return; @@ -4076,7 +4082,7 @@ public class PackageManagerService extends IPackageManager.Stub { // their permissions as always granted runtime ones since we need // to keep the review required permission flag per user while an // install permission's state is shared across all users. - if (Build.PERMISSIONS_REVIEW_REQUIRED + if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) { return; @@ -9866,7 +9872,8 @@ public class PackageManagerService extends IPackageManager.Stub { // their permissions as always granted runtime ones since we need // to keep the review required permission flag per user while an // install permission's state is shared across all users. - if (!appSupportsRuntimePermissions && !Build.PERMISSIONS_REVIEW_REQUIRED) { + if (!appSupportsRuntimePermissions && !mPermissionReviewRequired + && !Build.PERMISSIONS_REVIEW_REQUIRED) { // For legacy apps dangerous permissions are install time ones. grant = GRANT_INSTALL; } else if (origPermissions.hasInstallPermission(bp.name)) { @@ -9952,7 +9959,7 @@ public class PackageManagerService extends IPackageManager.Stub { changedRuntimePermissionUserIds, userId); } // If the app supports runtime permissions no need for a review. - if (Build.PERMISSIONS_REVIEW_REQUIRED + if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) && appSupportsRuntimePermissions && (flags & PackageManager .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { @@ -9961,7 +9968,8 @@ public class PackageManagerService extends IPackageManager.Stub { changedRuntimePermissionUserIds = ArrayUtils.appendInt( changedRuntimePermissionUserIds, userId); } - } else if (Build.PERMISSIONS_REVIEW_REQUIRED + } else if ((mPermissionReviewRequired + || Build.PERMISSIONS_REVIEW_REQUIRED) && !appSupportsRuntimePermissions) { // For legacy apps that need a permission review, every new // runtime permission is granted but it is pending a review. @@ -16423,7 +16431,7 @@ public class PackageManagerService extends IPackageManager.Stub { // If permission review is enabled and this is a legacy app, mark the // permission as requiring a review as this is the initial state. int flags = 0; - if (Build.PERMISSIONS_REVIEW_REQUIRED + if ((mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) && ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { flags |= FLAG_PERMISSION_REVIEW_REQUIRED; } @@ -20272,7 +20280,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); // permissions to keep per user flag state whether review is needed. // Hence, if a new user is added we have to propagate dangerous // permission grants for these legacy apps. - if (Build.PERMISSIONS_REVIEW_REQUIRED) { + if (mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) { updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_ALL); } @@ -20726,7 +20734,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); public boolean isPermissionsReviewRequired(String packageName, int userId) { synchronized (mPackages) { // If we do not support permission review, done. - if (!Build.PERMISSIONS_REVIEW_REQUIRED) { + if (!mPermissionReviewRequired && !Build.PERMISSIONS_REVIEW_REQUIRED) { return false; } diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java index f7f79f324ecc..02dd53dc5243 100644 --- a/services/core/java/com/android/server/power/ShutdownThread.java +++ b/services/core/java/com/android/server/power/ShutdownThread.java @@ -546,7 +546,7 @@ public final class ShutdownThread extends Thread { bluetoothOff = bluetooth == null || !bluetooth.isEnabled(); if (!bluetoothOff) { Log.w(TAG, "Disabling Bluetooth..."); - bluetooth.disable(false); // disable but don't persist new state + bluetooth.disable(mContext.getPackageName(), false); // disable but don't persist new state } } catch (RemoteException ex) { Log.e(TAG, "RemoteException during bluetooth shutdown", ex); |