• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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