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.settings.wifi.repository; 18 19 import android.app.ActivityOptions; 20 import android.app.PendingIntent; 21 import android.content.Context; 22 import android.net.wifi.sharedconnectivity.app.HotspotNetwork; 23 import android.net.wifi.sharedconnectivity.app.HotspotNetworkConnectionStatus; 24 import android.net.wifi.sharedconnectivity.app.KnownNetwork; 25 import android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus; 26 import android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback; 27 import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager; 28 import android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState; 29 import android.os.Bundle; 30 import android.os.HandlerThread; 31 import android.provider.DeviceConfig; 32 import android.util.Log; 33 34 import androidx.annotation.NonNull; 35 import androidx.annotation.VisibleForTesting; 36 import androidx.annotation.WorkerThread; 37 import androidx.lifecycle.LiveData; 38 import androidx.lifecycle.MutableLiveData; 39 40 import com.android.settings.overlay.FeatureFactory; 41 42 import java.util.List; 43 import java.util.concurrent.Executor; 44 45 /** 46 * Shared Connectivity Repository for {@link SharedConnectivityManager} 47 */ 48 public class SharedConnectivityRepository { 49 private static final String TAG = "SharedConnectivityRepository"; 50 private static final String DEVICE_CONFIG_NAMESPACE = "wifi"; 51 private static final String DEVICE_CONFIG_KEY = "shared_connectivity_enabled"; 52 53 private Context mAppContext; 54 private SharedConnectivityManager mManager; 55 private ClientCallback mClientCallback = new ClientCallback(); 56 private HandlerThread mWorkerThread = new HandlerThread(TAG); 57 private Executor mWorkerExecutor = cmd -> mWorkerThread.getThreadHandler().post(cmd); 58 private Runnable mLaunchSettingsRunnable = () -> handleLaunchSettings(); 59 @VisibleForTesting 60 MutableLiveData<SharedConnectivitySettingsState> mSettingsState = new MutableLiveData<>(); 61 SharedConnectivityRepository(@onNull Context appContext)62 public SharedConnectivityRepository(@NonNull Context appContext) { 63 this(appContext, isDeviceConfigEnabled()); 64 } 65 66 @VisibleForTesting SharedConnectivityRepository(@onNull Context appContext, boolean isConfigEnabled)67 SharedConnectivityRepository(@NonNull Context appContext, boolean isConfigEnabled) { 68 mAppContext = appContext; 69 if (!isConfigEnabled) { 70 return; 71 } 72 mManager = mAppContext.getSystemService(SharedConnectivityManager.class); 73 if (mManager == null) { 74 Log.w(TAG, "Failed to get SharedConnectivityManager"); 75 return; 76 } 77 mWorkerThread.start(); 78 mManager.registerCallback(mWorkerExecutor, mClientCallback); 79 } 80 81 /** 82 * Return whether Wi-Fi Shared Connectivity service is available or not. 83 * 84 * @return {@code true} if Wi-Fi Shared Connectivity service is available 85 */ isServiceAvailable()86 public boolean isServiceAvailable() { 87 return mManager != null; 88 } 89 90 /** 91 * Gets SharedConnectivitySettingsState LiveData 92 */ getSettingsState()93 public LiveData<SharedConnectivitySettingsState> getSettingsState() { 94 return mSettingsState; 95 } 96 97 /** 98 * Launch Instant Hotspot Settings 99 */ launchSettings()100 public void launchSettings() { 101 mWorkerExecutor.execute(mLaunchSettingsRunnable); 102 } 103 104 @WorkerThread 105 @VisibleForTesting handleLaunchSettings()106 void handleLaunchSettings() { 107 if (mManager == null) { 108 return; 109 } 110 SharedConnectivitySettingsState state = mManager.getSettingsState(); 111 log("handleLaunchSettings(), state:" + state); 112 if (state == null) { 113 Log.e(TAG, "No SettingsState to launch Instant Hotspot settings"); 114 return; 115 } 116 PendingIntent intent = state.getInstantTetherSettingsPendingIntent(); 117 if (intent == null) { 118 Log.e(TAG, "No PendingIntent to launch Instant Hotspot settings"); 119 return; 120 } 121 sendSettingsIntent(intent); 122 } 123 124 @WorkerThread 125 @VisibleForTesting sendSettingsIntent(@onNull PendingIntent intent)126 void sendSettingsIntent(@NonNull PendingIntent intent) { 127 try { 128 log("sendSettingsIntent(), sent intent:" + intent); 129 final Bundle options = ActivityOptions.makeBasic() 130 .setPendingIntentBackgroundActivityStartMode( 131 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) 132 .toBundle(); 133 intent.send(options); 134 } catch (PendingIntent.CanceledException e) { 135 Log.e(TAG, "Failed to launch Instant Hotspot settings", e); 136 } 137 } 138 139 @WorkerThread 140 class ClientCallback implements SharedConnectivityClientCallback { 141 142 @Override onHotspotNetworkConnectionStatusChanged(HotspotNetworkConnectionStatus status)143 public void onHotspotNetworkConnectionStatusChanged(HotspotNetworkConnectionStatus status) { 144 log("onHotspotNetworkConnectionStatusChanged(), status:" + status); 145 } 146 147 @Override onHotspotNetworksUpdated(List<HotspotNetwork> networks)148 public void onHotspotNetworksUpdated(List<HotspotNetwork> networks) { 149 log("onHotspotNetworksUpdated(), networks:" + networks); 150 } 151 152 @Override onKnownNetworkConnectionStatusChanged(KnownNetworkConnectionStatus status)153 public void onKnownNetworkConnectionStatusChanged(KnownNetworkConnectionStatus status) { 154 log("onKnownNetworkConnectionStatusChanged(), status:" + status); 155 } 156 157 @Override onKnownNetworksUpdated(List<KnownNetwork> networks)158 public void onKnownNetworksUpdated(List<KnownNetwork> networks) { 159 log("onKnownNetworksUpdated(), networks:" + networks); 160 } 161 162 @Override onRegisterCallbackFailed(Exception e)163 public void onRegisterCallbackFailed(Exception e) { 164 Log.e(TAG, "onRegisterCallbackFailed(), e:" + e); 165 } 166 167 @Override onServiceConnected()168 public void onServiceConnected() { 169 SharedConnectivitySettingsState state = mManager.getSettingsState(); 170 Log.d(TAG, "onServiceConnected(), Manager#getSettingsState:" + state); 171 mSettingsState.postValue(state); 172 } 173 174 @Override onServiceDisconnected()175 public void onServiceDisconnected() { 176 log("onServiceDisconnected()"); 177 } 178 179 @Override onSharedConnectivitySettingsChanged(SharedConnectivitySettingsState state)180 public void onSharedConnectivitySettingsChanged(SharedConnectivitySettingsState state) { 181 Log.d(TAG, "onSharedConnectivitySettingsChanged(), state:" + state); 182 mSettingsState.postValue(state); 183 } 184 } 185 log(String msg)186 private void log(String msg) { 187 FeatureFactory.getFeatureFactory().getWifiFeatureProvider().verboseLog(TAG, msg); 188 } 189 190 /** 191 * Returns true if Shared Connectivity feature is enabled. 192 */ isDeviceConfigEnabled()193 public static boolean isDeviceConfigEnabled() { 194 return DeviceConfig.getBoolean(DEVICE_CONFIG_NAMESPACE, DEVICE_CONFIG_KEY, false); 195 } 196 } 197