• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.adb;
18 
19 import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
20 
21 import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
22 import static com.android.server.adb.AdbService.ADBD;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.app.ActivityManager;
27 import android.app.Notification;
28 import android.app.NotificationChannel;
29 import android.app.NotificationManager;
30 import android.content.ActivityNotFoundException;
31 import android.content.BroadcastReceiver;
32 import android.content.ComponentName;
33 import android.content.ContentResolver;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.IntentFilter;
37 import android.content.pm.PackageManager;
38 import android.content.pm.UserInfo;
39 import android.content.res.Resources;
40 import android.database.ContentObserver;
41 import android.debug.AdbManager;
42 import android.debug.AdbNotifications;
43 import android.debug.AdbProtoEnums;
44 import android.debug.AdbTransportType;
45 import android.debug.IAdbTransport;
46 import android.debug.PairDevice;
47 import android.net.ConnectivityManager;
48 import android.net.LocalSocket;
49 import android.net.LocalSocketAddress;
50 import android.net.NetworkInfo;
51 import android.net.Uri;
52 import android.net.nsd.NsdManager;
53 import android.net.nsd.NsdServiceInfo;
54 import android.net.wifi.WifiConfiguration;
55 import android.net.wifi.WifiInfo;
56 import android.net.wifi.WifiManager;
57 import android.os.Bundle;
58 import android.os.Environment;
59 import android.os.FileUtils;
60 import android.os.Handler;
61 import android.os.Looper;
62 import android.os.Message;
63 import android.os.SystemClock;
64 import android.os.SystemProperties;
65 import android.os.SystemService;
66 import android.os.UserHandle;
67 import android.os.UserManager;
68 import android.provider.Settings;
69 import android.service.adb.AdbDebuggingManagerProto;
70 import android.text.TextUtils;
71 import android.util.AtomicFile;
72 import android.util.Base64;
73 import android.util.Slog;
74 import android.util.Xml;
75 
76 import com.android.internal.R;
77 import com.android.internal.annotations.Keep;
78 import com.android.internal.annotations.VisibleForTesting;
79 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
80 import com.android.internal.util.FrameworkStatsLog;
81 import com.android.internal.util.XmlUtils;
82 import com.android.internal.util.dump.DualDumpOutputStream;
83 import com.android.modules.utils.TypedXmlPullParser;
84 import com.android.modules.utils.TypedXmlSerializer;
85 import com.android.server.FgThread;
86 
87 import org.xmlpull.v1.XmlPullParser;
88 import org.xmlpull.v1.XmlPullParserException;
89 
90 import java.io.BufferedReader;
91 import java.io.File;
92 import java.io.FileInputStream;
93 import java.io.FileOutputStream;
94 import java.io.FileReader;
95 import java.io.IOException;
96 import java.io.InputStream;
97 import java.io.OutputStream;
98 import java.security.MessageDigest;
99 import java.security.SecureRandom;
100 import java.util.AbstractMap;
101 import java.util.ArrayList;
102 import java.util.Arrays;
103 import java.util.HashMap;
104 import java.util.HashSet;
105 import java.util.Iterator;
106 import java.util.List;
107 import java.util.Map;
108 import java.util.Set;
109 import java.util.concurrent.TimeoutException;
110 import java.util.concurrent.atomic.AtomicBoolean;
111 
112 /**
113  * Provides communication to the Android Debug Bridge daemon to allow, deny, or clear public keys
114  * that are authorized to connect to the ADB service itself.
115  *
116  * <p>The AdbDebuggingManager controls two files:
117  * <ol>
118  *     <li>adb_keys
119  *     <li>adb_temp_keys.xml
120  * </ol>
121  *
122  * <p>The ADB Daemon (adbd) reads <em>only</em> the adb_keys file for authorization. Public keys
123  * from registered hosts are stored in adb_keys, one entry per line.
124  *
125  * <p>AdbDebuggingManager also keeps adb_temp_keys.xml, which is used for two things
126  * <ol>
127  *     <li>Removing unused keys from the adb_keys file
128  *     <li>Managing authorized WiFi access points for ADB over WiFi
129  * </ol>
130  */
131 public class AdbDebuggingManager {
132     private static final String TAG = AdbDebuggingManager.class.getSimpleName();
133 
134     private static final String ADBD_SOCKET = "adbd";
135     private static final String ADB_DIRECTORY = "misc/adb";
136     // This file contains keys that will always be allowed to connect to the device via adb.
137     private static final String ADB_KEYS_FILE = "adb_keys";
138     // This file contains keys that will be allowed to connect without user interaction as long
139     // as a subsequent connection occurs within the allowed duration.
140     private static final String ADB_TEMP_KEYS_FILE = "adb_temp_keys.xml";
141     private static final int BUFFER_SIZE = 65536;
142     private static final Ticker SYSTEM_TICKER = () -> System.currentTimeMillis();
143 
144     private final Context mContext;
145     private final ContentResolver mContentResolver;
146     @VisibleForTesting final AdbDebuggingHandler mHandler;
147     @Nullable private AdbDebuggingThread mThread;
148     private boolean mAdbUsbEnabled = false;
149     private boolean mAdbWifiEnabled = false;
150     private String mFingerprints;
151     // A key can be used more than once (e.g. USB, wifi), so need to keep a refcount
152     private final Map<String, Integer> mConnectedKeys = new HashMap<>();
153     private final String mConfirmComponent;
154     @Nullable private final File mUserKeyFile;
155     @Nullable private final File mTempKeysFile;
156 
157     private static final String WIFI_PERSISTENT_GUID =
158             "persist.adb.wifi.guid";
159     private static final int PAIRING_CODE_LENGTH = 6;
160     /**
161      * The maximum time to wait for the adbd service to change state when toggling.
162      */
163     private static final long ADBD_STATE_CHANGE_TIMEOUT = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
164     private PairingThread mPairingThread = null;
165     // A list of keys connected via wifi
166     private final Set<String> mWifiConnectedKeys = new HashSet<>();
167     // The current info of the adbwifi connection.
168     private AdbConnectionInfo mAdbConnectionInfo = new AdbConnectionInfo();
169     // Polls for a tls port property when adb wifi is enabled
170     private AdbConnectionPortPoller mConnectionPortPoller;
171     private final Ticker mTicker;
172 
AdbDebuggingManager(Context context)173     public AdbDebuggingManager(Context context) {
174         this(
175                 context,
176                 /* confirmComponent= */ null,
177                 getAdbFile(ADB_KEYS_FILE),
178                 getAdbFile(ADB_TEMP_KEYS_FILE),
179                 /* adbDebuggingThread= */ null,
180                 SYSTEM_TICKER);
181     }
182 
183     /**
184      * Constructor that accepts the component to be invoked to confirm if the user wants to allow
185      * an adb connection from the key.
186      */
187     @VisibleForTesting
AdbDebuggingManager( Context context, String confirmComponent, File testUserKeyFile, File tempKeysFile, AdbDebuggingThread adbDebuggingThread, Ticker ticker)188     AdbDebuggingManager(
189             Context context,
190             String confirmComponent,
191             File testUserKeyFile,
192             File tempKeysFile,
193             AdbDebuggingThread adbDebuggingThread,
194             Ticker ticker) {
195         mContext = context;
196         mContentResolver = mContext.getContentResolver();
197         mConfirmComponent = confirmComponent;
198         mUserKeyFile = testUserKeyFile;
199         mTempKeysFile = tempKeysFile;
200         mThread = adbDebuggingThread;
201         mTicker = ticker;
202         mHandler = new AdbDebuggingHandler(FgThread.get().getLooper(), mThread);
203     }
204 
sendBroadcastWithDebugPermission(@onNull Context context, @NonNull Intent intent, @NonNull UserHandle userHandle)205     static void sendBroadcastWithDebugPermission(@NonNull Context context, @NonNull Intent intent,
206             @NonNull UserHandle userHandle) {
207         context.sendBroadcastAsUser(intent, userHandle,
208                 android.Manifest.permission.MANAGE_DEBUGGING);
209     }
210 
211     class PairingThread extends Thread implements NsdManager.RegistrationListener {
212         private NsdManager mNsdManager;
213         @Keep private String mPublicKey;
214         private String mPairingCode;
215         private String mGuid;
216         private String mServiceName;
217         // From RFC6763 (https://tools.ietf.org/html/rfc6763#section-7.2),
218         // The rules for Service Names [RFC6335] state that they may be no more
219         // than fifteen characters long (not counting the mandatory underscore),
220         // consisting of only letters, digits, and hyphens, must begin and end
221         // with a letter or digit, must not contain consecutive hyphens, and
222         // must contain at least one letter.
223         @VisibleForTesting static final String SERVICE_PROTOCOL = "adb-tls-pairing";
224         private final String mServiceType = String.format("_%s._tcp.", SERVICE_PROTOCOL);
225         private int mPort;
226 
native_pairing_start(String guid, String password)227         private native int native_pairing_start(String guid, String password);
native_pairing_cancel()228         private native void native_pairing_cancel();
native_pairing_wait()229         private native boolean native_pairing_wait();
230 
PairingThread(String pairingCode, String serviceName)231         PairingThread(String pairingCode, String serviceName) {
232             super(TAG);
233             mPairingCode = pairingCode;
234             mGuid = SystemProperties.get(WIFI_PERSISTENT_GUID);
235             mServiceName = serviceName;
236             if (serviceName == null || serviceName.isEmpty()) {
237                 mServiceName = mGuid;
238             }
239             mPort = -1;
240             mNsdManager = (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
241         }
242 
243         @Override
run()244         public void run() {
245             // Register the mdns service
246             NsdServiceInfo serviceInfo = new NsdServiceInfo();
247             serviceInfo.setServiceName(mServiceName);
248             serviceInfo.setServiceType(mServiceType);
249             serviceInfo.setPort(mPort);
250             mNsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, this);
251 
252             // Send pairing port to UI
253             Message msg = mHandler.obtainMessage(
254                     AdbDebuggingHandler.MSG_RESPONSE_PAIRING_PORT);
255             msg.obj = mPort;
256             mHandler.sendMessage(msg);
257 
258             boolean paired = native_pairing_wait();
259             if (mPublicKey != null) {
260                 Slog.i(TAG, "Pairing succeeded key=" + mPublicKey);
261             } else {
262                 Slog.i(TAG, "Pairing failed");
263             }
264 
265             mNsdManager.unregisterService(this);
266 
267             Bundle bundle = new Bundle();
268             bundle.putString("publicKey", paired ? mPublicKey : null);
269             Message message = Message.obtain(mHandler,
270                                              AdbDebuggingHandler.MSG_RESPONSE_PAIRING_RESULT,
271                                              bundle);
272             mHandler.sendMessage(message);
273         }
274 
275         @Override
start()276         public void start() {
277             /*
278              * If a user is fast enough to click cancel, native_pairing_cancel can be invoked
279              * while native_pairing_start is running which run the destruction of the object
280              * while it is being constructed. Here we start the pairing server on foreground
281              * Thread so native_pairing_cancel can never be called concurrently. Then we let
282              * the pairing server run on a background Thread.
283              */
284             if (mGuid.isEmpty()) {
285                 Slog.e(TAG, "adbwifi guid was not set");
286                 return;
287             }
288             mPort = native_pairing_start(mGuid, mPairingCode);
289             if (mPort <= 0) {
290                 Slog.e(TAG, "Unable to start pairing server");
291                 return;
292             }
293 
294             super.start();
295         }
296 
cancelPairing()297         public void cancelPairing() {
298             native_pairing_cancel();
299         }
300 
301         @Override
onServiceRegistered(NsdServiceInfo serviceInfo)302         public void onServiceRegistered(NsdServiceInfo serviceInfo) {
303             Slog.i(TAG, "Registered pairing service: " + serviceInfo);
304         }
305 
306         @Override
onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode)307         public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
308             Slog.e(TAG, "Failed to register pairing service(err=" + errorCode
309                     + "): " + serviceInfo);
310             cancelPairing();
311         }
312 
313         @Override
onServiceUnregistered(NsdServiceInfo serviceInfo)314         public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
315             Slog.i(TAG, "Unregistered pairing service: " + serviceInfo);
316         }
317 
318         @Override
onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode)319         public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
320             Slog.w(TAG, "Failed to unregister pairing service(err=" + errorCode
321                     + "): " + serviceInfo);
322         }
323     }
324 
325     /**
326      * This class will poll for a period of time for adbd to write the port
327      * it connected to.
328      *
329      * TODO(joshuaduong): The port is being sent via system property because the adbd socket
330      * (AdbDebuggingManager) is not created when ro.adb.secure=0. Thus, we must communicate the
331      * port through different means. A better fix would be to always start AdbDebuggingManager, but
332      * it needs to adjust accordingly on whether ro.adb.secure is set.
333      */
334     private class AdbConnectionPortPoller extends Thread {
335         private final String mAdbPortProp = "service.adb.tls.port";
336         private final int mDurationSecs = 10;
337         private AtomicBoolean mCanceled = new AtomicBoolean(false);
338 
339         @Override
run()340         public void run() {
341             Slog.d(TAG, "Starting adb port property poller");
342             // Once adbwifi is enabled, we poll the service.adb.tls.port
343             // system property until we get the port, or -1 on failure.
344             // Let's also limit the polling to 10 seconds, just in case
345             // something went wrong.
346             for (int i = 0; i < mDurationSecs; ++i) {
347                 if (mCanceled.get()) {
348                     return;
349                 }
350 
351                 // If the property is set to -1, then that means adbd has failed
352                 // to start the server. Otherwise we should have a valid port.
353                 int port = SystemProperties.getInt(mAdbPortProp, Integer.MAX_VALUE);
354                 if (port == -1 || (port > 0 && port <= 65535)) {
355                     onPortReceived(port);
356                     return;
357                 }
358                 SystemClock.sleep(1000);
359             }
360             Slog.w(TAG, "Failed to receive adb connection port");
361             onPortReceived(-1);
362         }
363 
onPortReceived(int port)364         private void onPortReceived(int port) {
365             Slog.d(TAG, "Received tls port=" + port);
366             Message msg = mHandler.obtainMessage(port > 0
367                     ? AdbDebuggingHandler.MSG_SERVER_CONNECTED
368                     : AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
369             msg.obj = port;
370             mHandler.sendMessage(msg);
371         }
372 
cancelAndWait()373         public void cancelAndWait() {
374             mCanceled.set(true);
375             if (this.isAlive()) {
376                 try {
377                     this.join();
378                 } catch (InterruptedException e) {
379                 }
380             }
381         }
382     }
383 
384     @VisibleForTesting
385     static class AdbDebuggingThread extends Thread {
386         private boolean mStopped;
387         private LocalSocket mSocket;
388         private OutputStream mOutputStream;
389         private InputStream mInputStream;
390         private Handler mHandler;
391 
392         @VisibleForTesting
AdbDebuggingThread()393         AdbDebuggingThread() {
394             super(TAG);
395         }
396 
397         @VisibleForTesting
setHandler(Handler handler)398         void setHandler(Handler handler) {
399             mHandler = handler;
400         }
401 
402         @Override
run()403         public void run() {
404             Slog.d(TAG, "Entering thread");
405             while (true) {
406                 synchronized (this) {
407                     if (mStopped) {
408                         Slog.d(TAG, "Exiting thread");
409                         return;
410                     }
411                     try {
412                         openSocketLocked();
413                     } catch (Exception e) {
414                         /* Don't loop too fast if adbd dies, before init restarts it */
415                         SystemClock.sleep(1000);
416                     }
417                 }
418                 try {
419                     listenToSocket();
420                 } catch (Exception e) {
421                     /* Don't loop too fast if adbd dies, before init restarts it */
422                     SystemClock.sleep(1000);
423                 }
424             }
425         }
426 
openSocketLocked()427         private void openSocketLocked() throws IOException {
428             try {
429                 LocalSocketAddress address = new LocalSocketAddress(ADBD_SOCKET,
430                         LocalSocketAddress.Namespace.RESERVED);
431                 mInputStream = null;
432 
433                 Slog.d(TAG, "Creating socket");
434                 mSocket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET);
435                 mSocket.connect(address);
436 
437                 mOutputStream = mSocket.getOutputStream();
438                 mInputStream = mSocket.getInputStream();
439                 mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_ADBD_SOCKET_CONNECTED);
440             } catch (IOException ioe) {
441                 Slog.e(TAG, "Caught an exception opening the socket: " + ioe);
442                 closeSocketLocked();
443                 throw ioe;
444             }
445         }
446 
listenToSocket()447         private void listenToSocket() throws IOException {
448             try {
449                 byte[] buffer = new byte[BUFFER_SIZE];
450                 while (true) {
451                     int count = mInputStream.read(buffer);
452                     // if less than 2 bytes are read the if statements below will throw an
453                     // IndexOutOfBoundsException.
454                     if (count < 2) {
455                         Slog.w(TAG, "Read failed with count " + count);
456                         break;
457                     }
458 
459                     if (buffer[0] == 'P' && buffer[1] == 'K') {
460                         String key = new String(Arrays.copyOfRange(buffer, 2, count));
461                         Slog.d(TAG, "Received public key: " + key);
462                         Message msg = mHandler.obtainMessage(
463                                 AdbDebuggingHandler.MESSAGE_ADB_CONFIRM);
464                         msg.obj = key;
465                         mHandler.sendMessage(msg);
466                     } else if (buffer[0] == 'D' && buffer[1] == 'C') {
467                         String key = new String(Arrays.copyOfRange(buffer, 2, count));
468                         Slog.d(TAG, "Received disconnected message: " + key);
469                         Message msg = mHandler.obtainMessage(
470                                 AdbDebuggingHandler.MESSAGE_ADB_DISCONNECT);
471                         msg.obj = key;
472                         mHandler.sendMessage(msg);
473                     } else if (buffer[0] == 'C' && buffer[1] == 'K') {
474                         String key = new String(Arrays.copyOfRange(buffer, 2, count));
475                         Slog.d(TAG, "Received connected key message: " + key);
476                         Message msg = mHandler.obtainMessage(
477                                 AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY);
478                         msg.obj = key;
479                         mHandler.sendMessage(msg);
480                     } else if (buffer[0] == 'W' && buffer[1] == 'E') {
481                         // adbd_auth.h and AdbTransportType.aidl need to be kept in
482                         // sync.
483                         byte transportType = buffer[2];
484                         String key = new String(Arrays.copyOfRange(buffer, 3, count));
485                         if (transportType == AdbTransportType.USB) {
486                             Slog.d(TAG, "Received USB TLS connected key message: " + key);
487                             Message msg = mHandler.obtainMessage(
488                                     AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY);
489                             msg.obj = key;
490                             mHandler.sendMessage(msg);
491                         } else if (transportType == AdbTransportType.WIFI) {
492                             Slog.d(TAG, "Received WIFI TLS connected key message: " + key);
493                             Message msg = mHandler.obtainMessage(
494                                     AdbDebuggingHandler.MSG_WIFI_DEVICE_CONNECTED);
495                             msg.obj = key;
496                             mHandler.sendMessage(msg);
497                         } else {
498                             Slog.e(TAG, "Got unknown transport type from adbd (" + transportType
499                                     + ")");
500                         }
501                     } else if (buffer[0] == 'W' && buffer[1] == 'F') {
502                         byte transportType = buffer[2];
503                         String key = new String(Arrays.copyOfRange(buffer, 3, count));
504                         if (transportType == AdbTransportType.USB) {
505                             Slog.d(TAG, "Received USB TLS disconnect message: " + key);
506                             Message msg = mHandler.obtainMessage(
507                                     AdbDebuggingHandler.MESSAGE_ADB_DISCONNECT);
508                             msg.obj = key;
509                             mHandler.sendMessage(msg);
510                         } else if (transportType == AdbTransportType.WIFI) {
511                             Slog.d(TAG, "Received WIFI TLS disconnect key message: " + key);
512                             Message msg = mHandler.obtainMessage(
513                                     AdbDebuggingHandler.MSG_WIFI_DEVICE_DISCONNECTED);
514                             msg.obj = key;
515                             mHandler.sendMessage(msg);
516                         } else {
517                             Slog.e(TAG, "Got unknown transport type from adbd (" + transportType
518                                     + ")");
519                         }
520                     } else {
521                         Slog.e(TAG, "Wrong message: "
522                                 + (new String(Arrays.copyOfRange(buffer, 0, 2))));
523                         break;
524                     }
525                 }
526             } finally {
527                 synchronized (this) {
528                     closeSocketLocked();
529                 }
530             }
531         }
532 
closeSocketLocked()533         private void closeSocketLocked() {
534             Slog.d(TAG, "Closing socket");
535             try {
536                 if (mOutputStream != null) {
537                     mOutputStream.close();
538                     mOutputStream = null;
539                 }
540             } catch (IOException e) {
541                 Slog.e(TAG, "Failed closing output stream: " + e);
542             }
543 
544             try {
545                 if (mSocket != null) {
546                     mSocket.close();
547                     mSocket = null;
548                 }
549             } catch (IOException ex) {
550                 Slog.e(TAG, "Failed closing socket: " + ex);
551             }
552             mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_ADBD_SOCKET_DISCONNECTED);
553         }
554 
555         /** Call to stop listening on the socket and exit the thread. */
stopListening()556         void stopListening() {
557             synchronized (this) {
558                 mStopped = true;
559                 closeSocketLocked();
560             }
561         }
562 
sendResponse(String msg)563         void sendResponse(String msg) {
564             synchronized (this) {
565                 if (!mStopped && mOutputStream != null) {
566                     try {
567                         mOutputStream.write(msg.getBytes());
568                     } catch (IOException ex) {
569                         Slog.e(TAG, "Failed to write response:", ex);
570                     }
571                 }
572             }
573         }
574     }
575 
576     private static class AdbConnectionInfo {
577         private String mBssid;
578         private String mSsid;
579         private int mPort;
580 
AdbConnectionInfo()581         AdbConnectionInfo() {
582             mBssid = "";
583             mSsid = "";
584             mPort = -1;
585         }
586 
AdbConnectionInfo(String bssid, String ssid)587         AdbConnectionInfo(String bssid, String ssid) {
588             mBssid = bssid;
589             mSsid = ssid;
590         }
591 
AdbConnectionInfo(AdbConnectionInfo other)592         AdbConnectionInfo(AdbConnectionInfo other) {
593             mBssid = other.mBssid;
594             mSsid = other.mSsid;
595             mPort = other.mPort;
596         }
597 
getBSSID()598         public String getBSSID() {
599             return mBssid;
600         }
601 
getSSID()602         public String getSSID() {
603             return mSsid;
604         }
605 
getPort()606         public int getPort() {
607             return mPort;
608         }
609 
setPort(int port)610         public void setPort(int port) {
611             mPort = port;
612         }
613 
clear()614         public void clear() {
615             mBssid = "";
616             mSsid = "";
617             mPort = -1;
618         }
619     }
620 
setAdbConnectionInfo(AdbConnectionInfo info)621     private void setAdbConnectionInfo(AdbConnectionInfo info) {
622         synchronized (mAdbConnectionInfo) {
623             if (info == null) {
624                 mAdbConnectionInfo.clear();
625                 return;
626             }
627             mAdbConnectionInfo = info;
628         }
629     }
630 
getAdbConnectionInfo()631     private AdbConnectionInfo getAdbConnectionInfo() {
632         synchronized (mAdbConnectionInfo) {
633             return new AdbConnectionInfo(mAdbConnectionInfo);
634         }
635     }
636 
637     class AdbDebuggingHandler extends Handler {
638         private NotificationManager mNotificationManager;
639         private boolean mAdbNotificationShown;
640 
641         private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
642             @Override
643             public void onReceive(Context context, Intent intent) {
644                 String action = intent.getAction();
645                 // We only care about when wifi is disabled, and when there is a wifi network
646                 // change.
647                 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
648                     int state = intent.getIntExtra(
649                             WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
650                     if (state == WifiManager.WIFI_STATE_DISABLED) {
651                         Slog.i(TAG, "Wifi disabled. Disabling adbwifi.");
652                         Settings.Global.putInt(mContentResolver,
653                                 Settings.Global.ADB_WIFI_ENABLED, 0);
654                     }
655                 } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
656                     // We only care about wifi type connections
657                     NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
658                             WifiManager.EXTRA_NETWORK_INFO, android.net.NetworkInfo.class);
659                     if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
660                         // Check for network disconnect
661                         if (!networkInfo.isConnected()) {
662                             Slog.i(TAG, "Network disconnected. Disabling adbwifi.");
663                             Settings.Global.putInt(mContentResolver,
664                                     Settings.Global.ADB_WIFI_ENABLED, 0);
665                             return;
666                         }
667 
668                         WifiManager wifiManager =
669                                 (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
670                         WifiInfo wifiInfo = wifiManager.getConnectionInfo();
671                         if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
672                             Slog.i(TAG, "Not connected to any wireless network."
673                                     + " Not enabling adbwifi.");
674                             Settings.Global.putInt(mContentResolver,
675                                     Settings.Global.ADB_WIFI_ENABLED, 0);
676                             return;
677                         }
678 
679                         synchronized (mAdbConnectionInfo) {
680                             // Check for network change
681                             final String bssid = wifiInfo.getBSSID();
682                             if (TextUtils.isEmpty(bssid)) {
683                                 Slog.e(TAG,
684                                         "Unable to get the wifi ap's BSSID. Disabling adbwifi.");
685                                 Settings.Global.putInt(mContentResolver,
686                                         Settings.Global.ADB_WIFI_ENABLED, 0);
687                                 return;
688                             }
689                             if (!TextUtils.equals(bssid, mAdbConnectionInfo.getBSSID())) {
690                                 Slog.i(TAG, "Detected wifi network change. Disabling adbwifi.");
691                                 Settings.Global.putInt(mContentResolver,
692                                         Settings.Global.ADB_WIFI_ENABLED, 0);
693                             }
694                         }
695                     }
696                 }
697             }
698         };
699 
700         private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
701 
isTv()702         private boolean isTv() {
703             return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
704         }
705 
setupNotifications()706         private void setupNotifications() {
707             if (mNotificationManager != null) {
708                 return;
709             }
710             mNotificationManager = (NotificationManager)
711                     mContext.getSystemService(Context.NOTIFICATION_SERVICE);
712             if (mNotificationManager == null) {
713                 Slog.e(TAG, "Unable to setup notifications for wireless debugging");
714                 return;
715             }
716 
717             // Ensure that the notification channels are set up
718             if (isTv()) {
719                 // TV-specific notification channel
720                 mNotificationManager.createNotificationChannel(
721                         new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV,
722                                 mContext.getString(
723                                         com.android.internal.R.string
724                                                 .adb_debugging_notification_channel_tv),
725                                 NotificationManager.IMPORTANCE_HIGH));
726             }
727         }
728 
729         // The default time to schedule the job to keep the keystore updated with a currently
730         // connected key as well as to removed expired keys.
731         static final long UPDATE_KEYSTORE_JOB_INTERVAL = 86400000;
732         // The minimum interval at which the job should run to update the keystore. This is intended
733         // to prevent the job from running too often if the allowed connection time for adb grants
734         // is set to an extremely small value.
735         static final long UPDATE_KEYSTORE_MIN_JOB_INTERVAL = 60000;
736 
737         static final int MESSAGE_ADB_ENABLED = 1;
738         static final int MESSAGE_ADB_DISABLED = 2;
739         static final int MESSAGE_ADB_ALLOW = 3;
740         static final int MESSAGE_ADB_DENY = 4;
741         static final int MESSAGE_ADB_CONFIRM = 5;
742         static final int MESSAGE_ADB_CLEAR = 6;
743         static final int MESSAGE_ADB_DISCONNECT = 7;
744         static final int MESSAGE_ADB_PERSIST_KEYSTORE = 8;
745         static final int MESSAGE_ADB_UPDATE_KEYSTORE = 9;
746         static final int MESSAGE_ADB_CONNECTED_KEY = 10;
747 
748         // === Messages from the UI ==============
749         // UI asks adbd to enable adbdwifi
750         static final int MSG_ADBDWIFI_ENABLE = 11;
751         // UI asks adbd to disable adbdwifi
752         static final int MSG_ADBDWIFI_DISABLE = 12;
753         // Cancel pairing
754         static final int MSG_PAIRING_CANCEL = 14;
755         // Enable pairing by pairing code
756         static final int MSG_PAIR_PAIRING_CODE = 15;
757         // Enable pairing by QR code
758         static final int MSG_PAIR_QR_CODE = 16;
759         // UI asks to unpair (forget) a device.
760         static final int MSG_REQ_UNPAIR = 17;
761         // User allows debugging on the current network
762         static final int MSG_ADBWIFI_ALLOW = 18;
763         // User denies debugging on the current network
764         static final int MSG_ADBWIFI_DENY = 19;
765 
766         // === Messages from the PairingThread ===========
767         // Result of the pairing
768         static final int MSG_RESPONSE_PAIRING_RESULT = 20;
769         // The port opened for pairing
770         static final int MSG_RESPONSE_PAIRING_PORT = 21;
771 
772         // === Messages from adbd ================
773         // Notifies us a wifi device connected.
774         static final int MSG_WIFI_DEVICE_CONNECTED = 22;
775         // Notifies us a wifi device disconnected.
776         static final int MSG_WIFI_DEVICE_DISCONNECTED = 23;
777         // Notifies us the TLS server is connected and listening
778         static final int MSG_SERVER_CONNECTED = 24;
779         // Notifies us the TLS server is disconnected
780         static final int MSG_SERVER_DISCONNECTED = 25;
781         // Notification when adbd socket successfully connects.
782         static final int MSG_ADBD_SOCKET_CONNECTED = 26;
783         // Notification when adbd socket is disconnected.
784         static final int MSG_ADBD_SOCKET_DISCONNECTED = 27;
785 
786         // === Messages from other parts of the system
787         private static final int MESSAGE_KEY_FILES_UPDATED = 28;
788 
789         // === Messages we can send to adbd ===========
790         static final String MSG_DISCONNECT_DEVICE = "DD";
791 
792         @Nullable @VisibleForTesting AdbKeyStore mAdbKeyStore;
793 
794         // Usb, Wi-Fi transports can be enabled together or separately, so don't break the framework
795         // connection unless all transport types are disconnected.
796         private int mAdbEnabledRefCount = 0;
797 
798         private ContentObserver mAuthTimeObserver = new ContentObserver(this) {
799             @Override
800             public void onChange(boolean selfChange, Uri uri) {
801                 Slog.d(TAG, "Received notification that uri " + uri
802                         + " was modified; rescheduling keystore job");
803                 scheduleJobToUpdateAdbKeyStore();
804             }
805         };
806 
807         /** Constructor that accepts the AdbDebuggingThread to which responses should be sent. */
808         @VisibleForTesting
AdbDebuggingHandler(Looper looper, AdbDebuggingThread thread)809         AdbDebuggingHandler(Looper looper, AdbDebuggingThread thread) {
810             super(looper);
811             mThread = thread;
812         }
813 
814         /** Initialize the AdbKeyStore so tests can grab mAdbKeyStore immediately. */
815         @VisibleForTesting
initKeyStore()816         void initKeyStore() {
817             if (mAdbKeyStore == null) {
818                 mAdbKeyStore = new AdbKeyStore();
819             }
820         }
821 
822         // Show when at least one device is connected.
showAdbConnectedNotification(boolean show)823         public void showAdbConnectedNotification(boolean show) {
824             final int id = SystemMessage.NOTE_ADB_WIFI_ACTIVE;
825             if (show == mAdbNotificationShown) {
826                 return;
827             }
828             setupNotifications();
829             if (!mAdbNotificationShown) {
830                 Notification notification = AdbNotifications.createNotification(mContext,
831                         AdbTransportType.WIFI);
832                 mAdbNotificationShown = true;
833                 mNotificationManager.notifyAsUser(null, id, notification,
834                         UserHandle.ALL);
835             } else {
836                 mAdbNotificationShown = false;
837                 mNotificationManager.cancelAsUser(null, id, UserHandle.ALL);
838             }
839         }
840 
startAdbDebuggingThread()841         private void startAdbDebuggingThread() {
842             ++mAdbEnabledRefCount;
843             Slog.i(TAG, "startAdbDebuggingThread ref=" + mAdbEnabledRefCount);
844             if (mAdbEnabledRefCount > 1) {
845                 return;
846             }
847 
848             registerForAuthTimeChanges();
849             mThread = new AdbDebuggingThread();
850             mThread.setHandler(mHandler);
851             mThread.start();
852 
853             mAdbKeyStore.updateKeyStore();
854             scheduleJobToUpdateAdbKeyStore();
855         }
856 
stopAdbDebuggingThread()857         private void stopAdbDebuggingThread() {
858             --mAdbEnabledRefCount;
859             Slog.i(TAG, "stopAdbDebuggingThread ref=" + mAdbEnabledRefCount);
860             if (mAdbEnabledRefCount > 0) {
861                 return;
862             }
863 
864             if (mThread != null) {
865                 mThread.stopListening();
866                 mThread = null;
867             }
868 
869             if (!mConnectedKeys.isEmpty()) {
870                 for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
871                     mAdbKeyStore.setLastConnectionTime(entry.getKey(), mTicker.currentTimeMillis());
872                 }
873                 sendPersistKeyStoreMessage();
874                 mConnectedKeys.clear();
875                 mWifiConnectedKeys.clear();
876             }
877             scheduleJobToUpdateAdbKeyStore();
878         }
879 
handleMessage(Message msg)880         public void handleMessage(Message msg) {
881             initKeyStore();
882 
883             switch (msg.what) {
884                 case MESSAGE_ADB_ENABLED:
885                     if (mAdbUsbEnabled) {
886                         break;
887                     }
888                     startAdbDebuggingThread();
889                     mAdbUsbEnabled = true;
890                     break;
891 
892                 case MESSAGE_ADB_DISABLED:
893                     if (!mAdbUsbEnabled) {
894                         break;
895                     }
896                     stopAdbDebuggingThread();
897                     mAdbUsbEnabled = false;
898                     break;
899 
900                 case MESSAGE_ADB_ALLOW: {
901                     String key = (String) msg.obj;
902                     String fingerprints = getFingerprints(key);
903                     if (!fingerprints.equals(mFingerprints)) {
904                         Slog.e(TAG, "Fingerprints do not match. Got "
905                                 + fingerprints + ", expected " + mFingerprints);
906                         break;
907                     }
908 
909                     boolean alwaysAllow = msg.arg1 == 1;
910                     if (mThread != null) {
911                         mThread.sendResponse("OK");
912                         if (alwaysAllow) {
913                             if (!mConnectedKeys.containsKey(key)) {
914                                 mConnectedKeys.put(key, 1);
915                             }
916                             mAdbKeyStore.setLastConnectionTime(key, mTicker.currentTimeMillis());
917                             sendPersistKeyStoreMessage();
918                             scheduleJobToUpdateAdbKeyStore();
919                         }
920                         logAdbConnectionChanged(key, AdbProtoEnums.USER_ALLOWED, alwaysAllow);
921                     }
922                     break;
923                 }
924 
925                 case MESSAGE_ADB_DENY:
926                     if (mThread != null) {
927                         Slog.w(TAG, "Denying adb confirmation");
928                         mThread.sendResponse("NO");
929                         logAdbConnectionChanged(null, AdbProtoEnums.USER_DENIED, false);
930                     }
931                     break;
932 
933                 case MESSAGE_ADB_CONFIRM: {
934                     String key = (String) msg.obj;
935                     String fingerprints = getFingerprints(key);
936                     if ("".equals(fingerprints)) {
937                         if (mThread != null) {
938                             mThread.sendResponse("NO");
939                             logAdbConnectionChanged(key, AdbProtoEnums.DENIED_INVALID_KEY, false);
940                         }
941                         break;
942                     }
943                     logAdbConnectionChanged(key, AdbProtoEnums.AWAITING_USER_APPROVAL, false);
944                     mFingerprints = fingerprints;
945                     startConfirmationForKey(key, mFingerprints);
946                     break;
947                 }
948 
949                 case MESSAGE_ADB_CLEAR: {
950                     Slog.d(TAG, "Received a request to clear the adb authorizations");
951                     mConnectedKeys.clear();
952                     // If the key store has not yet been instantiated then do so now; this avoids
953                     // the unnecessary creation of the key store when adb is not enabled.
954                     initKeyStore();
955                     mWifiConnectedKeys.clear();
956                     mAdbKeyStore.deleteKeyStore();
957                     cancelJobToUpdateAdbKeyStore();
958                     // Disconnect all active sessions unless the user opted out through Settings.
959                     if (Settings.Global.getInt(mContentResolver,
960                             Settings.Global.ADB_DISCONNECT_SESSIONS_ON_REVOKE, 1) == 1) {
961                         // If adb is currently enabled, then toggle it off and back on to disconnect
962                         // any existing sessions.
963                         if (mAdbUsbEnabled) {
964                             try {
965                                 SystemService.stop(ADBD);
966                                 SystemService.waitForState(ADBD, SystemService.State.STOPPED,
967                                         ADBD_STATE_CHANGE_TIMEOUT);
968                                 SystemService.start(ADBD);
969                                 SystemService.waitForState(ADBD, SystemService.State.RUNNING,
970                                         ADBD_STATE_CHANGE_TIMEOUT);
971                             } catch (TimeoutException e) {
972                                 Slog.e(TAG, "Timeout occurred waiting for adbd to cycle: ", e);
973                                 // TODO(b/281758086): Display a dialog to the user to warn them
974                                 // of this state and direct them to manually toggle adb.
975                                 // If adbd fails to toggle within the timeout window, set adb to
976                                 // disabled to alert the user that further action is required if
977                                 // they want to continue using adb after revoking the grants.
978                                 Settings.Global.putInt(mContentResolver,
979                                         Settings.Global.ADB_ENABLED, 0);
980                             }
981                         }
982                     }
983                     break;
984                 }
985 
986                 case MESSAGE_ADB_DISCONNECT: {
987                     String key = (String) msg.obj;
988                     boolean alwaysAllow = false;
989                     if (key != null && key.length() > 0) {
990                         if (mConnectedKeys.containsKey(key)) {
991                             alwaysAllow = true;
992                             int refcount = mConnectedKeys.get(key) - 1;
993                             if (refcount == 0) {
994                                 mAdbKeyStore.setLastConnectionTime(
995                                         key, mTicker.currentTimeMillis());
996                                 sendPersistKeyStoreMessage();
997                                 scheduleJobToUpdateAdbKeyStore();
998                                 mConnectedKeys.remove(key);
999                             } else {
1000                                 mConnectedKeys.put(key, refcount);
1001                             }
1002                         }
1003                     } else {
1004                         Slog.w(TAG, "Received a disconnected key message with an empty key");
1005                     }
1006                     logAdbConnectionChanged(key, AdbProtoEnums.DISCONNECTED, alwaysAllow);
1007                     break;
1008                 }
1009 
1010                 case MESSAGE_ADB_PERSIST_KEYSTORE: {
1011                     if (mAdbKeyStore != null) {
1012                         mAdbKeyStore.persistKeyStore();
1013                     }
1014                     break;
1015                 }
1016 
1017                 case MESSAGE_ADB_UPDATE_KEYSTORE: {
1018                     if (!mConnectedKeys.isEmpty()) {
1019                         for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
1020                             mAdbKeyStore.setLastConnectionTime(entry.getKey(),
1021                                     mTicker.currentTimeMillis());
1022                         }
1023                         sendPersistKeyStoreMessage();
1024                         scheduleJobToUpdateAdbKeyStore();
1025                     } else if (!mAdbKeyStore.isEmpty()) {
1026                         mAdbKeyStore.updateKeyStore();
1027                         scheduleJobToUpdateAdbKeyStore();
1028                     }
1029                     break;
1030                 }
1031 
1032                 case MESSAGE_ADB_CONNECTED_KEY: {
1033                     String key = (String) msg.obj;
1034                     if (key == null || key.length() == 0) {
1035                         Slog.w(TAG, "Received a connected key message with an empty key");
1036                     } else {
1037                         if (!mConnectedKeys.containsKey(key)) {
1038                             mConnectedKeys.put(key, 1);
1039                         } else {
1040                             mConnectedKeys.put(key, mConnectedKeys.get(key) + 1);
1041                         }
1042                         mAdbKeyStore.setLastConnectionTime(key, mTicker.currentTimeMillis());
1043                         sendPersistKeyStoreMessage();
1044                         scheduleJobToUpdateAdbKeyStore();
1045                         logAdbConnectionChanged(key, AdbProtoEnums.AUTOMATICALLY_ALLOWED, true);
1046                     }
1047                     break;
1048                 }
1049                 case MSG_ADBDWIFI_ENABLE: {
1050                     if (mAdbWifiEnabled) {
1051                         break;
1052                     }
1053 
1054                     AdbConnectionInfo currentInfo = getCurrentWifiApInfo();
1055                     if (currentInfo == null) {
1056                         Settings.Global.putInt(mContentResolver,
1057                                 Settings.Global.ADB_WIFI_ENABLED, 0);
1058                         break;
1059                     }
1060 
1061                     if (!verifyWifiNetwork(currentInfo.getBSSID(),
1062                             currentInfo.getSSID())) {
1063                         // This means that the network is not in the list of trusted networks.
1064                         // We'll give user a prompt on whether to allow wireless debugging on
1065                         // the current wifi network.
1066                         Settings.Global.putInt(mContentResolver,
1067                                 Settings.Global.ADB_WIFI_ENABLED, 0);
1068                         break;
1069                     }
1070 
1071                     setAdbConnectionInfo(currentInfo);
1072                     IntentFilter intentFilter =
1073                             new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
1074                     intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
1075                     mContext.registerReceiver(mBroadcastReceiver, intentFilter);
1076 
1077                     SystemProperties.set(AdbService.WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
1078                     mConnectionPortPoller = new AdbDebuggingManager.AdbConnectionPortPoller();
1079                     mConnectionPortPoller.start();
1080 
1081                     startAdbDebuggingThread();
1082                     mAdbWifiEnabled = true;
1083 
1084                     Slog.i(TAG, "adb start wireless adb");
1085                     break;
1086                 }
1087                 case MSG_ADBDWIFI_DISABLE:
1088                     if (!mAdbWifiEnabled) {
1089                         break;
1090                     }
1091                     mAdbWifiEnabled = false;
1092                     setAdbConnectionInfo(null);
1093                     mContext.unregisterReceiver(mBroadcastReceiver);
1094 
1095                     onAdbdWifiServerDisconnected(-1);
1096                     stopAdbDebuggingThread();
1097                     break;
1098                 case MSG_ADBWIFI_ALLOW:
1099                     if (mAdbWifiEnabled) {
1100                         break;
1101                     }
1102                     String bssid = (String) msg.obj;
1103                     boolean alwaysAllow = msg.arg1 == 1;
1104                     if (alwaysAllow) {
1105                         mAdbKeyStore.addTrustedNetwork(bssid);
1106                     }
1107 
1108                     // Let's check again to make sure we didn't switch networks while verifying
1109                     // the wifi bssid.
1110                     AdbConnectionInfo newInfo = getCurrentWifiApInfo();
1111                     if (newInfo == null || !bssid.equals(newInfo.getBSSID())) {
1112                         break;
1113                     }
1114 
1115                     setAdbConnectionInfo(newInfo);
1116                     Settings.Global.putInt(mContentResolver,
1117                             Settings.Global.ADB_WIFI_ENABLED, 1);
1118                     IntentFilter intentFilter =
1119                             new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
1120                     intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
1121                     mContext.registerReceiver(mBroadcastReceiver, intentFilter);
1122 
1123                     SystemProperties.set(AdbService.WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
1124                     mConnectionPortPoller = new AdbDebuggingManager.AdbConnectionPortPoller();
1125                     mConnectionPortPoller.start();
1126 
1127                     startAdbDebuggingThread();
1128                     mAdbWifiEnabled = true;
1129 
1130                     Slog.i(TAG, "adb start wireless adb");
1131                     break;
1132                 case MSG_ADBWIFI_DENY:
1133                     Settings.Global.putInt(mContentResolver,
1134                             Settings.Global.ADB_WIFI_ENABLED, 0);
1135                     sendServerConnectionState(false, -1);
1136                     break;
1137                 case MSG_REQ_UNPAIR: {
1138                     String fingerprint = (String) msg.obj;
1139                     // Tell adbd to disconnect the device if connected.
1140                     String publicKey = mAdbKeyStore.findKeyFromFingerprint(fingerprint);
1141                     if (publicKey == null || publicKey.isEmpty()) {
1142                         Slog.e(TAG, "Not a known fingerprint [" + fingerprint + "]");
1143                         break;
1144                     }
1145                     String cmdStr = MSG_DISCONNECT_DEVICE + publicKey;
1146                     if (mThread != null) {
1147                         mThread.sendResponse(cmdStr);
1148                     }
1149                     mAdbKeyStore.removeKey(publicKey);
1150                     // Send the updated paired devices list to the UI.
1151                     sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
1152                     break;
1153                 }
1154                 case MSG_RESPONSE_PAIRING_RESULT: {
1155                     Bundle bundle = (Bundle) msg.obj;
1156                     String publicKey = bundle.getString("publicKey");
1157                     onPairingResult(publicKey);
1158                     // Send the updated paired devices list to the UI.
1159                     sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
1160                     break;
1161                 }
1162                 case MSG_RESPONSE_PAIRING_PORT: {
1163                     int port = (int) msg.obj;
1164                     sendPairingPortToUI(port);
1165                     break;
1166                 }
1167                 case MSG_PAIR_PAIRING_CODE: {
1168                     String pairingCode = createPairingCode(PAIRING_CODE_LENGTH);
1169                     updateUIPairCode(pairingCode);
1170                     mPairingThread = new PairingThread(pairingCode, null);
1171                     mPairingThread.start();
1172                     break;
1173                 }
1174                 case MSG_PAIR_QR_CODE: {
1175                     Bundle bundle = (Bundle) msg.obj;
1176                     String serviceName = bundle.getString("serviceName");
1177                     String password = bundle.getString("password");
1178                     mPairingThread = new PairingThread(password, serviceName);
1179                     mPairingThread.start();
1180                     break;
1181                 }
1182                 case MSG_PAIRING_CANCEL:
1183                     if (mPairingThread != null) {
1184                         mPairingThread.cancelPairing();
1185                         try {
1186                             mPairingThread.join();
1187                         } catch (InterruptedException e) {
1188                             Slog.w(TAG, "Error while waiting for pairing thread to quit.");
1189                             e.printStackTrace();
1190                         }
1191                         mPairingThread = null;
1192                     }
1193                     break;
1194                 case MSG_WIFI_DEVICE_CONNECTED: {
1195                     String key = (String) msg.obj;
1196                     if (mWifiConnectedKeys.add(key)) {
1197                         sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
1198                         showAdbConnectedNotification(true);
1199                     }
1200                     break;
1201                 }
1202                 case MSG_WIFI_DEVICE_DISCONNECTED: {
1203                     String key = (String) msg.obj;
1204                     if (mWifiConnectedKeys.remove(key)) {
1205                         sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
1206                         if (mWifiConnectedKeys.isEmpty()) {
1207                             showAdbConnectedNotification(false);
1208                         }
1209                     }
1210                     break;
1211                 }
1212                 case MSG_SERVER_CONNECTED: {
1213                     int port = (int) msg.obj;
1214                     onAdbdWifiServerConnected(port);
1215                     synchronized (mAdbConnectionInfo) {
1216                         mAdbConnectionInfo.setPort(port);
1217                     }
1218                     Settings.Global.putInt(mContentResolver,
1219                             Settings.Global.ADB_WIFI_ENABLED, 1);
1220                     break;
1221                 }
1222                 case MSG_SERVER_DISCONNECTED: {
1223                     if (!mAdbWifiEnabled) {
1224                         break;
1225                     }
1226                     int port = (int) msg.obj;
1227                     onAdbdWifiServerDisconnected(port);
1228                     Settings.Global.putInt(mContentResolver,
1229                             Settings.Global.ADB_WIFI_ENABLED, 0);
1230                     stopAdbDebuggingThread();
1231                     if (mConnectionPortPoller != null) {
1232                         mConnectionPortPoller.cancelAndWait();
1233                         mConnectionPortPoller = null;
1234                     }
1235                     break;
1236                 }
1237                 case MSG_ADBD_SOCKET_CONNECTED: {
1238                     Slog.d(TAG, "adbd socket connected");
1239                     if (mAdbWifiEnabled) {
1240                         // In scenarios where adbd is restarted, the tls port may change.
1241                         mConnectionPortPoller =
1242                                 new AdbDebuggingManager.AdbConnectionPortPoller();
1243                         mConnectionPortPoller.start();
1244                     }
1245                     break;
1246                 }
1247                 case MSG_ADBD_SOCKET_DISCONNECTED: {
1248                     Slog.d(TAG, "adbd socket disconnected");
1249                     if (mConnectionPortPoller != null) {
1250                         mConnectionPortPoller.cancelAndWait();
1251                         mConnectionPortPoller = null;
1252                     }
1253                     if (mAdbWifiEnabled) {
1254                         // In scenarios where adbd is restarted, the tls port may change.
1255                         onAdbdWifiServerDisconnected(-1);
1256                     }
1257                     break;
1258                 }
1259                 case MESSAGE_KEY_FILES_UPDATED: {
1260                     mAdbKeyStore.reloadKeyMap();
1261                     break;
1262                 }
1263             }
1264         }
1265 
registerForAuthTimeChanges()1266         void registerForAuthTimeChanges() {
1267             Uri uri = Settings.Global.getUriFor(Settings.Global.ADB_ALLOWED_CONNECTION_TIME);
1268             mContext.getContentResolver().registerContentObserver(uri, false, mAuthTimeObserver);
1269         }
1270 
logAdbConnectionChanged(String key, int state, boolean alwaysAllow)1271         private void logAdbConnectionChanged(String key, int state, boolean alwaysAllow) {
1272             long lastConnectionTime = mAdbKeyStore.getLastConnectionTime(key);
1273             long authWindow = mAdbKeyStore.getAllowedConnectionTime();
1274             Slog.d(TAG,
1275                     "Logging key " + key + ", state = " + state + ", alwaysAllow = " + alwaysAllow
1276                             + ", lastConnectionTime = " + lastConnectionTime + ", authWindow = "
1277                             + authWindow);
1278             FrameworkStatsLog.write(FrameworkStatsLog.ADB_CONNECTION_CHANGED, lastConnectionTime,
1279                     authWindow, state, alwaysAllow);
1280         }
1281 
1282 
1283         /**
1284          * Schedules a job to update the connection time of the currently connected key and filter
1285          * out any keys that are beyond their expiration time.
1286          *
1287          * @return the time in ms when the next job will run or -1 if the job should not be
1288          * scheduled to run.
1289          */
1290         @VisibleForTesting
scheduleJobToUpdateAdbKeyStore()1291         long scheduleJobToUpdateAdbKeyStore() {
1292             cancelJobToUpdateAdbKeyStore();
1293             long keyExpiration = mAdbKeyStore.getNextExpirationTime();
1294             // if the keyExpiration time is -1 then either the keys are set to never expire or
1295             // there are no keys in the keystore, just return for now as a new job will be
1296             // scheduled on the next connection or when the auth time changes.
1297             if (keyExpiration == -1) {
1298                 return -1;
1299             }
1300             long delay;
1301             // if the keyExpiration is 0 this indicates a key has already expired; schedule the job
1302             // to run now to ensure the key is removed immediately from adb_keys.
1303             if (keyExpiration == 0) {
1304                 delay = 0;
1305             } else {
1306                 // else the next job should be run either daily or when the next key is set to
1307                 // expire with a min job interval to ensure this job does not run too often if a
1308                 // small value is set for the key expiration.
1309                 delay = Math.max(Math.min(UPDATE_KEYSTORE_JOB_INTERVAL, keyExpiration),
1310                         UPDATE_KEYSTORE_MIN_JOB_INTERVAL);
1311             }
1312             Message message = obtainMessage(MESSAGE_ADB_UPDATE_KEYSTORE);
1313             sendMessageDelayed(message, delay);
1314             return delay;
1315         }
1316 
1317         /**
1318          * Cancels the scheduled job to update the connection time of the currently connected key
1319          * and to remove any expired keys.
1320          */
cancelJobToUpdateAdbKeyStore()1321         private void cancelJobToUpdateAdbKeyStore() {
1322             removeMessages(AdbDebuggingHandler.MESSAGE_ADB_UPDATE_KEYSTORE);
1323         }
1324 
1325         // Generates a random string of digits with size |size|.
createPairingCode(int size)1326         private String createPairingCode(int size) {
1327             String res = "";
1328             SecureRandom rand = new SecureRandom();
1329             for (int i = 0; i < size; ++i) {
1330                 res += rand.nextInt(10);
1331             }
1332 
1333             return res;
1334         }
1335 
sendServerConnectionState(boolean connected, int port)1336         private void sendServerConnectionState(boolean connected, int port) {
1337             Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
1338             intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, connected
1339                     ? AdbManager.WIRELESS_STATUS_CONNECTED
1340                     : AdbManager.WIRELESS_STATUS_DISCONNECTED);
1341             intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
1342             AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
1343         }
1344 
onAdbdWifiServerConnected(int port)1345         private void onAdbdWifiServerConnected(int port) {
1346             // Send the paired devices list to the UI
1347             sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
1348             sendServerConnectionState(true, port);
1349         }
1350 
onAdbdWifiServerDisconnected(int port)1351         private void onAdbdWifiServerDisconnected(int port) {
1352             // The TLS server disconnected while we had wireless debugging enabled.
1353             // Let's disable it.
1354             mWifiConnectedKeys.clear();
1355             showAdbConnectedNotification(false);
1356             sendServerConnectionState(false, port);
1357         }
1358 
1359         /**
1360          * Returns the [bssid, ssid] of the current access point.
1361          */
getCurrentWifiApInfo()1362         private AdbConnectionInfo getCurrentWifiApInfo() {
1363             WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
1364             WifiInfo wifiInfo = wifiManager.getConnectionInfo();
1365             if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
1366                 Slog.i(TAG, "Not connected to any wireless network. Not enabling adbwifi.");
1367                 return null;
1368             }
1369 
1370             String ssid = null;
1371             if (wifiInfo.isPasspointAp() || wifiInfo.isOsuAp()) {
1372                 ssid = wifiInfo.getPasspointProviderFriendlyName();
1373             } else {
1374                 ssid = wifiInfo.getSSID();
1375                 if (ssid == null || WifiManager.UNKNOWN_SSID.equals(ssid)) {
1376                     // OK, it's not in the connectionInfo; we have to go hunting for it
1377                     List<WifiConfiguration> networks = wifiManager.getConfiguredNetworks();
1378                     int length = networks.size();
1379                     for (int i = 0; i < length; i++) {
1380                         if (networks.get(i).networkId == wifiInfo.getNetworkId()) {
1381                             ssid = networks.get(i).SSID;
1382                         }
1383                     }
1384                     if (ssid == null) {
1385                         Slog.e(TAG, "Unable to get ssid of the wifi AP.");
1386                         return null;
1387                     }
1388                 }
1389             }
1390 
1391             String bssid = wifiInfo.getBSSID();
1392             if (TextUtils.isEmpty(bssid)) {
1393                 Slog.e(TAG, "Unable to get the wifi ap's BSSID.");
1394                 return null;
1395             }
1396             return new AdbConnectionInfo(bssid, ssid);
1397         }
1398 
verifyWifiNetwork(String bssid, String ssid)1399         private boolean verifyWifiNetwork(String bssid, String ssid) {
1400             // Check against a list of user-trusted networks.
1401             if (mAdbKeyStore.isTrustedNetwork(bssid)) {
1402                 return true;
1403             }
1404 
1405             // Ask user to confirm using wireless debugging on this network.
1406             startConfirmationForNetwork(ssid, bssid);
1407             return false;
1408         }
1409 
onPairingResult(String publicKey)1410         private void onPairingResult(String publicKey) {
1411             if (publicKey == null) {
1412                 Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
1413                 intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_FAIL);
1414                 AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent,
1415                         UserHandle.ALL);
1416             } else {
1417                 Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
1418                 intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
1419                         AdbManager.WIRELESS_STATUS_SUCCESS);
1420                 String fingerprints = getFingerprints(publicKey);
1421                 String hostname = "nouser@nohostname";
1422                 String[] args = publicKey.split("\\s+");
1423                 if (args.length > 1) {
1424                     hostname = args[1];
1425                 }
1426                 PairDevice device = new PairDevice();
1427                 device.name = fingerprints;
1428                 device.guid = hostname;
1429                 device.connected = false;
1430                 intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device);
1431                 AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent,
1432                         UserHandle.ALL);
1433                 // Add the key into the keystore
1434                 mAdbKeyStore.setLastConnectionTime(publicKey, mTicker.currentTimeMillis());
1435                 sendPersistKeyStoreMessage();
1436                 scheduleJobToUpdateAdbKeyStore();
1437             }
1438         }
1439 
sendPairingPortToUI(int port)1440         private void sendPairingPortToUI(int port) {
1441             Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
1442             intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
1443                     AdbManager.WIRELESS_STATUS_CONNECTED);
1444             intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
1445             AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
1446         }
1447 
sendPairedDevicesToUI(Map<String, PairDevice> devices)1448         private void sendPairedDevicesToUI(Map<String, PairDevice> devices) {
1449             Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
1450             // Map is not serializable, so need to downcast
1451             intent.putExtra(AdbManager.WIRELESS_DEVICES_EXTRA, (HashMap) devices);
1452             AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
1453         }
1454 
updateUIPairCode(String code)1455         private void updateUIPairCode(String code) {
1456             Slog.i(TAG, "updateUIPairCode: " + code);
1457 
1458             Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
1459             intent.putExtra(AdbManager.WIRELESS_PAIRING_CODE_EXTRA, code);
1460             intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
1461                     AdbManager.WIRELESS_STATUS_PAIRING_CODE);
1462             AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
1463         }
1464     }
1465 
getFingerprints(String key)1466     private String getFingerprints(String key) {
1467         String hex = "0123456789ABCDEF";
1468         StringBuilder sb = new StringBuilder();
1469         MessageDigest digester;
1470 
1471         if (key == null) {
1472             return "";
1473         }
1474 
1475         try {
1476             digester = MessageDigest.getInstance("MD5");
1477         } catch (Exception ex) {
1478             Slog.e(TAG, "Error getting digester", ex);
1479             return "";
1480         }
1481 
1482         byte[] base64_data = key.split("\\s+")[0].getBytes();
1483         byte[] digest;
1484         try {
1485             digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT));
1486         } catch (IllegalArgumentException e) {
1487             Slog.e(TAG, "error doing base64 decoding", e);
1488             return "";
1489         }
1490         for (int i = 0; i < digest.length; i++) {
1491             sb.append(hex.charAt((digest[i] >> 4) & 0xf));
1492             sb.append(hex.charAt(digest[i] & 0xf));
1493             if (i < digest.length - 1) {
1494                 sb.append(":");
1495             }
1496         }
1497         return sb.toString();
1498     }
1499 
startConfirmationForNetwork(String ssid, String bssid)1500     private void startConfirmationForNetwork(String ssid, String bssid) {
1501         List<Map.Entry<String, String>> extras = new ArrayList<Map.Entry<String, String>>();
1502         extras.add(new AbstractMap.SimpleEntry<String, String>("ssid", ssid));
1503         extras.add(new AbstractMap.SimpleEntry<String, String>("bssid", bssid));
1504         int currentUserId = ActivityManager.getCurrentUser();
1505         String componentString =
1506                 Resources.getSystem().getString(
1507                         R.string.config_customAdbWifiNetworkConfirmationComponent);
1508         ComponentName componentName = ComponentName.unflattenFromString(componentString);
1509         UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
1510         if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras)
1511                 || startConfirmationService(componentName, userInfo.getUserHandle(), extras)) {
1512             return;
1513         }
1514         Slog.e(TAG, "Unable to start customAdbWifiNetworkConfirmation[SecondaryUser]Component "
1515                 + componentString + " as an Activity or a Service");
1516     }
1517 
startConfirmationForKey(String key, String fingerprints)1518     private void startConfirmationForKey(String key, String fingerprints) {
1519         List<Map.Entry<String, String>> extras = new ArrayList<Map.Entry<String, String>>();
1520         extras.add(new AbstractMap.SimpleEntry<String, String>("key", key));
1521         extras.add(new AbstractMap.SimpleEntry<String, String>("fingerprints", fingerprints));
1522         int currentUserId = ActivityManager.getCurrentUser();
1523         UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
1524         String componentString;
1525         if (userInfo.isAdmin()) {
1526             componentString = mConfirmComponent != null
1527                     ? mConfirmComponent : Resources.getSystem().getString(
1528                     com.android.internal.R.string.config_customAdbPublicKeyConfirmationComponent);
1529         } else {
1530             // If the current foreground user is not the admin user we send a different
1531             // notification specific to secondary users.
1532             componentString = Resources.getSystem().getString(
1533                     R.string.config_customAdbPublicKeyConfirmationSecondaryUserComponent);
1534         }
1535         ComponentName componentName = ComponentName.unflattenFromString(componentString);
1536         if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras)
1537                 || startConfirmationService(componentName, userInfo.getUserHandle(),
1538                         extras)) {
1539             return;
1540         }
1541         Slog.e(TAG, "unable to start customAdbPublicKeyConfirmation[SecondaryUser]Component "
1542                 + componentString + " as an Activity or a Service");
1543     }
1544 
1545     /**
1546      * @return true if the componentName led to an Activity that was started.
1547      */
startConfirmationActivity(ComponentName componentName, UserHandle userHandle, List<Map.Entry<String, String>> extras)1548     private boolean startConfirmationActivity(ComponentName componentName, UserHandle userHandle,
1549             List<Map.Entry<String, String>> extras) {
1550         PackageManager packageManager = mContext.getPackageManager();
1551         Intent intent = createConfirmationIntent(componentName, extras);
1552         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1553         if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
1554             try {
1555                 mContext.startActivityAsUser(intent, userHandle);
1556                 return true;
1557             } catch (ActivityNotFoundException e) {
1558                 Slog.e(TAG, "unable to start adb whitelist activity: " + componentName, e);
1559             }
1560         }
1561         return false;
1562     }
1563 
1564     /**
1565      * @return true if the componentName led to a Service that was started.
1566      */
startConfirmationService(ComponentName componentName, UserHandle userHandle, List<Map.Entry<String, String>> extras)1567     private boolean startConfirmationService(ComponentName componentName, UserHandle userHandle,
1568             List<Map.Entry<String, String>> extras) {
1569         Intent intent = createConfirmationIntent(componentName, extras);
1570         try {
1571             if (mContext.startServiceAsUser(intent, userHandle) != null) {
1572                 return true;
1573             }
1574         } catch (SecurityException e) {
1575             Slog.e(TAG, "unable to start adb whitelist service: " + componentName, e);
1576         }
1577         return false;
1578     }
1579 
createConfirmationIntent(ComponentName componentName, List<Map.Entry<String, String>> extras)1580     private Intent createConfirmationIntent(ComponentName componentName,
1581             List<Map.Entry<String, String>> extras) {
1582         Intent intent = new Intent();
1583         intent.setClassName(componentName.getPackageName(), componentName.getClassName());
1584         for (Map.Entry<String, String> entry : extras) {
1585             intent.putExtra(entry.getKey(), entry.getValue());
1586         }
1587         return intent;
1588     }
1589 
1590     /**
1591      * Returns a new File with the specified name in the adb directory.
1592      */
getAdbFile(String fileName)1593     private static File getAdbFile(String fileName) {
1594         File dataDir = Environment.getDataDirectory();
1595         File adbDir = new File(dataDir, ADB_DIRECTORY);
1596 
1597         if (!adbDir.exists()) {
1598             Slog.e(TAG, "ADB data directory does not exist");
1599             return null;
1600         }
1601 
1602         return new File(adbDir, fileName);
1603     }
1604 
getAdbTempKeysFile()1605     File getAdbTempKeysFile() {
1606         return mTempKeysFile;
1607     }
1608 
getUserKeyFile()1609     File getUserKeyFile() {
1610         return mUserKeyFile;
1611     }
1612 
writeKeys(Iterable<String> keys)1613     private void writeKeys(Iterable<String> keys) {
1614         if (mUserKeyFile == null) {
1615             return;
1616         }
1617 
1618         AtomicFile atomicKeyFile = new AtomicFile(mUserKeyFile);
1619         // Note: Do not use a try-with-resources with the FileOutputStream, because AtomicFile
1620         // requires that it's cleaned up with AtomicFile.failWrite();
1621         FileOutputStream fo = null;
1622         try {
1623             fo = atomicKeyFile.startWrite();
1624             for (String key : keys) {
1625                 fo.write(key.getBytes());
1626                 fo.write('\n');
1627             }
1628             atomicKeyFile.finishWrite(fo);
1629         } catch (IOException ex) {
1630             Slog.e(TAG, "Error writing keys: " + ex);
1631             atomicKeyFile.failWrite(fo);
1632             return;
1633         }
1634 
1635         FileUtils.setPermissions(
1636                 mUserKeyFile.toString(),
1637                 FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP, -1, -1);
1638     }
1639 
1640     /**
1641      * When {@code enabled} is {@code true}, this allows ADB debugging and starts the ADB handler
1642      * thread. When {@code enabled} is {@code false}, this disallows ADB debugging for the given
1643      * @{code transportType}. See {@link IAdbTransport} for all available transport types.
1644      * If all transport types are disabled, the ADB handler thread will shut down.
1645      */
setAdbEnabled(boolean enabled, byte transportType)1646     public void setAdbEnabled(boolean enabled, byte transportType) {
1647         if (transportType == AdbTransportType.USB) {
1648             mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MESSAGE_ADB_ENABLED
1649                                               : AdbDebuggingHandler.MESSAGE_ADB_DISABLED);
1650         } else if (transportType == AdbTransportType.WIFI) {
1651             mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MSG_ADBDWIFI_ENABLE
1652                                               : AdbDebuggingHandler.MSG_ADBDWIFI_DISABLE);
1653         } else {
1654             throw new IllegalArgumentException(
1655                     "setAdbEnabled called with unimplemented transport type=" + transportType);
1656         }
1657     }
1658 
1659     /**
1660      * Allows the debugging from the endpoint identified by {@code publicKey} either once or
1661      * always if {@code alwaysAllow} is {@code true}.
1662      */
allowDebugging(boolean alwaysAllow, String publicKey)1663     public void allowDebugging(boolean alwaysAllow, String publicKey) {
1664         Message msg = mHandler.obtainMessage(AdbDebuggingHandler.MESSAGE_ADB_ALLOW);
1665         msg.arg1 = alwaysAllow ? 1 : 0;
1666         msg.obj = publicKey;
1667         mHandler.sendMessage(msg);
1668     }
1669 
1670     /**
1671      * Denies debugging connection from the device that last requested to connect.
1672      */
denyDebugging()1673     public void denyDebugging() {
1674         mHandler.sendEmptyMessage(AdbDebuggingHandler.MESSAGE_ADB_DENY);
1675     }
1676 
1677     /**
1678      * Clears all previously accepted ADB debugging public keys. Any subsequent request will need
1679      * to pass through {@link #allowUsbDebugging(boolean, String)} again.
1680      */
clearDebuggingKeys()1681     public void clearDebuggingKeys() {
1682         mHandler.sendEmptyMessage(AdbDebuggingHandler.MESSAGE_ADB_CLEAR);
1683     }
1684 
1685     /**
1686      * Allows wireless debugging on the network identified by {@code bssid} either once
1687      * or always if {@code alwaysAllow} is {@code true}.
1688      */
allowWirelessDebugging(boolean alwaysAllow, String bssid)1689     public void allowWirelessDebugging(boolean alwaysAllow, String bssid) {
1690         Message msg = mHandler.obtainMessage(AdbDebuggingHandler.MSG_ADBWIFI_ALLOW);
1691         msg.arg1 = alwaysAllow ? 1 : 0;
1692         msg.obj = bssid;
1693         mHandler.sendMessage(msg);
1694     }
1695 
1696     /**
1697      * Denies wireless debugging connection on the last requested network.
1698      */
denyWirelessDebugging()1699     public void denyWirelessDebugging() {
1700         mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_ADBWIFI_DENY);
1701     }
1702 
1703     /**
1704      * Returns the port adbwifi is currently opened on.
1705      */
getAdbWirelessPort()1706     public int getAdbWirelessPort() {
1707         AdbConnectionInfo info = getAdbConnectionInfo();
1708         if (info == null) {
1709             return 0;
1710         }
1711         return info.getPort();
1712     }
1713 
1714     /**
1715      * Returns the list of paired devices.
1716      */
getPairedDevices()1717     public Map<String, PairDevice> getPairedDevices() {
1718         AdbKeyStore keystore = new AdbKeyStore();
1719         return keystore.getPairedDevices();
1720     }
1721 
1722     /**
1723      * Unpair with device
1724      */
unpairDevice(String fingerprint)1725     public void unpairDevice(String fingerprint) {
1726         Message message = Message.obtain(mHandler,
1727                                          AdbDebuggingHandler.MSG_REQ_UNPAIR,
1728                                          fingerprint);
1729         mHandler.sendMessage(message);
1730     }
1731 
1732     /**
1733      * Enable pairing by pairing code
1734      */
enablePairingByPairingCode()1735     public void enablePairingByPairingCode() {
1736         mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_PAIR_PAIRING_CODE);
1737     }
1738 
1739     /**
1740      * Enable pairing by pairing code
1741      */
enablePairingByQrCode(String serviceName, String password)1742     public void enablePairingByQrCode(String serviceName, String password) {
1743         Bundle bundle = new Bundle();
1744         bundle.putString("serviceName", serviceName);
1745         bundle.putString("password", password);
1746         Message message = Message.obtain(mHandler,
1747                                          AdbDebuggingHandler.MSG_PAIR_QR_CODE,
1748                                          bundle);
1749         mHandler.sendMessage(message);
1750     }
1751 
1752     /**
1753      * Disables pairing
1754      */
disablePairing()1755     public void disablePairing() {
1756         mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_PAIRING_CANCEL);
1757     }
1758 
1759     /**
1760      * Status enabled/disabled check
1761      */
isAdbWifiEnabled()1762     public boolean isAdbWifiEnabled() {
1763         return mAdbWifiEnabled;
1764     }
1765 
1766     /**
1767      * Notify that they key files were updated so the AdbKeyManager reloads the keys.
1768      */
notifyKeyFilesUpdated()1769     public void notifyKeyFilesUpdated() {
1770         mHandler.sendEmptyMessage(AdbDebuggingHandler.MESSAGE_KEY_FILES_UPDATED);
1771     }
1772 
1773     /**
1774      * Sends a message to the handler to persist the keystore.
1775      */
sendPersistKeyStoreMessage()1776     private void sendPersistKeyStoreMessage() {
1777         Message msg = mHandler.obtainMessage(AdbDebuggingHandler.MESSAGE_ADB_PERSIST_KEYSTORE);
1778         mHandler.sendMessage(msg);
1779     }
1780 
1781     /**
1782      * Dump the USB debugging state.
1783      */
dump(DualDumpOutputStream dump, String idName, long id)1784     public void dump(DualDumpOutputStream dump, String idName, long id) {
1785         long token = dump.start(idName, id);
1786 
1787         dump.write("connected_to_adb", AdbDebuggingManagerProto.CONNECTED_TO_ADB, mThread != null);
1788         writeStringIfNotNull(dump, "last_key_received", AdbDebuggingManagerProto.LAST_KEY_RECEVIED,
1789                 mFingerprints);
1790 
1791         try {
1792             File userKeys = new File("/data/misc/adb/adb_keys");
1793             if (userKeys.exists()) {
1794                 dump.write("user_keys", AdbDebuggingManagerProto.USER_KEYS,
1795                            FileUtils.readTextFile(userKeys, 0, null));
1796             } else {
1797                 Slog.i(TAG, "No user keys on this device");
1798             }
1799         } catch (IOException e) {
1800             Slog.i(TAG, "Cannot read user keys", e);
1801         }
1802 
1803         try {
1804             dump.write("system_keys", AdbDebuggingManagerProto.SYSTEM_KEYS,
1805                     FileUtils.readTextFile(new File("/adb_keys"), 0, null));
1806         } catch (IOException e) {
1807             Slog.i(TAG, "Cannot read system keys", e);
1808         }
1809 
1810         try {
1811             dump.write("keystore", AdbDebuggingManagerProto.KEYSTORE,
1812                     FileUtils.readTextFile(mTempKeysFile, 0, null));
1813         } catch (IOException e) {
1814             Slog.i(TAG, "Cannot read keystore: ", e);
1815         }
1816 
1817         dump.end(token);
1818     }
1819 
1820     /**
1821      * Handles adb keys for which the user has granted the 'always allow' option. This class ensures
1822      * these grants are revoked after a period of inactivity as specified in the
1823      * ADB_ALLOWED_CONNECTION_TIME setting.
1824      */
1825     class AdbKeyStore {
1826         private AtomicFile mAtomicKeyFile;
1827 
1828         private final Set<String> mSystemKeys;
1829         private final Map<String, Long> mKeyMap = new HashMap<>();
1830         private final List<String> mTrustedNetworks = new ArrayList<>();
1831 
1832         private static final int KEYSTORE_VERSION = 1;
1833         private static final int MAX_SUPPORTED_KEYSTORE_VERSION = 1;
1834         private static final String XML_KEYSTORE_START_TAG = "keyStore";
1835         private static final String XML_ATTRIBUTE_VERSION = "version";
1836         private static final String XML_TAG_ADB_KEY = "adbKey";
1837         private static final String XML_ATTRIBUTE_KEY = "key";
1838         private static final String XML_ATTRIBUTE_LAST_CONNECTION = "lastConnection";
1839         // A list of trusted networks a device can always wirelessly debug on (always allow).
1840         // TODO: Move trusted networks list into a different file?
1841         private static final String XML_TAG_WIFI_ACCESS_POINT = "wifiAP";
1842         private static final String XML_ATTRIBUTE_WIFI_BSSID = "bssid";
1843 
1844         private static final String SYSTEM_KEY_FILE = "/adb_keys";
1845 
1846         /**
1847          * Value returned by {@code getLastConnectionTime} when there is no previously saved
1848          * connection time for the specified key.
1849          */
1850         public static final long NO_PREVIOUS_CONNECTION = 0;
1851 
1852         /**
1853          * Create an AdbKeyStore instance.
1854          *
1855          * <p>Upon creation, we parse {@link #mTempKeysFile} to determine authorized WiFi APs and
1856          * retrieve the map of stored ADB keys and their last connected times. After that, we read
1857          * the {@link #mUserKeyFile}, and any keys that exist in that file that do not exist in the
1858          * map are added to the map (for backwards compatibility).
1859          */
AdbKeyStore()1860         AdbKeyStore() {
1861             initKeyFile();
1862             readTempKeysFile();
1863             mSystemKeys = getSystemKeysFromFile(SYSTEM_KEY_FILE);
1864             addExistingUserKeysToKeyStore();
1865         }
1866 
reloadKeyMap()1867         public void reloadKeyMap() {
1868             readTempKeysFile();
1869         }
1870 
addTrustedNetwork(String bssid)1871         public void addTrustedNetwork(String bssid) {
1872             mTrustedNetworks.add(bssid);
1873             sendPersistKeyStoreMessage();
1874         }
1875 
getPairedDevices()1876         public Map<String, PairDevice> getPairedDevices() {
1877             Map<String, PairDevice> pairedDevices = new HashMap<String, PairDevice>();
1878             for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
1879                 String fingerprints = getFingerprints(keyEntry.getKey());
1880                 String hostname = "nouser@nohostname";
1881                 String[] args = keyEntry.getKey().split("\\s+");
1882                 if (args.length > 1) {
1883                     hostname = args[1];
1884                 }
1885                 PairDevice pairDevice = new PairDevice();
1886                 pairDevice.name = hostname;
1887                 pairDevice.guid = fingerprints;
1888                 pairDevice.connected = mWifiConnectedKeys.contains(keyEntry.getKey());
1889                 pairedDevices.put(keyEntry.getKey(), pairDevice);
1890             }
1891             return pairedDevices;
1892         }
1893 
findKeyFromFingerprint(String fingerprint)1894         public String findKeyFromFingerprint(String fingerprint) {
1895             for (Map.Entry<String, Long> entry : mKeyMap.entrySet()) {
1896                 String f = getFingerprints(entry.getKey());
1897                 if (fingerprint.equals(f)) {
1898                     return entry.getKey();
1899                 }
1900             }
1901             return null;
1902         }
1903 
removeKey(String key)1904         public void removeKey(String key) {
1905             if (mKeyMap.containsKey(key)) {
1906                 mKeyMap.remove(key);
1907                 sendPersistKeyStoreMessage();
1908             }
1909         }
1910 
1911         /**
1912          * Initializes the key file that will be used to persist the adb grants.
1913          */
initKeyFile()1914         private void initKeyFile() {
1915             // mTempKeysFile can be null if the adb file cannot be obtained
1916             if (mTempKeysFile != null) {
1917                 mAtomicKeyFile = new AtomicFile(mTempKeysFile);
1918             }
1919         }
1920 
getSystemKeysFromFile(String fileName)1921         private Set<String> getSystemKeysFromFile(String fileName) {
1922             Set<String> systemKeys = new HashSet<>();
1923             File systemKeyFile = new File(fileName);
1924             if (systemKeyFile.exists()) {
1925                 try (BufferedReader in = new BufferedReader(new FileReader(systemKeyFile))) {
1926                     String key;
1927                     while ((key = in.readLine()) != null) {
1928                         key = key.trim();
1929                         if (key.length() > 0) {
1930                             systemKeys.add(key);
1931                         }
1932                     }
1933                 } catch (IOException e) {
1934                     Slog.e(TAG, "Caught an exception reading " + fileName + ": " + e);
1935                 }
1936             }
1937             return systemKeys;
1938         }
1939 
1940         /**
1941          * Returns whether there are any 'always allowed' keys in the keystore.
1942          */
isEmpty()1943         public boolean isEmpty() {
1944             return mKeyMap.isEmpty();
1945         }
1946 
1947         /**
1948          * Iterates through the keys in the keystore and removes any that are beyond the window
1949          * within which connections are automatically allowed without user interaction.
1950          */
updateKeyStore()1951         public void updateKeyStore() {
1952             if (filterOutOldKeys()) {
1953                 sendPersistKeyStoreMessage();
1954             }
1955         }
1956 
1957         /**
1958          * Update the key map and the trusted networks list with values parsed from the temp keys
1959          * file.
1960          */
readTempKeysFile()1961         private void readTempKeysFile() {
1962             mKeyMap.clear();
1963             mTrustedNetworks.clear();
1964             if (mAtomicKeyFile == null) {
1965                 initKeyFile();
1966                 if (mAtomicKeyFile == null) {
1967                     Slog.e(
1968                             TAG,
1969                             "Unable to obtain the key file, " + mTempKeysFile + ", for reading");
1970                     return;
1971                 }
1972             }
1973             if (!mAtomicKeyFile.exists()) {
1974                 return;
1975             }
1976             try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
1977                 TypedXmlPullParser parser;
1978                 try {
1979                     parser = Xml.resolvePullParser(keyStream);
1980                     XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
1981 
1982                     int keystoreVersion = parser.getAttributeInt(null, XML_ATTRIBUTE_VERSION);
1983                     if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
1984                         Slog.e(TAG, "Keystore version=" + keystoreVersion
1985                                 + " not supported (max_supported="
1986                                 + MAX_SUPPORTED_KEYSTORE_VERSION + ")");
1987                         return;
1988                     }
1989                 } catch (XmlPullParserException e) {
1990                     // This could be because the XML document doesn't start with
1991                     // XML_KEYSTORE_START_TAG. Try again, instead just starting the document with
1992                     // the adbKey tag (the old format).
1993                     parser = Xml.resolvePullParser(keyStream);
1994                 }
1995                 readKeyStoreContents(parser);
1996             } catch (IOException e) {
1997                 Slog.e(TAG, "Caught an IOException parsing the XML key file: ", e);
1998             } catch (XmlPullParserException e) {
1999                 Slog.e(TAG, "Caught XmlPullParserException parsing the XML key file: ", e);
2000             }
2001         }
2002 
readKeyStoreContents(TypedXmlPullParser parser)2003         private void readKeyStoreContents(TypedXmlPullParser parser)
2004                 throws XmlPullParserException, IOException {
2005             // This parser is very forgiving. For backwards-compatibility, we simply iterate through
2006             // all the tags in the file, skipping over anything that's not an <adbKey> tag or a
2007             // <wifiAP> tag. Invalid tags (such as ones that don't have a valid "lastConnection"
2008             // attribute) are simply ignored.
2009             while ((parser.next()) != XmlPullParser.END_DOCUMENT) {
2010                 String tagName = parser.getName();
2011                 if (XML_TAG_ADB_KEY.equals(tagName)) {
2012                     addAdbKeyToKeyMap(parser);
2013                 } else if (XML_TAG_WIFI_ACCESS_POINT.equals(tagName)) {
2014                     addTrustedNetworkToTrustedNetworks(parser);
2015                 } else {
2016                     Slog.w(TAG, "Ignoring tag '" + tagName + "'. Not recognized.");
2017                 }
2018                 XmlUtils.skipCurrentTag(parser);
2019             }
2020         }
2021 
addAdbKeyToKeyMap(TypedXmlPullParser parser)2022         private void addAdbKeyToKeyMap(TypedXmlPullParser parser) {
2023             String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
2024             try {
2025                 long connectionTime =
2026                         parser.getAttributeLong(null, XML_ATTRIBUTE_LAST_CONNECTION);
2027                 mKeyMap.put(key, connectionTime);
2028             } catch (XmlPullParserException e) {
2029                 Slog.e(TAG, "Error reading adbKey attributes", e);
2030             }
2031         }
2032 
addTrustedNetworkToTrustedNetworks(TypedXmlPullParser parser)2033         private void addTrustedNetworkToTrustedNetworks(TypedXmlPullParser parser) {
2034             String bssid = parser.getAttributeValue(null, XML_ATTRIBUTE_WIFI_BSSID);
2035             mTrustedNetworks.add(bssid);
2036         }
2037 
2038         /**
2039          * Updates the keystore with keys that were previously set to be always allowed before the
2040          * connection time of keys was tracked.
2041          */
addExistingUserKeysToKeyStore()2042         private void addExistingUserKeysToKeyStore() {
2043             if (mUserKeyFile == null || !mUserKeyFile.exists()) {
2044                 return;
2045             }
2046             boolean mapUpdated = false;
2047             try (BufferedReader in = new BufferedReader(new FileReader(mUserKeyFile))) {
2048                 String key;
2049                 while ((key = in.readLine()) != null) {
2050                     // if the keystore does not contain the key from the user key file then add
2051                     // it to the Map with the current system time to prevent it from expiring
2052                     // immediately if the user is actively using this key.
2053                     if (!mKeyMap.containsKey(key)) {
2054                         mKeyMap.put(key, mTicker.currentTimeMillis());
2055                         mapUpdated = true;
2056                     }
2057                 }
2058             } catch (IOException e) {
2059                 Slog.e(TAG, "Caught an exception reading " + mUserKeyFile + ": " + e);
2060             }
2061             if (mapUpdated) {
2062                 sendPersistKeyStoreMessage();
2063             }
2064         }
2065 
2066         /**
2067          * Writes the key map to the key file.
2068          */
persistKeyStore()2069         public void persistKeyStore() {
2070             // if there is nothing in the key map then ensure any keys left in the keystore files
2071             // are deleted as well.
2072             filterOutOldKeys();
2073             if (mKeyMap.isEmpty() && mTrustedNetworks.isEmpty()) {
2074                 deleteKeyStore();
2075                 return;
2076             }
2077             if (mAtomicKeyFile == null) {
2078                 initKeyFile();
2079                 if (mAtomicKeyFile == null) {
2080                     Slog.e(
2081                             TAG,
2082                             "Unable to obtain the key file, " + mTempKeysFile + ", for writing");
2083                     return;
2084                 }
2085             }
2086             FileOutputStream keyStream = null;
2087             try {
2088                 keyStream = mAtomicKeyFile.startWrite();
2089                 TypedXmlSerializer serializer = Xml.resolveSerializer(keyStream);
2090                 serializer.startDocument(null, true);
2091 
2092                 serializer.startTag(null, XML_KEYSTORE_START_TAG);
2093                 serializer.attributeInt(null, XML_ATTRIBUTE_VERSION, KEYSTORE_VERSION);
2094                 for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
2095                     serializer.startTag(null, XML_TAG_ADB_KEY);
2096                     serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey());
2097                     serializer.attributeLong(null, XML_ATTRIBUTE_LAST_CONNECTION,
2098                             keyEntry.getValue());
2099                     serializer.endTag(null, XML_TAG_ADB_KEY);
2100                 }
2101                 for (String bssid : mTrustedNetworks) {
2102                     serializer.startTag(null, XML_TAG_WIFI_ACCESS_POINT);
2103                     serializer.attribute(null, XML_ATTRIBUTE_WIFI_BSSID, bssid);
2104                     serializer.endTag(null, XML_TAG_WIFI_ACCESS_POINT);
2105                 }
2106                 serializer.endTag(null, XML_KEYSTORE_START_TAG);
2107                 serializer.endDocument();
2108                 mAtomicKeyFile.finishWrite(keyStream);
2109             } catch (IOException e) {
2110                 Slog.e(TAG, "Caught an exception writing the key map: ", e);
2111                 mAtomicKeyFile.failWrite(keyStream);
2112             }
2113             writeKeys(mKeyMap.keySet());
2114         }
2115 
filterOutOldKeys()2116         private boolean filterOutOldKeys() {
2117             long allowedTime = getAllowedConnectionTime();
2118             if (allowedTime == 0) {
2119                 return false;
2120             }
2121             boolean keysDeleted = false;
2122             long systemTime = mTicker.currentTimeMillis();
2123             Iterator<Map.Entry<String, Long>> keyMapIterator = mKeyMap.entrySet().iterator();
2124             while (keyMapIterator.hasNext()) {
2125                 Map.Entry<String, Long> keyEntry = keyMapIterator.next();
2126                 long connectionTime = keyEntry.getValue();
2127                 if (systemTime > (connectionTime + allowedTime)) {
2128                     keyMapIterator.remove();
2129                     keysDeleted = true;
2130                 }
2131             }
2132             // if any keys were deleted then the key file should be rewritten with the active keys
2133             // to prevent authorizing a key that is now beyond the allowed window.
2134             if (keysDeleted) {
2135                 writeKeys(mKeyMap.keySet());
2136             }
2137             return keysDeleted;
2138         }
2139 
2140         /**
2141          * Returns the time in ms that the next key will expire or -1 if there are no keys or the
2142          * keys will not expire.
2143          */
getNextExpirationTime()2144         public long getNextExpirationTime() {
2145             long minExpiration = -1;
2146             long allowedTime = getAllowedConnectionTime();
2147             // if the allowedTime is 0 then keys never expire; return -1 to indicate this
2148             if (allowedTime == 0) {
2149                 return minExpiration;
2150             }
2151             long systemTime = mTicker.currentTimeMillis();
2152             Iterator<Map.Entry<String, Long>> keyMapIterator = mKeyMap.entrySet().iterator();
2153             while (keyMapIterator.hasNext()) {
2154                 Map.Entry<String, Long> keyEntry = keyMapIterator.next();
2155                 long connectionTime = keyEntry.getValue();
2156                 // if the key has already expired then ensure that the result is set to 0 so that
2157                 // any scheduled jobs to clean up the keystore can run right away.
2158                 long keyExpiration = Math.max(0, (connectionTime + allowedTime) - systemTime);
2159                 if (minExpiration == -1 || keyExpiration < minExpiration) {
2160                     minExpiration = keyExpiration;
2161                 }
2162             }
2163             return minExpiration;
2164         }
2165 
2166         /**
2167          * Removes all of the entries in the key map and deletes the key file.
2168          */
deleteKeyStore()2169         public void deleteKeyStore() {
2170             mKeyMap.clear();
2171             mTrustedNetworks.clear();
2172             if (mUserKeyFile != null) {
2173                 mUserKeyFile.delete();
2174             }
2175             if (mAtomicKeyFile == null) {
2176                 return;
2177             }
2178             mAtomicKeyFile.delete();
2179         }
2180 
2181         /**
2182          * Returns the time of the last connection from the specified key, or {@code
2183          * NO_PREVIOUS_CONNECTION} if the specified key does not have an active adb grant.
2184          */
getLastConnectionTime(String key)2185         public long getLastConnectionTime(String key) {
2186             return mKeyMap.getOrDefault(key, NO_PREVIOUS_CONNECTION);
2187         }
2188 
2189         /**
2190          * Sets the time of the last connection for the specified key to the provided time.
2191          */
setLastConnectionTime(String key, long connectionTime)2192         public void setLastConnectionTime(String key, long connectionTime) {
2193             setLastConnectionTime(key, connectionTime, false);
2194         }
2195 
2196         /**
2197          * Sets the time of the last connection for the specified key to the provided time. If force
2198          * is set to true the time will be set even if it is older than the previously written
2199          * connection time.
2200          */
2201         @VisibleForTesting
setLastConnectionTime(String key, long connectionTime, boolean force)2202         void setLastConnectionTime(String key, long connectionTime, boolean force) {
2203             // Do not set the connection time to a value that is earlier than what was previously
2204             // stored as the last connection time unless force is set.
2205             if (mKeyMap.containsKey(key) && mKeyMap.get(key) >= connectionTime && !force) {
2206                 return;
2207             }
2208             // System keys are always allowed so there's no need to keep track of their connection
2209             // time.
2210             if (mSystemKeys.contains(key)) {
2211                 return;
2212             }
2213             mKeyMap.put(key, connectionTime);
2214         }
2215 
2216         /**
2217          * Returns the connection time within which a connection from an allowed key is
2218          * automatically allowed without user interaction.
2219          */
getAllowedConnectionTime()2220         public long getAllowedConnectionTime() {
2221             return Settings.Global.getLong(mContext.getContentResolver(),
2222                     Settings.Global.ADB_ALLOWED_CONNECTION_TIME,
2223                     Settings.Global.DEFAULT_ADB_ALLOWED_CONNECTION_TIME);
2224         }
2225 
2226         /**
2227          * Returns whether the specified key should be authroized to connect without user
2228          * interaction. This requires that the user previously connected this device and selected
2229          * the option to 'Always allow', and the time since the last connection is within the
2230          * allowed window.
2231          */
isKeyAuthorized(String key)2232         public boolean isKeyAuthorized(String key) {
2233             // A system key is always authorized to connect.
2234             if (mSystemKeys.contains(key)) {
2235                 return true;
2236             }
2237             long lastConnectionTime = getLastConnectionTime(key);
2238             if (lastConnectionTime == NO_PREVIOUS_CONNECTION) {
2239                 return false;
2240             }
2241             long allowedConnectionTime = getAllowedConnectionTime();
2242             // if the allowed connection time is 0 then revert to the previous behavior of always
2243             // allowing previously granted adb grants.
2244             return allowedConnectionTime == 0
2245                     || (mTicker.currentTimeMillis() < (lastConnectionTime + allowedConnectionTime));
2246         }
2247 
2248         /**
2249          * Returns whether the specified bssid is in the list of trusted networks. This requires
2250          * that the user previously allowed wireless debugging on this network and selected the
2251          * option to 'Always allow'.
2252          */
isTrustedNetwork(String bssid)2253         public boolean isTrustedNetwork(String bssid) {
2254             return mTrustedNetworks.contains(bssid);
2255         }
2256     }
2257 
2258     /**
2259      * A Guava-like interface for getting the current system time.
2260      *
2261      * This allows us to swap a fake ticker in for testing to reduce "Thread.sleep()" calls and test
2262      * for exact expected times instead of random ones.
2263      */
2264     @VisibleForTesting
2265     interface Ticker {
currentTimeMillis()2266         long currentTimeMillis();
2267     }
2268 }
2269