1/*
2 * Copyright (C) 2013 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
17package android.media;
18
19import android.media.MediaFocusControl.AudioFocusDeathHandler;
20import android.os.IBinder;
21import android.util.Log;
22
23import java.io.PrintWriter;
24
25/**
26 * @hide
27 * Class to handle all the information about a user of audio focus. The lifecycle of each
28 * instance is managed by android.media.MediaFocusControl, from its addition to the audio focus
29 * stack to its release.
30 */
31class FocusRequester {
32
33    // on purpose not using this classe's name, as it will only be used from MediaFocusControl
34    private static final String TAG = "MediaFocusControl";
35    private static final boolean DEBUG = false;
36
37    private AudioFocusDeathHandler mDeathHandler;
38    private final IAudioFocusDispatcher mFocusDispatcher; // may be null
39    private final IBinder mSourceRef;
40    private final String mClientId;
41    private final String mPackageName;
42    private final int mCallingUid;
43    /**
44     * the audio focus gain request that caused the addition of this object in the focus stack.
45     */
46    private final int mFocusGainRequest;
47    /**
48     * the audio focus loss received my mFocusDispatcher, is AudioManager.AUDIOFOCUS_NONE if
49     *  it never lost focus.
50     */
51    private int mFocusLossReceived;
52    /**
53     * the stream type associated with the focus request
54     */
55    private final int mStreamType;
56
57    FocusRequester(int streamType, int focusRequest,
58            IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
59            String pn, int uid) {
60        mStreamType = streamType;
61        mFocusDispatcher = afl;
62        mSourceRef = source;
63        mClientId = id;
64        mDeathHandler = hdlr;
65        mPackageName = pn;
66        mCallingUid = uid;
67        mFocusGainRequest = focusRequest;
68        mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
69    }
70
71
72    boolean hasSameClient(String otherClient) {
73        try {
74            return mClientId.compareTo(otherClient) == 0;
75        } catch (NullPointerException e) {
76            return false;
77        }
78    }
79
80    boolean hasSameBinder(IBinder ib) {
81        return (mSourceRef != null) && mSourceRef.equals(ib);
82    }
83
84    boolean hasSamePackage(String pack) {
85        try {
86            return mPackageName.compareTo(pack) == 0;
87        } catch (NullPointerException e) {
88            return false;
89        }
90    }
91
92    boolean hasSameUid(int uid) {
93        return mCallingUid == uid;
94    }
95
96
97    int getGainRequest() {
98        return mFocusGainRequest;
99    }
100
101    int getStreamType() {
102        return mStreamType;
103    }
104
105
106    private static String focusChangeToString(int focus) {
107        switch(focus) {
108            case AudioManager.AUDIOFOCUS_NONE:
109                return "none";
110            case AudioManager.AUDIOFOCUS_GAIN:
111                return "GAIN";
112            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
113                return "GAIN_TRANSIENT";
114            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
115                return "GAIN_TRANSIENT_MAY_DUCK";
116            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
117                return "GAIN_TRANSIENT_EXCLUSIVE";
118            case AudioManager.AUDIOFOCUS_LOSS:
119                return "LOSS";
120            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
121                return "LOSS_TRANSIENT";
122            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
123                return "LOSS_TRANSIENT_CAN_DUCK";
124            default:
125                return "[invalid focus change" + focus + "]";
126        }
127    }
128
129    private String focusGainToString() {
130        return focusChangeToString(mFocusGainRequest);
131    }
132
133    private String focusLossToString() {
134        return focusChangeToString(mFocusLossReceived);
135    }
136
137    void dump(PrintWriter pw) {
138        pw.println("  source:" + mSourceRef
139                + " -- pack: " + mPackageName
140                + " -- client: " + mClientId
141                + " -- gain: " + focusGainToString()
142                + " -- loss: " + focusLossToString()
143                + " -- uid: " + mCallingUid
144                + " -- stream: " + mStreamType);
145    }
146
147
148    void release() {
149        try {
150            if (mSourceRef != null && mDeathHandler != null) {
151                mSourceRef.unlinkToDeath(mDeathHandler, 0);
152                mDeathHandler = null;
153            }
154        } catch (java.util.NoSuchElementException e) {
155            Log.e(TAG, "FocusRequester.release() hit ", e);
156        }
157    }
158
159    @Override
160    protected void finalize() throws Throwable {
161        release();
162        super.finalize();
163    }
164
165    /**
166     * For a given audio focus gain request, return the audio focus loss type that will result
167     * from it, taking into account any previous focus loss.
168     * @param gainRequest
169     * @return the audio focus loss type that matches the gain request
170     */
171    private int focusLossForGainRequest(int gainRequest) {
172        switch(gainRequest) {
173            case AudioManager.AUDIOFOCUS_GAIN:
174                switch(mFocusLossReceived) {
175                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
176                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
177                    case AudioManager.AUDIOFOCUS_LOSS:
178                    case AudioManager.AUDIOFOCUS_NONE:
179                        return AudioManager.AUDIOFOCUS_LOSS;
180                }
181            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
182            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
183                switch(mFocusLossReceived) {
184                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
185                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
186                    case AudioManager.AUDIOFOCUS_NONE:
187                        return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
188                    case AudioManager.AUDIOFOCUS_LOSS:
189                        return AudioManager.AUDIOFOCUS_LOSS;
190                }
191            case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
192                switch(mFocusLossReceived) {
193                    case AudioManager.AUDIOFOCUS_NONE:
194                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
195                        return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
196                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
197                        return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
198                    case AudioManager.AUDIOFOCUS_LOSS:
199                        return AudioManager.AUDIOFOCUS_LOSS;
200                }
201            default:
202                Log.e(TAG, "focusLossForGainRequest() for invalid focus request "+ gainRequest);
203                        return AudioManager.AUDIOFOCUS_NONE;
204        }
205    }
206
207    void handleExternalFocusGain(int focusGain) {
208        int focusLoss = focusLossForGainRequest(focusGain);
209        handleFocusLoss(focusLoss);
210    }
211
212    void handleFocusGain(int focusGain) {
213        try {
214            if (mFocusDispatcher != null) {
215                if (DEBUG) {
216                    Log.v(TAG, "dispatching " + focusChangeToString(focusGain) + " to "
217                        + mClientId);
218                }
219                mFocusDispatcher.dispatchAudioFocusChange(focusGain, mClientId);
220            }
221            mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
222        } catch (android.os.RemoteException e) {
223            Log.e(TAG, "Failure to signal gain of audio focus due to: ", e);
224        }
225    }
226
227    void handleFocusLoss(int focusLoss) {
228        try {
229            if (focusLoss != mFocusLossReceived) {
230                if (mFocusDispatcher != null) {
231                    if (DEBUG) {
232                        Log.v(TAG, "dispatching " + focusChangeToString(focusLoss) + " to "
233                            + mClientId);
234                    }
235                    mFocusDispatcher.dispatchAudioFocusChange(focusLoss, mClientId);
236                }
237                mFocusLossReceived = focusLoss;
238            }
239        } catch (android.os.RemoteException e) {
240            Log.e(TAG, "Failure to signal loss of audio focus due to:", e);
241        }
242    }
243
244}
245