1 /* 2 * Copyright (C) 2022 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.server; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import android.content.Intent; 22 import android.os.Looper; 23 import android.provider.Settings; 24 import android.testing.AndroidTestingRunner; 25 import android.testing.TestableContext; 26 import android.testing.TestableLooper; 27 28 import androidx.test.core.app.ApplicationProvider; 29 30 import com.android.internal.R; 31 import com.android.internal.util.test.BroadcastInterceptingContext; 32 33 import org.junit.Before; 34 import org.junit.Rule; 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 38 import java.util.concurrent.ExecutionException; 39 40 @RunWith(AndroidTestingRunner.class) 41 @TestableLooper.RunWithLooper 42 public class DockObserverTest { 43 44 @Rule 45 public TestableContext mContext = 46 new TestableContext(ApplicationProvider.getApplicationContext(), null); 47 48 private final BroadcastInterceptingContext mInterceptingContext = 49 new BroadcastInterceptingContext(mContext); 50 updateExtconDockState(DockObserver observer, String extconDockState)51 BroadcastInterceptingContext.FutureIntent updateExtconDockState(DockObserver observer, 52 String extconDockState) { 53 BroadcastInterceptingContext.FutureIntent futureIntent = 54 mInterceptingContext.nextBroadcastIntent(Intent.ACTION_DOCK_EVENT); 55 observer.setDockStateFromProviderForTesting( 56 DockObserver.ExtconStateProvider.fromString(extconDockState)); 57 TestableLooper.get(this).processAllMessages(); 58 return futureIntent; 59 } 60 observerWithMappingConfig(String[] configEntries)61 DockObserver observerWithMappingConfig(String[] configEntries) { 62 mContext.getOrCreateTestableResources().addOverride( 63 R.array.config_dockExtconStateMapping, 64 configEntries); 65 return new DockObserver(mInterceptingContext); 66 } 67 assertDockEventIntentWithExtraThenUndock(DockObserver observer, String extconDockState, int expectedExtra)68 void assertDockEventIntentWithExtraThenUndock(DockObserver observer, String extconDockState, 69 int expectedExtra) throws ExecutionException, InterruptedException { 70 assertThat(updateExtconDockState(observer, extconDockState) 71 .get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1)) 72 .isEqualTo(expectedExtra); 73 assertThat(updateExtconDockState(observer, "DOCK=0") 74 .get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1)) 75 .isEqualTo(Intent.EXTRA_DOCK_STATE_UNDOCKED); 76 } 77 setDeviceProvisioned(boolean provisioned)78 void setDeviceProvisioned(boolean provisioned) { 79 Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 80 provisioned ? 1 : 0); 81 } 82 83 @Before setUp()84 public void setUp() { 85 if (Looper.myLooper() == null) { 86 Looper.prepare(); 87 } 88 } 89 90 @Test testDockIntentBroadcast_onlyAfterBootReady()91 public void testDockIntentBroadcast_onlyAfterBootReady() 92 throws ExecutionException, InterruptedException { 93 DockObserver observer = new DockObserver(mInterceptingContext); 94 BroadcastInterceptingContext.FutureIntent futureIntent = 95 updateExtconDockState(observer, "DOCK=1"); 96 updateExtconDockState(observer, "DOCK=1").assertNotReceived(); 97 // Last boot phase reached 98 observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); 99 TestableLooper.get(this).processAllMessages(); 100 assertThat(futureIntent.get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1)) 101 .isEqualTo(Intent.EXTRA_DOCK_STATE_DESK); 102 } 103 104 @Test testDockIntentBroadcast_customConfigResource()105 public void testDockIntentBroadcast_customConfigResource() 106 throws ExecutionException, InterruptedException { 107 DockObserver observer = observerWithMappingConfig( 108 new String[] {"2,KEY1=1,KEY2=2", "3,KEY3=3"}); 109 observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); 110 111 // Mapping should not match 112 assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1", 113 Intent.EXTRA_DOCK_STATE_DESK); 114 assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY1=1", 115 Intent.EXTRA_DOCK_STATE_DESK); 116 assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY2=2", 117 Intent.EXTRA_DOCK_STATE_DESK); 118 119 // 1st mapping now matches 120 assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY2=2\nKEY1=1", 121 Intent.EXTRA_DOCK_STATE_CAR); 122 123 // 2nd mapping now matches 124 assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY3=3", 125 Intent.EXTRA_DOCK_STATE_LE_DESK); 126 } 127 128 @Test testDockIntentBroadcast_customConfigResourceWithWildcard()129 public void testDockIntentBroadcast_customConfigResourceWithWildcard() 130 throws ExecutionException, InterruptedException { 131 DockObserver observer = observerWithMappingConfig(new String[] { 132 "2,KEY2=2", 133 "3,KEY3=3", 134 "4" 135 }); 136 observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); 137 assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY5=5", 138 Intent.EXTRA_DOCK_STATE_HE_DESK); 139 } 140 141 @Test testDockIntentBroadcast_deviceNotProvisioned()142 public void testDockIntentBroadcast_deviceNotProvisioned() 143 throws ExecutionException, InterruptedException { 144 DockObserver observer = new DockObserver(mInterceptingContext); 145 // Set the device as not provisioned. 146 setDeviceProvisioned(false); 147 observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); 148 149 BroadcastInterceptingContext.FutureIntent futureIntent = 150 updateExtconDockState(observer, "DOCK=1"); 151 TestableLooper.get(this).processAllMessages(); 152 // Verify no broadcast was sent as device was not provisioned. 153 futureIntent.assertNotReceived(); 154 155 // Ensure we send the broadcast when the device is provisioned. 156 setDeviceProvisioned(true); 157 TestableLooper.get(this).processAllMessages(); 158 assertThat(futureIntent.get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1)) 159 .isEqualTo(Intent.EXTRA_DOCK_STATE_DESK); 160 } 161 } 162