SdpManager.java revision bbb4110b455b3aa29106d5b4f0a37e1be8e09475
1bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde/* 2bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde* Copyright (C) 2015 Samsung System LSI 3bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde* Licensed under the Apache License, Version 2.0 (the "License"); 4bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde* you may not use this file except in compliance with the License. 5bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde* You may obtain a copy of the License at 6bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde* 7bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde* http://www.apache.org/licenses/LICENSE-2.0 8bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde* 9bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde* Unless required by applicable law or agreed to in writing, software 10bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde* distributed under the License is distributed on an "AS IS" BASIS, 11bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde* See the License for the specific language governing permissions and 13bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde* limitations under the License. 14bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde*/ 15bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondepackage com.android.bluetooth.sdp; 16bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 17bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport android.bluetooth.BluetoothDevice; 18bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport android.bluetooth.SdpMasRecord; 19bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport android.bluetooth.SdpMnsRecord; 20bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport android.bluetooth.SdpOppOpsRecord; 21bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport android.bluetooth.SdpPseRecord; 22bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport android.bluetooth.SdpRecord; 23bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport android.content.Intent; 24bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport android.os.Handler; 25bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport android.os.Message; 26bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport android.os.ParcelUuid; 27bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport android.os.Parcelable; 28bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport android.util.Log; 29bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 30bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport com.android.bluetooth.Utils; 31bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport com.android.bluetooth.btservice.AbstractionLayer; 32bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport com.android.bluetooth.btservice.AdapterService; 33bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 34bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport java.util.ArrayList; 35bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondeimport java.util.Arrays; 36bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 37bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bondepublic class SdpManager { 38bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 39bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private static final boolean D = true; 40bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private static final boolean V = false; 41bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private static final String TAG="SdpManager"; 42bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 43bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde // TODO: When changing PBAP to use this new API. 44bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde // Move the defines to the profile (PBAP already have the feature bits) 45bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /* PBAP repositories */ 46bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static final byte PBAP_REPO_LOCAL = 0x01<<0; 47bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static final byte PBAP_REPO_SIM = 0x01<<1; 48bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static final byte PBAP_REPO_SPEED_DAIL = 0x01<<2; 49bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static final byte PBAP_REPO_FAVORITES = 0x01<<3; 50bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 51bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde // TODO: When changing OPP to use this new API. 52bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde // Move the defines to the profile 53bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /* Object Push formats */ 54bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static final byte OPP_FORMAT_VCARD21 = 0x01; 55bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static final byte OPP_FORMAT_VCARD30 = 0x02; 56bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static final byte OPP_FORMAT_VCAL10 = 0x03; 57bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static final byte OPP_FORMAT_ICAL20 = 0x04; 58bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static final byte OPP_FORMAT_VNOTE = 0x05; 59bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static final byte OPP_FORMAT_VMESSAGE = 0x06; 60bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static final byte OPP_FORMAT_ANY_TYPE_OF_OBJ = (byte)0xFF; 61bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 62bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static final byte[] OPP_FORMAT_ALL= { 63bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde OPP_FORMAT_VCARD21, 64bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde OPP_FORMAT_VCARD30, 65bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde OPP_FORMAT_VCAL10, 66bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde OPP_FORMAT_ICAL20, 67bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde OPP_FORMAT_VNOTE, 68bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde OPP_FORMAT_VMESSAGE, 69bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde OPP_FORMAT_ANY_TYPE_OF_OBJ}; 70bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 71bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /* Variables to keep track of ongoing and queued search requests. 72bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * mTrackerLock must be held, when using/changing sSdpSearchTracker 73bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * and mSearchInProgress. */ 74bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde static SdpSearchTracker sSdpSearchTracker; 75bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde static boolean mSearchInProgress = false; 76bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde static Object mTrackerLock = new Object(); 77bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 78bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /* The timeout to wait for reply from native. Should never fire. */ 79bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private static final int SDP_INTENT_DELAY = 6000; 80bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private static final int MESSAGE_SDP_INTENT = 2; 81bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 82bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde // We need a reference to the adapter service, to be able to send intents 83bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private static AdapterService sAdapterService; 84bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private static boolean sNativeAvailable; 85bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 86bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde // This object is a singleton 87bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private static SdpManager sSdpManager = null; 88bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 89bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde static { 90bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde classInitNative(); 91bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 92bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 93bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private native static void classInitNative(); 94bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private native void initializeNative(); 95bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private native void cleanupNative(); 96bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private native boolean sdpSearchNative(byte[] address, byte[] uuid); 97bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 98bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private native int sdpCreateMapMasRecordNative(String serviceName, int masId, 99bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int rfcommChannel, int l2capPsm, int version, int msgTypes, int features); 100bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 101bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private native int sdpCreateMapMnsRecordNative(String serviceName, 102bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int rfcommChannel, int l2capPsm, int version, int features); 103bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 104bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private native int sdpCreatePbapPseRecordNative(String serviceName, int rfcommChannel, 105bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int l2capPsm, int version, int repositories, int features); 106bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 107bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private native int sdpCreateOppOpsRecordNative(String serviceName, 108bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int rfcommChannel, int l2capPsm, int version, byte[] formats_list); 109bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 110bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private native boolean sdpRemoveSdpRecordNative(int record_id); 111bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 112bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 113bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /* Inner class used for wrapping sdp search instance data */ 114bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private class SdpSearchInstance { 115bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private final BluetoothDevice mDevice; 116bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private final ParcelUuid mUuid; 117bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private int mStatus = 0; 118bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private boolean mSearching; 119bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /* TODO: If we change the API to use another mechanism than intents for 120bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * delivering the results, this would be the place to keep a list 121bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * of the objects to deliver the results to. */ 122bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public SdpSearchInstance(int status, BluetoothDevice device, ParcelUuid uuid){ 123bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde this.mDevice = device; 124bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde this.mUuid = uuid; 125bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde this.mStatus = status; 126bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde mSearching = true; 127bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 128bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public BluetoothDevice getDevice() { 129bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return mDevice; 130bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 131bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public ParcelUuid getUuid() { 132bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return mUuid; 133bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 134bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public int getStatus(){ 135bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return mStatus; 136bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 137bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 138bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public void setStatus(int status) { 139bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde this.mStatus = status; 140bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 141bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 142bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public void startSearch() { 143bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde mSearching = true; 144bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde Message message = mHandler.obtainMessage(MESSAGE_SDP_INTENT, this); 145bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde mHandler.sendMessageDelayed(message, SDP_INTENT_DELAY); 146bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 147bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 148bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public void stopSearch() { 149bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(mSearching) { 150bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde mHandler.removeMessages(MESSAGE_SDP_INTENT, this); 151bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 152bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde mSearching = false; 153bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 154bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public boolean isSearching() { 155bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return mSearching; 156bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 157bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 158bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 159bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 160bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /* We wrap the ArrayList class to decorate with functionality to 161bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * find an instance based on UUID AND device address. 162bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * As we use a mix of byte[] and object instances, this is more 163bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * efficient than implementing comparable. */ 164bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde class SdpSearchTracker { 165bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private final ArrayList<SdpSearchInstance> list = new ArrayList<SdpSearchInstance>(); 166bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 167bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde void clear() { 168bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde list.clear(); 169bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 170bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 171bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde boolean add(SdpSearchInstance inst){ 172bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return list.add(inst); 173bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 174bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 175bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde boolean remove(SdpSearchInstance inst) { 176bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return list.remove(inst); 177bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 178bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 179bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpSearchInstance getNext() { 180bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(list.size() > 0) { 181bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return list.get(0); 182bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 183bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return null; 184bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 185bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 186bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpSearchInstance getSearchInstance(byte[] address, byte[] uuidBytes) { 187bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde String addressString = Utils.getAddressStringFromByte(address); 188bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde ParcelUuid uuid = Utils.byteArrayToUuid(uuidBytes)[0]; 189bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde for (SdpSearchInstance inst : list) { 190bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if (inst.getDevice().getAddress().equals(addressString) 191bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde && inst.getUuid().equals(uuid)) { 192bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return inst; 193bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 194bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 195bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return null; 196bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 197bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 198bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde boolean isSearching(BluetoothDevice device, ParcelUuid uuid) { 199bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde String addressString = device.getAddress(); 200bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde for (SdpSearchInstance inst : list) { 201bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if (inst.getDevice().getAddress().equals(addressString) 202bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde && inst.getUuid().equals(uuid)) { 203bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return inst.isSearching(); 204bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 205bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 206bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return false; 207bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 208bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 209bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 210bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 211bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private SdpManager(AdapterService adapterService) { 212bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sSdpSearchTracker = new SdpSearchTracker(); 213bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 214bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /* This is only needed until intents are no longer used */ 215bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sAdapterService = adapterService; 216bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde initializeNative(); 217bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sNativeAvailable=true; 218bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 219bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 220bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 221bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static SdpManager init(AdapterService adapterService) { 222bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sSdpManager = new SdpManager(adapterService); 223bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return sSdpManager; 224bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 225bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 226bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public static SdpManager getDefaultManager() { 227bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return sSdpManager; 228bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 229bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 230bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public void cleanup() { 231bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if (sSdpSearchTracker !=null) { 232bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde synchronized(mTrackerLock) { 233bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sSdpSearchTracker.clear(); 234bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 235bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 236bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 237bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if (sNativeAvailable) { 238bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde cleanupNative(); 239bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sNativeAvailable=false; 240bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 241bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sSdpManager = null; 242bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 243bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 244bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 245bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde void sdpMasRecordFoundCallback(int status, byte[] address, byte[] uuid, 246bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int masInstanceId, 247bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int l2capPsm, 248bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int rfcommCannelNumber, 249bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int profileVersion, 250bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int supportedFeatures, 251bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int supportedMessageTypes, 252bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde String serviceName, 253bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde boolean moreResults) { 254bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 255bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde synchronized(mTrackerLock) { 256bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid); 257bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpMasRecord sdpRecord = null; 258bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if (inst == null) { 259bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL"); 260bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return; 261bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 262bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde inst.setStatus(status); 263bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(status == AbstractionLayer.BT_STATUS_SUCCESS) { 264bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sdpRecord = new SdpMasRecord(masInstanceId, 265bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde l2capPsm, 266bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde rfcommCannelNumber, 267bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde profileVersion, 268bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde supportedFeatures, 269bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde supportedMessageTypes, 270bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde serviceName); 271bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 272bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "UUID: " + Arrays.toString(uuid)); 273bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString()); 274bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sendSdpIntent(inst, sdpRecord, moreResults); 275bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 276bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 277bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 278bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde void sdpMnsRecordFoundCallback(int status, byte[] address, byte[] uuid, 279bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int l2capPsm, 280bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int rfcommCannelNumber, 281bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int profileVersion, 282bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int supportedFeatures, 283bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde String serviceName, 284bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde boolean moreResults) { 285bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde synchronized(mTrackerLock) { 286bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 287bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid); 288bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpMnsRecord sdpRecord = null; 289bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if (inst == null) { 290bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL"); 291bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return; 292bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 293bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde inst.setStatus(status); 294bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(status == AbstractionLayer.BT_STATUS_SUCCESS) { 295bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sdpRecord = new SdpMnsRecord(l2capPsm, 296bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde rfcommCannelNumber, 297bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde profileVersion, 298bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde supportedFeatures, 299bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde serviceName); 300bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 301bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "UUID: " + Arrays.toString(uuid)); 302bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString()); 303bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sendSdpIntent(inst, sdpRecord, moreResults); 304bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 305bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 306bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 307bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde void sdpPseRecordFoundCallback(int status, byte[] address, byte[] uuid, 308bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int l2capPsm, 309bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int rfcommCannelNumber, 310bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int profileVersion, 311bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int supportedFeatures, 312bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int supportedRepositories, 313bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde String serviceName, 314bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde boolean moreResults) { 315bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde synchronized(mTrackerLock) { 316bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid); 317bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpPseRecord sdpRecord = null; 318bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if (inst == null) { 319bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL"); 320bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return; 321bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 322bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde inst.setStatus(status); 323bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(status == AbstractionLayer.BT_STATUS_SUCCESS) { 324bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sdpRecord = new SdpPseRecord(l2capPsm, 325bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde rfcommCannelNumber, 326bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde profileVersion, 327bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde supportedFeatures, 328bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde supportedRepositories, 329bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde serviceName); 330bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 331bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "UUID: " + Arrays.toString(uuid)); 332bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString()); 333bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sendSdpIntent(inst, sdpRecord, moreResults); 334bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 335bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 336bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 337bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde void sdpOppOpsRecordFoundCallback(int status, byte[] address, byte[] uuid, 338bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int l2capPsm, 339bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int rfcommCannelNumber, 340bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int profileVersion, 341bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde String serviceName, 342bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde byte[] formatsList, 343bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde boolean moreResults) { 344bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 345bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde synchronized(mTrackerLock) { 346bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid); 347bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpOppOpsRecord sdpRecord = null; 348bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 349bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if (inst == null) { 350bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde Log.e(TAG, "sdpOppOpsRecordFoundCallback: Search instance is NULL"); 351bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return; 352bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 353bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde inst.setStatus(status); 354bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(status == AbstractionLayer.BT_STATUS_SUCCESS) { 355bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sdpRecord = new SdpOppOpsRecord(serviceName, 356bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde rfcommCannelNumber, 357bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde l2capPsm, 358bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde profileVersion, 359bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde formatsList); 360bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 361bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "UUID: " + Arrays.toString(uuid)); 362bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString()); 363bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sendSdpIntent(inst, sdpRecord, moreResults); 364bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 365bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 366bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 367bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /* TODO: Test or remove! */ 368bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde void sdpRecordFoundCallback(int status, byte[] address, byte[] uuid, 369bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int size_record, byte[] record) { 370bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde synchronized(mTrackerLock) { 371bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 372bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid); 373bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpRecord sdpRecord = null; 374bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if (inst == null) { 375bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL"); 376bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return; 377bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 378bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde inst.setStatus(status); 379bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(status == AbstractionLayer.BT_STATUS_SUCCESS) { 380bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "sdpRecordFoundCallback: found a sdp record of size " 381bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde + size_record ); 382bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "Record:"+ Arrays.toString(record)); 383bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sdpRecord = new SdpRecord(size_record, record); 384bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 385bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "UUID: " + Arrays.toString(uuid)); 386bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "UUID in parcel: " + ((Utils.byteArrayToUuid(uuid))[0]).toString()); 387bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sendSdpIntent(inst, sdpRecord, false); 388bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 389bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 390bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 391bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public void sdpSearch(BluetoothDevice device, ParcelUuid uuid) { 392bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if (sNativeAvailable == false) { 393bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde Log.e(TAG, "Native not initialized!"); 394bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return; 395bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 396bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde synchronized (mTrackerLock) { 397bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if (sSdpSearchTracker.isSearching(device, uuid)) { 398bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /* Search already in progress */ 399bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return; 400bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 401bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 402bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpSearchInstance inst = new SdpSearchInstance(0, device, uuid); 403bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sSdpSearchTracker.add(inst); // Queue the request 404bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 405bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde startSearch(); // Start search if not busy 406bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 407bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 408bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 409bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 410bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /* Caller must hold the mTrackerLock */ 411bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private void startSearch() { 412bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 413bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpSearchInstance inst = sSdpSearchTracker.getNext(); 414bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 415bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if((inst != null) && (mSearchInProgress == false)) { 416bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "Starting search for UUID: "+ inst.getUuid()); 417bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde mSearchInProgress = true; 418bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 419bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde inst.startSearch(); // Trigger timeout message 420bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 421bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sdpSearchNative(Utils.getBytesFromAddress(inst.getDevice().getAddress()), 422bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde Utils.uuidToByteArray(inst.getUuid())); 423bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } // Else queue is empty. 424bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde else { 425bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(D) Log.d(TAG, "startSearch(): nextInst = " + inst + 426bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde " mSearchInProgress = " + mSearchInProgress 427bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde + " - search busy or queue empty."); 428bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 429bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 430bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 431bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /* Caller must hold the mTrackerLock */ 432bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private void sendSdpIntent(SdpSearchInstance inst, 433bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde Parcelable record, boolean moreResults) { 434bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 435bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde inst.stopSearch(); 436bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 437bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde Intent intent = new Intent(BluetoothDevice.ACTION_SDP_RECORD); 438bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 439bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde intent.putExtra(BluetoothDevice.EXTRA_DEVICE, inst.getDevice()); 440bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde intent.putExtra(BluetoothDevice.EXTRA_SDP_SEARCH_STATUS, inst.getStatus()); 441bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if (record != null) intent.putExtra(BluetoothDevice.EXTRA_SDP_RECORD, record); 442bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde intent.putExtra(BluetoothDevice.EXTRA_UUID, inst.getUuid()); 443bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /* TODO: BLUETOOTH_ADMIN_PERM was private... change to callback interface. 444bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Keep in mind that the MAP client needs to use this as well, 445bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * hence to make it call-backs, the MAP client profile needs to be 446bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * part of the Bluetooth APK. */ 447bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_ADMIN_PERM); 448bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 449bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(moreResults == false) { 450bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde //Remove the outstanding UUID request 451bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sSdpSearchTracker.remove(inst); 452bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde mSearchInProgress = false; 453bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde startSearch(); 454bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 455bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 456bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 457bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde private final Handler mHandler = new Handler() { 458bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde @Override 459bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public void handleMessage(Message msg) { 460bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde switch (msg.what) { 461bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde case MESSAGE_SDP_INTENT: 462bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde SdpSearchInstance msgObj = (SdpSearchInstance)msg.obj; 463bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde Log.w(TAG, "Search timedout for UUID " + msgObj.getUuid()); 464bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde synchronized (mTrackerLock) { 465bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde sendSdpIntent(msgObj, null, false); 466bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 467bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde break; 468bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 469bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 470bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde }; 471bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 472bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /** 473bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Create a server side Message Access Profile Service Record. 474bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Create the record once, and reuse it for all connections. 475bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * If changes to a record is needed remove the old record using {@link removeSdpRecord} 476bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * and then create a new one. 477bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param serviceName The textual name of the service 478bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param masId The MAS ID to associate with this SDP record 479bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param rfcommChannel The RFCOMM channel that clients can connect to 480bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * (obtain from BluetoothServerSocket) 481bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param l2capPsm The L2CAP PSM channel that clients can connect to 482bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * (obtain from BluetoothServerSocket) 483bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Supply -1 to omit the L2CAP PSM from the record. 484bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param version The Profile version number (As specified in the Bluetooth 485bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * MAP specification) 486bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param msgTypes The supported message types bit mask (As specified in 487bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * the Bluetooth MAP specification) 488bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param features The feature bit mask (As specified in the Bluetooth 489bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * MAP specification) 490bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @return a handle to the record created. The record can be removed again 491bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * using {@link removeSdpRecord}(). The record is not linked to the 492bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * creation/destruction of BluetoothSockets, hence SDP record cleanup 493bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * is a separate process. 494bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde */ 495bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public int createMapMasRecord(String serviceName, int masId, 496bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int rfcommChannel, int l2capPsm, int version, 497bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int msgTypes, int features) { 498bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(sNativeAvailable == false) { 499bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized"); 500bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 501bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return sdpCreateMapMasRecordNative(serviceName, masId, rfcommChannel, 502bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde l2capPsm, version, msgTypes, features); 503bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 504bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 505bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /** 506bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Create a client side Message Access Profile Service Record. 507bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Create the record once, and reuse it for all connections. 508bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * If changes to a record is needed remove the old record using {@link removeSdpRecord} 509bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * and then create a new one. 510bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param serviceName The textual name of the service 511bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param rfcommChannel The RFCOMM channel that clients can connect to 512bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * (obtain from BluetoothServerSocket) 513bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param l2capPsm The L2CAP PSM channel that clients can connect to 514bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * (obtain from BluetoothServerSocket) 515bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Supply -1 to omit the L2CAP PSM from the record. 516bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param version The Profile version number (As specified in the Bluetooth 517bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * MAP specification) 518bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param features The feature bit mask (As specified in the Bluetooth 519bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * MAP specification) 520bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @return a handle to the record created. The record can be removed again 521bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * using {@link removeSdpRecord}(). The record is not linked to the 522bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * creation/destruction of BluetoothSockets, hence SDP record cleanup 523bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * is a separate process. 524bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde */ 525bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public int createMapMnsRecord(String serviceName, int rfcommChannel, 526bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int l2capPsm, int version, int features) { 527bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(sNativeAvailable == false) { 528bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized"); 529bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 530bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return sdpCreateMapMnsRecordNative(serviceName, rfcommChannel, 531bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde l2capPsm, version, features); 532bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 533bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 534bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /** 535bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Create a Server side Phone Book Access Profile Service Record. 536bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Create the record once, and reuse it for all connections. 537bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * If changes to a record is needed remove the old record using {@link removeSdpRecord} 538bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * and then create a new one. 539bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param serviceName The textual name of the service 540bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param rfcommChannel The RFCOMM channel that clients can connect to 541bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * (obtain from BluetoothServerSocket) 542bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param l2capPsm The L2CAP PSM channel that clients can connect to 543bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * (obtain from BluetoothServerSocket) 544bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Supply -1 to omit the L2CAP PSM from the record. 545bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param version The Profile version number (As specified in the Bluetooth 546bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * PBAP specification) 547bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param repositories The supported repositories bit mask (As specified in 548bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * the Bluetooth PBAP specification) 549bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param features The feature bit mask (As specified in the Bluetooth 550bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * PBAP specification) 551bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @return a handle to the record created. The record can be removed again 552bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * using {@link removeSdpRecord}(). The record is not linked to the 553bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * creation/destruction of BluetoothSockets, hence SDP record cleanup 554bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * is a separate process. 555bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde */ 556bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public int createPbapPseRecord(String serviceName, int rfcommChannel, int l2capPsm, 557bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int version, int repositories, int features) { 558bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(sNativeAvailable == false) { 559bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized"); 560bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 561bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return sdpCreatePbapPseRecordNative(serviceName, rfcommChannel, 562bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde l2capPsm, version, repositories, features); 563bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 564bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 565bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /** 566bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Create a Server side Object Push Profile Service Record. 567bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Create the record once, and reuse it for all connections. 568bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * If changes to a record is needed remove the old record using {@link removeSdpRecord} 569bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * and then create a new one. 570bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param serviceName The textual name of the service 571bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param rfcommChannel The RFCOMM channel that clients can connect to 572bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * (obtain from BluetoothServerSocket) 573bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param l2capPsm The L2CAP PSM channel that clients can connect to 574bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * (obtain from BluetoothServerSocket) 575bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Supply -1 to omit the L2CAP PSM from the record. 576bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param version The Profile version number (As specified in the Bluetooth 577bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * OPP specification) 578bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param formatsList A list of the supported formats (As specified in 579bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * the Bluetooth OPP specification) 580bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @return a handle to the record created. The record can be removed again 581bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * using {@link removeSdpRecord}(). The record is not linked to the 582bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * creation/destruction of BluetoothSockets, hence SDP record cleanup 583bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * is a separate process. 584bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde */ 585bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public int createOppOpsRecord(String serviceName, int rfcommChannel, int l2capPsm, 586bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde int version, byte[] formatsList) { 587bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(sNativeAvailable == false) { 588bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized"); 589bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 590bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return sdpCreateOppOpsRecordNative(serviceName, rfcommChannel, 591bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde l2capPsm, version, formatsList); 592bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 593bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde 594bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde /** 595bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * Remove a SDP record. 596bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * When Bluetooth is disabled all records will be deleted, hence there 597bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * is no need to call this function when bluetooth is disabled. 598bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @param recordId The Id returned by on of the createXxxXxxRecord() functions. 599bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * @return TRUE if the record removal was initiated successfully. FALSE if the record 600bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde * handle is not known/have already been removed. 601bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde */ 602bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde public boolean removeSdpRecord(int recordId){ 603bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde if(sNativeAvailable == false) { 604bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized"); 605bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 606bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde return sdpRemoveSdpRecordNative(recordId); 607bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde } 608bbb4110b455b3aa29106d5b4f0a37e1be8e09475Casper Bonde} 609