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