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 android.annotation.NonNull; 20 import android.util.Log; 21 22 import java.util.LinkedList; 23 import java.util.List; 24 import java.util.concurrent.Semaphore; 25 26 /** 27 * This is a never-give-up listener for sound trigger external capture state notifications, as 28 * published by the audio policy service. 29 * 30 * This class will constantly try to connect to the service over a background thread and tolerate 31 * its death. 32 */ 33 class ExternalCaptureStateTracker implements ICaptureStateNotifier { 34 private static final String TAG = "CaptureStateTracker"; 35 36 /** Our client's listeners. Also used as lock. */ 37 private final List<Listener> mListeners = new LinkedList<>(); 38 39 /** Conservatively, until notified otherwise, we assume capture is active. */ 40 private boolean mCaptureActive = true; 41 42 /** This semaphore will get a permit every time we need to reconnect. */ 43 private final Semaphore mNeedToConnect = new Semaphore(1); 44 45 /** 46 * Constructor. Will start a background thread to do the work. 47 */ ExternalCaptureStateTracker()48 ExternalCaptureStateTracker() { 49 new Thread(this::run).start(); 50 } 51 52 53 @Override registerListener(@onNull Listener listener)54 public boolean registerListener(@NonNull Listener listener) { 55 synchronized (mListeners) { 56 mListeners.add(listener); 57 return mCaptureActive; 58 } 59 } 60 61 @Override unregisterListener(Listener listener)62 public void unregisterListener(Listener listener) { 63 synchronized (mListeners) { 64 mListeners.remove(listener); 65 } 66 } 67 68 /** 69 * Routine for the background thread. Keeps trying to reconnect. 70 */ run()71 private void run() { 72 while (true) { 73 mNeedToConnect.acquireUninterruptibly(); 74 connect(); 75 } 76 } 77 78 /** 79 * Connect to the service, install listener and death notifier. 80 */ connect()81 private native void connect(); 82 83 /** 84 * Called by native code to invoke the client listener. 85 * 86 * @param active true when external capture is active. 87 */ setCaptureState(boolean active)88 private void setCaptureState(boolean active) { 89 try { 90 synchronized (mListeners) { 91 mCaptureActive = active; 92 for (Listener listener : mListeners) { 93 listener.onCaptureStateChange(active); 94 } 95 } 96 } catch (Exception e) { 97 Log.e(TAG, "Exception caught while setting capture state", e); 98 } 99 } 100 101 /** 102 * Called by native code when the remote service died. 103 */ binderDied()104 private void binderDied() { 105 Log.w(TAG, "Audio policy service died"); 106 mNeedToConnect.release(); 107 } 108 } 109