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