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