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