blob: 6f9025d6d693f69fb72aa5955d7a10066da748f8 [file] [log] [blame]
/*
* 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.content.Context;
import android.os.Binder;
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;
import java.util.HashMap;
import java.util.Map;
import com.android.bluetooth.btservice.BluetoothProto;
/**
* Helper class that keeps track of registered GATT applications.
* This class manages application callbacks and keeps track of GATT connections.
* @hide
*/
/*package*/ class ContextMap<T> {
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;
long startTime;
Connection(int connId, String address,int appId) {
this.connId = connId;
this.address = address;
this.appId = appId;
this.startTime = System.currentTimeMillis();
}
}
/**
* Application entry mapping UUIDs to appIDs and callbacks.
*/
class App {
/** The UUID of the application */
UUID uuid;
/** The id of the application */
int id;
/** The package name of the application */
String name;
/** Statistics for this app */
AppScanStats appScanStats;
/** Application callbacks */
T callback;
/** Death receipient */
private IBinder.DeathRecipient mDeathRecipient;
/** Flag to signal that transport is congested */
Boolean isCongested = false;
/** Internal callback info queue, waiting to be send on congestion clear */
private List<CallbackInfo> congestionQueue = new ArrayList<CallbackInfo>();
/**
* Creates a new app context.
*/
App(UUID uuid, T callback, String name, AppScanStats appScanStats) {
this.uuid = uuid;
this.callback = callback;
this.name = name;
this.appScanStats = appScanStats;
}
/**
* 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);
}
}
}
void queueCallback(CallbackInfo callbackInfo) {
congestionQueue.add(callbackInfo);
}
CallbackInfo popQueuedCallback() {
if (congestionQueue.size() == 0) return null;
return congestionQueue.remove(0);
}
}
/** Our internal application list */
List<App> mApps = new ArrayList<App>();
/** Internal map to keep track of logging information by app name */
HashMap<String, AppScanStats> mAppScanStats = new HashMap<String, AppScanStats>();
/** Internal list of connected devices **/
Set<Connection> mConnections = new HashSet<Connection>();
/**
* Add an entry to the application context list.
*/
void add(UUID uuid, T callback, GattService service) {
String appName = service.getPackageManager().getNameForUid(
Binder.getCallingUid());
if (appName == null) {
// Assign an app name if one isn't found
appName = "Unknown App (UID: " + Binder.getCallingUid() + ")";
}
synchronized (mApps) {
AppScanStats appScanStats = mAppScanStats.get(appName);
mApps.add(new App(uuid, callback, appName, appScanStats));
if (appScanStats == null) {
appScanStats = new AppScanStats(appName, this, service);
mAppScanStats.put(appName, appScanStats);
}
appScanStats.isRegistered = true;
}
}
/**
* Remove the context for a given UUID
*/
void remove(UUID uuid) {
synchronized (mApps) {
Iterator<App> i = mApps.iterator();
while (i.hasNext()) {
App entry = i.next();
if (entry.uuid.equals(uuid)) {
entry.unlinkToDeath();
entry.appScanStats.isRegistered = false;
i.remove();
break;
}
}
}
}
/**
* Remove the context for a given application ID.
*/
void remove(int id) {
synchronized (mApps) {
Iterator<App> i = mApps.iterator();
while (i.hasNext()) {
App entry = i.next();
if (entry.id == id) {
entry.unlinkToDeath();
mAppScanStats.get(entry.name).isRegistered = false;
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<Connection> 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<App> 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<App> 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 an application context by the calling Apps name.
*/
App getByName(String name) {
Iterator<App> i = mApps.iterator();
while (i.hasNext()) {
App entry = i.next();
if (entry.name.equals(name)) return entry;
}
Log.e(TAG, "Context not found for name " + name);
return null;
}
/**
* Get Logging info by ID
*/
AppScanStats getAppScanStatsById(int id) {
App temp = getById(id);
if (temp != null) {
return temp.appScanStats;
}
return null;
}
/**
* Get Logging info by application name
*/
AppScanStats getAppScanStatsByName(String name) {
return mAppScanStats.get(name);
}
/**
* Get the device addresses for all connected devices
*/
Set<String> getConnectedDevices() {
Set<String> addresses = new HashSet<String>();
Iterator<Connection> 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<Connection> 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<Connection> 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<Connection> i = mConnections.iterator();
while (i.hasNext()) {
Connection connection = i.next();
if (connection.connId == connId) return connection.address;
}
return null;
}
List<Connection> getConnectionByApp(int appId) {
List<Connection> currentConnections = new ArrayList<Connection>();
Iterator<Connection> 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<App> i = mApps.iterator();
while (i.hasNext()) {
App entry = i.next();
entry.unlinkToDeath();
i.remove();
}
}
synchronized (mConnections) {
mConnections.clear();
}
}
/**
* Returns connect device map with addr and appid
*/
Map<Integer, String> getConnectedMap(){
Map<Integer, String> connectedmap = new HashMap<Integer, String>();
for(Connection conn: mConnections){
connectedmap.put(conn.appId, conn.address);
}
return connectedmap;
}
/**
* Logs debug information.
*/
void dump(StringBuilder sb) {
sb.append(" Entries: " + mAppScanStats.size() + "\n\n");
Iterator<Map.Entry<String, AppScanStats>> it = mAppScanStats.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, AppScanStats> entry = it.next();
String name = entry.getKey();
AppScanStats appScanStats = entry.getValue();
appScanStats.dumpToString(sb);
}
}
}