Android-x86
Fork
Donation

  • R/O
  • HTTP
  • SSH
  • HTTPS

frameworks-base: Commit

frameworks/base


Commit MetaInfo

Revision65cecae44357f0507c7e1dda7eb0172671682d81 (tree)
Time2011-10-30 17:33:22
AuthorChih-Wei Huang <cwhuang@linu...>
CommiterChih-Wei Huang

Log Message

Merge remote branch 'aosp/gingerbread-mr4-release' into gingerbread-x86

Change Summary

Incremental Difference

--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -269,6 +269,22 @@ public final class BluetoothA2dp {
269269 }
270270 }
271271
272+ /**
273+ * Allow or disallow incoming connection
274+ * @param device Sink
275+ * @param value True / False
276+ * @return Success or Failure of the binder call.
277+ */
278+ public boolean allowIncomingConnect(BluetoothDevice device, boolean value) {
279+ if (DBG) log("allowIncomingConnect(" + device + ":" + value + ")");
280+ try {
281+ return mService.allowIncomingConnect(device, value);
282+ } catch (RemoteException e) {
283+ Log.e(TAG, "", e);
284+ return false;
285+ }
286+ }
287+
272288 /** Helper for converting a state to a string.
273289 * For debug use only - strings are not internationalized.
274290 * @hide
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -276,6 +276,33 @@ public final class BluetoothDevice implements Parcelable {
276276 public static final String ACTION_PAIRING_CANCEL =
277277 "android.bluetooth.device.action.PAIRING_CANCEL";
278278
279+ /** @hide */
280+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
281+ public static final String ACTION_CONNECTION_ACCESS_REQUEST =
282+ "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST";
283+
284+ /** @hide */
285+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
286+ public static final String ACTION_CONNECTION_ACCESS_REPLY =
287+ "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY";
288+
289+ /** @hide */
290+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
291+ public static final String ACTION_CONNECTION_ACCESS_CANCEL =
292+ "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL";
293+ /**
294+ * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REPLY} intent.
295+ * @hide
296+ */
297+ public static final String EXTRA_CONNECTION_ACCESS_RESULT =
298+ "android.bluetooth.device.extra.CONNECTION_ACCESS_RESULT";
299+
300+ /**@hide*/
301+ public static final int CONNECTION_ACCESS_YES = 1;
302+
303+ /**@hide*/
304+ public static final int CONNECTION_ACCESS_NO = 2;
305+
279306 /** A bond attempt succeeded
280307 * @hide */
281308 public static final int BOND_SUCCESS = 0;
--- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java
+++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java
@@ -21,9 +21,11 @@ import android.content.Context;
2121 import android.content.Intent;
2222 import android.content.IntentFilter;
2323 import android.os.Message;
24+import android.os.PowerManager;
2425 import android.server.BluetoothA2dpService;
2526 import android.server.BluetoothService;
2627 import android.util.Log;
28+import android.util.Pair;
2729
2830 import com.android.internal.util.HierarchicalState;
2931 import com.android.internal.util.HierarchicalStateMachine;
@@ -73,9 +75,17 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
7375 public static final int AUTO_CONNECT_PROFILES = 101;
7476 public static final int TRANSITION_TO_STABLE = 102;
7577 public static final int CONNECT_OTHER_PROFILES = 103;
78+ private static final int CONNECTION_ACCESS_REQUEST_REPLY = 104;
79+ private static final int CONNECTION_ACCESS_REQUEST_EXPIRY = 105;
7680
7781 private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs
7882 private static final int CONNECT_OTHER_PROFILES_DELAY = 4000; // 4 secs
83+ private static final int CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT = 7000; // 7 secs
84+ private static final int CONNECTION_ACCESS_UNDEFINED = -1;
85+ private static final long INIT_INCOMING_REJECT_TIMER = 1000; // 1 sec
86+ private static final long MAX_INCOMING_REJECT_TIMER = 3600 * 1000 * 4; // 4 hours
87+
88+ private static final String PREFS_NAME = "ConnectionAccess";
7989
8090 private BondedDevice mBondedDevice = new BondedDevice();
8191 private OutgoingHandsfree mOutgoingHandsfree = new OutgoingHandsfree();
@@ -90,10 +100,16 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
90100 private BluetoothPbap mPbapService;
91101 private boolean mHeadsetServiceConnected;
92102 private boolean mPbapServiceConnected;
103+ private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
93104
94105 private BluetoothDevice mDevice;
95106 private int mHeadsetState;
96107 private int mA2dpState;
108+ private long mIncomingRejectTimer;
109+ private boolean mConnectionAccessReplyReceived = false;
110+ private Pair<Integer, String> mIncomingConnections;
111+ private PowerManager.WakeLock mWakeLock;
112+ private PowerManager mPowerManager;
97113
98114 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
99115 @Override
@@ -108,6 +124,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
108124 int initiator = intent.getIntExtra(
109125 BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR,
110126 BluetoothHeadset.LOCAL_DISCONNECT);
127+ // We trust this device now
128+ if (newState == BluetoothHeadset.STATE_CONNECTED) {
129+ setTrust(BluetoothDevice.CONNECTION_ACCESS_YES);
130+ }
111131 mHeadsetState = newState;
112132 if (newState == BluetoothHeadset.STATE_DISCONNECTED &&
113133 initiator == BluetoothHeadset.REMOTE_DISCONNECT) {
@@ -121,6 +141,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
121141 int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
122142 int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0);
123143 mA2dpState = newState;
144+ // We trust this device now
145+ if (newState == BluetoothA2dp.STATE_CONNECTED) {
146+ setTrust(BluetoothDevice.CONNECTION_ACCESS_YES);
147+ }
124148 if ((oldState == BluetoothA2dp.STATE_CONNECTED ||
125149 oldState == BluetoothA2dp.STATE_PLAYING) &&
126150 newState == BluetoothA2dp.STATE_DISCONNECTED) {
@@ -134,6 +158,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
134158 // This is technically not needed, but we can get stuck sometimes.
135159 // For example, if incoming A2DP fails, we are not informed by Bluez
136160 sendMessage(TRANSITION_TO_STABLE);
161+ } else if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
162+ mWakeLock.release();
163+ int val = intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
164+ BluetoothDevice.CONNECTION_ACCESS_NO);
165+ Message msg = obtainMessage(CONNECTION_ACCESS_REQUEST_REPLY);
166+ msg.arg1 = val;
167+ sendMessage(msg);
137168 }
138169 }
139170 };
@@ -174,11 +205,20 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
174205 filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
175206 filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
176207 filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
208+ filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
177209
178210 mContext.registerReceiver(mBroadcastReceiver, filter);
179211
180212 HeadsetServiceListener l = new HeadsetServiceListener();
181213 PbapServiceListener p = new PbapServiceListener();
214+
215+ mIncomingConnections = mService.getIncomingState(address);
216+ mIncomingRejectTimer = readTimerValue();
217+ mPowerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
218+ mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK |
219+ PowerManager.ACQUIRE_CAUSES_WAKEUP |
220+ PowerManager.ON_AFTER_RELEASE, TAG);
221+ mWakeLock.setReferenceCounted(false);
182222 }
183223
184224 private class HeadsetServiceListener implements BluetoothHeadset.ServiceListener {
@@ -438,6 +478,24 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
438478 // Ignore
439479 Log.e(TAG, "Error: Incoming connection with a pending incoming connection");
440480 break;
481+ case CONNECTION_ACCESS_REQUEST_REPLY:
482+ int val = message.arg1;
483+ mConnectionAccessReplyReceived = true;
484+ boolean value = false;
485+ if (val == BluetoothDevice.CONNECTION_ACCESS_YES) {
486+ value = true;
487+ }
488+ setTrust(val);
489+
490+ handleIncomingConnection(CONNECT_HFP_INCOMING, value);
491+ break;
492+ case CONNECTION_ACCESS_REQUEST_EXPIRY:
493+ if (!mConnectionAccessReplyReceived) {
494+ handleIncomingConnection(CONNECT_HFP_INCOMING, false);
495+ sendConnectionAccessRemovalIntent();
496+ sendMessage(TRANSITION_TO_STABLE);
497+ }
498+ break;
441499 case CONNECT_A2DP_INCOMING:
442500 // Serialize the commands.
443501 deferMessage(message);
@@ -607,6 +665,25 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
607665 case CONNECT_A2DP_INCOMING:
608666 // ignore
609667 break;
668+ case CONNECTION_ACCESS_REQUEST_REPLY:
669+ int val = message.arg1;
670+ mConnectionAccessReplyReceived = true;
671+ boolean value = false;
672+ if (val == BluetoothDevice.CONNECTION_ACCESS_YES) {
673+ value = true;
674+ }
675+ setTrust(val);
676+ handleIncomingConnection(CONNECT_A2DP_INCOMING, value);
677+ break;
678+ case CONNECTION_ACCESS_REQUEST_EXPIRY:
679+ // The check protects the race condition between REQUEST_REPLY
680+ // and the timer expiry.
681+ if (!mConnectionAccessReplyReceived) {
682+ handleIncomingConnection(CONNECT_A2DP_INCOMING, false);
683+ sendConnectionAccessRemovalIntent();
684+ sendMessage(TRANSITION_TO_STABLE);
685+ }
686+ break;
610687 case CONNECT_A2DP_OUTGOING:
611688 // Defer message and retry
612689 deferMessage(message);
@@ -662,8 +739,138 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
662739 deferMessage(msg);
663740 }
664741
742+ private void updateIncomingAllowedTimer() {
743+ // Not doing a perfect exponential backoff because
744+ // we want two different rates. For all practical
745+ // purposes, this is good enough.
746+ if (mIncomingRejectTimer == 0) mIncomingRejectTimer = INIT_INCOMING_REJECT_TIMER;
747+
748+ mIncomingRejectTimer *= 5;
749+ if (mIncomingRejectTimer > MAX_INCOMING_REJECT_TIMER) {
750+ mIncomingRejectTimer = MAX_INCOMING_REJECT_TIMER;
751+ }
752+ writeTimerValue(mIncomingRejectTimer);
753+ }
754+
755+ private boolean handleIncomingConnection(int command, boolean accept) {
756+ boolean ret = false;
757+ Log.i(TAG, "handleIncomingConnection:" + command + ":" + accept);
758+ switch (command) {
759+ case CONNECT_HFP_INCOMING:
760+ if (!accept) {
761+ ret = mHeadsetService.rejectIncomingConnect(mDevice);
762+ sendMessage(TRANSITION_TO_STABLE);
763+ updateIncomingAllowedTimer();
764+ } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) {
765+ writeTimerValue(0);
766+ ret = mHeadsetService.acceptIncomingConnect(mDevice);
767+ } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) {
768+ writeTimerValue(0);
769+ handleConnectionOfOtherProfiles(command);
770+ ret = mHeadsetService.createIncomingConnect(mDevice);
771+ }
772+ break;
773+ case CONNECT_A2DP_INCOMING:
774+ if (!accept) {
775+ ret = mA2dpService.allowIncomingConnect(mDevice, false);
776+ sendMessage(TRANSITION_TO_STABLE);
777+ updateIncomingAllowedTimer();
778+ } else {
779+ writeTimerValue(0);
780+ ret = mA2dpService.allowIncomingConnect(mDevice, true);
781+ handleConnectionOfOtherProfiles(command);
782+ }
783+ break;
784+ default:
785+ Log.e(TAG, "Waiting for incoming connection but state changed to:" + command);
786+ break;
787+ }
788+ return ret;
789+ }
790+
791+ private void sendConnectionAccessIntent() {
792+ mConnectionAccessReplyReceived = false;
793+
794+ if (!mPowerManager.isScreenOn()) mWakeLock.acquire();
795+
796+ Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
797+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
798+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
799+ }
800+
801+ private void sendConnectionAccessRemovalIntent() {
802+ mWakeLock.release();
803+ Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
804+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
805+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
806+ }
807+
808+ private int getTrust() {
809+ String address = mDevice.getAddress();
810+ if (mIncomingConnections != null) return mIncomingConnections.first;
811+ return CONNECTION_ACCESS_UNDEFINED;
812+ }
813+
814+
815+ private String getStringValue(long value) {
816+ StringBuilder sbr = new StringBuilder();
817+ sbr.append(Long.toString(System.currentTimeMillis()));
818+ sbr.append("-");
819+ sbr.append(Long.toString(value));
820+ return sbr.toString();
821+ }
822+
823+ private void setTrust(int value) {
824+ String second;
825+ if (mIncomingConnections == null) {
826+ second = getStringValue(INIT_INCOMING_REJECT_TIMER);
827+ } else {
828+ second = mIncomingConnections.second;
829+ }
830+
831+ mIncomingConnections = new Pair(value, second);
832+ mService.writeIncomingConnectionState(mDevice.getAddress(), mIncomingConnections);
833+ }
834+
835+ private void writeTimerValue(long value) {
836+ Integer first;
837+ if (mIncomingConnections == null) {
838+ first = CONNECTION_ACCESS_UNDEFINED;
839+ } else {
840+ first = mIncomingConnections.first;
841+ }
842+ mIncomingConnections = new Pair(first, getStringValue(value));
843+ mService.writeIncomingConnectionState(mDevice.getAddress(), mIncomingConnections);
844+ }
845+
846+ private long readTimerValue() {
847+ if (mIncomingConnections == null)
848+ return 0;
849+ String value = mIncomingConnections.second;
850+ String[] splits = value.split("-");
851+ if (splits != null && splits.length == 2) {
852+ return Long.parseLong(splits[1]);
853+ }
854+ return 0;
855+ }
856+
857+ private boolean readIncomingAllowedValue() {
858+ if (readTimerValue() == 0) return true;
859+ String value = mIncomingConnections.second;
860+ String[] splits = value.split("-");
861+ if (splits != null && splits.length == 2) {
862+ long val1 = Long.parseLong(splits[0]);
863+ long val2 = Long.parseLong(splits[1]);
864+ if (val1 + val2 <= System.currentTimeMillis()) {
865+ return true;
866+ }
867+ }
868+ return false;
869+ }
870+
665871 synchronized boolean processCommand(int command) {
666- Log.i(TAG, "Processing command:" + command);
872+ Log.e(TAG, "Processing command:" + command);
873+ Message msg;
667874 switch(command) {
668875 case CONNECT_HFP_OUTGOING:
669876 if (mHeadsetService != null) {
@@ -673,11 +880,21 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
673880 case CONNECT_HFP_INCOMING:
674881 if (!mHeadsetServiceConnected) {
675882 deferProfileServiceMessage(command);
676- } else if (mHeadsetState == BluetoothHeadset.STATE_CONNECTING) {
677- return mHeadsetService.acceptIncomingConnect(mDevice);
678- } else if (mHeadsetState == BluetoothHeadset.STATE_DISCONNECTED) {
679- handleConnectionOfOtherProfiles(command);
680- return mHeadsetService.createIncomingConnect(mDevice);
883+ } else {
884+ // Check if device is already trusted
885+ int access = getTrust();
886+ if (access == BluetoothDevice.CONNECTION_ACCESS_YES) {
887+ handleIncomingConnection(command, true);
888+ } else if (access == BluetoothDevice.CONNECTION_ACCESS_NO &&
889+ !readIncomingAllowedValue()) {
890+ handleIncomingConnection(command, false);
891+ } else {
892+ sendConnectionAccessIntent();
893+ msg = obtainMessage(CONNECTION_ACCESS_REQUEST_EXPIRY);
894+ sendMessageDelayed(msg,
895+ CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT);
896+ }
897+ return true;
681898 }
682899 break;
683900 case CONNECT_A2DP_OUTGOING:
@@ -686,8 +903,19 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
686903 }
687904 break;
688905 case CONNECT_A2DP_INCOMING:
689- handleConnectionOfOtherProfiles(command);
690- // ignore, Bluez takes care
906+ // Check if device is already trusted
907+ int access = getTrust();
908+ if (access == BluetoothDevice.CONNECTION_ACCESS_YES) {
909+ handleIncomingConnection(command, true);
910+ } else if (access == BluetoothDevice.CONNECTION_ACCESS_NO &&
911+ !readIncomingAllowedValue()) {
912+ handleIncomingConnection(command, false);
913+ } else {
914+ sendConnectionAccessIntent();
915+ msg = obtainMessage(CONNECTION_ACCESS_REQUEST_EXPIRY);
916+ sendMessageDelayed(msg,
917+ CONNECTION_ACCESS_REQUEST_EXPIRY_TIMEOUT);
918+ }
691919 return true;
692920 case DISCONNECT_HFP_OUTGOING:
693921 if (!mHeadsetServiceConnected) {
@@ -728,6 +956,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine
728956 }
729957 break;
730958 case UNPAIR:
959+ writeTimerValue(INIT_INCOMING_REJECT_TIMER);
960+ setTrust(CONNECTION_ACCESS_UNDEFINED);
731961 return mService.removeBondInternal(mDevice.getAddress());
732962 default:
733963 Log.e(TAG, "Error: Unknown Command");
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -457,6 +457,23 @@ public final class BluetoothHeadset {
457457 }
458458
459459 /**
460+ * Reject the incoming connection.
461+ * @hide
462+ */
463+ public boolean rejectIncomingConnect(BluetoothDevice device) {
464+ if (DBG) log("rejectIncomingConnect");
465+ if (mService != null) {
466+ try {
467+ return mService.rejectIncomingConnect(device);
468+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
469+ } else {
470+ Log.w(TAG, "Proxy not attached to service");
471+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
472+ }
473+ return false;
474+ }
475+
476+ /**
460477 * Connect to a Bluetooth Headset.
461478 * Note: This is an internal function and shouldn't be exposed
462479 * @hide
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -36,4 +36,6 @@ interface IBluetoothA2dp {
3636
3737 boolean connectSinkInternal(in BluetoothDevice device);
3838 boolean disconnectSinkInternal(in BluetoothDevice device);
39+ boolean allowIncomingConnect(in BluetoothDevice device, boolean value);
40+
3941 }
--- a/core/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -37,6 +37,7 @@ interface IBluetoothHeadset {
3737
3838 boolean createIncomingConnect(in BluetoothDevice device);
3939 boolean acceptIncomingConnect(in BluetoothDevice device);
40+ boolean rejectIncomingConnect(in BluetoothDevice device);
4041 boolean cancelConnectThread();
4142 boolean connectHeadsetInternal(in BluetoothDevice device);
4243 boolean disconnectHeadsetInternal(in BluetoothDevice device);
--- a/core/java/android/nfc/INfcAdapterExtras.aidl
+++ b/core/java/android/nfc/INfcAdapterExtras.aidl
@@ -16,7 +16,6 @@
1616
1717 package android.nfc;
1818
19-import android.nfc.ApduList;
2019 import android.os.Bundle;
2120
2221
@@ -29,6 +28,5 @@ interface INfcAdapterExtras {
2928 Bundle transceive(in byte[] data_in);
3029 int getCardEmulationRoute();
3130 void setCardEmulationRoute(int route);
32- void registerTearDownApdus(String packageName, in ApduList apdu);
33- void unregisterTearDownApdus(String packageName);
31+ void authenticate(in byte[] token);
3432 }
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -457,6 +457,22 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
457457 Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority);
458458 }
459459
460+ public synchronized boolean allowIncomingConnect(BluetoothDevice device, boolean value) {
461+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
462+ "Need BLUETOOTH_ADMIN permission");
463+ String address = device.getAddress();
464+ if (!BluetoothAdapter.checkBluetoothAddress(address)) {
465+ return false;
466+ }
467+ Integer data = mBluetoothService.getAuthorizationAgentRequestData(address);
468+ if (data == null) {
469+ Log.w(TAG, "allowIncomingConnect(" + device + ") called but no native data available");
470+ return false;
471+ }
472+ log("allowIncomingConnect: A2DP: " + device + ":" + value);
473+ return mBluetoothService.setAuthorizationNative(address, value, data.intValue());
474+ }
475+
460476 private synchronized void onSinkPropertyChanged(String path, String []propValues) {
461477 if (!mBluetoothService.isEnabled()) {
462478 return;
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -50,6 +50,7 @@ class BluetoothEventLoop {
5050 private boolean mInterrupted;
5151
5252 private final HashMap<String, Integer> mPasskeyAgentRequestData;
53+ private final HashMap<String, Integer> mAuthorizationAgentRequestData;
5354 private final BluetoothService mBluetoothService;
5455 private final BluetoothAdapter mAdapter;
5556 private final Context mContext;
@@ -109,6 +110,7 @@ class BluetoothEventLoop {
109110 mBluetoothService = bluetoothService;
110111 mContext = context;
111112 mPasskeyAgentRequestData = new HashMap();
113+ mAuthorizationAgentRequestData = new HashMap<String, Integer>();
112114 mAdapter = adapter;
113115 //WakeLock instantiation in BluetoothEventLoop class
114116 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -130,6 +132,10 @@ class BluetoothEventLoop {
130132 return mPasskeyAgentRequestData;
131133 }
132134
135+ /* package */ HashMap<String, Integer> getAuthorizationAgentRequestData() {
136+ return mAuthorizationAgentRequestData;
137+ }
138+
133139 /* package */ void start() {
134140
135141 if (!isEventLoopRunningNative()) {
@@ -518,27 +524,29 @@ class BluetoothEventLoop {
518524 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
519525 }
520526
521- private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
527+ private void onAgentAuthorize(String objectPath, String deviceUuid, int nativeData) {
522528 String address = mBluetoothService.getAddressFromObjectPath(objectPath);
523529 if (address == null) {
524530 Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
525- return false;
531+ return;
526532 }
527533
528534 boolean authorized = false;
529535 ParcelUuid uuid = ParcelUuid.fromString(deviceUuid);
530536 BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
531537
538+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
539+ mAuthorizationAgentRequestData.put(address, new Integer(nativeData));
540+
532541 // Bluez sends the UUID of the local service being accessed, _not_ the
533542 // remote service
534543 if (mBluetoothService.isEnabled() &&
535544 (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
536545 || BluetoothUuid.isAdvAudioDist(uuid)) &&
537546 !isOtherSinkInNonDisconnectingState(address)) {
538- BluetoothDevice device = mAdapter.getRemoteDevice(address);
539547 authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
540548 if (authorized) {
541- Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
549+ Log.i(TAG, "First check pass for incoming A2DP / AVRCP connection from " + address);
542550 // Some headsets try to connect AVCTP before AVDTP - against the recommendation
543551 // If AVCTP connection fails, we get stuck in IncomingA2DP state in the state
544552 // machine. We don't handle AVCTP signals currently. We only send
@@ -546,6 +554,8 @@ class BluetoothEventLoop {
546554 // some cases. For now, just don't move to incoming state in this case.
547555 if (!BluetoothUuid.isAvrcpTarget(uuid)) {
548556 mBluetoothService.notifyIncomingA2dpConnection(address);
557+ } else {
558+ a2dp.allowIncomingConnect(device, authorized);
549559 }
550560 } else {
551561 Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
@@ -554,7 +564,7 @@ class BluetoothEventLoop {
554564 Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
555565 }
556566 log("onAgentAuthorize(" + objectPath + ", " + deviceUuid + ") = " + authorized);
557- return authorized;
567+ if (!authorized) a2dp.allowIncomingConnect(device, authorized);
558568 }
559569
560570 private boolean onAgentOutOfBandDataAvailable(String objectPath) {
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -27,8 +27,8 @@ package android.server;
2727 import android.bluetooth.BluetoothAdapter;
2828 import android.bluetooth.BluetoothClass;
2929 import android.bluetooth.BluetoothDevice;
30-import android.bluetooth.BluetoothHeadset;
3130 import android.bluetooth.BluetoothDeviceProfileState;
31+import android.bluetooth.BluetoothHeadset;
3232 import android.bluetooth.BluetoothProfileState;
3333 import android.bluetooth.BluetoothSocket;
3434 import android.bluetooth.BluetoothUuid;
@@ -67,6 +67,7 @@ import java.io.FileWriter;
6767 import java.io.IOException;
6868 import java.io.InputStreamReader;
6969 import java.io.PrintWriter;
70+import java.io.RandomAccessFile;
7071 import java.io.UnsupportedEncodingException;
7172 import java.util.ArrayList;
7273 import java.util.Arrays;
@@ -142,6 +143,11 @@ public class BluetoothService extends IBluetooth.Stub {
142143 private static String mDockAddress;
143144 private String mDockPin;
144145
146+ private static final String INCOMING_CONNECTION_FILE =
147+ "/data/misc/bluetooth/incoming_connection.conf";
148+ private HashMap<String, Pair<Integer, String>> mIncomingConnections;
149+
150+
145151 private static class RemoteService {
146152 public String address;
147153 public ParcelUuid uuid;
@@ -209,6 +215,7 @@ public class BluetoothService extends IBluetooth.Stub {
209215
210216 filter.addAction(Intent.ACTION_DOCK_EVENT);
211217 mContext.registerReceiver(mReceiver, filter);
218+ mIncomingConnections = new HashMap<String, Pair<Integer, String>>();
212219 }
213220
214221 public static synchronized String readDockBluetoothAddress() {
@@ -733,8 +740,6 @@ public class BluetoothService extends IBluetooth.Stub {
733740
734741 if (state == BluetoothDevice.BOND_BONDED) {
735742 addProfileState(address);
736- } else if (state == BluetoothDevice.BOND_NONE) {
737- removeProfileState(address);
738743 }
739744
740745 if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
@@ -1312,6 +1317,8 @@ public class BluetoothService extends IBluetooth.Stub {
13121317 }
13131318
13141319 public synchronized boolean removeBondInternal(String address) {
1320+ // Unset the trusted device state and then unpair
1321+ setTrust(address, false);
13151322 return removeDeviceNative(getObjectPathFromAddress(address));
13161323 }
13171324
@@ -2161,10 +2168,6 @@ public class BluetoothService extends IBluetooth.Stub {
21612168 return state;
21622169 }
21632170
2164- private void removeProfileState(String address) {
2165- mDeviceProfileState.remove(address);
2166- }
2167-
21682171 private void initProfileState() {
21692172 String []bonds = null;
21702173 String val = getPropertyInternal("Devices");
@@ -2213,6 +2216,11 @@ public class BluetoothService extends IBluetooth.Stub {
22132216 mA2dpService = a2dpService;
22142217 }
22152218
2219+ /*package*/ Integer getAuthorizationAgentRequestData(String address) {
2220+ Integer data = mEventLoop.getAuthorizationAgentRequestData().remove(address);
2221+ return data;
2222+ }
2223+
22162224 public void sendProfileStateMessage(int profile, int cmd) {
22172225 Message msg = new Message();
22182226 msg.what = cmd;
@@ -2223,6 +2231,116 @@ public class BluetoothService extends IBluetooth.Stub {
22232231 }
22242232 }
22252233
2234+ private void createIncomingConnectionStateFile() {
2235+ File f = new File(INCOMING_CONNECTION_FILE);
2236+ if (!f.exists()) {
2237+ try {
2238+ f.createNewFile();
2239+ } catch (IOException e) {
2240+ Log.e(TAG, "IOException: cannot create file");
2241+ }
2242+ }
2243+ }
2244+
2245+ /** @hide */
2246+ public Pair<Integer, String> getIncomingState(String address) {
2247+ if (mIncomingConnections.isEmpty()) {
2248+ createIncomingConnectionStateFile();
2249+ readIncomingConnectionState();
2250+ }
2251+ return mIncomingConnections.get(address);
2252+ }
2253+
2254+ private void readIncomingConnectionState() {
2255+ synchronized(mIncomingConnections) {
2256+ FileInputStream fstream = null;
2257+ try {
2258+ fstream = new FileInputStream(INCOMING_CONNECTION_FILE);
2259+ DataInputStream in = new DataInputStream(fstream);
2260+ BufferedReader file = new BufferedReader(new InputStreamReader(in));
2261+ String line;
2262+ while((line = file.readLine()) != null) {
2263+ line = line.trim();
2264+ if (line.length() == 0) continue;
2265+ String[] value = line.split(",");
2266+ if (value != null && value.length == 3) {
2267+ Integer val1 = Integer.parseInt(value[1]);
2268+ Pair<Integer, String> val = new Pair(val1, value[2]);
2269+ mIncomingConnections.put(value[0], val);
2270+ }
2271+ }
2272+ } catch (FileNotFoundException e) {
2273+ log("FileNotFoundException: readIncomingConnectionState" + e.toString());
2274+ } catch (IOException e) {
2275+ log("IOException: readIncomingConnectionState" + e.toString());
2276+ } finally {
2277+ if (fstream != null) {
2278+ try {
2279+ fstream.close();
2280+ } catch (IOException e) {
2281+ // Ignore
2282+ }
2283+ }
2284+ }
2285+ }
2286+ }
2287+
2288+ private void truncateIncomingConnectionFile() {
2289+ RandomAccessFile r = null;
2290+ try {
2291+ r = new RandomAccessFile(INCOMING_CONNECTION_FILE, "rw");
2292+ r.setLength(0);
2293+ } catch (FileNotFoundException e) {
2294+ log("FileNotFoundException: truncateIncomingConnectionState" + e.toString());
2295+ } catch (IOException e) {
2296+ log("IOException: truncateIncomingConnectionState" + e.toString());
2297+ } finally {
2298+ if (r != null) {
2299+ try {
2300+ r.close();
2301+ } catch (IOException e) {
2302+ // ignore
2303+ }
2304+ }
2305+ }
2306+ }
2307+
2308+ /** @hide */
2309+ public void writeIncomingConnectionState(String address, Pair<Integer, String> data) {
2310+ synchronized(mIncomingConnections) {
2311+ mIncomingConnections.put(address, data);
2312+
2313+ truncateIncomingConnectionFile();
2314+ BufferedWriter out = null;
2315+ StringBuilder value = new StringBuilder();
2316+ try {
2317+ out = new BufferedWriter(new FileWriter(INCOMING_CONNECTION_FILE, true));
2318+ for (String devAddress: mIncomingConnections.keySet()) {
2319+ Pair<Integer, String> val = mIncomingConnections.get(devAddress);
2320+ value.append(devAddress);
2321+ value.append(",");
2322+ value.append(val.first.toString());
2323+ value.append(",");
2324+ value.append(val.second);
2325+ value.append("\n");
2326+ }
2327+ out.write(value.toString());
2328+ } catch (FileNotFoundException e) {
2329+ log("FileNotFoundException: writeIncomingConnectionState" + e.toString());
2330+ } catch (IOException e) {
2331+ log("IOException: writeIncomingConnectionState" + e.toString());
2332+ } finally {
2333+ if (out != null) {
2334+ try {
2335+ out.close();
2336+ } catch (IOException e) {
2337+ // Ignore
2338+ }
2339+ }
2340+ }
2341+ }
2342+ }
2343+
22262344 private static void log(String msg) {
22272345 Log.d(TAG, msg);
22282346 }
@@ -2273,4 +2391,5 @@ public class BluetoothService extends IBluetooth.Stub {
22732391 short channel);
22742392 private native boolean removeServiceRecordNative(int handle);
22752393 private native boolean setLinkTimeoutNative(String path, int num_slots);
2394+ native boolean setAuthorizationNative(String address, boolean value, int data);
22762395 }
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -106,7 +106,7 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
106106 "(Ljava/lang/String;Z)V");
107107
108108 method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
109- "(Ljava/lang/String;Ljava/lang/String;)Z");
109+ "(Ljava/lang/String;Ljava/lang/String;I)V");
110110 method_onAgentOutOfBandDataAvailable = env->GetMethodID(clazz, "onAgentOutOfBandDataAvailable",
111111 "(Ljava/lang/String;)Z");
112112 method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
@@ -917,29 +917,11 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn,
917917 LOGV("... object_path = %s", object_path);
918918 LOGV("... uuid = %s", uuid);
919919
920- bool auth_granted =
921- env->CallBooleanMethod(nat->me, method_onAgentAuthorize,
922- env->NewStringUTF(object_path), env->NewStringUTF(uuid));
920+ dbus_message_ref(msg); // increment refcount because we pass to java
921+ env->CallBooleanMethod(nat->me, method_onAgentAuthorize,
922+ env->NewStringUTF(object_path), env->NewStringUTF(uuid),
923+ int(msg));
923924
924- // reply
925- if (auth_granted) {
926- DBusMessage *reply = dbus_message_new_method_return(msg);
927- if (!reply) {
928- LOGE("%s: Cannot create message reply\n", __FUNCTION__);
929- goto failure;
930- }
931- dbus_connection_send(nat->conn, reply, NULL);
932- dbus_message_unref(reply);
933- } else {
934- DBusMessage *reply = dbus_message_new_error(msg,
935- "org.bluez.Error.Rejected", "Authorization rejected");
936- if (!reply) {
937- LOGE("%s: Cannot create message reply\n", __FUNCTION__);
938- goto failure;
939- }
940- dbus_connection_send(nat->conn, reply, NULL);
941- dbus_message_unref(reply);
942- }
943925 goto success;
944926 } else if (dbus_message_is_method_call(msg,
945927 "org.bluez.Agent", "OutOfBandAvailable")) {
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -599,6 +599,35 @@ static jboolean setRemoteOutOfBandDataNative(JNIEnv *env, jobject object, jstrin
599599 return JNI_FALSE;
600600 }
601601
602+static jboolean setAuthorizationNative(JNIEnv *env, jobject object, jstring address,
603+ jboolean val, int nativeData) {
604+#ifdef HAVE_BLUETOOTH
605+ LOGV(__FUNCTION__);
606+ native_data_t *nat = get_native_data(env, object);
607+ if (nat) {
608+ DBusMessage *msg = (DBusMessage *)nativeData;
609+ DBusMessage *reply;
610+ if (val) {
611+ reply = dbus_message_new_method_return(msg);
612+ } else {
613+ reply = dbus_message_new_error(msg,
614+ "org.bluez.Error.Rejected", "Authorization rejected");
615+ }
616+ if (!reply) {
617+ LOGE("%s: Cannot create message reply D-Bus\n", __FUNCTION__);
618+ dbus_message_unref(msg);
619+ return JNI_FALSE;
620+ }
621+
622+ dbus_connection_send(nat->conn, reply, NULL);
623+ dbus_message_unref(msg);
624+ dbus_message_unref(reply);
625+ return JNI_TRUE;
626+ }
627+#endif
628+ return JNI_FALSE;
629+}
630+
602631 static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
603632 jstring pin, int nativeData) {
604633 #ifdef HAVE_BLUETOOTH
@@ -1029,6 +1058,7 @@ static JNINativeMethod sMethods[] = {
10291058 (void *)setPairingConfirmationNative},
10301059 {"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative},
10311060 {"setRemoteOutOfBandDataNative", "(Ljava/lang/String;[B[BI)Z", (void *)setRemoteOutOfBandDataNative},
1061+ {"setAuthorizationNative", "(Ljava/lang/String;ZI)Z", (void *)setAuthorizationNative},
10321062 {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
10331063 {"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z",
10341064 (void *)cancelPairingUserInputNative},
--- a/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
+++ b/nfc-extras/java/com/android/nfc_extras/NfcAdapterExtras.java
@@ -18,7 +18,6 @@ package com.android.nfc_extras;
1818
1919 import android.annotation.SdkConstant;
2020 import android.annotation.SdkConstant.SdkConstantType;
21-import android.nfc.ApduList;
2221 import android.nfc.INfcAdapterExtras;
2322 import android.nfc.NfcAdapter;
2423 import android.os.RemoteException;
@@ -68,7 +67,11 @@ public final class NfcAdapterExtras {
6867
6968 /** get service handles */
7069 private static void initService() {
71- sService = sAdapter.getNfcAdapterExtrasInterface();
70+ final INfcAdapterExtras service = sAdapter.getNfcAdapterExtrasInterface();
71+ if (service != null) {
72+ // Leave stale rather than receive a null value.
73+ sService = service;
74+ }
7275 }
7376
7477 /**
@@ -85,18 +88,19 @@ public final class NfcAdapterExtras {
8588 if (sSingleton == null) {
8689 try {
8790 sAdapter = adapter;
88- sRouteOff = new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null);
8991 sSingleton = new NfcAdapterExtras();
9092 sEmbeddedEe = new NfcExecutionEnvironment(sSingleton);
93+ sRouteOff = new CardEmulationRoute(CardEmulationRoute.ROUTE_OFF, null);
9194 sRouteOnWhenScreenOn = new CardEmulationRoute(
9295 CardEmulationRoute.ROUTE_ON_WHEN_SCREEN_ON, sEmbeddedEe);
9396 initService();
9497 } finally {
95- if (sSingleton == null) {
96- sService = null;
97- sEmbeddedEe = null;
98- sRouteOff = null;
98+ if (sService == null) {
9999 sRouteOnWhenScreenOn = null;
100+ sRouteOff = null;
101+ sEmbeddedEe = null;
102+ sSingleton = null;
103+ sAdapter = null;
100104 }
101105 }
102106 }
@@ -208,17 +212,18 @@ public final class NfcAdapterExtras {
208212 return sEmbeddedEe;
209213 }
210214
211- public void registerTearDownApdus(String packageName, ApduList apdus) {
212- try {
213- sService.registerTearDownApdus(packageName, apdus);
214- } catch (RemoteException e) {
215- attemptDeadServiceRecovery(e);
216- }
217- }
218-
219- public void unregisterTearDownApdus(String packageName) {
215+ /**
216+ * Authenticate the client application.
217+ *
218+ * Some implementations of NFC Adapter Extras may require applications
219+ * to authenticate with a token, before using other methods.
220+ *
221+ * @param a implementation specific token
222+ * @throws a {@link java.lang.SecurityException} if authentication failed
223+ */
224+ public void authenticate(byte[] token) {
220225 try {
221- sService.unregisterTearDownApdus(packageName);
226+ sService.authenticate(token);
222227 } catch (RemoteException e) {
223228 attemptDeadServiceRecovery(e);
224229 }
--- a/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
+++ b/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
@@ -55,6 +55,64 @@ public class NfcExecutionEnvironment {
5555 */
5656 public static final String EXTRA_AID = "com.android.nfc_extras.extra.AID";
5757
58+ /**
59+ * Broadcast action: A filtered APDU was received.
60+ *
61+ * <p>This happens when an APDU of interest was matched by the Nfc adapter,
62+ * for instance as the result of matching an externally-configured filter.
63+ *
64+ * <p>The filter configuration mechanism is not currently defined.
65+ *
66+ * <p>Always contains the extra field {@link EXTRA_APDU_BYTES}.
67+ *
68+ * @hide
69+ */
70+ public static final String ACTION_APDU_RECEIVED =
71+ "com.android.nfc_extras.action.APDU_RECEIVED";
72+
73+ /**
74+ * Mandatory byte array extra field in {@link #ACTION_APDU_RECEIVED}.
75+ *
76+ * <p>Contains the bytes of the received APDU.
77+ *
78+ * @hide
79+ */
80+ public static final String EXTRA_APDU_BYTES =
81+ "com.android.nfc_extras.extra.APDU_BYTES";
82+
83+ /**
84+ * Broadcast action: An EMV card removal event was detected.
85+ *
86+ * @hide
87+ */
88+ public static final String ACTION_EMV_CARD_REMOVAL =
89+ "com.android.nfc_extras.action.EMV_CARD_REMOVAL";
90+
91+ /**
92+ * Broadcast action: An adapter implementing MIFARE Classic via card
93+ * emulation detected that a block has been accessed.
94+ *
95+ * <p>This may only be issued for the first block that the reader
96+ * authenticates to.
97+ *
98+ * <p>May contain the extra field {@link #EXTRA_MIFARE_BLOCK}.
99+ *
100+ * @hide
101+ */
102+ public static final String ACTION_MIFARE_ACCESS_DETECTED =
103+ "com.android.nfc_extras.action.MIFARE_ACCESS_DETECTED";
104+
105+ /**
106+ * Optional integer extra field in {@link #ACTION_MIFARE_ACCESS_DETECTED}.
107+ *
108+ * <p>Provides the block number being accessed. If not set, the block
109+ * number being accessed is unknown.
110+ *
111+ * @hide
112+ */
113+ public static final String EXTRA_MIFARE_BLOCK =
114+ "com.android.nfc_extras.extra.MIFARE_BLOCK";
115+
58116 NfcExecutionEnvironment(NfcAdapterExtras extras) {
59117 mExtras = extras;
60118 }
Show on old repository browser