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 package android.wearable.cts; 17 18 import android.app.ambientcontext.AmbientContextEventRequest; 19 import android.app.ambientcontext.AmbientContextManager; 20 import android.os.ParcelFileDescriptor; 21 import android.os.PersistableBundle; 22 import android.os.SharedMemory; 23 import android.service.ambientcontext.AmbientContextDetectionResult; 24 import android.service.ambientcontext.AmbientContextDetectionServiceStatus; 25 import android.service.wearable.WearableSensingDataRequester; 26 import android.service.wearable.WearableSensingService; 27 import android.util.Log; 28 29 import com.google.common.collect.HashMultimap; 30 31 import java.util.Set; 32 import java.util.concurrent.CountDownLatch; 33 import java.util.concurrent.TimeUnit; 34 import java.util.function.Consumer; 35 36 /** An implementation of {@link WearableSensingService} for CTS testing. */ 37 public class CtsWearableSensingService extends WearableSensingService { 38 private static final String TAG = "CtsWearableSensingService"; 39 private static final String FAKE_APP_PACKAGE = "foo.bar.baz"; 40 private static final int INITIAL_STATUS_TO_CONSUME = -1; 41 42 private static final AmbientContextDetectionServiceStatus INITIAL_SERVICE_STATUS_TO_CONSUME = 43 new AmbientContextDetectionServiceStatus.Builder(FAKE_APP_PACKAGE).setStatusCode( 44 AmbientContextManager.STATUS_UNKNOWN).build(); 45 46 private static CountDownLatch sRespondLatch = new CountDownLatch(1); 47 private static Consumer<Integer> sStatusConsumer; 48 private static int sStatusToConsume; 49 private static ParcelFileDescriptor sParcelFileDescriptor; 50 private static SharedMemory sSharedMemory; 51 private static PersistableBundle sData; 52 53 private static Consumer<AmbientContextDetectionResult> sDetectionResultConsumer; 54 private static Consumer<AmbientContextDetectionServiceStatus> 55 sAmbientContextDetectionServiceStatusConsumer; 56 private static AmbientContextDetectionServiceStatus sServiceStatusToConsume; 57 private static String sPackageName = null; 58 private static final HashMultimap<Integer, WearableSensingDataRequester> sDataRequesters = 59 HashMultimap.create(); 60 private static boolean sOnStopHotwordRecognitionCalled = false; 61 private static boolean sShouldCallParentAndReturn = false; 62 63 @Override onDataStreamProvided(ParcelFileDescriptor parcelFileDescriptor, Consumer<Integer> statusConsumer)64 public void onDataStreamProvided(ParcelFileDescriptor parcelFileDescriptor, 65 Consumer<Integer> statusConsumer) { 66 Log.w(TAG, "onDataStreamProvided"); 67 sParcelFileDescriptor = parcelFileDescriptor; 68 sStatusConsumer = statusConsumer; 69 sRespondLatch.countDown(); 70 } 71 72 @Override onDataProvided(PersistableBundle data, SharedMemory sharedMemory, Consumer<Integer> statusConsumer)73 public void onDataProvided(PersistableBundle data, SharedMemory sharedMemory, 74 Consumer<Integer> statusConsumer) { 75 Log.w(TAG, "onDataProvided"); 76 sData = data; 77 sSharedMemory = sharedMemory; 78 sStatusConsumer = statusConsumer; 79 sRespondLatch.countDown(); 80 } 81 82 @Override onDataRequestObserverRegistered( int dataType, String packageName, WearableSensingDataRequester dataRequester, Consumer<Integer> statusConsumer)83 public void onDataRequestObserverRegistered( 84 int dataType, 85 String packageName, 86 WearableSensingDataRequester dataRequester, 87 Consumer<Integer> statusConsumer) { 88 if (sShouldCallParentAndReturn) { 89 super.onDataRequestObserverRegistered( 90 dataType, packageName, dataRequester, statusConsumer); 91 return; 92 } 93 sDataRequesters.put(dataType, dataRequester); 94 sPackageName = packageName; 95 sStatusConsumer = statusConsumer; 96 sRespondLatch.countDown(); 97 } 98 99 @Override onDataRequestObserverUnregistered( int dataType, String packageName, WearableSensingDataRequester dataRequester, Consumer<Integer> statusConsumer)100 public void onDataRequestObserverUnregistered( 101 int dataType, 102 String packageName, 103 WearableSensingDataRequester dataRequester, 104 Consumer<Integer> statusConsumer) { 105 if (sShouldCallParentAndReturn) { 106 super.onDataRequestObserverUnregistered( 107 dataType, packageName, dataRequester, statusConsumer); 108 return; 109 } 110 sDataRequesters.remove(dataType, dataRequester); 111 sPackageName = packageName; 112 sStatusConsumer = statusConsumer; 113 sRespondLatch.countDown(); 114 } 115 116 @Override onStopHotwordRecognition(Consumer<Integer> statusConsumer)117 public void onStopHotwordRecognition(Consumer<Integer> statusConsumer) { 118 if (sShouldCallParentAndReturn) { 119 super.onStopHotwordRecognition(statusConsumer); 120 return; 121 } 122 Log.i(TAG, "onStopHotwordRecognition"); 123 sOnStopHotwordRecognitionCalled = true; 124 sStatusConsumer = statusConsumer; 125 sRespondLatch.countDown(); 126 } 127 128 @Override onStartDetection(AmbientContextEventRequest request, String packageName, Consumer<AmbientContextDetectionServiceStatus> statusConsumer, Consumer<AmbientContextDetectionResult> detectionResultConsumer)129 public void onStartDetection(AmbientContextEventRequest request, 130 String packageName, 131 Consumer<AmbientContextDetectionServiceStatus> statusConsumer, 132 Consumer<AmbientContextDetectionResult> detectionResultConsumer) { 133 Log.w(TAG, "onStartDetection"); 134 sPackageName = packageName; 135 sDetectionResultConsumer = detectionResultConsumer; 136 sAmbientContextDetectionServiceStatusConsumer = statusConsumer; 137 sRespondLatch.countDown(); 138 } 139 140 @Override onStopDetection(String packageName)141 public void onStopDetection(String packageName) { 142 Log.w(TAG, "onStopDetection"); 143 sPackageName = packageName; 144 sRespondLatch.countDown(); 145 } 146 147 @Override onQueryServiceStatus(Set<Integer> eventTypes, String packageName, Consumer<AmbientContextDetectionServiceStatus> consumer)148 public void onQueryServiceStatus(Set<Integer> eventTypes, 149 String packageName, 150 Consumer<AmbientContextDetectionServiceStatus> consumer) { 151 Log.w(TAG, "onQueryServiceStatus"); 152 sPackageName = packageName; 153 sAmbientContextDetectionServiceStatusConsumer = consumer; 154 sRespondLatch.countDown(); 155 } 156 reset()157 public static void reset() { 158 sRespondLatch = new CountDownLatch(1); 159 sStatusConsumer = null; 160 sStatusToConsume = INITIAL_STATUS_TO_CONSUME; 161 sParcelFileDescriptor = null; 162 sSharedMemory = null; 163 sData = null; 164 sDetectionResultConsumer = null; 165 sAmbientContextDetectionServiceStatusConsumer = null; 166 sServiceStatusToConsume = null; 167 sPackageName = null; 168 sDataRequesters.clear(); 169 sOnStopHotwordRecognitionCalled = false; 170 sShouldCallParentAndReturn = false; 171 } 172 whenCallbackTriggeredRespondWithStatus(int status)173 public static void whenCallbackTriggeredRespondWithStatus(int status) { 174 Log.w(TAG, "whenCallbackTriggeredRespondWithStatus"); 175 sStatusToConsume = status; 176 } 177 whenCallbackTriggeredRespondWithServiceStatus(int status)178 public static void whenCallbackTriggeredRespondWithServiceStatus(int status) { 179 sServiceStatusToConsume = new AmbientContextDetectionServiceStatus.Builder( 180 FAKE_APP_PACKAGE).setStatusCode(status).build(); 181 } 182 expectTimeOut()183 public static void expectTimeOut() { 184 try { 185 if (!sRespondLatch.await(3000, TimeUnit.MILLISECONDS)) { 186 Log.e(TAG, "Timed out waiting for result, this is expected"); 187 } else { 188 throw new AssertionError("CtsWearableSensingService" 189 + " expected timeout but did not timeout."); 190 } 191 // reset for next 192 sRespondLatch = new CountDownLatch(1); 193 sStatusToConsume = AmbientContextManager.STATUS_UNKNOWN; 194 } catch (InterruptedException e) { 195 Log.e(TAG, e.getMessage()); 196 Thread.currentThread().interrupt(); 197 throw new AssertionError("Got InterruptedException while waiting for serviceStatus."); 198 } 199 } 200 awaitResultAmbientContextDetectionService()201 public static void awaitResultAmbientContextDetectionService() { 202 try { 203 if (!sRespondLatch.await(3000, TimeUnit.MILLISECONDS)) { 204 throw new AssertionError("CtsWearableSensingService" 205 + " timed out while expecting a call."); 206 } 207 sAmbientContextDetectionServiceStatusConsumer.accept(sServiceStatusToConsume); 208 // reset for next 209 sRespondLatch = new CountDownLatch(1); 210 sServiceStatusToConsume = INITIAL_SERVICE_STATUS_TO_CONSUME; 211 } catch (InterruptedException e) { 212 Log.e(TAG, e.getMessage()); 213 Thread.currentThread().interrupt(); 214 throw new AssertionError("Got InterruptedException while waiting for serviceStatus."); 215 } 216 } 217 awaitResult()218 public static void awaitResult() { 219 try { 220 if (!sRespondLatch.await(3000, TimeUnit.MILLISECONDS)) { 221 throw new AssertionError("CtsWearableSensingService" 222 + " timed out while expecting a call."); 223 } 224 Log.i(TAG, "Sending status " + sStatusToConsume); 225 sStatusConsumer.accept(sStatusToConsume); 226 // reset for next 227 sRespondLatch = new CountDownLatch(1); 228 sStatusToConsume = INITIAL_STATUS_TO_CONSUME; 229 } catch (InterruptedException e) { 230 Log.e(TAG, e.getMessage()); 231 Thread.currentThread().interrupt(); 232 throw new AssertionError("Got InterruptedException while waiting for serviceStatus."); 233 } 234 } 235 236 getData()237 public static PersistableBundle getData() { 238 return sData; 239 } 240 getParcelFileDescriptor()241 public static ParcelFileDescriptor getParcelFileDescriptor() { 242 return sParcelFileDescriptor; 243 } 244 getLastCallingPackage()245 public static String getLastCallingPackage() { 246 return sPackageName; 247 } 248 249 /** Gets the data requesters registered for the provided data type. */ getDataRequesters(int dataType)250 public static Set<WearableSensingDataRequester> getDataRequesters(int dataType) { 251 return sDataRequesters.get(dataType); 252 } 253 getOnStopHotwordRecognitionCalled()254 public static boolean getOnStopHotwordRecognitionCalled() { 255 return sOnStopHotwordRecognitionCalled; 256 } 257 258 /** 259 * Configures this WearableSensingService to delegate to its parent when a non-abstract method 260 * is called. 261 */ configureMethodsToCallParentAndReturn()262 public static void configureMethodsToCallParentAndReturn() { 263 sShouldCallParentAndReturn = true; 264 } 265 } 266