diff options
| author | 2016-12-29 12:16:34 -0800 | |
|---|---|---|
| committer | 2016-12-29 12:44:03 -0800 | |
| commit | c816141bceb61d8f5a4e6aa6067c61f97a02efba (patch) | |
| tree | 49f15014d243ccb71c7ec577eb0f934e84e1a082 | |
| parent | 59d63e8b1d99ec7f90cc425b5d3908d05e14dcba (diff) | |
| parent | 9cc7ebe7d90c8da1340c8115023bd38451e58f7e (diff) | |
resolve merge conflicts of 9cc7ebe to stage-aosp-master
Change-Id: Ia1cdf49d77a574c38ed5cc33c31d5cf930103484
4 files changed, 265 insertions, 145 deletions
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 1fcf82ac0c4a..927d382f6482 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; @@ -272,6 +273,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> * @@ -656,30 +680,7 @@ public final class BluetoothAdapter { } /** - * Performs action based on user action to turn BT ON - * or OFF if BT is in BLE_ON state - */ - private void notifyUserAction(boolean enable) { - try { - mServiceLock.readLock().lock(); - if (mService == null) { - Log.e(TAG, "mService is null"); - return; - } - if (enable) { - mService.onLeServiceUp(); //NA:TODO implementation pending - } else { - mService.onBrEdrDown(); //NA:TODO implementation pending - } - } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); - } - } - - /** - * Turns off Bluetooth LE which was earlier turned on by calling EnableBLE(). + * Turns off Bluetooth LE which was earlier turned on by calling enableBLE(). * * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition * to STATE_OFF and completely shut-down Bluetooth @@ -709,61 +710,50 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) return false; int state = getLeState(); - if (state == BluetoothAdapter.STATE_ON) { - if (DBG) Log.d (TAG, "STATE_ON: shouldn't disable"); + if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) { + String packageName = ActivityThread.currentPackageName(); + if (DBG) Log.d (TAG, "disableBLE(): de-registering " + packageName); try { - mManagerService.updateBleAppCount(mToken, false); + mManagerService.updateBleAppCount(mToken, false, packageName); } catch (RemoteException e) { Log.e(TAG, "", e); } return true; - - } else if (state == BluetoothAdapter.STATE_BLE_ON) { - if (DBG) Log.d (TAG, "STATE_BLE_ON"); - int bleAppCnt = 0; - try { - bleAppCnt = mManagerService.updateBleAppCount(mToken, false); - } catch (RemoteException e) { - Log.e(TAG, "", e); - } - if (bleAppCnt == 0) { - // Disable only if there are no other clients - notifyUserAction(false); - } - return true; } - if (DBG) Log.d (TAG, "STATE_OFF: Already disabled"); + if (DBG) Log.d (TAG, "disableBLE(): Already disabled"); return false; } /** - * Special Applications who want to only turn on Bluetooth Low Energy (BLE) would - * EnableBLE, EnableBLE brings-up Bluetooth so that application can access - * only LE related feature (Bluetooth GATT layers interfaces using the respective class) - * EnableBLE in turn registers the existance of a special App which wants to - * turn on Bluetooth Low enrgy part without making it visible at the settings UI - * as Bluetooth ON. - * <p>Invoking EnableBLE when Bluetooth is already in ON state, would just registers - * the existance of special Application and doesn't do anything to current BT state. - * when user turn OFF Bluetooth from UI, if there is an existance of special app, Bluetooth - * would stay in BLE_ON state so that LE features are still acessible to the special - * Applications. + * Applications who want to only use Bluetooth Low Energy (BLE) can call enableBLE. * - * <p>This is an asynchronous call: it will return immediately, and + * enableBLE registers the existence of an app using only LE functions. + * + * enableBLE may enable Bluetooth to an LE only mode so that an app can use + * LE related features (BluetoothGatt or BluetoothGattServer classes) + * + * If the user disables Bluetooth while an app is registered to use LE only features, + * Bluetooth will remain on in LE only mode for the app. + * + * When Bluetooth is in LE only mode, it is not shown as ON to the UI. + * + * <p>This is an asynchronous call: it returns immediately, and * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} - * to be notified of subsequent adapter state changes. If this call returns - * true, then the adapter state will immediately transition from {@link - * #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, and some time - * later transition to either {@link #STATE_OFF} or {@link - * #STATE_BLE_ON}. If this call returns false then there was an - * immediate problem that will prevent the adapter from being turned on - - * such as Airplane mode, or the adapter is already turned on. - * (@link #ACTION_BLE_STATE_CHANGED) returns the Bluetooth Adapter's various + * to be notified of adapter state changes. + * + * If this call returns * true, then the adapter state is either in a mode where + * LE is available, or will transition from {@link #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, + * and some time later transition to either {@link #STATE_OFF} or {@link #STATE_BLE_ON}. + * + * If this call returns false then there was an immediate problem that prevents the + * adapter from being turned on - such as Airplane mode. + * + * {@link #ACTION_BLE_STATE_CHANGED} returns the Bluetooth Adapter's various * states, It includes all the classic Bluetooth Adapter states along with * internal BLE only states * - * @return true to indicate Bluetooth LE start-up has begun, or false on + * @return true to indicate Bluetooth LE will be available, or false on * immediate error * @hide */ @@ -772,13 +762,14 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) return false; try { - mManagerService.updateBleAppCount(mToken, true); + String packageName = ActivityThread.currentPackageName(); + mManagerService.updateBleAppCount(mToken, true, packageName); if (isLeEnabled()) { if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled"); return true; } if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); - return mManagerService.enable(); + return mManagerService.enable(packageName); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -905,7 +896,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; } @@ -937,7 +928,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; } @@ -955,7 +946,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; } @@ -1916,9 +1907,6 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.MAP_CLIENT) { BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); return true; - } else if (profile == BluetoothProfile.INPUT_HOST) { - BluetoothInputHost iHost = new BluetoothInputHost(context, listener); - return true; } else { return false; } @@ -1995,10 +1983,6 @@ public final class BluetoothAdapter { BluetoothMapClient mapClient = (BluetoothMapClient)proxy; mapClient.close(); break; - case BluetoothProfile.INPUT_HOST: - BluetoothInputHost iHost = (BluetoothInputHost) proxy; - iHost.close(); - break; } } @@ -2070,7 +2054,7 @@ public final class BluetoothAdapter { return true; } try { - return mManagerService.enableNoAutoConnect(); + return mManagerService.enableNoAutoConnect(ActivityThread.currentPackageName()); } 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 2b853a373b52..5afd77418222 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 enableNoAutoConnect(); - boolean disable(boolean persist); + boolean enable(String packageName); + boolean enableNoAutoConnect(String packageName); + boolean disable(String packageName, boolean persist); int getState(); IBluetoothGatt getBluetoothGatt(); @@ -47,6 +47,6 @@ interface IBluetoothManager String getName(); boolean isBleScanAlwaysAvailable(); - int updateBleAppCount(IBinder b, boolean enable); + int updateBleAppCount(IBinder b, boolean enable, String packageName); boolean isBleAppPresent(); } diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index a37fc8be0a7c..d55b230fffc7 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -35,11 +35,13 @@ 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.Bundle; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -55,14 +57,16 @@ import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; -import java.util.concurrent.locks.ReentrantReadWriteLock; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.HashMap; +import java.util.LinkedList; import java.util.Map; + class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; private static final boolean DBG = true; @@ -135,16 +139,46 @@ class BluetoothManagerService extends IBluetoothManager.Stub { new ReentrantReadWriteLock(); private boolean mBinding; private boolean mUnbinding; + // used inside handler thread private boolean mQuietEnable = false; - // configuarion from external IBinder call which is used to + private boolean mEnable; + + /** + * Used for tracking apps that enabled / disabled Bluetooth. + */ + private class ActiveLog { + private String mPackageName; + private boolean mEnable; + private long mTimestamp; + + public ActiveLog(String packageName, boolean enable, long timestamp) { + mPackageName = packageName; + mEnable = enable; + mTimestamp = timestamp; + } + + public long getTime() { + return mTimestamp; + } + + public String toString() { + return android.text.format.DateFormat.format("MM-dd hh:mm:ss ", mTimestamp) + + (mEnable ? " Enabled " : " Disabled ") + " by " + mPackageName; + } + + } + + private LinkedList<ActiveLog> mActiveLogs; + + // configuration from external IBinder call which is used to // synchronize with broadcast receiver. private boolean mQuietEnableExternal; - // configuarion from external IBinder call which is used to - // synchronize with broadcast receiver. private boolean mEnableExternal; - // used inside handler thread - private boolean mEnable; + + // Map of apps registered to keep BLE scanning on. + private Map<IBinder, ClientDeathRecipient> mBleApps = new ConcurrentHashMap<IBinder, ClientDeathRecipient>(); + private int mState; private final BluetoothHandler mHandler; private int mErrorRecoveryRetryCounter; @@ -155,6 +189,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, @@ -168,7 +204,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { + private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() { @Override public void onBluetoothStateChange(int prevState, int newState) throws RemoteException { Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE,prevState,newState); @@ -184,7 +220,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { final boolean bluetoothDisallowed = newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); if ((mEnable || mEnableExternal) && bluetoothDisallowed) { - disable(true); + try { + disable("android.os.UserManagerInternal", true); + } catch (RemoteException e) { + // Shouldn't happen: startConsentUiIfNeeded not called + // when from system. + } } } }; @@ -242,12 +283,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else if (st == BluetoothAdapter.STATE_ON){ // disable without persisting the setting Slog.d(TAG, "Calling disable"); - sendDisableMsg(); + sendDisableMsg("airplane mode"); } } else if (mEnableExternal) { // enable without persisting the setting Slog.d(TAG, "Calling enable"); - sendEnableMsg(mQuietEnableExternal); + sendEnableMsg(mQuietEnableExternal, "airplane mode"); } } } @@ -258,6 +299,12 @@ 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); + + mActiveLogs = new LinkedList<ActiveLog>(); mBluetooth = null; mBluetoothBinder = null; mBluetoothGatt = null; @@ -284,15 +331,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mEnableExternal = true; } - int sysUiUid = -1; + int systemUiUid = -1; try { - sysUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", + systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); } catch (PackageManager.NameNotFoundException e) { // Some platforms, such as wearables do not have a system ui. Slog.w(TAG, "Unable to resolve SystemUI's UID.", e); } - mSystemUiUid = sysUiUid; + mSystemUiUid = systemUiUid; } /** @@ -470,8 +517,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } class ClientDeathRecipient implements IBinder.DeathRecipient { + private String mPackageName; + + public ClientDeathRecipient(String packageName) { + mPackageName = packageName; + } + public void binderDied() { - if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App"); + if (DBG) Slog.d(TAG, "Binder is dead - unregister " + mPackageName); if (isBleAppPresent()) { // Nothing to do, another app is here. return; @@ -490,10 +543,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); } } - } - /** Internal death rec list */ - Map<IBinder, ClientDeathRecipient> mBleApps = new ConcurrentHashMap<IBinder, ClientDeathRecipient>(); + public String getPackageName() { + return mPackageName; + } + } @Override public boolean isBleScanAlwaysAvailable() { @@ -551,28 +605,22 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - public int updateBleAppCount(IBinder token, boolean enable) { - if (enable) { - ClientDeathRecipient r = mBleApps.get(token); - if (r == null) { - ClientDeathRecipient deathRec = new ClientDeathRecipient(); - try { - token.linkToDeath(deathRec, 0); - } catch (RemoteException ex) { - throw new IllegalArgumentException("Wake lock is already dead."); - } - mBleApps.put(token, deathRec); - if (DBG) Slog.d(TAG, "Registered for death Notification"); - } - - } else { - ClientDeathRecipient r = mBleApps.get(token); - if (r != null) { - // Unregister death recipient as the app goes away. - token.unlinkToDeath(r, 0); - mBleApps.remove(token); - if (DBG) Slog.d(TAG, "Unregistered for death Notification"); + public int updateBleAppCount(IBinder token, boolean enable, String packageName) { + ClientDeathRecipient r = mBleApps.get(token); + if (r == null && enable) { + ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName); + try { + token.linkToDeath(deathRec, 0); + } catch (RemoteException ex) { + throw new IllegalArgumentException("BLE app (" + packageName + ") already dead!"); } + mBleApps.put(token, deathRec); + if (DBG) Slog.d(TAG, "Registered for death of " + packageName); + } else if (!enable && r != null) { + // Unregister death recipient as the app goes away. + token.unlinkToDeath(r, 0); + mBleApps.remove(token); + if (DBG) Slog.d(TAG, "Unregistered for death of " + packageName); } int appCount = mBleApps.size(); if (DBG) Slog.d(TAG, appCount + " registered Ble Apps"); @@ -587,7 +635,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBleApps.clear(); } - /** @hide*/ + /** @hide */ public boolean isBleAppPresent() { if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size()); return mBleApps.size() > 0; @@ -653,7 +701,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } - public boolean enableNoAutoConnect() + public boolean enableNoAutoConnect(String packageName) { if (isBluetoothDisallowed()) { if (DBG) { @@ -678,12 +726,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { synchronized(mReceiver) { mQuietEnableExternal = true; mEnableExternal = true; - sendEnableMsg(true); + sendEnableMsg(true, packageName); } return true; } - public boolean enable() { + public boolean enable(String packageName) throws RemoteException { if (isBluetoothDisallowed()) { if (DBG) { Slog.d(TAG,"enable(): not enabling - bluetooth disallowed"); @@ -691,16 +739,27 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } - if ((Binder.getCallingUid() != Process.SYSTEM_UID) && - (!checkIfCallerIsForegroundUser())) { - Slog.w(TAG,"enable(): not allowed for non-active and non system user"); - return false; + 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 + + Slog.d(TAG,"enable(" + packageName + "): mBluetooth =" + mBluetooth + " mBinding = " + mBinding + " mState = " + BluetoothAdapter.nameForState(mState)); } @@ -709,20 +768,30 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mQuietEnableExternal = false; mEnableExternal = true; // waive WRITE_SECURE_SETTINGS permission check - sendEnableMsg(false); + sendEnableMsg(false, packageName); } if (DBG) Slog.d(TAG, "enable returning"); 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) { @@ -735,11 +804,36 @@ class BluetoothManagerService extends IBluetoothManager.Stub { persistBluetoothSetting(BLUETOOTH_OFF); } mEnableExternal = false; - sendDisableMsg(); + sendDisableMsg(packageName); } 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 + @@ -855,7 +949,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); - sendEnableMsg(mQuietEnableExternal); + sendEnableMsg(mQuietEnableExternal, "system boot"); } else if (!isNameAndAddressSet()) { if (DBG) Slog.d(TAG, "Getting adapter name and address"); Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); @@ -1823,13 +1917,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; } - private void sendDisableMsg() { + private void sendDisableMsg(String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE)); + addActiveLog(packageName, false); } - private void sendEnableMsg(boolean quietMode) { + private void sendEnableMsg(boolean quietMode, String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0)); + addActiveLog(packageName, true); + } + + private void addActiveLog(String packageName, boolean enable) { + synchronized (mActiveLogs) { + if (mActiveLogs.size() > 10) { + mActiveLogs.remove(); + } + mActiveLogs.add(new ActiveLog(packageName, enable, System.currentTimeMillis())); + } } private void recoverBluetoothServiceFromError(boolean clearBle) { @@ -1900,19 +2005,50 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); String errorMsg = null; + + boolean protoOut = (args.length > 0) && args[0].startsWith("--proto"); + + if (!protoOut) { + writer.println("Bluetooth Status"); + writer.println(" enabled: " + isEnabled()); + writer.println(" state: " + BluetoothAdapter.nameForState(mState)); + writer.println(" address: " + mAddress); + writer.println(" name: " + mName); + if (mEnable) { + long onDuration = System.currentTimeMillis() - mActiveLogs.getLast().getTime(); + String onDurationString = String.format("%02d:%02d:%02d.%03d", + (int)(onDuration / (1000 * 60 * 60)), + (int)((onDuration / (1000 * 60)) % 60), + (int)((onDuration / 1000) % 60), + (int)(onDuration % 1000)); + writer.println(" time since enabled: " + onDurationString + "\n"); + } + + writer.println("Enable log:"); + for (ActiveLog log : mActiveLogs) { + writer.println(log); + } + + writer.println("\n" + mBleApps.size() + " BLE Apps registered:"); + for (ClientDeathRecipient app : mBleApps.values()) { + writer.println(app.getPackageName()); + } + + writer.flush(); + } + if (mBluetoothBinder == null) { errorMsg = "Bluetooth Service not connected"; } else { try { mBluetoothBinder.dump(fd, args); } catch (RemoteException re) { - errorMsg = "RemoteException while calling Bluetooth Service"; + errorMsg = "RemoteException while dumping Bluetooth Service"; } } if (errorMsg != null) { // Silently return if we are extracting metrics in Protobuf format - if ((args.length > 0) && args[0].startsWith("--proto")) - return; + if (protoOut) return; writer.println(errorMsg); } } diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java index 44894ed99bb2..badee82becbd 100644 --- a/services/core/java/com/android/server/power/ShutdownThread.java +++ b/services/core/java/com/android/server/power/ShutdownThread.java @@ -547,7 +547,7 @@ public final class ShutdownThread extends Thread { bluetooth.getState() == BluetoothAdapter.STATE_OFF; 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); |