/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.gatt; import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.IInterface; import android.os.RemoteException; import android.util.Log; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; import java.util.UUID; /** * Helper class that keeps track of registered GATT applications. * This class manages application callbacks and keeps track of GATT connections. * @hide */ /*package*/ class ContextMap { private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap"; /** * Connection class helps map connection IDs to device addresses. */ class Connection { int connId; String address; int appId; Connection(int connId, String address,int appId) { this.connId = connId; this.address = address; this.appId = appId; } } /** * Application entry mapping UUIDs to appIDs and callbacks. */ class App { /** The UUID of the application */ UUID uuid; /** The id of the application */ int id; /** Application callbacks */ T callback; /** Death receipient */ private IBinder.DeathRecipient mDeathRecipient; /** * Creates a new app context. */ App(UUID uuid, T callback) { this.uuid = uuid; this.callback = callback; } /** * Link death recipient */ void linkToDeath(IBinder.DeathRecipient deathRecipient) { try { IBinder binder = ((IInterface)callback).asBinder(); binder.linkToDeath(deathRecipient, 0); mDeathRecipient = deathRecipient; } catch (RemoteException e) { Log.e(TAG, "Unable to link deathRecipient for app id " + id); } } /** * Unlink death recipient */ void unlinkToDeath() { if (mDeathRecipient != null) { try { IBinder binder = ((IInterface)callback).asBinder(); binder.unlinkToDeath(mDeathRecipient,0); } catch (NoSuchElementException e) { Log.e(TAG, "Unable to unlink deathRecipient for app id " + id); } } } } /** Our internal application list */ List mApps = new ArrayList(); /** Internal list of connected devices **/ Set mConnections = new HashSet(); /** * Add an entry to the application context list. */ void add(UUID uuid, T callback) { synchronized (mApps) { mApps.add(new App(uuid, callback)); } } /** * Remove the context for a given application ID. */ void remove(int id) { synchronized (mApps) { Iterator i = mApps.iterator(); while(i.hasNext()) { App entry = i.next(); if (entry.id == id) { entry.unlinkToDeath(); i.remove(); break; } } } } /** * Add a new connection for a given application ID. */ void addConnection(int id, int connId, String address) { synchronized (mConnections) { App entry = getById(id); if (entry != null){ mConnections.add(new Connection(connId, address, id)); } } } /** * Remove a connection with the given ID. */ void removeConnection(int id, int connId) { synchronized (mConnections) { Iterator i = mConnections.iterator(); while(i.hasNext()) { Connection connection = i.next(); if (connection.connId == connId) { i.remove(); break; } } } } /** * Get an application context by ID. */ App getById(int id) { Iterator i = mApps.iterator(); while(i.hasNext()) { App entry = i.next(); if (entry.id == id) return entry; } Log.e(TAG, "Context not found for ID " + id); return null; } /** * Get an application context by UUID. */ App getByUuid(UUID uuid) { Iterator i = mApps.iterator(); while(i.hasNext()) { App entry = i.next(); if (entry.uuid.equals(uuid)) return entry; } Log.e(TAG, "Context not found for UUID " + uuid); return null; } /** * Get the device addresses for all connected devices */ Set getConnectedDevices() { Set addresses = new HashSet(); Iterator i = mConnections.iterator(); while(i.hasNext()) { Connection connection = i.next(); addresses.add(connection.address); } return addresses; } /** * Get an application context by a connection ID. */ App getByConnId(int connId) { Iterator ii = mConnections.iterator(); while(ii.hasNext()) { Connection connection = ii.next(); if (connection.connId == connId){ return getById(connection.appId); } } return null; } /** * Returns a connection ID for a given device address. */ Integer connIdByAddress(int id, String address) { App entry = getById(id); if (entry == null) return null; Iterator i = mConnections.iterator(); while(i.hasNext()) { Connection connection = i.next(); if (connection.address.equals(address) && connection.appId == id) return connection.connId; } return null; } /** * Returns the device address for a given connection ID. */ String addressByConnId(int connId) { Iterator i = mConnections.iterator(); while(i.hasNext()) { Connection connection = i.next(); if (connection.connId == connId) return connection.address; } return null; } List getConnectionByApp(int appId) { List currentConnections = new ArrayList(); Iterator i = mConnections.iterator(); while(i.hasNext()) { Connection connection = i.next(); if (connection.appId == appId) currentConnections.add(connection); } return currentConnections; } /** * Erases all application context entries. */ void clear() { synchronized (mApps) { Iterator i = mApps.iterator(); while(i.hasNext()) { App entry = i.next(); entry.unlinkToDeath(); i.remove(); } } synchronized (mConnections) { mConnections.clear(); } } /** * Logs debug information. */ void dump() { StringBuilder b = new StringBuilder(); b.append( "-------------- GATT Context Map ----------------"); b.append("\nEntries: " + mApps.size()); Iterator i = mApps.iterator(); while(i.hasNext()) { App entry = i.next(); List connections = getConnectionByApp(entry.id); b.append("\n\nApplication Id: " + entry.id); b.append("\nUUID: " + entry.uuid); b.append("\nConnections: " + connections.size()); Iterator ii = connections.iterator(); while(ii.hasNext()) { Connection connection = ii.next(); b.append("\n " + connection.connId + ": " + connection.address); } } b.append("\n------------------------------------------------"); Log.d(TAG, b.toString()); } }