• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.platform.systemui_tapl.controller
18 
19 import android.content.BroadcastReceiver
20 import android.content.Context
21 import android.content.Intent
22 import android.content.IntentFilter
23 import android.platform.uiautomatorhelpers.DeviceHelpers.context
24 import android.platform.uiautomatorhelpers.DeviceHelpers.shell
25 import android.platform.uiautomatorhelpers.WaitUtils.ensureThat
26 import android.util.Log
27 import java.time.Duration
28 import org.junit.rules.ExternalResource
29 
30 /** Controller for manipulating dock status of a tablet. */
31 class DockController : ExternalResource() {
32     private var lastDockState = UNKNOWN_DOCK_STATE
33 
34     private val dockChangedReceiver =
35         object : BroadcastReceiver() {
onReceivenull36             override fun onReceive(context: Context, intent: Intent) {
37                 val newDockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, UNKNOWN_DOCK_STATE)
38                 Log.i(
39                     TAG,
40                     "ACTION_DOCK_EVENT received, " +
41                         "lastDockState: $lastDockState (${DOCK_STATE_NAMES[lastDockState]}), " +
42                         "newDockState: $newDockState (${DOCK_STATE_NAMES[newDockState]})",
43                 )
44                 lastDockState = newDockState
45             }
46         }
47 
beforenull48     override fun before() {
49         registerReceiver()
50     }
51 
afternull52     override fun after() {
53         unregisterReceiver()
54     }
55 
enterDockStatenull56     fun enterDockState() {
57         if (DEBUG) Log.i(TAG, "enterDockState")
58         // Fake Korlan Dock. See go/dock-manager-guide#fake-korlan-dock
59         shell("dumpsys DockObserver set state ${Intent.EXTRA_DOCK_STATE_HE_DESK}")
60         context.sendBroadcast(
61             Intent("ACTION_DEBUG_DOCK")
62                 .putExtra("EXTRA_DEBUG_DOCK_PRODUCT", "korlan")
63                 .putExtra("EXTRA_DEBUG_DOCK_VERSION", "9.99.999999")
64                 .putExtra("EXTRA_DEBUG_DOCK_HGS_ID", "fake-hgs-id")
65                 .setClassName(context, DOCK_MANAGER_DEBUG_CONTROLLER_BROADCAST_RECEIVER)
66         )
67         waitUntilDocked()
68     }
69 
exitDockStatenull70     fun exitDockState() {
71         if (DEBUG) Log.i(TAG, "exitDockState")
72         shell("dumpsys DockObserver set state ${Intent.EXTRA_DOCK_STATE_UNDOCKED}")
73         context.sendBroadcast(
74             Intent("ACTION_DEBUG_UNDOCK")
75                 .setClassName(context, DOCK_MANAGER_DEBUG_CONTROLLER_BROADCAST_RECEIVER)
76         )
77         waitUntilUndocked()
78     }
79 
resetDockStatenull80     fun resetDockState() {
81         if (DEBUG) Log.i(TAG, "resetDockState")
82         shell("dumpsys DockObserver reset")
83         context.sendBroadcast(
84             Intent("ACTION_DEBUG_UNDOCK")
85                 .setClassName(context, DOCK_MANAGER_DEBUG_CONTROLLER_BROADCAST_RECEIVER)
86         )
87         waitUntilUndocked()
88     }
89 
isDockednull90     private fun isDocked(): Boolean {
91         val docked = lastDockState in DOCKED_STATES
92         if (DEBUG) Log.i(TAG, "isDocked: $docked")
93         return docked
94     }
95 
waitUntilDockednull96     fun waitUntilDocked() {
97         if (DEBUG) Log.i(TAG, "waitUntilDocked")
98         ensureThat("device is docked", DEFAULT_DEADLINE) { isDocked() }
99     }
100 
waitUntilUndockednull101     fun waitUntilUndocked() {
102         if (DEBUG) Log.i(TAG, "waitUntilUndocked")
103         ensureThat("device is undocked", DEFAULT_DEADLINE) { !isDocked() }
104     }
105 
registerReceivernull106     private fun registerReceiver() {
107         val intentFilter = IntentFilter(Intent.ACTION_DOCK_EVENT)
108         if (DEBUG) Log.i(TAG, "Registered event receiver")
109         context.registerReceiver(dockChangedReceiver, intentFilter)
110     }
111 
unregisterReceivernull112     private fun unregisterReceiver() {
113         if (DEBUG) Log.i(TAG, "Unregistered event receiver")
114         context.unregisterReceiver(dockChangedReceiver)
115     }
116 
117     companion object {
118         private val TAG = DockController::class.java.simpleName
119         private const val DOCK_MANAGER_DEBUG_CONTROLLER_BROADCAST_RECEIVER =
120             "com.google.android.apps.nest.dockmanager.app/.service.DebugControllerBroadcastReceiver"
121         private const val UNKNOWN_DOCK_STATE = -1
122         private const val DEBUG = false
123         private val DOCK_STATE_NAMES =
124             mapOf(
125                 UNKNOWN_DOCK_STATE to "UNKNOWN_DOCK_STATE",
126                 Intent.EXTRA_DOCK_STATE_CAR to "EXTRA_DOCK_STATE_CAR",
127                 Intent.EXTRA_DOCK_STATE_DESK to "EXTRA_DOCK_STATE_DESK",
128                 Intent.EXTRA_DOCK_STATE_HE_DESK to "EXTRA_DOCK_STATE_HE_DESK",
129                 Intent.EXTRA_DOCK_STATE_LE_DESK to "EXTRA_DOCK_STATE_LE_DESK",
130                 Intent.EXTRA_DOCK_STATE_UNDOCKED to "EXTRA_DOCK_STATE_UNDOCKED",
131             )
132         private val DOCKED_STATES =
133             arrayOf(
134                 Intent.EXTRA_DOCK_STATE_HE_DESK,
135                 Intent.EXTRA_DOCK_STATE_LE_DESK,
136                 Intent.EXTRA_DOCK_STATE_DESK,
137             )
138         private val DEFAULT_DEADLINE = Duration.ofSeconds(20)
139     }
140 }
141