1816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/*
2816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Copyright (C) 2015 The Android Open Source Project
3816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
4816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License");
5816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * you may not use this file except in compliance with the License.
6816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * You may obtain a copy of the License at
7816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
8816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *      http://www.apache.org/licenses/LICENSE-2.0
9816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko *
10816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Unless required by applicable law or agreed to in writing, software
11816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS,
12816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * See the License for the specific language governing permissions and
14816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * limitations under the License.
15816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */
16816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopackage com.android.tv.receiver;
17816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
18816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.BroadcastReceiver;
19816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.Context;
20816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.Intent;
21816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.IntentFilter;
22816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.SharedPreferences;
23816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.AudioFormat;
24816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.AudioManager;
257d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.support.annotation.NonNull;
267d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport android.support.annotation.Nullable;
27816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
281abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.ApplicationSingletons;
297d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.TvApplication;
307d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalkoimport com.android.tv.analytics.Analytics;
31816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.analytics.Tracker;
32ba5845f23b8fbc985890f892961abc8b39886611Nick Chalkoimport com.android.tv.common.SharedPreferencesUtils;
33816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
34816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/**
357d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * Creates HDMI plug broadcast receiver, and reports AC3 passthrough capabilities to Google
367d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * Analytics and listeners. Call {@link #register} to start receiving notifications, and
377d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko * {@link #unregister} to stop.
38816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */
39816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopublic final class AudioCapabilitiesReceiver {
40816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String SETTINGS_KEY_AC3_PASSTHRU_REPORTED = "ac3_passthrough_reported";
41816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private static final String SETTINGS_KEY_AC3_PASSTHRU_CAPABILITIES = "ac3_passthrough";
4207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private static final String SETTINGS_KEY_AC3_REPORT_REVISION = "ac3_report_revision";
4307b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
4407b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    // AC3 capabilities stat is sent to Google Analytics just once in order to avoid
4507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    // duplicated stat reports since it doesn't change over time in most cases.
4607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    // Increase this revision when we should force the stat to be sent again.
4707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    // TODO: Consier using custom metrics.
4807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private static final int REPORT_REVISION = 1;
49816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
50816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Context mContext;
517d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private final Analytics mAnalytics;
52816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final Tracker mTracker;
537d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    @Nullable
547d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private final OnAc3PassthroughCapabilityChangeListener mListener;
55816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final BroadcastReceiver mReceiver = new HdmiAudioPlugBroadcastReceiver();
56816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
57816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    /**
58816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * Constructs a new audio capabilities receiver.
59816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     *
60816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     * @param context context for registering to receive broadcasts
617d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko     * @param listener listener which receives AC3 passthrough capability change notification
62816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko     */
637d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    public AudioCapabilitiesReceiver(@NonNull Context context,
647d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            @Nullable OnAc3PassthroughCapabilityChangeListener listener) {
65816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mContext = context;
661abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        ApplicationSingletons appSingletons = TvApplication.getSingletons(context);
671abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mAnalytics = appSingletons.getAnalytics();
681abddd9f6225298066094e20a6c29061b6af4590Nick Chalko        mTracker = appSingletons.getTracker();
697d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        mListener = listener;
70816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
71816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
72816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void register() {
73816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mContext.registerReceiver(mReceiver, new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG));
74816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
75816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
76816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    public void unregister() {
77816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        mContext.unregisterReceiver(mReceiver);
78816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
79816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
80816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private final class HdmiAudioPlugBroadcastReceiver extends BroadcastReceiver {
81816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        @Override
82816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        public void onReceive(Context context, Intent intent) {
83816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            String action = intent.getAction();
84816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            if (!action.equals(AudioManager.ACTION_HDMI_AUDIO_PLUG)) {
85816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko                return;
86816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            }
877d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            boolean supported = false;
887d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            int[] supportedEncodings = intent.getIntArrayExtra(AudioManager.EXTRA_ENCODINGS);
897d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (supportedEncodings != null) {
907d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                for (int supportedEncoding : supportedEncodings) {
917d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    if (supportedEncoding == AudioFormat.ENCODING_AC3) {
927d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        supported = true;
937d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                        break;
947d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                    }
957d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                }
967d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
977d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (mListener != null) {
987d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                mListener.onAc3PassthroughCapabilityChange(supported);
997d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
1007d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            if (!mAnalytics.isAppOptOut()) {
1017d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko                reportAudioCapabilities(supported);
1027d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            }
103816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
104816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
105816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
1067d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    private void reportAudioCapabilities(boolean ac3Supported) {
10707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        boolean oldVal = getBoolean(SETTINGS_KEY_AC3_PASSTHRU_CAPABILITIES, false);
10807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        boolean reported = getBoolean(SETTINGS_KEY_AC3_PASSTHRU_REPORTED, false);
10907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        int revision = getInt(SETTINGS_KEY_AC3_REPORT_REVISION, 0);
110816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
111816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        // Send the value just once. But we send it again if the value changed, to include
112816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        // the case where users have switched TV device with different AC3 passthrough capabilities.
1137d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        if (!reported || oldVal != ac3Supported || REPORT_REVISION > revision) {
1147d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            mTracker.sendAc3PassthroughCapabilities(ac3Supported);
115816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko            setBoolean(SETTINGS_KEY_AC3_PASSTHRU_REPORTED, true);
1167d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko            setBoolean(SETTINGS_KEY_AC3_PASSTHRU_CAPABILITIES, ac3Supported);
11707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            if (REPORT_REVISION > revision) {
11807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko                setInt(SETTINGS_KEY_AC3_REPORT_REVISION, REPORT_REVISION);
11907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko            }
120816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        }
121816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
122816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
123816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private SharedPreferences getSharedPreferences() {
124ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko        return mContext.getSharedPreferences(SharedPreferencesUtils.SHARED_PREF_AUDIO_CAPABILITIES,
125ba5845f23b8fbc985890f892961abc8b39886611Nick Chalko                Context.MODE_PRIVATE);
126816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
127816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
128816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private boolean getBoolean(String key, boolean def) {
129816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        return getSharedPreferences().getBoolean(key, def);
130816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
131816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko
132816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    private void setBoolean(String key, boolean val) {
133816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko        getSharedPreferences().edit().putBoolean(key, val).apply();
134816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko    }
13507b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
13607b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private int getInt(String key, int def) {
13707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        return getSharedPreferences().getInt(key, def);
13807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
13907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko
14007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    private void setInt(String key, int val) {
14107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko        getSharedPreferences().edit().putInt(key, val).apply();
14207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko    }
1437d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko
1447d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    /**
1457d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko     * Listener notified when AC3 passthrough capability changes.
1467d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko     */
1477d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    public interface OnAc3PassthroughCapabilityChangeListener {
1487d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        /**
1497d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko         * Called when the AC3 passthrough capability changes.
1507d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko         */
1517d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko        void onAc3PassthroughCapabilityChange(boolean capability);
1527d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko    }
153816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko}
154