blob: eaf790bb05c4e8dda9dcb350176391d5cb1708ce [file] [log] [blame]
Benoit Goby4e68bd42012-04-25 18:06:00 -07001/*
2 * Copyright (C) 2012 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.
Kenny Roote0e84082018-01-16 11:17:20 -080013 * See the License for the specific language governing permissions and
Benoit Goby4e68bd42012-04-25 18:06:00 -070014 * limitations under the License.
15 */
16
Kenny Roote0e84082018-01-16 11:17:20 -080017package com.android.server.adb;
Benoit Goby4e68bd42012-04-25 18:06:00 -070018
Philip P. Moltmann371a3b82018-01-26 13:00:22 -080019import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
20
Michael Groover9e7b06e2018-12-28 19:55:51 -080021import android.annotation.TestApi;
Xiaohui Chenffcfe342015-05-13 13:18:36 -070022import android.app.ActivityManager;
Benoit Goby4e68bd42012-04-25 18:06:00 -070023import android.content.ActivityNotFoundException;
Aaron Whytef10d0392014-03-28 14:42:39 -070024import android.content.ComponentName;
Benoit Goby4e68bd42012-04-25 18:06:00 -070025import android.content.Context;
26import android.content.Intent;
Aaron Whyted1931ff2014-04-11 10:53:58 -070027import android.content.pm.PackageManager;
Xiaohui Chen05d58af2015-07-13 14:17:48 -070028import android.content.pm.UserInfo;
Aaron Whytef10d0392014-03-28 14:42:39 -070029import android.content.res.Resources;
Michael Groover9e7b06e2018-12-28 19:55:51 -080030import android.debug.AdbProtoEnums;
Benoit Goby4e68bd42012-04-25 18:06:00 -070031import android.net.LocalSocket;
32import android.net.LocalSocketAddress;
Benoit Goby4e68bd42012-04-25 18:06:00 -070033import android.os.Environment;
34import android.os.FileUtils;
Xiaohui Chenffcfe342015-05-13 13:18:36 -070035import android.os.Handler;
Benoit Goby4e68bd42012-04-25 18:06:00 -070036import android.os.Looper;
37import android.os.Message;
Benoit Goby4e68bd42012-04-25 18:06:00 -070038import android.os.SystemClock;
Craig Mautnerb43d3cf2015-02-06 00:01:28 +000039import android.os.SystemProperties;
40import android.os.UserHandle;
Xiaohui Chenffcfe342015-05-13 13:18:36 -070041import android.os.UserManager;
Michael Groover9e7b06e2018-12-28 19:55:51 -080042import android.provider.Settings;
Kenny Rootf74bfde2018-01-18 15:42:48 -080043import android.service.adb.AdbDebuggingManagerProto;
Michael Groover9e7b06e2018-12-28 19:55:51 -080044import android.util.AtomicFile;
Benoit Goby4e68bd42012-04-25 18:06:00 -070045import android.util.Base64;
Xiaohui Chenffcfe342015-05-13 13:18:36 -070046import android.util.Slog;
Michael Groover9e7b06e2018-12-28 19:55:51 -080047import android.util.StatsLog;
48import android.util.Xml;
Amith Yamasanif6197e82015-02-26 13:54:09 -080049
Xiaohui Chenffcfe342015-05-13 13:18:36 -070050import com.android.internal.R;
Michael Groover9e7b06e2018-12-28 19:55:51 -080051import com.android.internal.util.FastXmlSerializer;
52import com.android.internal.util.XmlUtils;
Philip P. Moltmann371a3b82018-01-26 13:00:22 -080053import com.android.internal.util.dump.DualDumpOutputStream;
Dianne Hackborn8d044e82013-04-30 17:24:15 -070054import com.android.server.FgThread;
Benoit Goby4e68bd42012-04-25 18:06:00 -070055
Michael Groover9e7b06e2018-12-28 19:55:51 -080056import org.xmlpull.v1.XmlPullParser;
57import org.xmlpull.v1.XmlPullParserException;
58import org.xmlpull.v1.XmlSerializer;
59
Benoit Goby4e68bd42012-04-25 18:06:00 -070060import java.io.File;
Michael Groover9e7b06e2018-12-28 19:55:51 -080061import java.io.FileInputStream;
Benoit Goby4e68bd42012-04-25 18:06:00 -070062import java.io.FileOutputStream;
63import java.io.IOException;
64import java.io.InputStream;
65import java.io.OutputStream;
Michael Groover9e7b06e2018-12-28 19:55:51 -080066import java.nio.charset.StandardCharsets;
Benoit Goby4e68bd42012-04-25 18:06:00 -070067import java.security.MessageDigest;
68import java.util.Arrays;
Michael Groover9e7b06e2018-12-28 19:55:51 -080069import java.util.HashMap;
70import java.util.Iterator;
71import java.util.Map;
Benoit Goby4e68bd42012-04-25 18:06:00 -070072
Kenny Roote0e84082018-01-16 11:17:20 -080073/**
74 * Provides communication to the Android Debug Bridge daemon to allow, deny, or clear public keysi
75 * that are authorized to connect to the ADB service itself.
76 */
Kenny Root7c4e6612018-01-16 11:19:47 -080077public class AdbDebuggingManager {
78 private static final String TAG = "AdbDebuggingManager";
Benoit Goby4e68bd42012-04-25 18:06:00 -070079 private static final boolean DEBUG = false;
80
Philip P. Moltmannbee04d02017-06-26 15:32:00 -070081 private static final String ADBD_SOCKET = "adbd";
82 private static final String ADB_DIRECTORY = "misc/adb";
Michael Groover9e7b06e2018-12-28 19:55:51 -080083 // This file contains keys that will always be allowed to connect to the device via adb.
Philip P. Moltmannbee04d02017-06-26 15:32:00 -070084 private static final String ADB_KEYS_FILE = "adb_keys";
William Hester99a7d632019-02-06 17:36:37 -080085 // This file contains keys that will be allowed to connect without user interaction as long
86 // as a subsequent connection occurs within the allowed duration.
87 private static final String ADB_TEMP_KEYS_FILE = "adb_temp_keys.xml";
Philip P. Moltmannbee04d02017-06-26 15:32:00 -070088 private static final int BUFFER_SIZE = 4096;
Benoit Goby4e68bd42012-04-25 18:06:00 -070089
90 private final Context mContext;
Benoit Goby4e68bd42012-04-25 18:06:00 -070091 private final Handler mHandler;
Kenny Root7c4e6612018-01-16 11:19:47 -080092 private AdbDebuggingThread mThread;
Benoit Goby4e68bd42012-04-25 18:06:00 -070093 private boolean mAdbEnabled = false;
94 private String mFingerprints;
Michael Groover9e7b06e2018-12-28 19:55:51 -080095 private String mConnectedKey;
96 private String mConfirmComponent;
Benoit Goby4e68bd42012-04-25 18:06:00 -070097
Kenny Root7c4e6612018-01-16 11:19:47 -080098 public AdbDebuggingManager(Context context) {
99 mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
Benoit Goby4e68bd42012-04-25 18:06:00 -0700100 mContext = context;
101 }
102
Michael Groover9e7b06e2018-12-28 19:55:51 -0800103 /**
104 * Constructor that accepts the component to be invoked to confirm if the user wants to allow
105 * an adb connection from the key.
106 */
107 @TestApi
108 protected AdbDebuggingManager(Context context, String confirmComponent) {
109 mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
110 mContext = context;
111 mConfirmComponent = confirmComponent;
112 }
113
Kenny Root7c4e6612018-01-16 11:19:47 -0800114 class AdbDebuggingThread extends Thread {
Amith Yamasanif6197e82015-02-26 13:54:09 -0800115 private boolean mStopped;
116 private LocalSocket mSocket;
117 private OutputStream mOutputStream;
118 private InputStream mInputStream;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700119
Kenny Root7c4e6612018-01-16 11:19:47 -0800120 AdbDebuggingThread() {
Amith Yamasanif6197e82015-02-26 13:54:09 -0800121 super(TAG);
122 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700123
Amith Yamasanif6197e82015-02-26 13:54:09 -0800124 @Override
125 public void run() {
126 if (DEBUG) Slog.d(TAG, "Entering thread");
Benoit Goby4e68bd42012-04-25 18:06:00 -0700127 while (true) {
Amith Yamasanif6197e82015-02-26 13:54:09 -0800128 synchronized (this) {
129 if (mStopped) {
130 if (DEBUG) Slog.d(TAG, "Exiting thread");
131 return;
132 }
133 try {
134 openSocketLocked();
135 } catch (Exception e) {
136 /* Don't loop too fast if adbd dies, before init restarts it */
137 SystemClock.sleep(1000);
138 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700139 }
Amith Yamasanif6197e82015-02-26 13:54:09 -0800140 try {
141 listenToSocket();
Amith Yamasanif4a8df42015-03-04 10:00:11 -0800142 } catch (Exception e) {
Amith Yamasanif6197e82015-02-26 13:54:09 -0800143 /* Don't loop too fast if adbd dies, before init restarts it */
144 SystemClock.sleep(1000);
Benoit Goby4e68bd42012-04-25 18:06:00 -0700145 }
146 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700147 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700148
Amith Yamasanif6197e82015-02-26 13:54:09 -0800149 private void openSocketLocked() throws IOException {
Benoit Goby4e68bd42012-04-25 18:06:00 -0700150 try {
Amith Yamasanif6197e82015-02-26 13:54:09 -0800151 LocalSocketAddress address = new LocalSocketAddress(ADBD_SOCKET,
152 LocalSocketAddress.Namespace.RESERVED);
153 mInputStream = null;
154
155 if (DEBUG) Slog.d(TAG, "Creating socket");
156 mSocket = new LocalSocket();
157 mSocket.connect(address);
158
159 mOutputStream = mSocket.getOutputStream();
160 mInputStream = mSocket.getInputStream();
161 } catch (IOException ioe) {
162 closeSocketLocked();
163 throw ioe;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700164 }
165 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700166
Amith Yamasanif6197e82015-02-26 13:54:09 -0800167 private void listenToSocket() throws IOException {
Benoit Goby4e68bd42012-04-25 18:06:00 -0700168 try {
Amith Yamasanif6197e82015-02-26 13:54:09 -0800169 byte[] buffer = new byte[BUFFER_SIZE];
170 while (true) {
171 int count = mInputStream.read(buffer);
Michael Groover9e7b06e2018-12-28 19:55:51 -0800172 // if less than 2 bytes are read the if statements below will throw an
173 // IndexOutOfBoundsException.
174 if (count < 2) {
Amith Yamasanif6197e82015-02-26 13:54:09 -0800175 break;
176 }
177
178 if (buffer[0] == 'P' && buffer[1] == 'K') {
179 String key = new String(Arrays.copyOfRange(buffer, 2, count));
180 Slog.d(TAG, "Received public key: " + key);
Kenny Roote0e84082018-01-16 11:17:20 -0800181 Message msg = mHandler.obtainMessage(
Kenny Root7c4e6612018-01-16 11:19:47 -0800182 AdbDebuggingHandler.MESSAGE_ADB_CONFIRM);
Amith Yamasanif6197e82015-02-26 13:54:09 -0800183 msg.obj = key;
184 mHandler.sendMessage(msg);
Michael Groover9e7b06e2018-12-28 19:55:51 -0800185 } else if (buffer[0] == 'D' && buffer[1] == 'C') {
186 Slog.d(TAG, "Received disconnected message");
187 Message msg = mHandler.obtainMessage(
188 AdbDebuggingHandler.MESSAGE_ADB_DISCONNECT);
189 mHandler.sendMessage(msg);
Amith Yamasanif6197e82015-02-26 13:54:09 -0800190 } else {
191 Slog.e(TAG, "Wrong message: "
192 + (new String(Arrays.copyOfRange(buffer, 0, 2))));
193 break;
194 }
195 }
196 } finally {
197 synchronized (this) {
198 closeSocketLocked();
199 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700200 }
Amith Yamasanif6197e82015-02-26 13:54:09 -0800201 }
202
203 private void closeSocketLocked() {
204 if (DEBUG) Slog.d(TAG, "Closing socket");
205 try {
206 if (mOutputStream != null) {
207 mOutputStream.close();
208 mOutputStream = null;
209 }
210 } catch (IOException e) {
211 Slog.e(TAG, "Failed closing output stream: " + e);
212 }
213
214 try {
215 if (mSocket != null) {
216 mSocket.close();
217 mSocket = null;
218 }
219 } catch (IOException ex) {
220 Slog.e(TAG, "Failed closing socket: " + ex);
221 }
222 }
223
224 /** Call to stop listening on the socket and exit the thread. */
225 void stopListening() {
226 synchronized (this) {
227 mStopped = true;
228 closeSocketLocked();
229 }
230 }
231
232 void sendResponse(String msg) {
233 synchronized (this) {
234 if (!mStopped && mOutputStream != null) {
235 try {
236 mOutputStream.write(msg.getBytes());
Kenny Roote0e84082018-01-16 11:17:20 -0800237 } catch (IOException ex) {
Amith Yamasanif6197e82015-02-26 13:54:09 -0800238 Slog.e(TAG, "Failed to write response:", ex);
239 }
240 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700241 }
242 }
243 }
244
Kenny Root7c4e6612018-01-16 11:19:47 -0800245 class AdbDebuggingHandler extends Handler {
Michael Groover9e7b06e2018-12-28 19:55:51 -0800246 // The time to schedule the job to keep the key store updated with a currently connected
247 // key. This job is required because a deveoper could keep a device connected to their
248 // system beyond the time within which a subsequent connection is allowed. But since the
249 // last connection time is only written when a device is connected and disconnected then
250 // if the device is rebooted while connected to the development system it would appear as
251 // though the adb grant for the system is no longer authorized and the developer would need
252 // to manually allow the connection again.
253 private static final long UPDATE_KEY_STORE_JOB_INTERVAL = 86400000;
254
255 static final int MESSAGE_ADB_ENABLED = 1;
256 static final int MESSAGE_ADB_DISABLED = 2;
257 static final int MESSAGE_ADB_ALLOW = 3;
258 static final int MESSAGE_ADB_DENY = 4;
259 static final int MESSAGE_ADB_CONFIRM = 5;
260 static final int MESSAGE_ADB_CLEAR = 6;
261 static final int MESSAGE_ADB_DISCONNECT = 7;
262 static final int MESSAGE_ADB_PERSIST_KEY_STORE = 8;
263 static final int MESSAGE_ADB_UPDATE_KEY_CONNECTION_TIME = 9;
264
265 private AdbKeyStore mAdbKeyStore;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700266
Kenny Root7c4e6612018-01-16 11:19:47 -0800267 AdbDebuggingHandler(Looper looper) {
Benoit Goby4e68bd42012-04-25 18:06:00 -0700268 super(looper);
Michael Groover9e7b06e2018-12-28 19:55:51 -0800269 }
270
271 /**
272 * Constructor that accepts the AdbDebuggingThread to which responses should be sent
273 * and the AdbKeyStore to be used to store the temporary grants.
274 */
275 @TestApi
276 AdbDebuggingHandler(Looper looper, AdbDebuggingThread thread, AdbKeyStore adbKeyStore) {
277 super(looper);
278 mThread = thread;
279 mAdbKeyStore = adbKeyStore;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700280 }
281
282 public void handleMessage(Message msg) {
283 switch (msg.what) {
284 case MESSAGE_ADB_ENABLED:
Kenny Roote0e84082018-01-16 11:17:20 -0800285 if (mAdbEnabled) {
Benoit Goby4e68bd42012-04-25 18:06:00 -0700286 break;
Kenny Roote0e84082018-01-16 11:17:20 -0800287 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700288
289 mAdbEnabled = true;
290
Kenny Root7c4e6612018-01-16 11:19:47 -0800291 mThread = new AdbDebuggingThread();
Benoit Goby4e68bd42012-04-25 18:06:00 -0700292 mThread.start();
293
William Hester99a7d632019-02-06 17:36:37 -0800294 mAdbKeyStore = new AdbKeyStore();
Benoit Goby4e68bd42012-04-25 18:06:00 -0700295 break;
296
297 case MESSAGE_ADB_DISABLED:
Kenny Roote0e84082018-01-16 11:17:20 -0800298 if (!mAdbEnabled) {
Benoit Goby4e68bd42012-04-25 18:06:00 -0700299 break;
Kenny Roote0e84082018-01-16 11:17:20 -0800300 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700301
302 mAdbEnabled = false;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700303
Amith Yamasanif6197e82015-02-26 13:54:09 -0800304 if (mThread != null) {
305 mThread.stopListening();
306 mThread = null;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700307 }
308
William Hester99a7d632019-02-06 17:36:37 -0800309 cancelJobToUpdateAdbKeyStore();
310 mAdbKeyStore = null;
311 mConnectedKey = null;
Benoit Goby37ce5c52012-08-29 13:49:07 -0700312 break;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700313
314 case MESSAGE_ADB_ALLOW: {
Kenny Roote0e84082018-01-16 11:17:20 -0800315 String key = (String) msg.obj;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700316 String fingerprints = getFingerprints(key);
317
318 if (!fingerprints.equals(mFingerprints)) {
319 Slog.e(TAG, "Fingerprints do not match. Got "
320 + fingerprints + ", expected " + mFingerprints);
321 break;
322 }
323
Michael Groover9e7b06e2018-12-28 19:55:51 -0800324 boolean alwaysAllow = msg.arg1 == 1;
Amith Yamasanif6197e82015-02-26 13:54:09 -0800325 if (mThread != null) {
326 mThread.sendResponse("OK");
Michael Groover9e7b06e2018-12-28 19:55:51 -0800327 if (alwaysAllow) {
328 mConnectedKey = key;
329 mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
330 scheduleJobToUpdateAdbKeyStore();
331 }
332 logAdbConnectionChanged(key, AdbProtoEnums.USER_ALLOWED, alwaysAllow);
Amith Yamasanif6197e82015-02-26 13:54:09 -0800333 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700334 break;
335 }
336
337 case MESSAGE_ADB_DENY:
Amith Yamasanif6197e82015-02-26 13:54:09 -0800338 if (mThread != null) {
339 mThread.sendResponse("NO");
Michael Groover9e7b06e2018-12-28 19:55:51 -0800340 logAdbConnectionChanged(null, AdbProtoEnums.USER_DENIED, false);
Amith Yamasanif6197e82015-02-26 13:54:09 -0800341 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700342 break;
343
344 case MESSAGE_ADB_CONFIRM: {
Michael Groover9e7b06e2018-12-28 19:55:51 -0800345 String key = (String) msg.obj;
Craig Mautnerb43d3cf2015-02-06 00:01:28 +0000346 if ("trigger_restart_min_framework".equals(
347 SystemProperties.get("vold.decrypt"))) {
348 Slog.d(TAG, "Deferring adb confirmation until after vold decrypt");
Amith Yamasanif6197e82015-02-26 13:54:09 -0800349 if (mThread != null) {
350 mThread.sendResponse("NO");
Michael Groover9e7b06e2018-12-28 19:55:51 -0800351 logAdbConnectionChanged(key, AdbProtoEnums.DENIED_VOLD_DECRYPT, false);
Amith Yamasanif6197e82015-02-26 13:54:09 -0800352 }
Craig Mautnerb43d3cf2015-02-06 00:01:28 +0000353 break;
354 }
Nick Kralevich451a6f82014-11-12 12:47:50 -0800355 String fingerprints = getFingerprints(key);
356 if ("".equals(fingerprints)) {
Amith Yamasanif6197e82015-02-26 13:54:09 -0800357 if (mThread != null) {
358 mThread.sendResponse("NO");
Michael Groover9e7b06e2018-12-28 19:55:51 -0800359 logAdbConnectionChanged(key, AdbProtoEnums.DENIED_INVALID_KEY, false);
Amith Yamasanif6197e82015-02-26 13:54:09 -0800360 }
Nick Kralevich451a6f82014-11-12 12:47:50 -0800361 break;
362 }
Michael Groover9e7b06e2018-12-28 19:55:51 -0800363 // Check if the key should be allowed without user interaction.
364 if (mAdbKeyStore.isKeyAuthorized(key)) {
365 if (mThread != null) {
366 mThread.sendResponse("OK");
367 mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
368 logAdbConnectionChanged(key, AdbProtoEnums.AUTOMATICALLY_ALLOWED, true);
369 mConnectedKey = key;
370 scheduleJobToUpdateAdbKeyStore();
371 }
372 } else {
373 logAdbConnectionChanged(key, AdbProtoEnums.AWAITING_USER_APPROVAL, false);
374 mFingerprints = fingerprints;
375 startConfirmation(key, mFingerprints);
376 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700377 break;
378 }
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800379
Michael Groover9e7b06e2018-12-28 19:55:51 -0800380 case MESSAGE_ADB_CLEAR: {
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800381 deleteKeyFile();
Michael Groover9e7b06e2018-12-28 19:55:51 -0800382 mConnectedKey = null;
383 mAdbKeyStore.deleteKeyStore();
384 cancelJobToUpdateAdbKeyStore();
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800385 break;
Michael Groover9e7b06e2018-12-28 19:55:51 -0800386 }
387
388 case MESSAGE_ADB_DISCONNECT: {
389 if (mConnectedKey != null) {
390 mAdbKeyStore.setLastConnectionTime(mConnectedKey,
391 System.currentTimeMillis());
392 cancelJobToUpdateAdbKeyStore();
393 }
394 logAdbConnectionChanged(mConnectedKey, AdbProtoEnums.DISCONNECTED,
395 (mConnectedKey != null));
396 mConnectedKey = null;
397 break;
398 }
399
400 case MESSAGE_ADB_PERSIST_KEY_STORE: {
401 mAdbKeyStore.persistKeyStore();
402 break;
403 }
404
405 case MESSAGE_ADB_UPDATE_KEY_CONNECTION_TIME: {
406 if (mConnectedKey != null) {
407 mAdbKeyStore.setLastConnectionTime(mConnectedKey,
408 System.currentTimeMillis());
409 scheduleJobToUpdateAdbKeyStore();
410 }
411 break;
412 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700413 }
414 }
Michael Groover9e7b06e2018-12-28 19:55:51 -0800415
416 private void logAdbConnectionChanged(String key, int state, boolean alwaysAllow) {
417 long lastConnectionTime = mAdbKeyStore.getLastConnectionTime(key);
418 long authWindow = mAdbKeyStore.getAllowedConnectionTime();
419 StatsLog.write(StatsLog.ADB_CONNECTION_CHANGED, lastConnectionTime, authWindow, state,
420 alwaysAllow);
421 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700422 }
423
424 private String getFingerprints(String key) {
425 String hex = "0123456789ABCDEF";
426 StringBuilder sb = new StringBuilder();
427 MessageDigest digester;
428
Nick Kralevich451a6f82014-11-12 12:47:50 -0800429 if (key == null) {
430 return "";
431 }
432
Benoit Goby4e68bd42012-04-25 18:06:00 -0700433 try {
434 digester = MessageDigest.getInstance("MD5");
435 } catch (Exception ex) {
Nick Kralevich451a6f82014-11-12 12:47:50 -0800436 Slog.e(TAG, "Error getting digester", ex);
Benoit Goby4e68bd42012-04-25 18:06:00 -0700437 return "";
438 }
439
440 byte[] base64_data = key.split("\\s+")[0].getBytes();
Nick Kralevich451a6f82014-11-12 12:47:50 -0800441 byte[] digest;
442 try {
443 digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT));
444 } catch (IllegalArgumentException e) {
445 Slog.e(TAG, "error doing base64 decoding", e);
446 return "";
447 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700448 for (int i = 0; i < digest.length; i++) {
449 sb.append(hex.charAt((digest[i] >> 4) & 0xf));
450 sb.append(hex.charAt(digest[i] & 0xf));
Kenny Roote0e84082018-01-16 11:17:20 -0800451 if (i < digest.length - 1) {
Benoit Goby4e68bd42012-04-25 18:06:00 -0700452 sb.append(":");
Kenny Roote0e84082018-01-16 11:17:20 -0800453 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700454 }
455 return sb.toString();
456 }
457
Aaron Whyted1931ff2014-04-11 10:53:58 -0700458 private void startConfirmation(String key, String fingerprints) {
Xiaohui Chenffcfe342015-05-13 13:18:36 -0700459 int currentUserId = ActivityManager.getCurrentUser();
Xiaohui Chen05d58af2015-07-13 14:17:48 -0700460 UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
Xiaohui Chenffcfe342015-05-13 13:18:36 -0700461 String componentString;
Xiaohui Chen05d58af2015-07-13 14:17:48 -0700462 if (userInfo.isAdmin()) {
Michael Groover9e7b06e2018-12-28 19:55:51 -0800463 componentString = mConfirmComponent != null
464 ? mConfirmComponent : Resources.getSystem().getString(
Xiaohui Chenffcfe342015-05-13 13:18:36 -0700465 com.android.internal.R.string.config_customAdbPublicKeyConfirmationComponent);
466 } else {
Xiaohui Chen05d58af2015-07-13 14:17:48 -0700467 // If the current foreground user is not the admin user we send a different
Xiaohui Chenffcfe342015-05-13 13:18:36 -0700468 // notification specific to secondary users.
469 componentString = Resources.getSystem().getString(
470 R.string.config_customAdbPublicKeyConfirmationSecondaryUserComponent);
471 }
472 ComponentName componentName = ComponentName.unflattenFromString(componentString);
Xiaohui Chen05d58af2015-07-13 14:17:48 -0700473 if (startConfirmationActivity(componentName, userInfo.getUserHandle(), key, fingerprints)
474 || startConfirmationService(componentName, userInfo.getUserHandle(),
475 key, fingerprints)) {
Aaron Whyted1931ff2014-04-11 10:53:58 -0700476 return;
477 }
Xiaohui Chenffcfe342015-05-13 13:18:36 -0700478 Slog.e(TAG, "unable to start customAdbPublicKeyConfirmation[SecondaryUser]Component "
479 + componentString + " as an Activity or a Service");
Aaron Whyted1931ff2014-04-11 10:53:58 -0700480 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700481
Aaron Whyted1931ff2014-04-11 10:53:58 -0700482 /**
Philip P. Moltmannbee04d02017-06-26 15:32:00 -0700483 * @return true if the componentName led to an Activity that was started.
Aaron Whyted1931ff2014-04-11 10:53:58 -0700484 */
Xiaohui Chenffcfe342015-05-13 13:18:36 -0700485 private boolean startConfirmationActivity(ComponentName componentName, UserHandle userHandle,
486 String key, String fingerprints) {
Aaron Whyted1931ff2014-04-11 10:53:58 -0700487 PackageManager packageManager = mContext.getPackageManager();
488 Intent intent = createConfirmationIntent(componentName, key, fingerprints);
Aaron Whytef10d0392014-03-28 14:42:39 -0700489 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Aaron Whyted1931ff2014-04-11 10:53:58 -0700490 if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
491 try {
Xiaohui Chenffcfe342015-05-13 13:18:36 -0700492 mContext.startActivityAsUser(intent, userHandle);
Aaron Whyted1931ff2014-04-11 10:53:58 -0700493 return true;
494 } catch (ActivityNotFoundException e) {
495 Slog.e(TAG, "unable to start adb whitelist activity: " + componentName, e);
496 }
497 }
498 return false;
499 }
500
501 /**
Philip P. Moltmannbee04d02017-06-26 15:32:00 -0700502 * @return true if the componentName led to a Service that was started.
Aaron Whyted1931ff2014-04-11 10:53:58 -0700503 */
Xiaohui Chenffcfe342015-05-13 13:18:36 -0700504 private boolean startConfirmationService(ComponentName componentName, UserHandle userHandle,
505 String key, String fingerprints) {
Aaron Whyted1931ff2014-04-11 10:53:58 -0700506 Intent intent = createConfirmationIntent(componentName, key, fingerprints);
507 try {
Xiaohui Chenffcfe342015-05-13 13:18:36 -0700508 if (mContext.startServiceAsUser(intent, userHandle) != null) {
Aaron Whyted1931ff2014-04-11 10:53:58 -0700509 return true;
510 }
511 } catch (SecurityException e) {
512 Slog.e(TAG, "unable to start adb whitelist service: " + componentName, e);
513 }
514 return false;
515 }
516
517 private Intent createConfirmationIntent(ComponentName componentName, String key,
518 String fingerprints) {
519 Intent intent = new Intent();
520 intent.setClassName(componentName.getPackageName(), componentName.getClassName());
Aaron Whytef10d0392014-03-28 14:42:39 -0700521 intent.putExtra("key", key);
522 intent.putExtra("fingerprints", fingerprints);
Aaron Whyted1931ff2014-04-11 10:53:58 -0700523 return intent;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700524 }
525
Michael Groover9e7b06e2018-12-28 19:55:51 -0800526 /**
527 * Returns a new File with the specified name in the adb directory.
528 */
529 private File getAdbFile(String fileName) {
Benoit Goby4e68bd42012-04-25 18:06:00 -0700530 File dataDir = Environment.getDataDirectory();
531 File adbDir = new File(dataDir, ADB_DIRECTORY);
532
533 if (!adbDir.exists()) {
534 Slog.e(TAG, "ADB data directory does not exist");
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800535 return null;
Benoit Goby4e68bd42012-04-25 18:06:00 -0700536 }
537
Michael Groover9e7b06e2018-12-28 19:55:51 -0800538 return new File(adbDir, fileName);
539 }
540
William Hester99a7d632019-02-06 17:36:37 -0800541 File getAdbTempKeysFile() {
542 return getAdbFile(ADB_TEMP_KEYS_FILE);
543 }
544
545 File getUserKeyFile() {
Michael Groover9e7b06e2018-12-28 19:55:51 -0800546 return getAdbFile(ADB_KEYS_FILE);
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800547 }
548
549 private void writeKey(String key) {
Benoit Goby4e68bd42012-04-25 18:06:00 -0700550 try {
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800551 File keyFile = getUserKeyFile();
552
553 if (keyFile == null) {
554 return;
555 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700556
557 if (!keyFile.exists()) {
558 keyFile.createNewFile();
559 FileUtils.setPermissions(keyFile.toString(),
Kenny Roote0e84082018-01-16 11:17:20 -0800560 FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP, -1, -1);
Benoit Goby4e68bd42012-04-25 18:06:00 -0700561 }
562
563 FileOutputStream fo = new FileOutputStream(keyFile, true);
564 fo.write(key.getBytes());
565 fo.write('\n');
566 fo.close();
Kenny Roote0e84082018-01-16 11:17:20 -0800567 } catch (IOException ex) {
Benoit Goby4e68bd42012-04-25 18:06:00 -0700568 Slog.e(TAG, "Error writing key:" + ex);
569 }
570 }
571
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800572 private void deleteKeyFile() {
573 File keyFile = getUserKeyFile();
574 if (keyFile != null) {
575 keyFile.delete();
576 }
577 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700578
Kenny Roote0e84082018-01-16 11:17:20 -0800579 /**
580 * When {@code enabled} is {@code true}, this allows ADB debugging and starts the ADB hanler
581 * thread. When {@code enabled} is {@code false}, this disallows ADB debugging and shuts
582 * down the handler thread.
583 */
Benoit Goby4e68bd42012-04-25 18:06:00 -0700584 public void setAdbEnabled(boolean enabled) {
Kenny Root7c4e6612018-01-16 11:19:47 -0800585 mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MESSAGE_ADB_ENABLED
586 : AdbDebuggingHandler.MESSAGE_ADB_DISABLED);
Benoit Goby4e68bd42012-04-25 18:06:00 -0700587 }
588
Kenny Roote0e84082018-01-16 11:17:20 -0800589 /**
590 * Allows the debugging from the endpoint identified by {@code publicKey} either once or
591 * always if {@code alwaysAllow} is {@code true}.
592 */
Kenny Roota5964c02018-01-23 20:08:39 +0900593 public void allowDebugging(boolean alwaysAllow, String publicKey) {
Kenny Root7c4e6612018-01-16 11:19:47 -0800594 Message msg = mHandler.obtainMessage(AdbDebuggingHandler.MESSAGE_ADB_ALLOW);
Benoit Goby4e68bd42012-04-25 18:06:00 -0700595 msg.arg1 = alwaysAllow ? 1 : 0;
596 msg.obj = publicKey;
597 mHandler.sendMessage(msg);
598 }
599
Kenny Roote0e84082018-01-16 11:17:20 -0800600 /**
601 * Denies debugging connection from the device that last requested to connect.
602 */
Kenny Roota5964c02018-01-23 20:08:39 +0900603 public void denyDebugging() {
Kenny Root7c4e6612018-01-16 11:19:47 -0800604 mHandler.sendEmptyMessage(AdbDebuggingHandler.MESSAGE_ADB_DENY);
Benoit Goby4e68bd42012-04-25 18:06:00 -0700605 }
606
Kenny Roote0e84082018-01-16 11:17:20 -0800607 /**
608 * Clears all previously accepted ADB debugging public keys. Any subsequent request will need
609 * to pass through {@link #allowUsbDebugging(boolean, String)} again.
610 */
Kenny Roota5964c02018-01-23 20:08:39 +0900611 public void clearDebuggingKeys() {
Kenny Root7c4e6612018-01-16 11:19:47 -0800612 mHandler.sendEmptyMessage(AdbDebuggingHandler.MESSAGE_ADB_CLEAR);
Benoit Gobycd7a17c2012-12-21 16:44:50 -0800613 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700614
Philip P. Moltmann371a3b82018-01-26 13:00:22 -0800615 /**
Michael Groover9e7b06e2018-12-28 19:55:51 -0800616 * Sends a message to the handler to persist the key store.
617 */
618 private void sendPersistKeyStoreMessage() {
619 Message msg = mHandler.obtainMessage(AdbDebuggingHandler.MESSAGE_ADB_PERSIST_KEY_STORE);
620 mHandler.sendMessage(msg);
621 }
622
623 /**
624 * Schedules a job to update the connection time of the currently connected key. This is
625 * intended for cases such as development devices that are left connected to a user's
626 * system beyond the window within which a connection is allowed without user interaction.
627 * A job should be rescheduled daily so that if the device is rebooted while connected to
628 * the user's system the last time in the key store will show within 24 hours which should
629 * be within the allowed window.
630 */
631 private void scheduleJobToUpdateAdbKeyStore() {
632 Message message = mHandler.obtainMessage(
633 AdbDebuggingHandler.MESSAGE_ADB_UPDATE_KEY_CONNECTION_TIME);
634 mHandler.sendMessageDelayed(message, AdbDebuggingHandler.UPDATE_KEY_STORE_JOB_INTERVAL);
635 }
636
637 /**
638 * Cancels the scheduled job to update the connection time of the currently connected key.
639 * This should be invoked once the adb session is disconnected.
640 */
641 private void cancelJobToUpdateAdbKeyStore() {
642 mHandler.removeMessages(AdbDebuggingHandler.MESSAGE_ADB_UPDATE_KEY_CONNECTION_TIME);
643 }
644
645 /**
Philip P. Moltmann371a3b82018-01-26 13:00:22 -0800646 * Dump the USB debugging state.
647 */
648 public void dump(DualDumpOutputStream dump, String idName, long id) {
649 long token = dump.start(idName, id);
650
Kenny Rootf74bfde2018-01-18 15:42:48 -0800651 dump.write("connected_to_adb", AdbDebuggingManagerProto.CONNECTED_TO_ADB, mThread != null);
652 writeStringIfNotNull(dump, "last_key_received", AdbDebuggingManagerProto.LAST_KEY_RECEVIED,
Philip P. Moltmann371a3b82018-01-26 13:00:22 -0800653 mFingerprints);
654
Benoit Goby4e68bd42012-04-25 18:06:00 -0700655 try {
Kenny Rootf74bfde2018-01-18 15:42:48 -0800656 dump.write("user_keys", AdbDebuggingManagerProto.USER_KEYS,
Philip P. Moltmann371a3b82018-01-26 13:00:22 -0800657 FileUtils.readTextFile(new File("/data/misc/adb/adb_keys"), 0, null));
Benoit Goby4e68bd42012-04-25 18:06:00 -0700658 } catch (IOException e) {
Philip P. Moltmann371a3b82018-01-26 13:00:22 -0800659 Slog.e(TAG, "Cannot read user keys", e);
Benoit Goby4e68bd42012-04-25 18:06:00 -0700660 }
Philip P. Moltmann371a3b82018-01-26 13:00:22 -0800661
Benoit Goby4e68bd42012-04-25 18:06:00 -0700662 try {
Kenny Rootf74bfde2018-01-18 15:42:48 -0800663 dump.write("system_keys", AdbDebuggingManagerProto.SYSTEM_KEYS,
Philip P. Moltmann371a3b82018-01-26 13:00:22 -0800664 FileUtils.readTextFile(new File("/adb_keys"), 0, null));
Benoit Goby4e68bd42012-04-25 18:06:00 -0700665 } catch (IOException e) {
Philip P. Moltmann371a3b82018-01-26 13:00:22 -0800666 Slog.e(TAG, "Cannot read system keys", e);
Benoit Goby4e68bd42012-04-25 18:06:00 -0700667 }
Philip P. Moltmann371a3b82018-01-26 13:00:22 -0800668
669 dump.end(token);
Benoit Goby4e68bd42012-04-25 18:06:00 -0700670 }
Michael Groover9e7b06e2018-12-28 19:55:51 -0800671
672 /**
673 * Handles adb keys for which the user has granted the 'always allow' option. This class ensures
674 * these grants are revoked after a period of inactivity as specified in the
675 * ADB_ALLOWED_CONNECTION_TIME setting.
676 */
677 class AdbKeyStore {
678 private Map<String, Long> mKeyMap;
679 private File mKeyFile;
680 private AtomicFile mAtomicKeyFile;
Michael Groover9e7b06e2018-12-28 19:55:51 -0800681 private static final String XML_TAG_ADB_KEY = "adbKey";
682 private static final String XML_ATTRIBUTE_KEY = "key";
683 private static final String XML_ATTRIBUTE_LAST_CONNECTION = "lastConnection";
684
685 /**
686 * Value returned by {@code getLastConnectionTime} when there is no previously saved
687 * connection time for the specified key.
688 */
689 public static final long NO_PREVIOUS_CONNECTION = 0;
690
691 /**
692 * Constructor that uses the default location for the persistent adb key store.
693 */
694 AdbKeyStore() {
695 initKeyFile();
696 mKeyMap = getKeyMapFromFile();
697 }
698
699 /**
700 * Constructor that uses the specified file as the location for the persistent adb key
701 * store.
702 */
703 AdbKeyStore(File keyFile) {
704 mKeyFile = keyFile;
705 initKeyFile();
706 mKeyMap = getKeyMapFromFile();
707 }
708
709 /**
710 * Initializes the key file that will be used to persist the adb grants.
711 */
712 private void initKeyFile() {
713 if (mKeyFile == null) {
William Hester99a7d632019-02-06 17:36:37 -0800714 mKeyFile = getAdbTempKeysFile();
Michael Groover9e7b06e2018-12-28 19:55:51 -0800715 }
William Hester99a7d632019-02-06 17:36:37 -0800716 // getAdbTempKeysFile can return null if the adb file cannot be obtained
Michael Groover9e7b06e2018-12-28 19:55:51 -0800717 if (mKeyFile != null) {
718 mAtomicKeyFile = new AtomicFile(mKeyFile);
719 }
720 }
721
722 /**
723 * Returns the key map with the keys and last connection times from the key file.
724 */
725 private Map<String, Long> getKeyMapFromFile() {
726 Map<String, Long> keyMap = new HashMap<String, Long>();
727 // if the AtomicFile could not be instantiated before attempt again; if it still fails
728 // return an empty key map.
729 if (mAtomicKeyFile == null) {
730 initKeyFile();
731 if (mAtomicKeyFile == null) {
732 Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
733 return keyMap;
734 }
735 }
736 if (!mAtomicKeyFile.exists()) {
737 return keyMap;
738 }
739 try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
740 XmlPullParser parser = Xml.newPullParser();
741 parser.setInput(keyStream, StandardCharsets.UTF_8.name());
742 XmlUtils.beginDocument(parser, XML_TAG_ADB_KEY);
743 while (parser.next() != XmlPullParser.END_DOCUMENT) {
744 String tagName = parser.getName();
745 if (tagName == null) {
746 break;
747 } else if (!tagName.equals(XML_TAG_ADB_KEY)) {
748 XmlUtils.skipCurrentTag(parser);
749 continue;
750 }
751 String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
752 long connectionTime;
753 try {
754 connectionTime = Long.valueOf(
755 parser.getAttributeValue(null, XML_ATTRIBUTE_LAST_CONNECTION));
756 } catch (NumberFormatException e) {
757 Slog.e(TAG,
758 "Caught a NumberFormatException parsing the last connection time: "
759 + e);
760 XmlUtils.skipCurrentTag(parser);
761 continue;
762 }
763 keyMap.put(key, connectionTime);
764 }
765 } catch (IOException | XmlPullParserException e) {
766 Slog.e(TAG, "Caught an exception parsing the XML key file: ", e);
767 }
768 return keyMap;
769 }
770
771 /**
772 * Writes the key map to the key file.
773 */
774 public void persistKeyStore() {
775 // if there is nothing in the key map then ensure any keys left in the key store files
776 // are deleted as well.
William Hester99a7d632019-02-06 17:36:37 -0800777 filterOutOldKeys();
778 if (mKeyMap.isEmpty()) {
Michael Groover9e7b06e2018-12-28 19:55:51 -0800779 deleteKeyStore();
780 return;
781 }
782 if (mAtomicKeyFile == null) {
783 initKeyFile();
784 if (mAtomicKeyFile == null) {
785 Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for writing");
786 return;
787 }
788 }
789 FileOutputStream keyStream = null;
790 try {
791 XmlSerializer serializer = new FastXmlSerializer();
792 keyStream = mAtomicKeyFile.startWrite();
793 serializer.setOutput(keyStream, StandardCharsets.UTF_8.name());
794 serializer.startDocument(null, true);
William Hester99a7d632019-02-06 17:36:37 -0800795
796 for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
797 serializer.startTag(null, XML_TAG_ADB_KEY);
798 serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey());
799 serializer.attribute(null, XML_ATTRIBUTE_LAST_CONNECTION,
800 String.valueOf(keyEntry.getValue()));
801 serializer.endTag(null, XML_TAG_ADB_KEY);
Michael Groover9e7b06e2018-12-28 19:55:51 -0800802 }
William Hester99a7d632019-02-06 17:36:37 -0800803
Michael Groover9e7b06e2018-12-28 19:55:51 -0800804 serializer.endDocument();
805 mAtomicKeyFile.finishWrite(keyStream);
806 } catch (IOException e) {
807 Slog.e(TAG, "Caught an exception writing the key map: ", e);
808 mAtomicKeyFile.failWrite(keyStream);
809 }
810 }
811
William Hester99a7d632019-02-06 17:36:37 -0800812 private void filterOutOldKeys() {
813 long allowedTime = getAllowedConnectionTime();
814 long systemTime = System.currentTimeMillis();
815 Iterator<Map.Entry<String, Long>> keyMapIterator = mKeyMap.entrySet().iterator();
816 while (keyMapIterator.hasNext()) {
817 Map.Entry<String, Long> keyEntry = keyMapIterator.next();
818 long connectionTime = keyEntry.getValue();
819 if (allowedTime != 0 && systemTime > (connectionTime + allowedTime)) {
820 keyMapIterator.remove();
821 }
822 }
823 }
824
Michael Groover9e7b06e2018-12-28 19:55:51 -0800825 /**
826 * Removes all of the entries in the key map and deletes the key file.
827 */
828 public void deleteKeyStore() {
829 mKeyMap.clear();
830 if (mAtomicKeyFile == null) {
831 return;
832 }
833 mAtomicKeyFile.delete();
834 }
835
836 /**
837 * Returns the time of the last connection from the specified key, or {@code
838 * NO_PREVIOUS_CONNECTION} if the specified key does not have an active adb grant.
839 */
840 public long getLastConnectionTime(String key) {
841 return mKeyMap.getOrDefault(key, NO_PREVIOUS_CONNECTION);
842 }
843
844 /**
845 * Sets the time of the last connection for the specified key to the provided time.
846 */
847 public void setLastConnectionTime(String key, long connectionTime) {
848 // Do not set the connection time to a value that is earlier than what was previously
849 // stored as the last connection time.
850 if (mKeyMap.containsKey(key) && mKeyMap.get(key) >= connectionTime) {
851 return;
852 }
853 mKeyMap.put(key, connectionTime);
854 sendPersistKeyStoreMessage();
855 }
856
857 /**
858 * Returns whether the specified key should be authroized to connect without user
859 * interaction. This requires that the user previously connected this device and selected
860 * the option to 'Always allow', and the time since the last connection is within the
861 * allowed window.
862 */
863 public boolean isKeyAuthorized(String key) {
864 long lastConnectionTime = getLastConnectionTime(key);
865 if (lastConnectionTime == NO_PREVIOUS_CONNECTION) {
866 return false;
867 }
868 long allowedConnectionTime = getAllowedConnectionTime();
869 // if the allowed connection time is 0 then revert to the previous behavior of always
870 // allowing previously granted adb grants.
871 if (allowedConnectionTime == 0 || (System.currentTimeMillis() < (lastConnectionTime
872 + allowedConnectionTime))) {
873 return true;
874 } else {
875 // since this key is no longer auhorized remove it from the Map
876 removeKey(key);
877 return false;
878 }
879 }
880
881 /**
882 * Returns the connection time within which a connection from an allowed key is
883 * automatically allowed without user interaction.
884 */
885 public long getAllowedConnectionTime() {
886 return Settings.Global.getLong(mContext.getContentResolver(),
887 Settings.Global.ADB_ALLOWED_CONNECTION_TIME,
888 Settings.Global.DEFAULT_ADB_ALLOWED_CONNECTION_TIME);
889 }
890
891 /**
892 * Removes the specified key from the key store.
893 */
894 public void removeKey(String key) {
895 if (!mKeyMap.containsKey(key)) {
896 return;
897 }
898 mKeyMap.remove(key);
899 sendPersistKeyStoreMessage();
900 }
901 }
Benoit Goby4e68bd42012-04-25 18:06:00 -0700902}