Search code examples
androidandroid-auto

How to detect if phone is connected to Android Auto


I am making an app that should change its behavior when the phone is connected to an Android Auto. It does not have any Auto capabilities and will not be marketed/submitted as an Android Auto app.

Is there a way to detect if the phone is connected to an Android Auto? I know it is possible for Auto media apps to detect their connection status via a BroadcastReceiver registered for com.google.android.gms.car.media.STATUS action. The documentation is not 100% clear, so will this reliably work also for all other non-Auto apps?


Solution

  • Configuration.UI_MODE_TYPE_CAR is not working on Anroid 12. You can use CarConnection API in the androidx.car.app:app library. But that is too heavy to import entire library only for car connections if you don't need other features.

    So I write a piece of code base on the CarConnection to detect Android Auto connection, as below:

    class AutoConnectionDetector(val context: Context) {
    
        companion object {
            const val TAG = "AutoConnectionDetector"
    
            // columnName for provider to query on connection status
            const val CAR_CONNECTION_STATE = "CarConnectionState"
    
            // auto app on your phone will send broadcast with this action when connection state changes
            const val ACTION_CAR_CONNECTION_UPDATED = "androidx.car.app.connection.action.CAR_CONNECTION_UPDATED"
    
            // phone is not connected to car
            const val CONNECTION_TYPE_NOT_CONNECTED = 0
    
            // phone is connected to Automotive OS
            const val CONNECTION_TYPE_NATIVE = 1
    
            // phone is connected to Android Auto
            const val CONNECTION_TYPE_PROJECTION = 2
    
            private const val QUERY_TOKEN = 42
    
            private const val CAR_CONNECTION_AUTHORITY = "androidx.car.app.connection"
    
            private val PROJECTION_HOST_URI = Uri.Builder().scheme("content").authority(CAR_CONNECTION_AUTHORITY).build()
        }
    
        private val carConnectionReceiver = CarConnectionBroadcastReceiver()
        private val carConnectionQueryHandler = CarConnectionQueryHandler(context.contentResolver)
    
        fun registerCarConnectionReceiver() {
            context.registerReceiver(carConnectionReceiver, IntentFilter(ACTION_CAR_CONNECTION_UPDATED))
            queryForState()
        }
    
        fun unRegisterCarConnectionReceiver() {
            context.unregisterReceiver(carConnectionReceiver)
        }
    
        private fun queryForState() {
            carConnectionQueryHandler.startQuery(
                QUERY_TOKEN,
                null,
                PROJECTION_HOST_URI,
                arrayOf(CAR_CONNECTION_STATE),
                null,
                null,
                null
            )
        }
    
        inner class CarConnectionBroadcastReceiver : BroadcastReceiver() {
          // query for connection state every time the receiver receives the broadcast
            override fun onReceive(context: Context?, intent: Intent?) {
                queryForState()
            }
        }
    
        internal class CarConnectionQueryHandler(resolver: ContentResolver?) : AsyncQueryHandler(resolver) {
            // notify new queryed connection status when query complete
            override fun onQueryComplete(token: Int, cookie: Any?, response: Cursor?) {
                if (response == null) {
                    Log.w(TAG, "Null response from content provider when checking connection to the car, treating as disconnected")
                    notifyCarDisconnected()
                    return
                }
                val carConnectionTypeColumn = response.getColumnIndex(CAR_CONNECTION_STATE)
                if (carConnectionTypeColumn < 0) {
                    Log.w(TAG, "Connection to car response is missing the connection type, treating as disconnected")
                    notifyCarDisconnected()
                    return
                }
                if (!response.moveToNext()) {
                    Log.w(TAG, "Connection to car response is empty, treating as disconnected")
                    notifyCarDisconnected()
                    return
                }
                val connectionState = response.getInt(carConnectionTypeColumn)
                if (connectionState == CONNECTION_TYPE_NOT_CONNECTED) {
                    Log.i(TAG, "Android Auto disconnected")
                    notifyCarDisconnected()
                } else {
                    Log.i(TAG, "Android Auto connected")
                    notifyCarConnected()
                }
            }
        }
    }
    

    This solution works on android 6~12. If you need to detect car connection status on android 5, use the Configuration.UI_MODE_TYPE_CAR solution.