Compatibility.java revision 58f27f3da5487279c18ddada93be0bee682b758f
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                // TODO pick a default here?
73                finish();
74                return;
75            } else {
76                i.setComponent(new ComponentName(defPackage, defName));
77            }
78            startActivity(i);
79            finish();
80        }
81    }
82
83    /**
84     * This BroadcastReceiver responds to BOOT_COMPLETED, PACKAGE_ADDED,
85     * PACKAGE_REPLACED and PACKAGE_REMOVED intents. When run, it checks
86     * to see whether the active control panel needs to be updated:
87     * - if there is no default, it picks one
88     * - if a new control panel is installed, it becomes the default
89     * It then enables the open/close receivers in the active control panel,
90     * and disables them in the others.
91     */
92    public static class Receiver extends BroadcastReceiver {
93
94        @Override
95        public void onReceive(final Context context, final Intent intent) {
96
97            Intent updateIntent = new Intent(context, Service.class);
98            updateIntent.putExtra("reason", intent);
99            context.startService(updateIntent);
100        }
101
102        public static class Service extends IntentService {
103
104            PackageManager mPackageManager;
105
106            public Service() {
107                super("CompatibilityService");
108            }
109
110            @Override
111            protected void onHandleIntent(final Intent intent) {
112                if (mPackageManager == null) {
113                    mPackageManager = getPackageManager();
114                }
115                Intent packageIntent = intent.getParcelableExtra("reason");
116                Bundle b = packageIntent.getExtras();
117                if (b != null) b.size();
118                log("intentservice saw: " + packageIntent + " " + b);
119                // TODO, be smarter about package upgrades (which results in three
120                // broadcasts: removed, added, replaced)
121                Uri packageUri = packageIntent.getData();
122                String updatedPackage = null;
123                if (packageUri != null) {
124                    updatedPackage = packageUri.toString().substring(8);
125                    pickDefaultControlPanel(updatedPackage);
126                }
127            }
128
129            private void pickDefaultControlPanel(String updatedPackage) {
130
131                ResolveInfo defPanel = null;
132                ResolveInfo otherPanel = null;
133                ResolveInfo thisPanel = null;
134                Intent i = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL);
135                List<ResolveInfo> ris = mPackageManager.queryIntentActivities(i, PackageManager.GET_DISABLED_COMPONENTS);
136                log("found: " + ris.size());
137                ResolveInfo ri = mPackageManager.resolveActivity(i, 0);
138                log("resolved: " + ri + " " + (ri != null ? ri.activityInfo.enabled : ""));
139                for (ResolveInfo foo: ris) {
140                    if (foo.activityInfo.name.equals(Compatibility.Redirector.class.getName())) {
141                        log("skipping " + foo);
142                        continue;
143                    }
144                    log("considering " + foo);
145                    if (ri != null && foo.activityInfo.name.equals(ri.activityInfo.name) &&
146                            foo.activityInfo.packageName.equals(ri.activityInfo.packageName) &&
147                            foo.activityInfo.enabled) {
148                        log("default: " + ri.activityInfo.name);
149                        defPanel = foo;
150                        break;
151                    } else if (foo.activityInfo.packageName.equals(updatedPackage)) {
152                        log("choosing newly installed package " + updatedPackage);
153                        otherPanel = foo;
154                    } else if (otherPanel == null && !foo.activityInfo.packageName.equals(getPackageName())) {
155                        otherPanel = foo;
156                    } else {
157                        thisPanel = foo;
158                    }
159                }
160
161                if (defPanel == null) {
162                    // pick a default control panel
163                    if (otherPanel == null) {
164                        if (thisPanel == null) {
165                            Log.e(TAG, "No control panels found!");
166                            return;
167                        }
168                        otherPanel = thisPanel;
169                    }
170                    defPanel = otherPanel;
171                }
172
173                // Now that we have selected a default control panel activity, ensure
174                // that the broadcast receiver(s) in that same package are enabled,
175                // and the ones in the other packages are disabled.
176                String defPackage = defPanel.activityInfo.packageName;
177                i = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
178                ris = mPackageManager.queryBroadcastReceivers(i, PackageManager.GET_DISABLED_COMPONENTS);
179                setupReceivers(ris, defPackage);
180                // The open and close receivers are likely the same, but they may not be.
181                i = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
182                ris = mPackageManager.queryBroadcastReceivers(i, PackageManager.GET_DISABLED_COMPONENTS);
183                setupReceivers(ris, defPackage);
184
185                // Write the selected default to the prefs so that the Redirector activity
186                // knows which one to use.
187                SharedPreferences pref = getSharedPreferences("musicfx", MODE_PRIVATE);
188                Editor ed = pref.edit();
189                ed.putString("defaultpanelpackage", defPackage);
190                ed.putString("defaultpanelname", defPanel.activityInfo.name);
191                ed.commit();
192                log("wrote " + defPackage + "/" + defPanel.activityInfo.name + " as default");
193            }
194
195            private void setupReceivers(List<ResolveInfo> ris, String defPackage) {
196                for (ResolveInfo foo: ris) {
197                    ComponentName comp = new ComponentName(foo.activityInfo.packageName, foo.activityInfo.name);
198                    if (foo.activityInfo.packageName.equals(defPackage)) {
199                        log("enabling receiver " + foo);
200                        mPackageManager.setComponentEnabledSetting(comp,
201                                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
202                                PackageManager.DONT_KILL_APP);
203                    } else {
204                        log("disabling receiver " + foo);
205                        mPackageManager.setComponentEnabledSetting(comp,
206                                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
207                                PackageManager.DONT_KILL_APP);
208                    }
209                }
210            }
211        }
212    }
213
214    private static void log(String out) {
215        if (LOG) {
216            Log.d(TAG, out);
217        }
218    }
219}
220