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 android.net.wifi.sharedconnectivity.service; 18 19 import static android.Manifest.permission.NETWORK_SETTINGS; 20 import static android.Manifest.permission.NETWORK_SETUP_WIZARD; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.RequiresPermission; 25 import android.annotation.SystemApi; 26 import android.annotation.TestApi; 27 import android.app.Service; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.pm.PackageManager; 31 import android.net.wifi.sharedconnectivity.app.HotspotNetwork; 32 import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus; 33 import android.net.wifi.sharedconnectivity.app.KnownNetwork; 34 import android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus; 35 import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager; 36 import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState; 37 import android.os.Bundle; 38 import android.os.Handler; 39 import android.os.IBinder; 40 import android.os.RemoteCallbackList; 41 import android.os.RemoteException; 42 import android.util.Log; 43 44 import com.android.internal.R; 45 46 import java.util.Collections; 47 import java.util.List; 48 import java.util.Objects; 49 import java.util.concurrent.CountDownLatch; 50 51 52 /** 53 * This class is the partly implemented service for injecting Shared Connectivity networks into the 54 * Wi-Fi Pickers and other relevant UI surfaces. 55 * 56 * Implementing application should extend this service and override the indicated methods. 57 * Callers to the service should use {@link SharedConnectivityManager} to bind to the implemented 58 * service as specified in the configuration overlay. 59 * 60 * @hide 61 */ 62 @SystemApi 63 public abstract class SharedConnectivityService extends Service { 64 private static final String TAG = SharedConnectivityService.class.getSimpleName(); 65 private static final boolean DEBUG = true; 66 67 private Handler mHandler; 68 private final RemoteCallbackList<ISharedConnectivityCallback> mRemoteCallbackList = 69 new RemoteCallbackList<>(); 70 private List<HotspotNetwork> mHotspotNetworks = Collections.emptyList(); 71 private List<KnownNetwork> mKnownNetworks = Collections.emptyList(); 72 private SharedConnectivitySettingsState mSettingsState = null; 73 private HotspotNetworkConnectionStatus mHotspotNetworkConnectionStatus = 74 new HotspotNetworkConnectionStatus.Builder() 75 .setStatus(HotspotNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN) 76 .setExtras(Bundle.EMPTY).build(); 77 private KnownNetworkConnectionStatus mKnownNetworkConnectionStatus = 78 new KnownNetworkConnectionStatus.Builder() 79 .setStatus(KnownNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN) 80 .setExtras(Bundle.EMPTY).build(); 81 // Used for testing 82 private CountDownLatch mCountDownLatch; 83 84 @Override 85 @Nullable onBind(@onNull Intent intent)86 public final IBinder onBind(@NonNull Intent intent) { 87 if (DEBUG) Log.i(TAG, "onBind intent=" + intent); 88 mHandler = new Handler(getMainLooper()); 89 IBinder serviceStub = new ISharedConnectivityService.Stub() { 90 91 /** 92 * Registers a callback for receiving updates to the list of Hotspot Networks, Known 93 * Networks, shared connectivity settings state, hotspot network connection status and 94 * known network connection status. 95 * 96 * @param callback The callback of type {@link ISharedConnectivityCallback} to be called 97 * when there is update to the data. 98 */ 99 @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, 100 android.Manifest.permission.NETWORK_SETUP_WIZARD}) 101 @Override 102 public void registerCallback(ISharedConnectivityCallback callback) { 103 checkPermissions(); 104 mHandler.post(() -> onRegisterCallback(callback)); 105 } 106 107 /** 108 * Unregisters a previously registered callback. 109 * 110 * @param callback The callback to unregister. 111 */ 112 @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, 113 android.Manifest.permission.NETWORK_SETUP_WIZARD}) 114 @Override 115 public void unregisterCallback(ISharedConnectivityCallback callback) { 116 checkPermissions(); 117 mHandler.post(() -> onUnregisterCallback(callback)); 118 } 119 120 /** 121 * Connects to a hotspot network. 122 * 123 * @param network The network to connect to. 124 */ 125 @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, 126 android.Manifest.permission.NETWORK_SETUP_WIZARD}) 127 @Override 128 public void connectHotspotNetwork(HotspotNetwork network) { 129 checkPermissions(); 130 mHandler.post(() -> onConnectHotspotNetwork(network)); 131 } 132 133 /** 134 * Disconnects from a previously connected hotspot network. 135 * 136 * @param network The network to disconnect from. 137 */ 138 @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, 139 android.Manifest.permission.NETWORK_SETUP_WIZARD}) 140 public void disconnectHotspotNetwork(HotspotNetwork network) { 141 checkPermissions(); 142 mHandler.post(() -> onDisconnectHotspotNetwork(network)); 143 } 144 145 /** 146 * Adds a known network to the available networks on the device and connects to it. 147 * 148 * @param network The network to connect to. 149 */ 150 @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, 151 android.Manifest.permission.NETWORK_SETUP_WIZARD}) 152 @Override 153 public void connectKnownNetwork(KnownNetwork network) { 154 checkPermissions(); 155 mHandler.post(() -> onConnectKnownNetwork(network)); 156 } 157 158 /** 159 * Removes a known network from the available networks on the device which will also 160 * disconnect the device from the network if it is connected to it. 161 * 162 * @param network The network to forget. 163 */ 164 @Override 165 public void forgetKnownNetwork(KnownNetwork network) { 166 checkPermissions(); 167 mHandler.post(() -> onForgetKnownNetwork(network)); 168 } 169 170 /** 171 * Gets the list of hotspot networks the user can select to connect to. 172 * 173 * @return Returns a {@link List} of {@link HotspotNetwork} objects 174 */ 175 @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, 176 android.Manifest.permission.NETWORK_SETUP_WIZARD}) 177 @Override 178 public List<HotspotNetwork> getHotspotNetworks() { 179 checkPermissions(); 180 return mHotspotNetworks; 181 } 182 183 /** 184 * Gets the list of known networks the user can select to connect to. 185 * 186 * @return Returns a {@link List} of {@link KnownNetwork} objects. 187 */ 188 @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, 189 android.Manifest.permission.NETWORK_SETUP_WIZARD}) 190 @Override 191 public List<KnownNetwork> getKnownNetworks() { 192 checkPermissions(); 193 return mKnownNetworks; 194 } 195 196 /** 197 * Gets the shared connectivity settings state. 198 * 199 * @return Returns a {@link SharedConnectivitySettingsState} object with the state. 200 */ 201 @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, 202 android.Manifest.permission.NETWORK_SETUP_WIZARD}) 203 @Override 204 public SharedConnectivitySettingsState getSettingsState() { 205 checkPermissions(); 206 // Done lazily since creating it needs a context. 207 if (mSettingsState == null) { 208 mSettingsState = new SharedConnectivitySettingsState 209 .Builder() 210 .setInstantTetherEnabled(false) 211 .setExtras(Bundle.EMPTY).build(); 212 } 213 return mSettingsState; 214 } 215 216 /** 217 * Gets the connection status of the hotspot network the user selected to connect to. 218 * 219 * @return Returns a {@link HotspotNetworkConnectionStatus} object with the connection 220 * status. 221 */ 222 @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, 223 android.Manifest.permission.NETWORK_SETUP_WIZARD}) 224 @Override 225 public HotspotNetworkConnectionStatus getHotspotNetworkConnectionStatus() { 226 checkPermissions(); 227 return mHotspotNetworkConnectionStatus; 228 } 229 230 /** 231 * Gets the connection status of the known network the user selected to connect to. 232 * 233 * @return Returns a {@link KnownNetworkConnectionStatus} object with the connection 234 * status. 235 */ 236 @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, 237 android.Manifest.permission.NETWORK_SETUP_WIZARD}) 238 @Override 239 public KnownNetworkConnectionStatus getKnownNetworkConnectionStatus() { 240 checkPermissions(); 241 return mKnownNetworkConnectionStatus; 242 } 243 244 @RequiresPermission(anyOf = {android.Manifest.permission.NETWORK_SETTINGS, 245 android.Manifest.permission.NETWORK_SETUP_WIZARD}) 246 /** 247 * checkPermissions is using checkCallingOrSelfPermission to support CTS testing of this 248 * service. This does allow a process to bind to itself if it holds the proper 249 * permission. We do not consider this to be an issue given that the process can already 250 * access the service data since they are in the same process. 251 */ 252 private void checkPermissions() { 253 if (checkCallingOrSelfPermission(NETWORK_SETTINGS) 254 != PackageManager.PERMISSION_GRANTED 255 && checkCallingOrSelfPermission(NETWORK_SETUP_WIZARD) 256 != PackageManager.PERMISSION_GRANTED) { 257 throw new SecurityException("Calling process must have NETWORK_SETTINGS or" 258 + " NETWORK_SETUP_WIZARD permission"); 259 } 260 } 261 }; 262 onBind(); // For CTS testing 263 return serviceStub; 264 } 265 266 /** @hide */ 267 @TestApi onBind()268 public void onBind() { 269 } 270 271 /** @hide */ 272 @TestApi setCountdownLatch(@ullable CountDownLatch latch)273 public final void setCountdownLatch(@Nullable CountDownLatch latch) { 274 mCountDownLatch = latch; 275 } 276 onRegisterCallback(ISharedConnectivityCallback callback)277 private void onRegisterCallback(ISharedConnectivityCallback callback) { 278 mRemoteCallbackList.register(callback); 279 try { 280 callback.onServiceConnected(); 281 } catch (RemoteException e) { 282 if (DEBUG) Log.w(TAG, "Exception in onRegisterCallback", e); 283 } 284 if (mCountDownLatch != null) { 285 mCountDownLatch.countDown(); 286 } 287 } 288 onUnregisterCallback(ISharedConnectivityCallback callback)289 private void onUnregisterCallback(ISharedConnectivityCallback callback) { 290 mRemoteCallbackList.unregister(callback); 291 if (mCountDownLatch != null) { 292 mCountDownLatch.countDown(); 293 } 294 } 295 296 /** 297 * Implementing application should call this method to provide an up-to-date list of Hotspot 298 * Networks to be displayed to the user. 299 * 300 * This method updates the cached list and notifies all registered callbacks. Any callbacks that 301 * are inaccessible will be unregistered. 302 * 303 * @param networks The updated list of {@link HotspotNetwork} objects. 304 */ setHotspotNetworks(@onNull List<HotspotNetwork> networks)305 public final void setHotspotNetworks(@NonNull List<HotspotNetwork> networks) { 306 mHotspotNetworks = networks; 307 308 int count = mRemoteCallbackList.beginBroadcast(); 309 for (int i = 0; i < count; i++) { 310 try { 311 mRemoteCallbackList.getBroadcastItem(i).onHotspotNetworksUpdated(mHotspotNetworks); 312 } catch (RemoteException e) { 313 if (DEBUG) Log.w(TAG, "Exception in setHotspotNetworks", e); 314 } 315 } 316 mRemoteCallbackList.finishBroadcast(); 317 } 318 319 /** 320 * Implementing application should call this method to provide an up-to-date list of Known 321 * Networks to be displayed to the user. 322 * 323 * This method updates the cached list and notifies all registered callbacks. Any callbacks that 324 * are inaccessible will be unregistered. 325 * 326 * @param networks The updated list of {@link KnownNetwork} objects. 327 */ setKnownNetworks(@onNull List<KnownNetwork> networks)328 public final void setKnownNetworks(@NonNull List<KnownNetwork> networks) { 329 mKnownNetworks = networks; 330 331 int count = mRemoteCallbackList.beginBroadcast(); 332 for (int i = 0; i < count; i++) { 333 try { 334 mRemoteCallbackList.getBroadcastItem(i).onKnownNetworksUpdated(mKnownNetworks); 335 } catch (RemoteException e) { 336 if (DEBUG) Log.w(TAG, "Exception in setKnownNetworks", e); 337 } 338 } 339 mRemoteCallbackList.finishBroadcast(); 340 } 341 342 /** 343 * Implementing application should call this method to provide an up-to-date state of Shared 344 * connectivity settings state. 345 * 346 * This method updates the cached state and notifies all registered callbacks. Any callbacks 347 * that are inaccessible will be unregistered. 348 * 349 * @param settingsState The updated state {@link SharedConnectivitySettingsState} 350 * objects. 351 */ setSettingsState(@onNull SharedConnectivitySettingsState settingsState)352 public final void setSettingsState(@NonNull SharedConnectivitySettingsState settingsState) { 353 mSettingsState = settingsState; 354 355 int count = mRemoteCallbackList.beginBroadcast(); 356 for (int i = 0; i < count; i++) { 357 try { 358 mRemoteCallbackList.getBroadcastItem(i).onSharedConnectivitySettingsChanged( 359 mSettingsState); 360 } catch (RemoteException e) { 361 if (DEBUG) Log.w(TAG, "Exception in setSettingsState", e); 362 } 363 } 364 mRemoteCallbackList.finishBroadcast(); 365 } 366 367 /** 368 * Implementing application should call this method to provide an up-to-date status of enabling 369 * and connecting to the hotspot network. 370 * 371 * @param status The updated status {@link HotspotNetworkConnectionStatus} of the connection. 372 */ updateHotspotNetworkConnectionStatus( @onNull HotspotNetworkConnectionStatus status)373 public final void updateHotspotNetworkConnectionStatus( 374 @NonNull HotspotNetworkConnectionStatus status) { 375 mHotspotNetworkConnectionStatus = status; 376 377 int count = mRemoteCallbackList.beginBroadcast(); 378 for (int i = 0; i < count; i++) { 379 try { 380 mRemoteCallbackList 381 .getBroadcastItem(i).onHotspotNetworkConnectionStatusChanged( 382 mHotspotNetworkConnectionStatus); 383 } catch (RemoteException e) { 384 if (DEBUG) Log.w(TAG, "Exception in updateHotspotNetworkConnectionStatus", e); 385 } 386 } 387 mRemoteCallbackList.finishBroadcast(); 388 } 389 390 /** 391 * Implementing application should call this method to provide an up-to-date status of 392 * connecting to a known network. 393 * 394 * @param status The updated status {@link KnownNetworkConnectionStatus} of the connection. 395 */ updateKnownNetworkConnectionStatus( @onNull KnownNetworkConnectionStatus status)396 public final void updateKnownNetworkConnectionStatus( 397 @NonNull KnownNetworkConnectionStatus status) { 398 mKnownNetworkConnectionStatus = status; 399 400 int count = mRemoteCallbackList.beginBroadcast(); 401 for (int i = 0; i < count; i++) { 402 try { 403 mRemoteCallbackList 404 .getBroadcastItem(i).onKnownNetworkConnectionStatusChanged( 405 mKnownNetworkConnectionStatus); 406 } catch (RemoteException e) { 407 if (DEBUG) Log.w(TAG, "Exception in updateKnownNetworkConnectionStatus", e); 408 } 409 } 410 mRemoteCallbackList.finishBroadcast(); 411 } 412 413 /** 414 * System and settings UI support on the device for instant tether. 415 * @return True if the UI can display Instant Tether network data. False otherwise. 416 */ areHotspotNetworksEnabledForService(@onNull Context context)417 public static boolean areHotspotNetworksEnabledForService(@NonNull Context context) { 418 String servicePackage = context.getResources() 419 .getString(R.string.config_sharedConnectivityServicePackage); 420 return Objects.equals(context.getPackageName(), servicePackage) 421 && context.getResources() 422 .getBoolean(R.bool.config_hotspotNetworksEnabledForService); 423 } 424 425 /** 426 * System and settings UI support on the device for known networks. 427 * @return True if the UI can display known networks data. False otherwise. 428 */ areKnownNetworksEnabledForService(@onNull Context context)429 public static boolean areKnownNetworksEnabledForService(@NonNull Context context) { 430 String servicePackage = context.getResources() 431 .getString(R.string.config_sharedConnectivityServicePackage); 432 return Objects.equals(context.getPackageName(), servicePackage) 433 && context.getResources() 434 .getBoolean(R.bool.config_knownNetworksEnabledForService); 435 } 436 437 /** 438 * Implementing application should implement this method. 439 * 440 * Implementation should initiate a connection to the Hotspot Network indicated. 441 * 442 * @param network Object identifying the Hotspot Network the user has requested a connection to. 443 */ onConnectHotspotNetwork(@onNull HotspotNetwork network)444 public abstract void onConnectHotspotNetwork(@NonNull HotspotNetwork network); 445 446 /** 447 * Implementing application should implement this method. 448 * 449 * Implementation should initiate a disconnection from the active Hotspot Network. 450 * 451 * @param network Object identifying the Hotspot Network the user has requested to disconnect. 452 */ onDisconnectHotspotNetwork(@onNull HotspotNetwork network)453 public abstract void onDisconnectHotspotNetwork(@NonNull HotspotNetwork network); 454 455 /** 456 * Implementing application should implement this method. 457 * 458 * Implementation should initiate a connection to the Known Network indicated. 459 * 460 * @param network Object identifying the Known Network the user has requested a connection to. 461 */ onConnectKnownNetwork(@onNull KnownNetwork network)462 public abstract void onConnectKnownNetwork(@NonNull KnownNetwork network); 463 464 /** 465 * Implementing application should implement this method. 466 * 467 * Implementation should remove the Known Network indicated from the synced list of networks. 468 * 469 * @param network Object identifying the Known Network the user has requested to forget. 470 */ onForgetKnownNetwork(@onNull KnownNetwork network)471 public abstract void onForgetKnownNetwork(@NonNull KnownNetwork network); 472 } 473