@@ -14,6 +14,14 @@ import android.content.pm.PackageManager
1414import android.hardware.input.IInputManager
1515import android.media.IAudioService
1616import android.net.IConnectivityManager
17+ import android.net.ITetheringConnector
18+ import android.net.ITetheringEventCallback
19+ import android.net.Network
20+ import android.net.TetherStatesParcel
21+ import android.net.TetheredClient
22+ import android.net.TetheringCallbackStartedParcel
23+ import android.net.TetheringConfigurationParcel
24+ import android.net.TetheringRequestParcel
1725import android.net.wifi.IWifiManager
1826import android.nfc.INfcAdapter
1927import android.nfc.NfcAdapterApis
@@ -28,6 +36,7 @@ import android.permission.IPermissionManager
2836import android.permission.PermissionManagerApis
2937import android.util.Log
3038import android.view.InputEvent
39+ import androidx.annotation.RequiresApi
3140import com.android.internal.telephony.ITelephony
3241import io.github.sds100.keymapper.common.models.EvdevDeviceHandle
3342import io.github.sds100.keymapper.common.models.ShellResult
@@ -175,6 +184,7 @@ internal class SystemBridge : ISystemBridge.Stub() {
175184 private val bluetoothManager: IBluetoothManager ?
176185 private val nfcAdapter: INfcAdapter ?
177186 private val connectivityManager: IConnectivityManager ?
187+ private val tetheringConnector: ITetheringConnector ?
178188 private val activityManager: IActivityManager
179189 private val activityTaskManager: IActivityTaskManager
180190 private val audioService: IAudioService ?
@@ -263,6 +273,14 @@ internal class SystemBridge : ISystemBridge.Stub() {
263273 audioService =
264274 IAudioService .Stub .asInterface(ServiceManager .getService(Context .AUDIO_SERVICE ))
265275
276+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
277+ waitSystemService(" tethering" )
278+ tetheringConnector =
279+ ITetheringConnector .Stub .asInterface(ServiceManager .getService(" tethering" ))
280+ } else {
281+ tetheringConnector = null
282+ }
283+
266284 val applicationInfo = getKeyMapperPackageInfo()
267285
268286 if (applicationInfo == null ) {
@@ -669,21 +687,84 @@ internal class SystemBridge : ISystemBridge.Stub() {
669687 audioService.setRingerModeInternal(ringerMode, processPackageName)
670688 }
671689
690+ @RequiresApi(Build .VERSION_CODES .R )
691+ override fun isTetheringEnabled (): Boolean {
692+ if (tetheringConnector == null ) {
693+ throw UnsupportedOperationException (" TetheringConnector not supported" )
694+ }
695+
696+ val lock = Object ()
697+ var result = false
698+ val timeoutMillis = 5000L
699+
700+ val callback = object : ITetheringEventCallback .Stub () {
701+ override fun onCallbackStarted (parcel : TetheringCallbackStartedParcel ? ) {
702+ if (parcel?.states?.tetheredList != null ) {
703+ // Check if any tethering interface is active
704+ result = parcel.states.tetheredList.isNotEmpty()
705+ }
706+
707+ synchronized(lock) {
708+ lock.notify()
709+ }
710+ }
711+
712+ override fun onCallbackStopped (errorCode : Int ) {}
713+ override fun onUpstreamChanged (network : Network ? ) {}
714+ override fun onConfigurationChanged (config : TetheringConfigurationParcel ? ) {}
715+ override fun onTetherStatesChanged (states : TetherStatesParcel ? ) {}
716+ override fun onTetherClientsChanged (clients : List <TetheredClient ?>? ) {}
717+ override fun onOffloadStatusChanged (status : Int ) {}
718+ override fun onSupportedTetheringTypes (supportedBitmap : Long ) {}
719+ }
720+
721+ try {
722+ // We register and immediately unregister the callback after getting the state
723+ // instead of keeping it registered for the lifetime of SystemBridge. This is
724+ // a safety measure in case there's a bug in the callback that could crash
725+ // the entire SystemBridge process.
726+ tetheringConnector.registerTetheringEventCallback(callback, processPackageName)
727+
728+ // Wait for callback with timeout using Handler
729+ mainHandler.postDelayed({
730+ synchronized(lock) {
731+ lock.notify()
732+ }
733+ }, timeoutMillis)
734+
735+ synchronized(lock) {
736+ lock.wait(timeoutMillis)
737+ }
738+ } catch (e: InterruptedException ) {
739+ Thread .currentThread().interrupt()
740+ } finally {
741+ tetheringConnector.unregisterTetheringEventCallback(callback, processPackageName)
742+ }
743+
744+ return result
745+ }
746+
747+ @RequiresApi(Build .VERSION_CODES .R )
672748 override fun setTetheringEnabled (enable : Boolean ) {
673- if (connectivityManager == null ) {
674- throw UnsupportedOperationException (" ConnectivityManager not supported" )
749+ if (tetheringConnector == null ) {
750+ throw UnsupportedOperationException (" TetheringConnector not supported" )
675751 }
676752
677753 if (enable) {
678- connectivityManager.startTethering(
679- TETHERING_WIFI ,
680- null , // ResultReceiver
681- false , // showProvisioningUi
682- processPackageName, // callerPkg
683- )
754+ val request = TetheringRequestParcel ().apply {
755+ // TetheringManager.TETHERING_WIFI
756+ tetheringType = TETHERING_WIFI
757+ localIPv4Address = null
758+ staticClientAddress = null
759+ exemptFromEntitlementCheck = false
760+ showProvisioningUi = true
761+ // TetheringManager.CONNECTIVITY_SCOPE_GLOBAL
762+ connectivityScope = 1
763+ }
764+
765+ tetheringConnector.startTethering(request, processPackageName, null , null )
684766 } else {
685- connectivityManager .stopTethering(TETHERING_WIFI )
767+ tetheringConnector .stopTethering(TETHERING_WIFI , processPackageName, null , null )
686768 }
687769 }
688- }
689770}
0 commit comments