1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.bluetooth.gatt;
18
19import android.content.Intent;
20import android.os.Bundle;
21import android.util.Log;
22import java.util.UUID;
23
24/**
25 * Helper class containing useful tools for GATT service debugging.
26 */
27/*package*/ class GattDebugUtils {
28    private static final String TAG = GattServiceConfig.TAG_PREFIX + "DebugUtils";
29    private static final boolean DEBUG_ADMIN = GattServiceConfig.DEBUG_ADMIN;
30
31    private static final String ACTION_DEBUG_DUMP_CLIENTMAP =
32                                "android.bluetooth.action.DEBUG_DUMP_CLIENTMAP";
33    private static final String ACTION_DEBUG_DUMP_SERVERMAP =
34                                "android.bluetooth.action.DEBUG_DUMP_SERVERMAP";
35    private static final String ACTION_DEBUG_DUMP_HANDLEMAP =
36                                "android.bluetooth.action.DEBUG_DUMP_HANDLEMAP";
37
38    private static final String ACTION_GATT_PAIRING_CONFIG =
39                                "android.bluetooth.action.GATT_PAIRING_CONFIG";
40
41    private static final String ACTION_GATT_TEST_USAGE =
42                                "android.bluetooth.action.GATT_TEST_USAGE";
43    private static final String ACTION_GATT_TEST_ENABLE =
44                                "android.bluetooth.action.GATT_TEST_ENABLE";
45    private static final String ACTION_GATT_TEST_CONNECT =
46                                "android.bluetooth.action.GATT_TEST_CONNECT";
47    private static final String ACTION_GATT_TEST_DISCONNECT =
48                                "android.bluetooth.action.GATT_TEST_DISCONNECT";
49    private static final String ACTION_GATT_TEST_DISCOVER =
50                                "android.bluetooth.action.GATT_TEST_DISCOVER";
51
52    private static final String EXTRA_ENABLE = "enable";
53    private static final String EXTRA_ADDRESS = "address";
54    private static final String EXTRA_UUID = "uuid";
55    private static final String EXTRA_TYPE = "type";
56    private static final String EXTRA_ADDR_TYPE = "addr_type";
57    private static final String EXTRA_SHANDLE = "start";
58    private static final String EXTRA_EHANDLE = "end";
59    private static final String EXTRA_AUTH_REQ = "auth_req";
60    private static final String EXTRA_IO_CAP = "io_cap";
61    private static final String EXTRA_INIT_KEY = "init_key";
62    private static final String EXTRA_RESP_KEY = "resp_key";
63    private static final String EXTRA_MAX_KEY = "max_key";
64
65    /**
66     * Handles intents passed in via GattService.onStartCommand().
67     * This allows sending debug actions to the running service.
68     * To trigger a debug action, invoke the following shell command:
69     *
70     *   adb shell am startservice -a <action> <component>
71     *
72     * Where <action> represents one of the ACTION_* constants defines above
73     * and  <component> identifies the GATT service.
74     *
75     * For example:
76     *   import com.android.bluetooth.gatt.GattService;
77     */
78    static boolean handleDebugAction(GattService svc, Intent intent) {
79        if (!DEBUG_ADMIN) return false;
80
81        String action = intent.getAction();
82        Log.d(TAG, "handleDebugAction() action=" + action);
83
84        /*
85         * Debug log functinos
86         */
87
88        if (ACTION_DEBUG_DUMP_CLIENTMAP.equals(action)) {
89            svc.mClientMap.dump();
90
91        } else if (ACTION_DEBUG_DUMP_SERVERMAP.equals(action)) {
92            svc.mServerMap.dump();
93
94        } else if (ACTION_DEBUG_DUMP_HANDLEMAP.equals(action)) {
95            svc.mHandleMap.dump();
96
97        /*
98         * PTS test commands
99         */
100
101        } else if (ACTION_GATT_TEST_USAGE.equals(action)) {
102            logUsageInfo();
103
104        } else if (ACTION_GATT_TEST_ENABLE.equals(action)) {
105            boolean bEnable = intent.getBooleanExtra(EXTRA_ENABLE, true);
106            svc.gattTestCommand( 0x01, null,null, bEnable ? 1 : 0, 0,0,0,0);
107
108        } else if (ACTION_GATT_TEST_CONNECT.equals(action)) {
109            String address = intent.getStringExtra(EXTRA_ADDRESS);
110            int type = intent.getIntExtra(EXTRA_TYPE, 2 /* LE device */);
111            int addr_type = intent.getIntExtra(EXTRA_ADDR_TYPE, 0 /* Static */);
112            svc.gattTestCommand( 0x02, null, address, type, addr_type, 0,0,0);
113
114        } else if (ACTION_GATT_TEST_DISCONNECT.equals(action)) {
115            svc.gattTestCommand( 0x03, null, null, 0,0,0,0,0);
116
117        } else if (ACTION_GATT_TEST_DISCOVER.equals(action)) {
118            UUID uuid = getUuidExtra(intent);
119            int type = intent.getIntExtra(EXTRA_TYPE, 1 /* All services */);
120            int shdl = getHandleExtra(intent, EXTRA_SHANDLE, 1);
121            int ehdl = getHandleExtra(intent, EXTRA_EHANDLE, 0xFFFF);
122            svc.gattTestCommand( 0x04, uuid, null, type, shdl, ehdl, 0,0);
123
124        } else if (ACTION_GATT_PAIRING_CONFIG.equals(action)) {
125            int auth_req = intent.getIntExtra(EXTRA_AUTH_REQ, 5);
126            int io_cap = intent.getIntExtra(EXTRA_IO_CAP, 4);
127            int init_key = intent.getIntExtra(EXTRA_INIT_KEY, 7);
128            int resp_key = intent.getIntExtra(EXTRA_RESP_KEY, 7);
129            int max_key = intent.getIntExtra(EXTRA_MAX_KEY, 16);
130            svc.gattTestCommand( 0xF0, null, null, auth_req, io_cap, init_key,
131                                 resp_key, max_key);
132
133        } else {
134            return false;
135        }
136
137        return true;
138    }
139
140    /**
141     * Attempts to retrieve an extra from an intent first as hex value,
142     * then as an ineger.
143     * @hide
144     */
145    static private int getHandleExtra(Intent intent, String extra, int default_value) {
146        Bundle extras = intent.getExtras();
147        Object uuid = extras != null ? extras.get(extra) : null;
148        if (uuid != null && uuid.getClass().getName().equals("java.lang.String")) {
149            try
150            {
151                return Integer.parseInt(extras.getString(extra), 16);
152            } catch (NumberFormatException e) {
153                return default_value;
154            }
155        }
156
157        return intent.getIntExtra(extra, default_value);
158    }
159
160    /**
161     * Retrieves the EXTRA_UUID parameter.
162     * If a string of length 4 is detected, a 16-bit hex UUID is assumed and
163     * the default Bluetooth UUID is appended.
164     * @hide
165     */
166    static private UUID getUuidExtra(Intent intent) {
167        String uuidStr = intent.getStringExtra(EXTRA_UUID);
168        if (uuidStr != null && uuidStr.length() == 4) {
169            uuidStr = String.format("0000%s-0000-1000-8000-00805f9b34fb", uuidStr);
170        }
171        return (uuidStr != null) ? UUID.fromString(uuidStr) : null;
172    }
173
174    /**
175     * Log usage information.
176     * @hide
177     */
178    static private void logUsageInfo() {
179        StringBuilder b = new StringBuilder();
180        b.append(  "------------ GATT TEST ACTIONS  ----------------");
181        b.append("\nGATT_TEST_ENABLE");
182        b.append("\n  [--ez enable <bool>] Enable or disable,");
183        b.append("\n                       defaults to true (enable).\n");
184        b.append("\nGATT_TEST_CONNECT");
185        b.append("\n   --es address <bda>");
186        b.append("\n  [--ei addr_type <type>] Possible values:");
187        b.append("\n                         0 = Static (default)");
188        b.append("\n                         1 = Random\n");
189        b.append("\n  [--ei type <type>]   Default is 2 (LE Only)\n");
190        b.append("\nGATT_TEST_DISCONNECT\n");
191        b.append("\nGATT_TEST_DISCOVER");
192        b.append("\n  [--ei type <type>]   Possible values:");
193        b.append("\n                         1 = Discover all services (default)");
194        b.append("\n                         2 = Discover services by UUID");
195        b.append("\n                         3 = Discover included services");
196        b.append("\n                         4 = Discover characteristics");
197        b.append("\n                         5 = Discover descriptors\n");
198        b.append("\n  [--es uuid <uuid>]   Optional; Can be either full 128-bit");
199        b.append("\n                       UUID hex string, or 4 hex characters");
200        b.append("\n                       for 16-bit UUIDs.\n");
201        b.append("\n  [--ei start <hdl>]   Start of handle range (default 1)");
202        b.append("\n  [--ei end <hdl>]     End of handle range (default 65355)");
203        b.append("\n    or");
204        b.append("\n  [--es start <hdl>]   Start of handle range (hex format)");
205        b.append("\n  [--es end <hdl>]     End of handle range (hex format)\n");
206        b.append("\nGATT_PAIRING_CONFIG");
207        b.append("\n  [--ei auth_req]      Authentication flag (default 5)");
208        b.append("\n  [--ei io_cap]        IO capabilities (default 4)");
209        b.append("\n  [--ei init_key]      Initial key size (default 7)");
210        b.append("\n  [--ei resp_key]      Response key size (default 7)");
211        b.append("\n  [--ei max_key]       Maximum key size (default 16)");
212        b.append("\n------------------------------------------------");
213        Log.i(TAG, b.toString());
214    }
215}
216