1/* 2 * Copyright (C) 2011 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 com.android.musicfx; 18 19import android.app.Activity; 20import android.app.IntentService; 21import android.content.BroadcastReceiver; 22import android.content.ComponentName; 23import android.content.Context; 24import android.content.Intent; 25import android.content.SharedPreferences; 26import android.content.SharedPreferences.Editor; 27import android.content.pm.PackageManager; 28import android.content.pm.ResolveInfo; 29import android.media.audiofx.AudioEffect; 30import android.net.Uri; 31import android.os.Bundle; 32import android.util.Log; 33 34import java.util.List; 35 36/** 37 * Provide backwards compatibility for existing control panels. 38 * There are two major parts to this: 39 * - a BroadcastReceiver that listens for installed or removed packages, and 40 * enables or disables control panel receivers as needed to ensure that only 41 * one control panel package will receive the broadcasts that applications end 42 * - a high priority control panel activity that redirects to the currently 43 * selected control panel activity 44 * 45 */ 46public class Compatibility { 47 48 private final static String TAG = "MusicFXCompat"; 49 // run "setprop log.tag.MusicFXCompat DEBUG" to turn on logging 50 private final static boolean LOG = Log.isLoggable(TAG, Log.DEBUG); 51 52 53 /** 54 * This activity has an intent filter with the highest possible priority, so 55 * it will always be chosen. It then looks up the correct control panel to 56 * use and launches that. 57 */ 58 public static class Redirector extends Activity { 59 60 @Override 61 public void onCreate(final Bundle savedInstanceState) { 62 super.onCreate(savedInstanceState); 63 log("Compatibility Activity called from " + getCallingPackage()); 64 Intent i = new Intent(getIntent()); 65 i.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); 66 SharedPreferences pref = getSharedPreferences("musicfx", MODE_PRIVATE); 67 String defPackage = pref.getString("defaultpanelpackage", null); 68 String defName = pref.getString("defaultpanelname", null); 69 log("read " + defPackage + "/" + defName + " as default"); 70 if (defPackage == null || defName == null) { 71 Log.e(TAG, "no default set!"); 72 // use the built-in panel 73 i.setComponent(new ComponentName(this, ActivityMusic.class)); 74 // also save it as the default 75 Intent updateIntent = new Intent(this, Service.class); 76 updateIntent.putExtra("defPackage", getPackageName()); 77 updateIntent.putExtra("defName", ActivityMusic.class.getName()); 78 startService(updateIntent); 79 } else { 80 i.setComponent(new ComponentName(defPackage, defName)); 81 } 82 startActivity(i); 83 finish(); 84 } 85 } 86 87 /** 88 * This BroadcastReceiver responds to BOOT_COMPLETED, PACKAGE_ADDED, 89 * PACKAGE_REPLACED and PACKAGE_REMOVED intents. When run, it checks 90 * to see whether the active control panel needs to be updated: 91 * - if there is no default, it picks one 92 * - if a new control panel is installed, it becomes the default 93 * It then enables the open/close receivers in the active control panel, 94 * and disables them in the others. 95 */ 96 public static class Receiver extends BroadcastReceiver { 97 98 @Override 99 public void onReceive(final Context context, final Intent intent) { 100 101 log("received"); 102 Intent updateIntent = new Intent(context, Service.class); 103 updateIntent.putExtra("reason", intent); 104 context.startService(updateIntent); 105 } 106 } 107 108 public static class Service extends IntentService { 109 110 PackageManager mPackageManager; 111 112 public Service() { 113 super("CompatibilityService"); 114 } 115 116 @Override 117 protected void onHandleIntent(final Intent intent) { 118 log("handleintent"); 119 if (mPackageManager == null) { 120 mPackageManager = getPackageManager(); 121 } 122 123 String defPackage = intent.getStringExtra("defPackage"); 124 String defName = intent.getStringExtra("defName"); 125 if (defPackage != null && defName != null) { 126 setDefault(defPackage, defName); 127 return; 128 } 129 130 Intent packageIntent = intent.getParcelableExtra("reason"); 131 Bundle b = packageIntent.getExtras(); 132 if (b != null) b.size(); 133 log("intentservice saw: " + packageIntent + " " + b); 134 // TODO, be smarter about package upgrades (which results in three 135 // broadcasts: removed, added, replaced) 136 Uri packageUri = packageIntent.getData(); 137 String updatedPackage = null; 138 if (packageUri != null) { 139 updatedPackage = packageUri.toString().substring(8); 140 pickDefaultControlPanel(updatedPackage); 141 } 142 } 143 144 private void pickDefaultControlPanel(String updatedPackage) { 145 146 ResolveInfo defPanel = null; 147 ResolveInfo otherPanel = null; 148 ResolveInfo thisPanel = null; 149 Intent i = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL); 150 List<ResolveInfo> ris = mPackageManager.queryIntentActivities(i, PackageManager.GET_DISABLED_COMPONENTS); 151 log("found: " + ris.size()); 152 SharedPreferences pref = getSharedPreferences("musicfx", MODE_PRIVATE); 153 String savedDefPackage = pref.getString("defaultpanelpackage", null); 154 String savedDefName = pref.getString("defaultpanelname", null); 155 log("saved default: " + savedDefName); 156 for (ResolveInfo foo: ris) { 157 if (foo.activityInfo.name.equals(Compatibility.Redirector.class.getName())) { 158 log("skipping " + foo); 159 continue; 160 } 161 log("considering " + foo); 162 if (foo.activityInfo.name.equals(savedDefName) && 163 foo.activityInfo.packageName.equals(savedDefPackage) && 164 foo.activityInfo.enabled) { 165 log("default: " + savedDefName); 166 defPanel = foo; 167 break; 168 } else if (foo.activityInfo.packageName.equals(updatedPackage)) { 169 log("choosing newly installed package " + updatedPackage); 170 otherPanel = foo; 171 } else if (otherPanel == null && !foo.activityInfo.packageName.equals(getPackageName())) { 172 otherPanel = foo; 173 } else { 174 thisPanel = foo; 175 } 176 } 177 178 if (defPanel == null) { 179 // pick a default control panel 180 if (otherPanel == null) { 181 if (thisPanel == null) { 182 Log.e(TAG, "No control panels found!"); 183 return; 184 } 185 otherPanel = thisPanel; 186 } 187 defPanel = otherPanel; 188 } 189 190 // Now that we have selected a default control panel activity, ensure 191 // that the broadcast receiver(s) in that same package are enabled, 192 // and the ones in the other packages are disabled. 193 String defPackage = defPanel.activityInfo.packageName; 194 String defName = defPanel.activityInfo.name; 195 setDefault(defPackage, defName); 196 } 197 198 private void setDefault(String defPackage, String defName) { 199 Intent i = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION); 200 List<ResolveInfo> ris = mPackageManager.queryBroadcastReceivers(i, PackageManager.GET_DISABLED_COMPONENTS); 201 setupReceivers(ris, defPackage); 202 // The open and close receivers are likely the same, but they may not be. 203 i = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); 204 ris = mPackageManager.queryBroadcastReceivers(i, PackageManager.GET_DISABLED_COMPONENTS); 205 setupReceivers(ris, defPackage); 206 207 // Write the selected default to the prefs so that the Redirector activity 208 // knows which one to use. 209 SharedPreferences pref = getSharedPreferences("musicfx", MODE_PRIVATE); 210 Editor ed = pref.edit(); 211 ed.putString("defaultpanelpackage", defPackage); 212 ed.putString("defaultpanelname", defName); 213 ed.commit(); 214 log("wrote " + defPackage + "/" + defName + " as default"); 215 } 216 217 private void setupReceivers(List<ResolveInfo> ris, String defPackage) { 218 // TODO - we may need to keep track of active sessions and send "open session" 219 // broadcast to newly enabled receivers, while sending "close session" to 220 // receivers that are about to be disabled. We could also consider just 221 // killing the process hosting the disabled components. 222 for (ResolveInfo foo: ris) { 223 ComponentName comp = new ComponentName(foo.activityInfo.packageName, foo.activityInfo.name); 224 if (foo.activityInfo.packageName.equals(defPackage)) { 225 log("enabling receiver " + foo); 226 mPackageManager.setComponentEnabledSetting(comp, 227 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 228 PackageManager.DONT_KILL_APP); 229 } else { 230 log("disabling receiver " + foo); 231 mPackageManager.setComponentEnabledSetting(comp, 232 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 233 PackageManager.DONT_KILL_APP); 234 } 235 } 236 } 237 } 238 239 private static void log(String out) { 240 if (LOG) { 241 Log.d(TAG, out); 242 } 243 } 244} 245