1 /* 2 * Copyright (C) 2015 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 package com.android.tv.receiver; 17 18 import android.content.BroadcastReceiver; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.IntentFilter; 22 import android.content.SharedPreferences; 23 import android.media.AudioFormat; 24 import android.media.AudioManager; 25 import android.support.annotation.NonNull; 26 import android.support.annotation.Nullable; 27 import com.android.tv.TvSingletons; 28 import com.android.tv.analytics.Analytics; 29 import com.android.tv.analytics.Tracker; 30 import com.android.tv.common.util.SharedPreferencesUtils; 31 32 /** 33 * Creates HDMI plug broadcast receiver, and reports AC3 passthrough capabilities to Google 34 * Analytics and listeners. Call {@link #register} to start receiving notifications, and {@link 35 * #unregister} to stop. 36 */ 37 public final class AudioCapabilitiesReceiver { 38 private static final String SETTINGS_KEY_AC3_PASSTHRU_REPORTED = "ac3_passthrough_reported"; 39 private static final String SETTINGS_KEY_AC3_PASSTHRU_CAPABILITIES = "ac3_passthrough"; 40 private static final String SETTINGS_KEY_AC3_REPORT_REVISION = "ac3_report_revision"; 41 42 // AC3 capabilities stat is sent to Google Analytics just once in order to avoid 43 // duplicated stat reports since it doesn't change over time in most cases. 44 // Increase this revision when we should force the stat to be sent again. 45 // TODO: Consider using custom metrics. 46 private static final int REPORT_REVISION = 1; 47 48 private final Context mContext; 49 private final Analytics mAnalytics; 50 private final Tracker mTracker; 51 @Nullable private final OnAc3PassthroughCapabilityChangeListener mListener; 52 private final BroadcastReceiver mReceiver = new HdmiAudioPlugBroadcastReceiver(); 53 54 /** 55 * Constructs a new audio capabilities receiver. 56 * 57 * @param context context for registering to receive broadcasts 58 * @param listener listener which receives AC3 passthrough capability change notification 59 */ AudioCapabilitiesReceiver( @onNull Context context, @Nullable OnAc3PassthroughCapabilityChangeListener listener)60 public AudioCapabilitiesReceiver( 61 @NonNull Context context, @Nullable OnAc3PassthroughCapabilityChangeListener listener) { 62 mContext = context; 63 TvSingletons tvSingletons = TvSingletons.getSingletons(context); 64 mAnalytics = tvSingletons.getAnalytics(); 65 mTracker = tvSingletons.getTracker(); 66 mListener = listener; 67 } 68 register()69 public void register() { 70 mContext.registerReceiver(mReceiver, new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG)); 71 } 72 unregister()73 public void unregister() { 74 mContext.unregisterReceiver(mReceiver); 75 } 76 77 private final class HdmiAudioPlugBroadcastReceiver extends BroadcastReceiver { 78 @Override onReceive(Context context, Intent intent)79 public void onReceive(Context context, Intent intent) { 80 String action = intent.getAction(); 81 if (!action.equals(AudioManager.ACTION_HDMI_AUDIO_PLUG)) { 82 return; 83 } 84 boolean supported = false; 85 int[] supportedEncodings = intent.getIntArrayExtra(AudioManager.EXTRA_ENCODINGS); 86 if (supportedEncodings != null) { 87 for (int supportedEncoding : supportedEncodings) { 88 if (supportedEncoding == AudioFormat.ENCODING_AC3) { 89 supported = true; 90 break; 91 } 92 } 93 } 94 if (mListener != null) { 95 mListener.onAc3PassthroughCapabilityChange(supported); 96 } 97 if (!mAnalytics.isAppOptOut()) { 98 reportAudioCapabilities(supported); 99 } 100 } 101 } 102 reportAudioCapabilities(boolean ac3Supported)103 private void reportAudioCapabilities(boolean ac3Supported) { 104 boolean oldVal = getBoolean(SETTINGS_KEY_AC3_PASSTHRU_CAPABILITIES, false); 105 boolean reported = getBoolean(SETTINGS_KEY_AC3_PASSTHRU_REPORTED, false); 106 int revision = getInt(SETTINGS_KEY_AC3_REPORT_REVISION, 0); 107 108 // Send the value just once. But we send it again if the value changed, to include 109 // the case where users have switched TV device with different AC3 passthrough capabilities. 110 if (!reported || oldVal != ac3Supported || REPORT_REVISION > revision) { 111 mTracker.sendAc3PassthroughCapabilities(ac3Supported); 112 setBoolean(SETTINGS_KEY_AC3_PASSTHRU_REPORTED, true); 113 setBoolean(SETTINGS_KEY_AC3_PASSTHRU_CAPABILITIES, ac3Supported); 114 if (REPORT_REVISION > revision) { 115 setInt(SETTINGS_KEY_AC3_REPORT_REVISION, REPORT_REVISION); 116 } 117 } 118 } 119 getSharedPreferences()120 private SharedPreferences getSharedPreferences() { 121 return mContext.getSharedPreferences( 122 SharedPreferencesUtils.SHARED_PREF_AUDIO_CAPABILITIES, Context.MODE_PRIVATE); 123 } 124 getBoolean(String key, boolean def)125 private boolean getBoolean(String key, boolean def) { 126 return getSharedPreferences().getBoolean(key, def); 127 } 128 setBoolean(String key, boolean val)129 private void setBoolean(String key, boolean val) { 130 getSharedPreferences().edit().putBoolean(key, val).apply(); 131 } 132 getInt(String key, int def)133 private int getInt(String key, int def) { 134 return getSharedPreferences().getInt(key, def); 135 } 136 setInt(String key, int val)137 private void setInt(String key, int val) { 138 getSharedPreferences().edit().putInt(key, val).apply(); 139 } 140 141 /** Listener notified when AC3 passthrough capability changes. */ 142 public interface OnAc3PassthroughCapabilityChangeListener { 143 /** Called when the AC3 passthrough capability changes. */ onAc3PassthroughCapabilityChange(boolean capability)144 void onAc3PassthroughCapabilityChange(boolean capability); 145 } 146 } 147