1 /* 2 * Copyright (C) 2020 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.soundtrigger_middleware; 18 19 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.DETACH; 20 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.FORCE_RECOGNITION; 21 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.GET_MODEL_PARAMETER; 22 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.LOAD_MODEL; 23 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.LOAD_PHRASE_MODEL; 24 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.MODEL_UNLOADED; 25 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.MODULE_DIED; 26 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.QUERY_MODEL_PARAMETER; 27 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.RECOGNITION; 28 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.RESOURCES_AVAILABLE; 29 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.SET_MODEL_PARAMETER; 30 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.START_RECOGNITION; 31 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.STOP_RECOGNITION; 32 import static com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging.SessionEvent.Type.UNLOAD_MODEL; 33 import static com.android.server.utils.EventLogger.Event.ALOGI; 34 import static com.android.server.utils.EventLogger.Event.ALOGW; 35 36 import android.annotation.NonNull; 37 import android.content.Context; 38 import android.media.permission.Identity; 39 import android.media.permission.IdentityContext; 40 import android.media.soundtrigger.ModelParameterRange; 41 import android.media.soundtrigger.PhraseRecognitionEvent; 42 import android.media.soundtrigger.PhraseSoundModel; 43 import android.media.soundtrigger.RecognitionConfig; 44 import android.media.soundtrigger.RecognitionStatus; 45 import android.media.soundtrigger.SoundModel; 46 import android.media.soundtrigger_middleware.ISoundTriggerCallback; 47 import android.media.soundtrigger_middleware.ISoundTriggerModule; 48 import android.media.soundtrigger_middleware.PhraseRecognitionEventSys; 49 import android.media.soundtrigger_middleware.RecognitionEventSys; 50 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; 51 import android.os.BatteryStatsInternal; 52 import android.os.IBinder; 53 import android.os.RemoteException; 54 import android.os.SystemClock; 55 import android.util.Slog; 56 57 import com.android.internal.annotations.VisibleForTesting; 58 import com.android.internal.util.ArrayUtils; 59 import com.android.internal.util.LatencyTracker; 60 import com.android.server.LocalServices; 61 import com.android.server.utils.EventLogger; 62 import com.android.server.utils.EventLogger.Event; 63 64 import java.io.PrintWriter; 65 import java.util.Arrays; 66 import java.util.Deque; 67 import java.util.Objects; 68 import java.util.Set; 69 import java.util.concurrent.ConcurrentHashMap; 70 import java.util.concurrent.LinkedBlockingDeque; 71 import java.util.concurrent.atomic.AtomicInteger; 72 import java.util.function.Supplier; 73 74 75 /** 76 * An ISoundTriggerMiddlewareService decorator, which adds logging of all API calls (and 77 * callbacks). 78 * 79 * All API methods should follow this structure: 80 * <pre><code> 81 * @Override 82 * public @NonNull ReturnType someMethod(ArgType1 arg1, ArgType2 arg2) throws ExceptionType { 83 * try { 84 * ReturnType result = mDelegate.someMethod(arg1, arg2); 85 * logReturn("someMethod", result, arg1, arg2); 86 * return result; 87 * } catch (Exception e) { 88 * logException("someMethod", e, arg1, arg2); 89 * throw e; 90 * } 91 * } 92 * </code></pre> 93 * The actual handling of these events is then done inside of 94 * {@link #logReturnWithObject(Object, Identity, String, Object, Object[])}, 95 * {@link #logVoidReturnWithObject(Object, Identity, String, Object[])} and {@link 96 * #logExceptionWithObject(Object, Identity, String, Exception, Object[])}. 97 */ 98 public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInternal, Dumpable { 99 private static final String TAG = "SoundTriggerMiddlewareLogging"; 100 private static final int SESSION_MAX_EVENT_SIZE = 128; 101 private final @NonNull ISoundTriggerMiddlewareInternal mDelegate; 102 private final @NonNull LatencyTracker mLatencyTracker; 103 private final @NonNull Supplier<BatteryStatsInternal> mBatteryStatsInternalSupplier; 104 private final @NonNull EventLogger mServiceEventLogger = new EventLogger(256, 105 "Service Events"); 106 107 private final Set<EventLogger> mSessionEventLoggers = ConcurrentHashMap.newKeySet(4); 108 private final Deque<EventLogger> mDetachedSessionEventLoggers = new LinkedBlockingDeque<>(4); 109 private final AtomicInteger mSessionCount = new AtomicInteger(0); 110 111 SoundTriggerMiddlewareLogging(@onNull Context context, @NonNull ISoundTriggerMiddlewareInternal delegate)112 public SoundTriggerMiddlewareLogging(@NonNull Context context, 113 @NonNull ISoundTriggerMiddlewareInternal delegate) { 114 this(LatencyTracker.getInstance(context), 115 () -> BatteryStatsHolder.INSTANCE, 116 delegate); 117 } 118 119 @VisibleForTesting SoundTriggerMiddlewareLogging(@onNull LatencyTracker latencyTracker, @NonNull Supplier<BatteryStatsInternal> batteryStatsInternalSupplier, @NonNull ISoundTriggerMiddlewareInternal delegate)120 public SoundTriggerMiddlewareLogging(@NonNull LatencyTracker latencyTracker, 121 @NonNull Supplier<BatteryStatsInternal> batteryStatsInternalSupplier, 122 @NonNull ISoundTriggerMiddlewareInternal delegate) { 123 mDelegate = delegate; 124 mLatencyTracker = latencyTracker; 125 mBatteryStatsInternalSupplier = batteryStatsInternalSupplier; 126 } 127 128 @Override 129 public @NonNull listModules()130 SoundTriggerModuleDescriptor[] listModules() { 131 try { 132 SoundTriggerModuleDescriptor[] result = mDelegate.listModules(); 133 var moduleSummary = Arrays.stream(result).map((descriptor) -> 134 new ModulePropertySummary(descriptor.handle, 135 descriptor.properties.implementor, 136 descriptor.properties.version)).toArray(ModulePropertySummary[]::new); 137 138 mServiceEventLogger.enqueue(ServiceEvent.createForReturn( 139 ServiceEvent.Type.LIST_MODULE, 140 IdentityContext.get().packageName, moduleSummary).printLog(ALOGI, TAG)); 141 return result; 142 } catch (Exception e) { 143 mServiceEventLogger.enqueue(ServiceEvent.createForException( 144 ServiceEvent.Type.LIST_MODULE, 145 IdentityContext.get().packageName, e).printLog(ALOGW, TAG)); 146 throw e; 147 } 148 } 149 150 @Override 151 public @NonNull attach(int handle, ISoundTriggerCallback callback, boolean isTrusted)152 ISoundTriggerModule attach(int handle, ISoundTriggerCallback callback, boolean isTrusted) { 153 try { 154 var originatorIdentity = IdentityContext.getNonNull(); 155 String packageIdentification = originatorIdentity.packageName 156 + mSessionCount.getAndIncrement() + (isTrusted ? "trusted" : ""); 157 ModuleLogging result = new ModuleLogging(); 158 var eventLogger = new EventLogger(SESSION_MAX_EVENT_SIZE, 159 "Session logger for: " + packageIdentification); 160 161 var callbackWrapper = new CallbackLogging(callback, eventLogger, originatorIdentity); 162 163 result.attach(mDelegate.attach(handle, callbackWrapper, isTrusted), eventLogger); 164 165 mServiceEventLogger.enqueue(ServiceEvent.createForReturn( 166 ServiceEvent.Type.ATTACH, 167 packageIdentification, result, handle, callback, isTrusted) 168 .printLog(ALOGI, TAG)); 169 170 mSessionEventLoggers.add(eventLogger); 171 return result; 172 } catch (Exception e) { 173 mServiceEventLogger.enqueue(ServiceEvent.createForException( 174 ServiceEvent.Type.ATTACH, 175 IdentityContext.get().packageName, e, handle, callback) 176 .printLog(ALOGW, TAG)); 177 throw e; 178 } 179 } 180 181 // Override toString() in order to have the delegate's ID in it. 182 @Override toString()183 public String toString() { 184 return mDelegate.toString(); 185 } 186 187 private class ModuleLogging implements ISoundTriggerModule { 188 private ISoundTriggerModule mDelegate; 189 private EventLogger mEventLogger; 190 attach(@onNull ISoundTriggerModule delegate, EventLogger eventLogger)191 void attach(@NonNull ISoundTriggerModule delegate, EventLogger eventLogger) { 192 mDelegate = delegate; 193 mEventLogger = eventLogger; 194 } 195 196 @Override loadModel(SoundModel model)197 public int loadModel(SoundModel model) throws RemoteException { 198 try { 199 int result = mDelegate.loadModel(model); 200 mEventLogger.enqueue(SessionEvent.createForReturn( 201 LOAD_MODEL, result, model.uuid) 202 .printLog(ALOGI, TAG)); 203 return result; 204 } catch (Exception e) { 205 mEventLogger.enqueue(SessionEvent.createForReturn( 206 LOAD_MODEL, e, model.uuid) 207 .printLog(ALOGW, TAG)); 208 throw e; 209 } 210 } 211 212 @Override loadPhraseModel(PhraseSoundModel model)213 public int loadPhraseModel(PhraseSoundModel model) throws RemoteException { 214 try { 215 int result = mDelegate.loadPhraseModel(model); 216 mEventLogger.enqueue(SessionEvent.createForReturn( 217 LOAD_PHRASE_MODEL, result, model.common.uuid) 218 .printLog(ALOGI, TAG)); 219 return result; 220 } catch (Exception e) { 221 mEventLogger.enqueue(SessionEvent.createForException( 222 LOAD_PHRASE_MODEL, e, model.common.uuid) 223 .printLog(ALOGW, TAG)); 224 throw e; 225 } 226 } 227 228 @Override unloadModel(int modelHandle)229 public void unloadModel(int modelHandle) throws RemoteException { 230 try { 231 mDelegate.unloadModel(modelHandle); 232 mEventLogger.enqueue(SessionEvent.createForVoid( 233 UNLOAD_MODEL, modelHandle) 234 .printLog(ALOGI, TAG)); 235 } catch (Exception e) { 236 mEventLogger.enqueue(SessionEvent.createForException( 237 UNLOAD_MODEL, e, modelHandle) 238 .printLog(ALOGW, TAG)); 239 throw e; 240 } 241 } 242 243 @Override startRecognition(int modelHandle, RecognitionConfig config)244 public IBinder startRecognition(int modelHandle, RecognitionConfig config) 245 throws RemoteException { 246 try { 247 var result = mDelegate.startRecognition(modelHandle, config); 248 mEventLogger.enqueue(SessionEvent.createForReturn( 249 START_RECOGNITION, result, modelHandle, config) 250 .printLog(ALOGI, TAG)); 251 return result; 252 } catch (Exception e) { 253 mEventLogger.enqueue(SessionEvent.createForException( 254 START_RECOGNITION, e, modelHandle, config) 255 .printLog(ALOGW, TAG)); 256 throw e; 257 } 258 } 259 260 @Override stopRecognition(int modelHandle)261 public void stopRecognition(int modelHandle) throws RemoteException { 262 try { 263 mDelegate.stopRecognition(modelHandle); 264 mEventLogger.enqueue(SessionEvent.createForVoid( 265 STOP_RECOGNITION, modelHandle) 266 .printLog(ALOGI, TAG)); 267 } catch (Exception e) { 268 mEventLogger.enqueue(SessionEvent.createForException( 269 STOP_RECOGNITION, e, modelHandle) 270 .printLog(ALOGW, TAG)); 271 throw e; 272 } 273 } 274 275 @Override forceRecognitionEvent(int modelHandle)276 public void forceRecognitionEvent(int modelHandle) throws RemoteException { 277 try { 278 mDelegate.forceRecognitionEvent(modelHandle); 279 mEventLogger.enqueue(SessionEvent.createForVoid( 280 FORCE_RECOGNITION, modelHandle) 281 .printLog(ALOGI, TAG)); 282 } catch (Exception e) { 283 mEventLogger.enqueue(SessionEvent.createForException( 284 FORCE_RECOGNITION, e, modelHandle) 285 .printLog(ALOGW, TAG)); 286 throw e; 287 } 288 } 289 290 @Override setModelParameter(int modelHandle, int modelParam, int value)291 public void setModelParameter(int modelHandle, int modelParam, int value) 292 throws RemoteException { 293 try { 294 mDelegate.setModelParameter(modelHandle, modelParam, value); 295 mEventLogger.enqueue(SessionEvent.createForVoid( 296 SET_MODEL_PARAMETER, modelHandle, modelParam, value) 297 .printLog(ALOGI, TAG)); 298 } catch (Exception e) { 299 mEventLogger.enqueue(SessionEvent.createForException( 300 SET_MODEL_PARAMETER, e, modelHandle, modelParam, value) 301 .printLog(ALOGW, TAG)); 302 throw e; 303 } 304 } 305 306 @Override getModelParameter(int modelHandle, int modelParam)307 public int getModelParameter(int modelHandle, int modelParam) throws RemoteException { 308 try { 309 int result = mDelegate.getModelParameter(modelHandle, modelParam); 310 mEventLogger.enqueue(SessionEvent.createForReturn( 311 GET_MODEL_PARAMETER, result, modelHandle, modelParam) 312 .printLog(ALOGI, TAG)); 313 return result; 314 } catch (Exception e) { 315 mEventLogger.enqueue(SessionEvent.createForException( 316 GET_MODEL_PARAMETER, e, modelHandle, modelParam) 317 .printLog(ALOGW, TAG)); 318 throw e; 319 } 320 } 321 322 @Override queryModelParameterSupport(int modelHandle, int modelParam)323 public ModelParameterRange queryModelParameterSupport(int modelHandle, int modelParam) 324 throws RemoteException { 325 try { 326 ModelParameterRange result = mDelegate.queryModelParameterSupport(modelHandle, 327 modelParam); 328 mEventLogger.enqueue(SessionEvent.createForReturn( 329 QUERY_MODEL_PARAMETER, result, modelHandle, modelParam) 330 .printLog(ALOGI, TAG)); 331 return result; 332 } catch (Exception e) { 333 mEventLogger.enqueue(SessionEvent.createForException( 334 QUERY_MODEL_PARAMETER, e, modelHandle, modelParam) 335 .printLog(ALOGW, TAG)); 336 throw e; 337 } 338 } 339 340 @Override detach()341 public void detach() throws RemoteException { 342 try { 343 if (mSessionEventLoggers.remove(mEventLogger)) { 344 while (!mDetachedSessionEventLoggers.offerFirst(mEventLogger)) { 345 // Remove the oldest element, if one still exists 346 mDetachedSessionEventLoggers.pollLast(); 347 } 348 } 349 mDelegate.detach(); 350 mEventLogger.enqueue(SessionEvent.createForVoid( 351 DETACH) 352 .printLog(ALOGI, TAG)); 353 } catch (Exception e) { 354 mEventLogger.enqueue(SessionEvent.createForException( 355 DETACH, e) 356 .printLog(ALOGW, TAG)); 357 throw e; 358 } 359 } 360 361 @Override asBinder()362 public IBinder asBinder() { 363 return mDelegate.asBinder(); 364 } 365 366 // Override toString() in order to have the delegate's ID in it. 367 @Override toString()368 public String toString() { 369 return Objects.toString(mDelegate); 370 } 371 } 372 373 private class CallbackLogging implements ISoundTriggerCallback { 374 private final ISoundTriggerCallback mCallbackDelegate; 375 private final EventLogger mEventLogger; 376 private final Identity mOriginatorIdentity; 377 CallbackLogging(ISoundTriggerCallback delegate, EventLogger eventLogger, Identity originatorIdentity)378 private CallbackLogging(ISoundTriggerCallback delegate, 379 EventLogger eventLogger, Identity originatorIdentity) { 380 mCallbackDelegate = Objects.requireNonNull(delegate); 381 mEventLogger = Objects.requireNonNull(eventLogger); 382 mOriginatorIdentity = originatorIdentity; 383 } 384 385 @Override onRecognition(int modelHandle, RecognitionEventSys event, int captureSession)386 public void onRecognition(int modelHandle, RecognitionEventSys event, int captureSession) 387 throws RemoteException { 388 try { 389 mBatteryStatsInternalSupplier.get().noteWakingSoundTrigger( 390 SystemClock.elapsedRealtime(), mOriginatorIdentity.uid); 391 mCallbackDelegate.onRecognition(modelHandle, event, captureSession); 392 mEventLogger.enqueue(SessionEvent.createForVoid( 393 RECOGNITION, modelHandle, event, captureSession) 394 .printLog(ALOGI, TAG)); 395 } catch (Exception e) { 396 mEventLogger.enqueue(SessionEvent.createForException( 397 RECOGNITION, e, modelHandle, event, captureSession) 398 .printLog(ALOGW, TAG)); 399 throw e; 400 } 401 } 402 403 @Override onPhraseRecognition(int modelHandle, PhraseRecognitionEventSys event, int captureSession)404 public void onPhraseRecognition(int modelHandle, PhraseRecognitionEventSys event, 405 int captureSession) 406 throws RemoteException { 407 try { 408 mBatteryStatsInternalSupplier.get().noteWakingSoundTrigger( 409 SystemClock.elapsedRealtime(), mOriginatorIdentity.uid); 410 startKeyphraseEventLatencyTracking(event.phraseRecognitionEvent); 411 mCallbackDelegate.onPhraseRecognition(modelHandle, event, captureSession); 412 mEventLogger.enqueue(SessionEvent.createForVoid( 413 RECOGNITION, modelHandle, event, captureSession) 414 .printLog(ALOGI, TAG)); 415 } catch (Exception e) { 416 mEventLogger.enqueue(SessionEvent.createForException( 417 RECOGNITION, e, modelHandle, event, captureSession) 418 .printLog(ALOGW, TAG)); 419 throw e; 420 } 421 } 422 423 @Override onModelUnloaded(int modelHandle)424 public void onModelUnloaded(int modelHandle) throws RemoteException { 425 try { 426 mCallbackDelegate.onModelUnloaded(modelHandle); 427 mEventLogger.enqueue(SessionEvent.createForVoid( 428 MODEL_UNLOADED, modelHandle) 429 .printLog(ALOGI, TAG)); 430 } catch (Exception e) { 431 mEventLogger.enqueue(SessionEvent.createForException( 432 MODEL_UNLOADED, e, modelHandle) 433 .printLog(ALOGW, TAG)); 434 throw e; 435 } 436 } 437 438 @Override onResourcesAvailable()439 public void onResourcesAvailable() throws RemoteException { 440 try { 441 mCallbackDelegate.onResourcesAvailable(); 442 mEventLogger.enqueue(SessionEvent.createForVoid( 443 RESOURCES_AVAILABLE) 444 .printLog(ALOGI, TAG)); 445 } catch (Exception e) { 446 mEventLogger.enqueue(SessionEvent.createForException( 447 RESOURCES_AVAILABLE, e) 448 .printLog(ALOGW, TAG)); 449 throw e; 450 } 451 } 452 453 @Override onModuleDied()454 public void onModuleDied() throws RemoteException { 455 try { 456 mCallbackDelegate.onModuleDied(); 457 mEventLogger.enqueue(SessionEvent.createForVoid( 458 MODULE_DIED) 459 .printLog(ALOGW, TAG)); 460 } catch (Exception e) { 461 mEventLogger.enqueue(SessionEvent.createForException( 462 MODULE_DIED, e) 463 .printLog(ALOGW, TAG)); 464 throw e; 465 } 466 } 467 468 @Override asBinder()469 public IBinder asBinder() { 470 return mCallbackDelegate.asBinder(); 471 } 472 473 // Override toString() in order to have the delegate's ID in it. 474 @Override toString()475 public String toString() { 476 return Objects.toString(mCallbackDelegate); 477 } 478 } 479 480 private static class BatteryStatsHolder { 481 private static final BatteryStatsInternal INSTANCE = 482 LocalServices.getService(BatteryStatsInternal.class); 483 } 484 485 /** 486 * Starts the latency tracking log for keyphrase hotword invocation. 487 * The measurement covers from when the SoundTrigger HAL emits an event to when the 488 * {@link android.service.voice.VoiceInteractionSession} system UI view is shown. 489 * 490 * <p>The session is only started if the {@link PhraseRecognitionEvent} has a status of 491 * {@link RecognitionStatus#SUCCESS} 492 */ startKeyphraseEventLatencyTracking(PhraseRecognitionEvent event)493 private void startKeyphraseEventLatencyTracking(PhraseRecognitionEvent event) { 494 if (event.common.status != RecognitionStatus.SUCCESS 495 || ArrayUtils.isEmpty(event.phraseExtras)) { 496 return; 497 } 498 499 String latencyTrackerTag = "KeyphraseId=" + event.phraseExtras[0].id; 500 // To avoid adding cancel to all the different failure modes between here and 501 // showing the system UI, we defensively cancel once. 502 // Either we hit the LatencyTracker timeout of 15 seconds or we defensively cancel 503 // here if any error occurs. 504 mLatencyTracker.onActionCancel(LatencyTracker.ACTION_SHOW_VOICE_INTERACTION); 505 mLatencyTracker.onActionStart(LatencyTracker.ACTION_SHOW_VOICE_INTERACTION, 506 latencyTrackerTag); 507 } 508 printArgs(StringBuilder builder, @NonNull Object[] args)509 private static StringBuilder printArgs(StringBuilder builder, @NonNull Object[] args) { 510 for (int i = 0; i < args.length; ++i) { 511 if (i > 0) { 512 builder.append(", "); 513 } 514 ObjectPrinter.print(builder, args[i]); 515 } 516 return builder; 517 } 518 519 @Override dump(PrintWriter pw)520 public void dump(PrintWriter pw) { 521 // Event loggers 522 pw.println("##Service-Wide logs:"); 523 mServiceEventLogger.dump(pw, /* indent = */ " "); 524 525 pw.println("\n##Active Session dumps:\n"); 526 for (var sessionLogger : mSessionEventLoggers) { 527 sessionLogger.dump(pw, /* indent= */ " "); 528 pw.println(""); 529 } 530 pw.println("##Detached Session dumps:\n"); 531 for (var sessionLogger : mDetachedSessionEventLoggers) { 532 sessionLogger.dump(pw, /* indent= */ " "); 533 pw.println(""); 534 } 535 536 if (mDelegate instanceof Dumpable) { 537 ((Dumpable) mDelegate).dump(pw); 538 } 539 } 540 printSystemLog(int type, String tag, String message, Exception e)541 public static void printSystemLog(int type, String tag, String message, Exception e) { 542 switch (type) { 543 case Event.ALOGI: 544 Slog.i(tag, message, e); 545 break; 546 case Event.ALOGE: 547 Slog.e(tag, message, e); 548 break; 549 case Event.ALOGW: 550 Slog.w(tag, message, e); 551 break; 552 case Event.ALOGV: 553 default: 554 Slog.v(tag, message, e); 555 } 556 } 557 558 public static class ServiceEvent extends Event { 559 private final Type mType; 560 private final String mPackageName; 561 private final Object mReturnValue; 562 private final Object[] mParams; 563 private final Exception mException; 564 565 public enum Type { 566 ATTACH, 567 LIST_MODULE, 568 } 569 createForException(Type type, String packageName, Exception exception, Object... params)570 public static ServiceEvent createForException(Type type, String packageName, 571 Exception exception, Object... params) { 572 return new ServiceEvent(exception, type, packageName, null, params); 573 } 574 createForReturn(Type type, String packageName, Object returnValue, Object... params)575 public static ServiceEvent createForReturn(Type type, String packageName, 576 Object returnValue, Object... params) { 577 return new ServiceEvent(null , type, packageName, returnValue, params); 578 } 579 ServiceEvent(Exception exception, Type type, String packageName, Object returnValue, Object... params)580 private ServiceEvent(Exception exception, Type type, String packageName, Object returnValue, 581 Object... params) { 582 mException = exception; 583 mType = type; 584 mPackageName = packageName; 585 mReturnValue = returnValue; 586 mParams = params; 587 } 588 589 @Override printLog(int type, String tag)590 public Event printLog(int type, String tag) { 591 printSystemLog(type, tag, eventToString(), mException); 592 return this; 593 } 594 595 @Override eventToString()596 public String eventToString() { 597 var sb = new StringBuilder(mType.name()).append(" [client= "); 598 ObjectPrinter.print(sb, mPackageName); 599 sb.append("] ("); 600 printArgs(sb, mParams); 601 sb.append(") -> "); 602 if (mException != null) { 603 sb.append("ERROR: "); 604 ObjectPrinter.print(sb, mException); 605 } else { 606 ObjectPrinter.print(sb, mReturnValue); 607 } 608 return sb.toString(); 609 } 610 } 611 612 public static class SessionEvent extends Event { 613 public enum Type { 614 LOAD_MODEL, 615 LOAD_PHRASE_MODEL, 616 START_RECOGNITION, 617 STOP_RECOGNITION, 618 FORCE_RECOGNITION, 619 UNLOAD_MODEL, 620 GET_MODEL_PARAMETER, 621 SET_MODEL_PARAMETER, 622 QUERY_MODEL_PARAMETER, 623 DETACH, 624 RECOGNITION, 625 MODEL_UNLOADED, 626 MODULE_DIED, 627 RESOURCES_AVAILABLE, 628 } 629 630 private final Type mType; 631 private final Exception mException; 632 private final Object mReturnValue; 633 private final Object[] mParams; 634 createForException(Type type, Exception exception, Object... params)635 public static SessionEvent createForException(Type type, Exception exception, 636 Object... params) { 637 return new SessionEvent(exception, type, null, params); 638 } 639 createForReturn(Type type, Object returnValue, Object... params)640 public static SessionEvent createForReturn(Type type, 641 Object returnValue, Object... params) { 642 return new SessionEvent(null , type, returnValue, params); 643 } 644 createForVoid(Type type, Object... params)645 public static SessionEvent createForVoid(Type type, Object... params) { 646 return new SessionEvent(null, type, null, params); 647 } 648 649 SessionEvent(Exception exception, Type type, Object returnValue, Object... params)650 private SessionEvent(Exception exception, Type type, Object returnValue, 651 Object... params) { 652 mException = exception; 653 mType = type; 654 mReturnValue = returnValue; 655 mParams = params; 656 } 657 658 @Override printLog(int type, String tag)659 public Event printLog(int type, String tag) { 660 printSystemLog(type, tag, eventToString(), mException); 661 return this; 662 } 663 664 @Override eventToString()665 public String eventToString() { 666 var sb = new StringBuilder(mType.name()); 667 sb.append(" ("); 668 printArgs(sb, mParams); 669 sb.append(")"); 670 if (mException != null) { 671 sb.append(" -> ERROR: "); 672 ObjectPrinter.print(sb, mException); 673 } else if (mReturnValue != null) { 674 sb.append(" -> "); 675 ObjectPrinter.print(sb, mReturnValue); 676 } 677 return sb.toString(); 678 } 679 } 680 681 private static final class ModulePropertySummary { 682 private int mId; 683 private String mImplementor; 684 private int mVersion; 685 ModulePropertySummary(int id, String implementor, int version)686 ModulePropertySummary(int id, String implementor, int version) { 687 mId = id; 688 mImplementor = implementor; 689 mVersion = version; 690 } 691 692 @Override toString()693 public String toString() { 694 return "{Id: " + mId + ", Implementor: " + mImplementor 695 + ", Version: " + mVersion + "}"; 696 } 697 } 698 } 699