• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.wifitrackerlib;
18 
19 import static android.net.wifi.WifiInfo.DEFAULT_MAC_ADDRESS;
20 import static android.os.Build.VERSION_CODES;
21 
22 import android.annotation.SuppressLint;
23 import android.annotation.TargetApi;
24 import android.content.Context;
25 import android.icu.text.MessageFormat;
26 import android.net.wifi.WifiInfo;
27 import android.net.wifi.WifiManager;
28 import android.net.wifi.sharedconnectivity.app.HotspotNetwork;
29 import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus;
30 import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo;
31 import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager;
32 import android.os.Handler;
33 import android.text.BidiFormatter;
34 import android.text.TextUtils;
35 import android.util.Log;
36 
37 import androidx.annotation.IntDef;
38 import androidx.annotation.IntRange;
39 import androidx.annotation.NonNull;
40 import androidx.annotation.Nullable;
41 import androidx.annotation.WorkerThread;
42 import androidx.core.os.BuildCompat;
43 
44 import org.json.JSONException;
45 import org.json.JSONObject;
46 
47 import java.lang.annotation.Retention;
48 import java.lang.annotation.RetentionPolicy;
49 import java.util.ArrayList;
50 import java.util.Collections;
51 import java.util.HashMap;
52 import java.util.Map;
53 import java.util.Objects;
54 
55 /**
56  * WifiEntry representation of a Hotspot Network provided via {@link SharedConnectivityManager}.
57  */
58 @TargetApi(VERSION_CODES.UPSIDE_DOWN_CAKE)
59 public class HotspotNetworkEntry extends WifiEntry {
60     static final String TAG = "HotspotNetworkEntry";
61     public static final String KEY_PREFIX = "HotspotNetworkEntry:";
62     public static final String EXTRA_KEY_IS_BATTERY_CHARGING = "is_battery_charging";
63 
64     private static final String DEVICE_TYPE_KEY = "DEVICE_TYPE";
65     private static final String NETWORK_NAME_KEY = "NETWORK_NAME";
66 
67     @NonNull private final WifiTrackerInjector mInjector;
68     @NonNull private final Context mContext;
69     @Nullable private final SharedConnectivityManager mSharedConnectivityManager;
70 
71     @Nullable private HotspotNetwork mHotspotNetworkData;
72     @NonNull private HotspotNetworkEntryKey mKey;
73     @ConnectionStatus
74     private int mLastStatus = HotspotNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN;
75     private boolean mConnectionError = false;
76 
77     /**
78      * If editing this IntDef also edit the definition in:
79      * {@link android.net.wifi.sharedconnectivity.app.HotspotNetwork}
80      *
81      * @hide
82      */
83     @Retention(RetentionPolicy.SOURCE)
84     @IntDef({
85             HotspotNetwork.NETWORK_TYPE_UNKNOWN,
86             HotspotNetwork.NETWORK_TYPE_CELLULAR,
87             HotspotNetwork.NETWORK_TYPE_WIFI,
88             HotspotNetwork.NETWORK_TYPE_ETHERNET
89     })
90     public @interface NetworkType {} // TODO(b/271868642): Add IfThisThanThat lint
91 
92     /**
93      * If editing this IntDef also edit the definition in:
94      * {@link android.net.wifi.sharedconnectivity.app.NetworkProviderInfo}
95      *
96      * @hide
97      */
98     @Retention(RetentionPolicy.SOURCE)
99     @IntDef({
100             NetworkProviderInfo.DEVICE_TYPE_UNKNOWN,
101             NetworkProviderInfo.DEVICE_TYPE_PHONE,
102             NetworkProviderInfo.DEVICE_TYPE_TABLET,
103             NetworkProviderInfo.DEVICE_TYPE_LAPTOP,
104             NetworkProviderInfo.DEVICE_TYPE_WATCH,
105             NetworkProviderInfo.DEVICE_TYPE_AUTO
106     })
107     public @interface DeviceType {} // TODO(b/271868642): Add IfThisThanThat lint
108 
109     public static final int CONNECTION_STATUS_CONNECTED = 10;
110 
111     /**
112      * If editing this IntDef also edit the definition in:
113      * {@link android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus}
114      *
115      * @hide
116      */
117     @Retention(RetentionPolicy.SOURCE)
118     @IntDef({
119             HotspotNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN,
120             HotspotNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT,
121             HotspotNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN_ERROR,
122             HotspotNetworkConnectionStatus.CONNECTION_STATUS_PROVISIONING_FAILED,
123             HotspotNetworkConnectionStatus.CONNECTION_STATUS_TETHERING_TIMEOUT,
124             HotspotNetworkConnectionStatus.CONNECTION_STATUS_TETHERING_UNSUPPORTED,
125             HotspotNetworkConnectionStatus.CONNECTION_STATUS_NO_CELL_DATA,
126             HotspotNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT_FAILED,
127             HotspotNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT_TIMEOUT,
128             HotspotNetworkConnectionStatus.CONNECTION_STATUS_CONNECT_TO_HOTSPOT_FAILED,
129             CONNECTION_STATUS_CONNECTED,
130     })
131     public @interface ConnectionStatus {} // TODO(b/271868642): Add IfThisThanThat lint
132 
133     /**
134      * Create a HotspotNetworkEntry from HotspotNetwork data.
135      */
HotspotNetworkEntry( @onNull WifiTrackerInjector injector, @NonNull Context context, @NonNull Handler callbackHandler, @NonNull WifiManager wifiManager, @Nullable SharedConnectivityManager sharedConnectivityManager, @NonNull HotspotNetwork hotspotNetworkData)136     HotspotNetworkEntry(
137             @NonNull WifiTrackerInjector injector,
138             @NonNull Context context, @NonNull Handler callbackHandler,
139             @NonNull WifiManager wifiManager,
140             @Nullable SharedConnectivityManager sharedConnectivityManager,
141             @NonNull HotspotNetwork hotspotNetworkData) {
142         super(injector, callbackHandler, wifiManager, false /*forSavedNetworksPage*/);
143         mInjector = injector;
144         mContext = context;
145         mSharedConnectivityManager = sharedConnectivityManager;
146         mHotspotNetworkData = hotspotNetworkData;
147         mKey = new HotspotNetworkEntryKey(hotspotNetworkData);
148     }
149 
150     /**
151      * Create a HotspotNetworkEntry from HotspotNetworkEntryKey.
152      */
HotspotNetworkEntry( @onNull WifiTrackerInjector injector, @NonNull Context context, @NonNull Handler callbackHandler, @NonNull WifiManager wifiManager, @Nullable SharedConnectivityManager sharedConnectivityManager, @NonNull HotspotNetworkEntryKey key)153     HotspotNetworkEntry(
154             @NonNull WifiTrackerInjector injector,
155             @NonNull Context context, @NonNull Handler callbackHandler,
156             @NonNull WifiManager wifiManager,
157             @Nullable SharedConnectivityManager sharedConnectivityManager,
158             @NonNull HotspotNetworkEntryKey key) {
159         super(injector, callbackHandler, wifiManager, false /*forSavedNetworksPage*/);
160         mInjector = injector;
161         mContext = context;
162         mSharedConnectivityManager = sharedConnectivityManager;
163         mHotspotNetworkData = null;
164         mKey = key;
165     }
166 
167     @Override
getKey()168     public String getKey() {
169         return mKey.toString();
170     }
171 
getHotspotNetworkEntryKey()172     public HotspotNetworkEntryKey getHotspotNetworkEntryKey() {
173         return mKey;
174     }
175 
176     /**
177      * Updates the hotspot data for this entry. Creates a new key when called.
178      *
179      * @param hotspotNetworkData An updated data set from SharedConnectivityService.
180      */
181     @WorkerThread
updateHotspotNetworkData( @onNull HotspotNetwork hotspotNetworkData)182     protected synchronized void updateHotspotNetworkData(
183             @NonNull HotspotNetwork hotspotNetworkData) {
184         mHotspotNetworkData = hotspotNetworkData;
185         mKey = new HotspotNetworkEntryKey(hotspotNetworkData);
186         notifyOnUpdated();
187     }
188 
189     @WorkerThread
connectionInfoMatches(@onNull WifiInfo wifiInfo)190     protected synchronized boolean connectionInfoMatches(@NonNull WifiInfo wifiInfo) {
191         if (mKey.isVirtualEntry()) {
192             return false;
193         }
194         return Objects.equals(mKey.getScanResultKey(),
195                 new StandardWifiEntry.ScanResultKey(WifiInfo.sanitizeSsid(wifiInfo.getSSID()),
196                         Collections.singletonList(wifiInfo.getCurrentSecurityType())));
197     }
198 
199     @Override
200     @ConnectedState
getConnectedState()201     public synchronized int getConnectedState() {
202         if (NonSdkApiWrapper.isHotspotNetworkConnectingStateForDetailsPageEnabled()
203                 && mCalledConnect) {
204             return CONNECTED_STATE_CONNECTING;
205         }
206         return super.getConnectedState();
207     }
208 
209     @Override
getLevel()210     public int getLevel() {
211         if (getConnectedState() == CONNECTED_STATE_DISCONNECTED) {
212             return WIFI_LEVEL_MAX;
213         }
214         return super.getLevel();
215     }
216 
217     @Override
getTitle()218     public synchronized String getTitle() {
219         if (mHotspotNetworkData == null) {
220             return "";
221         }
222         return mHotspotNetworkData.getNetworkProviderInfo().getDeviceName();
223     }
224 
225     @Override
getSummary(boolean concise)226     public synchronized String getSummary(boolean concise) {
227         if (mHotspotNetworkData == null) {
228             return "";
229         }
230         if (NonSdkApiWrapper.isHotspotNetworkConnectingStateForDetailsPageEnabled()
231                 ? mCalledConnect && !concise // Do not include connecting... for concise summary
232                 : mCalledConnect) {
233             return mContext.getString(R.string.wifitrackerlib_hotspot_network_connecting);
234         }
235         if (mConnectionError) {
236             switch (mLastStatus) {
237                 case HotspotNetworkConnectionStatus.CONNECTION_STATUS_PROVISIONING_FAILED:
238                 case HotspotNetworkConnectionStatus.CONNECTION_STATUS_TETHERING_TIMEOUT:
239                     return mContext.getString(
240                         R.string.wifitrackerlib_hotspot_network_summary_error_carrier_incomplete,
241                         BidiFormatter.getInstance().unicodeWrap(
242                                mHotspotNetworkData.getNetworkName()));
243                 case HotspotNetworkConnectionStatus.CONNECTION_STATUS_TETHERING_UNSUPPORTED:
244                     return mContext.getString(
245                             R.string.wifitrackerlib_hotspot_network_summary_error_carrier_block,
246                             BidiFormatter.getInstance().unicodeWrap(
247                                     mHotspotNetworkData.getNetworkName()));
248                 case HotspotNetworkConnectionStatus.CONNECTION_STATUS_NO_CELL_DATA:
249                 case HotspotNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT_FAILED:
250                 case HotspotNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT_TIMEOUT:
251                 case HotspotNetworkConnectionStatus.CONNECTION_STATUS_CONNECT_TO_HOTSPOT_FAILED:
252                     MessageFormat msg = new MessageFormat(mContext.getString(
253                             R.string.wifitrackerlib_hotspot_network_summary_error_settings));
254                     Map<String, Object> args = new HashMap<>();
255                     args.put(DEVICE_TYPE_KEY, getDeviceTypeId(
256                             mHotspotNetworkData.getNetworkProviderInfo().getDeviceType()));
257                     return msg.format(args);
258                 case HotspotNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN_ERROR:
259                 default:
260                     return mContext.getString(
261                             R.string.wifitrackerlib_hotspot_network_summary_error_generic);
262             }
263         }
264         MessageFormat msg = new MessageFormat(
265                 mContext.getString(R.string.wifitrackerlib_hotspot_network_summary_new));
266         Map<String, Object> args = new HashMap<>();
267         args.put(DEVICE_TYPE_KEY,
268                 getDeviceTypeId(mHotspotNetworkData.getNetworkProviderInfo().getDeviceType()));
269         args.put(NETWORK_NAME_KEY, mHotspotNetworkData.getNetworkName());
270         return msg.format(args);
271     }
272 
273     /**
274      * Alternate summary string to be used on Network & internet page.
275      *
276      * @return Display string.
277      */
getAlternateSummary()278     public synchronized String getAlternateSummary() {
279         if (mHotspotNetworkData == null) {
280             return "";
281         }
282         return mContext.getString(R.string.wifitrackerlib_hotspot_network_alternate,
283                 BidiFormatter.getInstance().unicodeWrap(mHotspotNetworkData.getNetworkName()),
284                 BidiFormatter.getInstance().unicodeWrap(
285                         mHotspotNetworkData.getNetworkProviderInfo().getDeviceName()));
286     }
287 
288     @Override
getSsid()289     public synchronized String getSsid() {
290         StandardWifiEntry.ScanResultKey scanResultKey = mKey.getScanResultKey();
291         if (scanResultKey == null) {
292             return null;
293         }
294         return scanResultKey.getSsid();
295     }
296 
297     @Override
298     @Nullable
299     @SuppressLint("HardwareIds")
getMacAddress()300     public synchronized String getMacAddress() {
301         if (mWifiInfo == null) {
302             return null;
303         }
304         final String wifiInfoMac = mWifiInfo.getMacAddress();
305         if (!TextUtils.isEmpty(wifiInfoMac)
306                 && !TextUtils.equals(wifiInfoMac, DEFAULT_MAC_ADDRESS)) {
307             return wifiInfoMac;
308         }
309         if (getPrivacy() != PRIVACY_RANDOMIZED_MAC) {
310             final String[] factoryMacs = mWifiManager.getFactoryMacAddresses();
311             if (factoryMacs.length > 0) {
312                 return factoryMacs[0];
313             }
314         }
315         return null;
316     }
317 
318     @Override
319     @Privacy
getPrivacy()320     public int getPrivacy() {
321         return PRIVACY_RANDOMIZED_MAC;
322     }
323 
324     @Override
getSecurityString(boolean concise)325     public synchronized String getSecurityString(boolean concise) {
326         if (mHotspotNetworkData == null) {
327             return "";
328         }
329         return Utils.getSecurityString(mContext,
330                 new ArrayList<>(mHotspotNetworkData.getHotspotSecurityTypes()), concise);
331     }
332 
333     @Override
getStandardString()334     public synchronized String getStandardString() {
335         if (mWifiInfo == null) {
336             return "";
337         }
338         return Utils.getStandardString(mContext, mWifiInfo.getWifiStandard());
339     }
340 
341     @Override
getBandString()342     public synchronized String getBandString() {
343         if (mWifiInfo == null) {
344             return "";
345         }
346         return Utils.wifiInfoToBandString(mContext, mWifiInfo);
347     }
348 
349     /**
350      * Connection strength between the host device and the internet.
351      *
352      * @return Displayed connection strength in the range 0 to 4.
353      */
354     @IntRange(from = 0, to = 4)
getUpstreamConnectionStrength()355     public synchronized int getUpstreamConnectionStrength() {
356         if (mHotspotNetworkData == null) {
357             return 0;
358         }
359         return mHotspotNetworkData.getNetworkProviderInfo().getConnectionStrength();
360     }
361 
362     /**
363      * Network type used by the host device to connect to the internet.
364      *
365      * @return NetworkType enum.
366      */
367     @NetworkType
getNetworkType()368     public synchronized int getNetworkType() {
369         if (mHotspotNetworkData == null) {
370             return HotspotNetwork.NETWORK_TYPE_UNKNOWN;
371         }
372         return mHotspotNetworkData.getHostNetworkType();
373     }
374 
375     /**
376      * Device type of the host device.
377      *
378      * @return DeviceType enum.
379      */
380     @DeviceType
getDeviceType()381     public synchronized int getDeviceType() {
382         if (mHotspotNetworkData == null) {
383             return NetworkProviderInfo.DEVICE_TYPE_UNKNOWN;
384         }
385         return mHotspotNetworkData.getNetworkProviderInfo().getDeviceType();
386     }
387 
388     /**
389      * The battery percentage of the host device.
390      */
391     @IntRange(from = 0, to = 100)
getBatteryPercentage()392     public synchronized int getBatteryPercentage() {
393         if (mHotspotNetworkData == null) {
394             return 0;
395         }
396         return mHotspotNetworkData.getNetworkProviderInfo().getBatteryPercentage();
397     }
398 
399     /**
400      * If the host device is currently charging its battery.
401      */
isBatteryCharging()402     public synchronized boolean isBatteryCharging() {
403         if (mHotspotNetworkData == null) {
404             return false;
405         }
406         if (BuildCompat.isAtLeastV()
407                 && NonSdkApiWrapper.isNetworkProviderBatteryChargingStatusEnabled()
408                 && mHotspotNetworkData.getNetworkProviderInfo().isBatteryCharging()) {
409             return true;
410         }
411         // With API flag on we still support either the API or the bundle for compatibility.
412         return mHotspotNetworkData.getNetworkProviderInfo().getExtras().getBoolean(
413                 EXTRA_KEY_IS_BATTERY_CHARGING, false);
414     }
415 
416     @Override
canConnect()417     public synchronized boolean canConnect() {
418         return getConnectedState() == CONNECTED_STATE_DISCONNECTED;
419     }
420 
421     @Override
connect(@ullable ConnectCallback callback)422     public synchronized void connect(@Nullable ConnectCallback callback) {
423         mConnectCallback = callback;
424         if (mSharedConnectivityManager == null) {
425             if (callback != null) {
426                 mCallbackHandler.post(() -> callback.onConnectResult(
427                         ConnectCallback.CONNECT_STATUS_FAILURE_UNKNOWN));
428             }
429             return;
430         }
431         mSharedConnectivityManager.connectHotspotNetwork(mHotspotNetworkData);
432     }
433 
434     @Override
canDisconnect()435     public synchronized boolean canDisconnect() {
436         return getConnectedState() != CONNECTED_STATE_DISCONNECTED;
437     }
438 
439     @Override
disconnect(@ullable DisconnectCallback callback)440     public synchronized void disconnect(@Nullable DisconnectCallback callback) {
441         mCalledDisconnect = true;
442         mDisconnectCallback = callback;
443         if (mSharedConnectivityManager == null) {
444             if (callback != null) {
445                 mCallbackHandler.post(() -> callback.onDisconnectResult(
446                         DisconnectCallback.DISCONNECT_STATUS_FAILURE_UNKNOWN));
447             }
448             return;
449         }
450         mSharedConnectivityManager.disconnectHotspotNetwork(mHotspotNetworkData);
451     }
452 
453     @Nullable
getHotspotNetworkData()454     public HotspotNetwork getHotspotNetworkData() {
455         return mHotspotNetworkData;
456     }
457 
458     /**
459      * Trigger ConnectCallback with data from SharedConnectivityService.
460      * @param status HotspotNetworkConnectionStatus#ConnectionStatus enum.
461      */
onConnectionStatusChanged(@onnectionStatus int status)462     public void onConnectionStatusChanged(@ConnectionStatus int status) {
463         mLastStatus = status;
464         switch (status) {
465             case HotspotNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN:
466                 if (NonSdkApiWrapper.isHotspotNetworkUnknownStatusResetsConnectingStateEnabled()) {
467                     mCalledConnect = false;
468                     mConnectionError = false;
469                     notifyOnUpdated();
470                 }
471                 break;
472             case HotspotNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT:
473                 mCalledConnect = true;
474                 mConnectionError = false;
475                 notifyOnUpdated();
476                 break;
477             case HotspotNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN_ERROR:
478             case HotspotNetworkConnectionStatus.CONNECTION_STATUS_PROVISIONING_FAILED:
479             case HotspotNetworkConnectionStatus.CONNECTION_STATUS_TETHERING_TIMEOUT:
480             case HotspotNetworkConnectionStatus.CONNECTION_STATUS_TETHERING_UNSUPPORTED:
481             case HotspotNetworkConnectionStatus.CONNECTION_STATUS_NO_CELL_DATA:
482             case HotspotNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT_FAILED:
483             case HotspotNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT_TIMEOUT:
484             case HotspotNetworkConnectionStatus.CONNECTION_STATUS_CONNECT_TO_HOTSPOT_FAILED:
485                 mCallbackHandler.post(() -> {
486                     final ConnectCallback connectCallback = mConnectCallback;
487                     if (connectCallback != null) {
488                         connectCallback.onConnectResult(
489                                 ConnectCallback.CONNECT_STATUS_FAILURE_UNKNOWN);
490                     }
491                 });
492                 mCalledConnect = false;
493                 mConnectionError = true;
494                 notifyOnUpdated();
495                 break;
496             case CONNECTION_STATUS_CONNECTED:
497                 mCallbackHandler.post(() -> {
498                     final ConnectCallback connectCallback = mConnectCallback;
499                     if (connectCallback != null) {
500                         connectCallback.onConnectResult(
501                                 ConnectCallback.CONNECT_STATUS_SUCCESS);
502                     }
503                 });
504                 mCalledConnect = false;
505                 mConnectionError = false;
506                 notifyOnUpdated();
507                 break;
508             default:
509                 // Do nothing
510         }
511     }
512 
513     static class HotspotNetworkEntryKey {
514         private static final String KEY_IS_VIRTUAL_ENTRY_KEY = "IS_VIRTUAL_ENTRY_KEY";
515         private static final String KEY_DEVICE_ID_KEY = "DEVICE_ID_KEY";
516         private static final String KEY_SCAN_RESULT_KEY = "SCAN_RESULT_KEY";
517 
518         private boolean mIsVirtualEntry;
519         private long mDeviceId;
520         @Nullable
521         private StandardWifiEntry.ScanResultKey mScanResultKey;
522 
523         /**
524          * Creates a HotspotNetworkEntryKey based on a {@link HotspotNetwork} parcelable object.
525          *
526          * @param hotspotNetworkData A {@link HotspotNetwork} object from SharedConnectivityService.
527          */
HotspotNetworkEntryKey(@onNull HotspotNetwork hotspotNetworkData)528         HotspotNetworkEntryKey(@NonNull HotspotNetwork hotspotNetworkData) {
529             mDeviceId = hotspotNetworkData.getDeviceId();
530             if (hotspotNetworkData.getHotspotSsid() == null || (
531                     hotspotNetworkData.getHotspotSecurityTypes() == null)) {
532                 mIsVirtualEntry = true;
533                 mScanResultKey = null;
534             } else {
535                 mIsVirtualEntry = false;
536                 mScanResultKey = new StandardWifiEntry.ScanResultKey(
537                         hotspotNetworkData.getHotspotSsid(),
538                         new ArrayList<>(hotspotNetworkData.getHotspotSecurityTypes()));
539             }
540         }
541 
542         /**
543          * Creates a HotspotNetworkEntryKey from its String representation.
544          */
HotspotNetworkEntryKey(@onNull String string)545         HotspotNetworkEntryKey(@NonNull String string) {
546             mScanResultKey = null;
547             if (!string.startsWith(KEY_PREFIX)) {
548                 Log.e(TAG, "String key does not start with key prefix!");
549                 return;
550             }
551             try {
552                 final JSONObject keyJson = new JSONObject(
553                         string.substring(KEY_PREFIX.length()));
554                 if (keyJson.has(KEY_IS_VIRTUAL_ENTRY_KEY)) {
555                     mIsVirtualEntry = keyJson.getBoolean(KEY_IS_VIRTUAL_ENTRY_KEY);
556                 }
557                 if (keyJson.has(KEY_DEVICE_ID_KEY)) {
558                     mDeviceId = keyJson.getLong(KEY_DEVICE_ID_KEY);
559                 }
560                 if (keyJson.has(KEY_SCAN_RESULT_KEY)) {
561                     mScanResultKey = new StandardWifiEntry.ScanResultKey(keyJson.getString(
562                             KEY_SCAN_RESULT_KEY));
563                 }
564             } catch (JSONException e) {
565                 Log.e(TAG, "JSONException while converting HotspotNetworkEntryKey to string: " + e);
566             }
567         }
568 
569         /**
570          * Returns the JSON String representation of this HotspotNetworkEntryKey.
571          */
572         @Override
toString()573         public String toString() {
574             final JSONObject keyJson = new JSONObject();
575             try {
576                 keyJson.put(KEY_IS_VIRTUAL_ENTRY_KEY, mIsVirtualEntry);
577                 keyJson.put(KEY_DEVICE_ID_KEY, mDeviceId);
578                 if (mScanResultKey != null) {
579                     keyJson.put(KEY_SCAN_RESULT_KEY, mScanResultKey.toString());
580                 }
581             } catch (JSONException e) {
582                 Log.wtf(TAG,
583                         "JSONException while converting HotspotNetworkEntryKey to string: " + e);
584             }
585             return KEY_PREFIX + keyJson.toString();
586         }
587 
isVirtualEntry()588         public boolean isVirtualEntry() {
589             return mIsVirtualEntry;
590         }
591 
getDeviceId()592         public long getDeviceId() {
593             return mDeviceId;
594         }
595 
596         /**
597          * Returns the ScanResultKey of this HotspotNetworkEntryKey to match against ScanResults
598          */
599         @Nullable
getScanResultKey()600         StandardWifiEntry.ScanResultKey getScanResultKey() {
601             return mScanResultKey;
602         }
603     }
604 
getDeviceTypeId(@eviceType int deviceType)605     private static String getDeviceTypeId(@DeviceType int deviceType) {
606         switch (deviceType) {
607             case NetworkProviderInfo.DEVICE_TYPE_PHONE:
608                 return "PHONE";
609             case NetworkProviderInfo.DEVICE_TYPE_TABLET:
610                 return "TABLET";
611             case NetworkProviderInfo.DEVICE_TYPE_LAPTOP:
612                 return "COMPUTER";
613             case NetworkProviderInfo.DEVICE_TYPE_WATCH:
614                 return "WATCH";
615             case NetworkProviderInfo.DEVICE_TYPE_AUTO:
616                 return "VEHICLE";
617             default:
618                 return "UNKNOWN";
619         }
620     }
621 }
622