LoadedApk.java revision 5a6ef737edbf57577443ac056613afe6cb121519
1/*
2 * Copyright (C) 2010 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 android.app;
18
19import com.android.internal.util.ArrayUtils;
20
21import android.content.BroadcastReceiver;
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.IIntentReceiver;
25import android.content.Intent;
26import android.content.ServiceConnection;
27import android.content.pm.ApplicationInfo;
28import android.content.pm.IPackageManager;
29import android.content.pm.PackageManager;
30import android.content.res.AssetManager;
31import android.content.res.CompatibilityInfo;
32import android.content.res.Resources;
33import android.os.Bundle;
34import android.os.Handler;
35import android.os.IBinder;
36import android.os.Process;
37import android.os.RemoteException;
38import android.os.StrictMode;
39import android.util.AndroidRuntimeException;
40import android.util.Slog;
41import android.view.CompatibilityInfoHolder;
42
43import java.io.File;
44import java.io.IOException;
45import java.io.InputStream;
46import java.lang.ref.WeakReference;
47import java.net.URL;
48import java.util.Enumeration;
49import java.util.HashMap;
50import java.util.Iterator;
51
52final class IntentReceiverLeaked extends AndroidRuntimeException {
53    public IntentReceiverLeaked(String msg) {
54        super(msg);
55    }
56}
57
58final class ServiceConnectionLeaked extends AndroidRuntimeException {
59    public ServiceConnectionLeaked(String msg) {
60        super(msg);
61    }
62}
63
64/**
65 * Local state maintained about a currently loaded .apk.
66 * @hide
67 */
68public final class LoadedApk {
69
70    private final ActivityThread mActivityThread;
71    private final ApplicationInfo mApplicationInfo;
72    final String mPackageName;
73    private final String mAppDir;
74    private final String mResDir;
75    private final String[] mSharedLibraries;
76    private final String mDataDir;
77    private final String mLibDir;
78    private final File mDataDirFile;
79    private final ClassLoader mBaseClassLoader;
80    private final boolean mSecurityViolation;
81    private final boolean mIncludeCode;
82    public final CompatibilityInfoHolder mCompatibilityInfo = new CompatibilityInfoHolder();
83    Resources mResources;
84    private ClassLoader mClassLoader;
85    private Application mApplication;
86
87    private final HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mReceivers
88        = new HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
89    private final HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers
90    = new HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
91    private final HashMap<Context, HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
92        = new HashMap<Context, HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
93    private final HashMap<Context, HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
94        = new HashMap<Context, HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
95
96    int mClientCount = 0;
97
98    Application getApplication() {
99        return mApplication;
100    }
101
102    /**
103     * Create information about a new .apk
104     *
105     * NOTE: This constructor is called with ActivityThread's lock held,
106     * so MUST NOT call back out to the activity manager.
107     */
108    public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
109            CompatibilityInfo compatInfo,
110            ActivityThread mainThread, ClassLoader baseLoader,
111            boolean securityViolation, boolean includeCode) {
112        mActivityThread = activityThread;
113        mApplicationInfo = aInfo;
114        mPackageName = aInfo.packageName;
115        mAppDir = aInfo.sourceDir;
116        mResDir = aInfo.uid == Process.myUid() ? aInfo.sourceDir
117                : aInfo.publicSourceDir;
118        mSharedLibraries = aInfo.sharedLibraryFiles;
119        mDataDir = aInfo.dataDir;
120        mDataDirFile = mDataDir != null ? new File(mDataDir) : null;
121        mLibDir = aInfo.nativeLibraryDir;
122        mBaseClassLoader = baseLoader;
123        mSecurityViolation = securityViolation;
124        mIncludeCode = includeCode;
125        mCompatibilityInfo.set(compatInfo);
126
127        if (mAppDir == null) {
128            if (ActivityThread.mSystemContext == null) {
129                ActivityThread.mSystemContext =
130                    ContextImpl.createSystemContext(mainThread);
131                ActivityThread.mSystemContext.getResources().updateConfiguration(
132                         mainThread.getConfiguration(),
133                         mainThread.getDisplayMetricsLocked(compatInfo, false),
134                         compatInfo);
135                //Slog.i(TAG, "Created system resources "
136                //        + mSystemContext.getResources() + ": "
137                //        + mSystemContext.getResources().getConfiguration());
138            }
139            mClassLoader = ActivityThread.mSystemContext.getClassLoader();
140            mResources = ActivityThread.mSystemContext.getResources();
141        }
142    }
143
144    public LoadedApk(ActivityThread activityThread, String name,
145            Context systemContext, ApplicationInfo info, CompatibilityInfo compatInfo) {
146        mActivityThread = activityThread;
147        mApplicationInfo = info != null ? info : new ApplicationInfo();
148        mApplicationInfo.packageName = name;
149        mPackageName = name;
150        mAppDir = null;
151        mResDir = null;
152        mSharedLibraries = null;
153        mDataDir = null;
154        mDataDirFile = null;
155        mLibDir = null;
156        mBaseClassLoader = null;
157        mSecurityViolation = false;
158        mIncludeCode = true;
159        mClassLoader = systemContext.getClassLoader();
160        mResources = systemContext.getResources();
161        mCompatibilityInfo.set(compatInfo);
162    }
163
164    public String getPackageName() {
165        return mPackageName;
166    }
167
168    public ApplicationInfo getApplicationInfo() {
169        return mApplicationInfo;
170    }
171
172    public boolean isSecurityViolation() {
173        return mSecurityViolation;
174    }
175
176    /**
177     * Gets the array of shared libraries that are listed as
178     * used by the given package.
179     *
180     * @param packageName the name of the package (note: not its
181     * file name)
182     * @return null-ok; the array of shared libraries, each one
183     * a fully-qualified path
184     */
185    private static String[] getLibrariesFor(String packageName) {
186        ApplicationInfo ai = null;
187        try {
188            ai = ActivityThread.getPackageManager().getApplicationInfo(packageName,
189                    PackageManager.GET_SHARED_LIBRARY_FILES);
190        } catch (RemoteException e) {
191            throw new AssertionError(e);
192        }
193
194        if (ai == null) {
195            return null;
196        }
197
198        return ai.sharedLibraryFiles;
199    }
200
201    /**
202     * Combines two arrays (of library names) such that they are
203     * concatenated in order but are devoid of duplicates. The
204     * result is a single string with the names of the libraries
205     * separated by colons, or <code>null</code> if both lists
206     * were <code>null</code> or empty.
207     *
208     * @param list1 null-ok; the first list
209     * @param list2 null-ok; the second list
210     * @return null-ok; the combination
211     */
212    private static String combineLibs(String[] list1, String[] list2) {
213        StringBuilder result = new StringBuilder(300);
214        boolean first = true;
215
216        if (list1 != null) {
217            for (String s : list1) {
218                if (first) {
219                    first = false;
220                } else {
221                    result.append(':');
222                }
223                result.append(s);
224            }
225        }
226
227        // Only need to check for duplicates if list1 was non-empty.
228        boolean dupCheck = !first;
229
230        if (list2 != null) {
231            for (String s : list2) {
232                if (dupCheck && ArrayUtils.contains(list1, s)) {
233                    continue;
234                }
235
236                if (first) {
237                    first = false;
238                } else {
239                    result.append(':');
240                }
241                result.append(s);
242            }
243        }
244
245        return result.toString();
246    }
247
248    public ClassLoader getClassLoader() {
249        synchronized (this) {
250            if (mClassLoader != null) {
251                return mClassLoader;
252            }
253
254            if (mIncludeCode && !mPackageName.equals("android")) {
255                String zip = mAppDir;
256
257                /*
258                 * The following is a bit of a hack to inject
259                 * instrumentation into the system: If the app
260                 * being started matches one of the instrumentation names,
261                 * then we combine both the "instrumentation" and
262                 * "instrumented" app into the path, along with the
263                 * concatenation of both apps' shared library lists.
264                 */
265
266                String instrumentationAppDir =
267                        mActivityThread.mInstrumentationAppDir;
268                String instrumentationAppPackage =
269                        mActivityThread.mInstrumentationAppPackage;
270                String instrumentedAppDir =
271                        mActivityThread.mInstrumentedAppDir;
272                String[] instrumentationLibs = null;
273
274                if (mAppDir.equals(instrumentationAppDir)
275                        || mAppDir.equals(instrumentedAppDir)) {
276                    zip = instrumentationAppDir + ":" + instrumentedAppDir;
277                    if (! instrumentedAppDir.equals(instrumentationAppDir)) {
278                        instrumentationLibs =
279                            getLibrariesFor(instrumentationAppPackage);
280                    }
281                }
282
283                if ((mSharedLibraries != null) ||
284                        (instrumentationLibs != null)) {
285                    zip =
286                        combineLibs(mSharedLibraries, instrumentationLibs)
287                        + ':' + zip;
288                }
289
290                /*
291                 * With all the combination done (if necessary, actually
292                 * create the class loader.
293                 */
294
295                if (ActivityThread.localLOGV)
296                    Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + mLibDir);
297
298                // Temporarily disable logging of disk reads on the Looper thread
299                // as this is early and necessary.
300                StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
301
302                mClassLoader =
303                    ApplicationLoaders.getDefault().getClassLoader(
304                        zip, mLibDir, mBaseClassLoader);
305                initializeJavaContextClassLoader();
306
307                StrictMode.setThreadPolicy(oldPolicy);
308            } else {
309                if (mBaseClassLoader == null) {
310                    mClassLoader = ClassLoader.getSystemClassLoader();
311                } else {
312                    mClassLoader = mBaseClassLoader;
313                }
314            }
315            return mClassLoader;
316        }
317    }
318
319    /**
320     * Setup value for Thread.getContextClassLoader(). If the
321     * package will not run in in a VM with other packages, we set
322     * the Java context ClassLoader to the
323     * PackageInfo.getClassLoader value. However, if this VM can
324     * contain multiple packages, we intead set the Java context
325     * ClassLoader to a proxy that will warn about the use of Java
326     * context ClassLoaders and then fall through to use the
327     * system ClassLoader.
328     *
329     * <p> Note that this is similar to but not the same as the
330     * android.content.Context.getClassLoader(). While both
331     * context class loaders are typically set to the
332     * PathClassLoader used to load the package archive in the
333     * single application per VM case, a single Android process
334     * may contain several Contexts executing on one thread with
335     * their own logical ClassLoaders while the Java context
336     * ClassLoader is a thread local. This is why in the case when
337     * we have multiple packages per VM we do not set the Java
338     * context ClassLoader to an arbitrary but instead warn the
339     * user to set their own if we detect that they are using a
340     * Java library that expects it to be set.
341     */
342    private void initializeJavaContextClassLoader() {
343        IPackageManager pm = ActivityThread.getPackageManager();
344        android.content.pm.PackageInfo pi;
345        try {
346            pi = pm.getPackageInfo(mPackageName, 0);
347        } catch (RemoteException e) {
348            throw new AssertionError(e);
349        }
350        /*
351         * Two possible indications that this package could be
352         * sharing its virtual machine with other packages:
353         *
354         * 1.) the sharedUserId attribute is set in the manifest,
355         *     indicating a request to share a VM with other
356         *     packages with the same sharedUserId.
357         *
358         * 2.) the application element of the manifest has an
359         *     attribute specifying a non-default process name,
360         *     indicating the desire to run in another packages VM.
361         */
362        boolean sharedUserIdSet = (pi.sharedUserId != null);
363        boolean processNameNotDefault =
364            (pi.applicationInfo != null &&
365             !mPackageName.equals(pi.applicationInfo.processName));
366        boolean sharable = (sharedUserIdSet || processNameNotDefault);
367        ClassLoader contextClassLoader =
368            (sharable)
369            ? new WarningContextClassLoader()
370            : mClassLoader;
371        Thread.currentThread().setContextClassLoader(contextClassLoader);
372    }
373
374    private static class WarningContextClassLoader extends ClassLoader {
375
376        private static boolean warned = false;
377
378        private void warn(String methodName) {
379            if (warned) {
380                return;
381            }
382            warned = true;
383            Thread.currentThread().setContextClassLoader(getParent());
384            Slog.w(ActivityThread.TAG, "ClassLoader." + methodName + ": " +
385                  "The class loader returned by " +
386                  "Thread.getContextClassLoader() may fail for processes " +
387                  "that host multiple applications. You should explicitly " +
388                  "specify a context class loader. For example: " +
389                  "Thread.setContextClassLoader(getClass().getClassLoader());");
390        }
391
392        @Override public URL getResource(String resName) {
393            warn("getResource");
394            return getParent().getResource(resName);
395        }
396
397        @Override public Enumeration<URL> getResources(String resName) throws IOException {
398            warn("getResources");
399            return getParent().getResources(resName);
400        }
401
402        @Override public InputStream getResourceAsStream(String resName) {
403            warn("getResourceAsStream");
404            return getParent().getResourceAsStream(resName);
405        }
406
407        @Override public Class<?> loadClass(String className) throws ClassNotFoundException {
408            warn("loadClass");
409            return getParent().loadClass(className);
410        }
411
412        @Override public void setClassAssertionStatus(String cname, boolean enable) {
413            warn("setClassAssertionStatus");
414            getParent().setClassAssertionStatus(cname, enable);
415        }
416
417        @Override public void setPackageAssertionStatus(String pname, boolean enable) {
418            warn("setPackageAssertionStatus");
419            getParent().setPackageAssertionStatus(pname, enable);
420        }
421
422        @Override public void setDefaultAssertionStatus(boolean enable) {
423            warn("setDefaultAssertionStatus");
424            getParent().setDefaultAssertionStatus(enable);
425        }
426
427        @Override public void clearAssertionStatus() {
428            warn("clearAssertionStatus");
429            getParent().clearAssertionStatus();
430        }
431    }
432
433    public String getAppDir() {
434        return mAppDir;
435    }
436
437    public String getResDir() {
438        return mResDir;
439    }
440
441    public String getDataDir() {
442        return mDataDir;
443    }
444
445    public File getDataDirFile() {
446        return mDataDirFile;
447    }
448
449    public AssetManager getAssets(ActivityThread mainThread) {
450        return getResources(mainThread).getAssets();
451    }
452
453    public Resources getResources(ActivityThread mainThread) {
454        if (mResources == null) {
455            mResources = mainThread.getTopLevelResources(mResDir, this);
456        }
457        return mResources;
458    }
459
460    public Application makeApplication(boolean forceDefaultAppClass,
461            Instrumentation instrumentation) {
462        if (mApplication != null) {
463            return mApplication;
464        }
465
466        Application app = null;
467
468        String appClass = mApplicationInfo.className;
469        if (forceDefaultAppClass || (appClass == null)) {
470            appClass = "android.app.Application";
471        }
472
473        try {
474            java.lang.ClassLoader cl = getClassLoader();
475            ContextImpl appContext = new ContextImpl();
476            appContext.init(this, null, mActivityThread);
477            app = mActivityThread.mInstrumentation.newApplication(
478                    cl, appClass, appContext);
479            appContext.setOuterContext(app);
480        } catch (Exception e) {
481            if (!mActivityThread.mInstrumentation.onException(app, e)) {
482                throw new RuntimeException(
483                    "Unable to instantiate application " + appClass
484                    + ": " + e.toString(), e);
485            }
486        }
487        mActivityThread.mAllApplications.add(app);
488        mApplication = app;
489
490        if (instrumentation != null) {
491            try {
492                instrumentation.callApplicationOnCreate(app);
493            } catch (Exception e) {
494                if (!instrumentation.onException(app, e)) {
495                    throw new RuntimeException(
496                        "Unable to create application " + app.getClass().getName()
497                        + ": " + e.toString(), e);
498                }
499            }
500        }
501
502        return app;
503    }
504
505    public void removeContextRegistrations(Context context,
506            String who, String what) {
507        HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap =
508            mReceivers.remove(context);
509        if (rmap != null) {
510            Iterator<LoadedApk.ReceiverDispatcher> it = rmap.values().iterator();
511            while (it.hasNext()) {
512                LoadedApk.ReceiverDispatcher rd = it.next();
513                IntentReceiverLeaked leak = new IntentReceiverLeaked(
514                        what + " " + who + " has leaked IntentReceiver "
515                        + rd.getIntentReceiver() + " that was " +
516                        "originally registered here. Are you missing a " +
517                        "call to unregisterReceiver()?");
518                leak.setStackTrace(rd.getLocation().getStackTrace());
519                Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
520                try {
521                    ActivityManagerNative.getDefault().unregisterReceiver(
522                            rd.getIIntentReceiver());
523                } catch (RemoteException e) {
524                    // system crashed, nothing we can do
525                }
526            }
527        }
528        mUnregisteredReceivers.remove(context);
529        //Slog.i(TAG, "Receiver registrations: " + mReceivers);
530        HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
531            mServices.remove(context);
532        if (smap != null) {
533            Iterator<LoadedApk.ServiceDispatcher> it = smap.values().iterator();
534            while (it.hasNext()) {
535                LoadedApk.ServiceDispatcher sd = it.next();
536                ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
537                        what + " " + who + " has leaked ServiceConnection "
538                        + sd.getServiceConnection() + " that was originally bound here");
539                leak.setStackTrace(sd.getLocation().getStackTrace());
540                Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
541                try {
542                    ActivityManagerNative.getDefault().unbindService(
543                            sd.getIServiceConnection());
544                } catch (RemoteException e) {
545                    // system crashed, nothing we can do
546                }
547                sd.doForget();
548            }
549        }
550        mUnboundServices.remove(context);
551        //Slog.i(TAG, "Service registrations: " + mServices);
552    }
553
554    public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
555            Context context, Handler handler,
556            Instrumentation instrumentation, boolean registered) {
557        synchronized (mReceivers) {
558            LoadedApk.ReceiverDispatcher rd = null;
559            HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
560            if (registered) {
561                map = mReceivers.get(context);
562                if (map != null) {
563                    rd = map.get(r);
564                }
565            }
566            if (rd == null) {
567                rd = new ReceiverDispatcher(r, context, handler,
568                        instrumentation, registered);
569                if (registered) {
570                    if (map == null) {
571                        map = new HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
572                        mReceivers.put(context, map);
573                    }
574                    map.put(r, rd);
575                }
576            } else {
577                rd.validate(context, handler);
578            }
579            rd.mForgotten = false;
580            return rd.getIIntentReceiver();
581        }
582    }
583
584    public IIntentReceiver forgetReceiverDispatcher(Context context,
585            BroadcastReceiver r) {
586        synchronized (mReceivers) {
587            HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = mReceivers.get(context);
588            LoadedApk.ReceiverDispatcher rd = null;
589            if (map != null) {
590                rd = map.get(r);
591                if (rd != null) {
592                    map.remove(r);
593                    if (map.size() == 0) {
594                        mReceivers.remove(context);
595                    }
596                    if (r.getDebugUnregister()) {
597                        HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> holder
598                                = mUnregisteredReceivers.get(context);
599                        if (holder == null) {
600                            holder = new HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
601                            mUnregisteredReceivers.put(context, holder);
602                        }
603                        RuntimeException ex = new IllegalArgumentException(
604                                "Originally unregistered here:");
605                        ex.fillInStackTrace();
606                        rd.setUnregisterLocation(ex);
607                        holder.put(r, rd);
608                    }
609                    rd.mForgotten = true;
610                    return rd.getIIntentReceiver();
611                }
612            }
613            HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> holder
614                    = mUnregisteredReceivers.get(context);
615            if (holder != null) {
616                rd = holder.get(r);
617                if (rd != null) {
618                    RuntimeException ex = rd.getUnregisterLocation();
619                    throw new IllegalArgumentException(
620                            "Unregistering Receiver " + r
621                            + " that was already unregistered", ex);
622                }
623            }
624            if (context == null) {
625                throw new IllegalStateException("Unbinding Receiver " + r
626                        + " from Context that is no longer in use: " + context);
627            } else {
628                throw new IllegalArgumentException("Receiver not registered: " + r);
629            }
630
631        }
632    }
633
634    static final class ReceiverDispatcher {
635
636        final static class InnerReceiver extends IIntentReceiver.Stub {
637            final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
638            final LoadedApk.ReceiverDispatcher mStrongRef;
639
640            InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
641                mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
642                mStrongRef = strong ? rd : null;
643            }
644            public void performReceive(Intent intent, int resultCode,
645                    String data, Bundle extras, boolean ordered, boolean sticky) {
646                LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
647                if (ActivityThread.DEBUG_BROADCAST) {
648                    int seq = intent.getIntExtra("seq", -1);
649                    Slog.i(ActivityThread.TAG, "Receiving broadcast " + intent.getAction() + " seq=" + seq
650                            + " to " + (rd != null ? rd.mReceiver : null));
651                }
652                if (rd != null) {
653                    rd.performReceive(intent, resultCode, data, extras,
654                            ordered, sticky);
655                } else {
656                    // The activity manager dispatched a broadcast to a registered
657                    // receiver in this process, but before it could be delivered the
658                    // receiver was unregistered.  Acknowledge the broadcast on its
659                    // behalf so that the system's broadcast sequence can continue.
660                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
661                            "Finishing broadcast to unregistered receiver");
662                    IActivityManager mgr = ActivityManagerNative.getDefault();
663                    try {
664                        if (extras != null) {
665                            extras.setAllowFds(false);
666                        }
667                        mgr.finishReceiver(this, resultCode, data, extras, false);
668                    } catch (RemoteException e) {
669                        Slog.w(ActivityThread.TAG, "Couldn't finish broadcast to unregistered receiver");
670                    }
671                }
672            }
673        }
674
675        final IIntentReceiver.Stub mIIntentReceiver;
676        final BroadcastReceiver mReceiver;
677        final Context mContext;
678        final Handler mActivityThread;
679        final Instrumentation mInstrumentation;
680        final boolean mRegistered;
681        final IntentReceiverLeaked mLocation;
682        RuntimeException mUnregisterLocation;
683        boolean mForgotten;
684
685        final class Args extends BroadcastReceiver.PendingResult implements Runnable {
686            private Intent mCurIntent;
687            private final boolean mOrdered;
688
689            public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
690                    boolean ordered, boolean sticky) {
691                super(resultCode, resultData, resultExtras,
692                        mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED,
693                        ordered, sticky, mIIntentReceiver.asBinder());
694                mCurIntent = intent;
695                mOrdered = ordered;
696            }
697
698            public void run() {
699                final BroadcastReceiver receiver = mReceiver;
700                final boolean ordered = mOrdered;
701
702                if (ActivityThread.DEBUG_BROADCAST) {
703                    int seq = mCurIntent.getIntExtra("seq", -1);
704                    Slog.i(ActivityThread.TAG, "Dispatching broadcast " + mCurIntent.getAction()
705                            + " seq=" + seq + " to " + mReceiver);
706                    Slog.i(ActivityThread.TAG, "  mRegistered=" + mRegistered
707                            + " mOrderedHint=" + ordered);
708                }
709
710                final IActivityManager mgr = ActivityManagerNative.getDefault();
711                final Intent intent = mCurIntent;
712                mCurIntent = null;
713
714                if (receiver == null || mForgotten) {
715                    if (mRegistered && ordered) {
716                        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
717                                "Finishing null broadcast to " + mReceiver);
718                        sendFinished(mgr);
719                    }
720                    return;
721                }
722
723                try {
724                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
725                    intent.setExtrasClassLoader(cl);
726                    setExtrasClassLoader(cl);
727                    receiver.setPendingResult(this);
728                    receiver.onReceive(mContext, intent);
729                } catch (Exception e) {
730                    if (mRegistered && ordered) {
731                        if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
732                                "Finishing failed broadcast to " + mReceiver);
733                        sendFinished(mgr);
734                    }
735                    if (mInstrumentation == null ||
736                            !mInstrumentation.onException(mReceiver, e)) {
737                        throw new RuntimeException(
738                            "Error receiving broadcast " + intent
739                            + " in " + mReceiver, e);
740                    }
741                }
742
743                if (receiver.getPendingResult() != null) {
744                    finish();
745                }
746            }
747        }
748
749        ReceiverDispatcher(BroadcastReceiver receiver, Context context,
750                Handler activityThread, Instrumentation instrumentation,
751                boolean registered) {
752            if (activityThread == null) {
753                throw new NullPointerException("Handler must not be null");
754            }
755
756            mIIntentReceiver = new InnerReceiver(this, !registered);
757            mReceiver = receiver;
758            mContext = context;
759            mActivityThread = activityThread;
760            mInstrumentation = instrumentation;
761            mRegistered = registered;
762            mLocation = new IntentReceiverLeaked(null);
763            mLocation.fillInStackTrace();
764        }
765
766        void validate(Context context, Handler activityThread) {
767            if (mContext != context) {
768                throw new IllegalStateException(
769                    "Receiver " + mReceiver +
770                    " registered with differing Context (was " +
771                    mContext + " now " + context + ")");
772            }
773            if (mActivityThread != activityThread) {
774                throw new IllegalStateException(
775                    "Receiver " + mReceiver +
776                    " registered with differing handler (was " +
777                    mActivityThread + " now " + activityThread + ")");
778            }
779        }
780
781        IntentReceiverLeaked getLocation() {
782            return mLocation;
783        }
784
785        BroadcastReceiver getIntentReceiver() {
786            return mReceiver;
787        }
788
789        IIntentReceiver getIIntentReceiver() {
790            return mIIntentReceiver;
791        }
792
793        void setUnregisterLocation(RuntimeException ex) {
794            mUnregisterLocation = ex;
795        }
796
797        RuntimeException getUnregisterLocation() {
798            return mUnregisterLocation;
799        }
800
801        public void performReceive(Intent intent, int resultCode,
802                String data, Bundle extras, boolean ordered, boolean sticky) {
803            if (ActivityThread.DEBUG_BROADCAST) {
804                int seq = intent.getIntExtra("seq", -1);
805                Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
806                        + " to " + mReceiver);
807            }
808            Args args = new Args(intent, resultCode, data, extras, ordered, sticky);
809            if (!mActivityThread.post(args)) {
810                if (mRegistered && ordered) {
811                    IActivityManager mgr = ActivityManagerNative.getDefault();
812                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
813                            "Finishing sync broadcast to " + mReceiver);
814                    args.sendFinished(mgr);
815                }
816            }
817        }
818
819    }
820
821    public final IServiceConnection getServiceDispatcher(ServiceConnection c,
822            Context context, Handler handler, int flags) {
823        synchronized (mServices) {
824            LoadedApk.ServiceDispatcher sd = null;
825            HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
826            if (map != null) {
827                sd = map.get(c);
828            }
829            if (sd == null) {
830                sd = new ServiceDispatcher(c, context, handler, flags);
831                if (map == null) {
832                    map = new HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
833                    mServices.put(context, map);
834                }
835                map.put(c, sd);
836            } else {
837                sd.validate(context, handler);
838            }
839            return sd.getIServiceConnection();
840        }
841    }
842
843    public final IServiceConnection forgetServiceDispatcher(Context context,
844            ServiceConnection c) {
845        synchronized (mServices) {
846            HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> map
847                    = mServices.get(context);
848            LoadedApk.ServiceDispatcher sd = null;
849            if (map != null) {
850                sd = map.get(c);
851                if (sd != null) {
852                    map.remove(c);
853                    sd.doForget();
854                    if (map.size() == 0) {
855                        mServices.remove(context);
856                    }
857                    if ((sd.getFlags()&Context.BIND_DEBUG_UNBIND) != 0) {
858                        HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
859                                = mUnboundServices.get(context);
860                        if (holder == null) {
861                            holder = new HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
862                            mUnboundServices.put(context, holder);
863                        }
864                        RuntimeException ex = new IllegalArgumentException(
865                                "Originally unbound here:");
866                        ex.fillInStackTrace();
867                        sd.setUnbindLocation(ex);
868                        holder.put(c, sd);
869                    }
870                    return sd.getIServiceConnection();
871                }
872            }
873            HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
874                    = mUnboundServices.get(context);
875            if (holder != null) {
876                sd = holder.get(c);
877                if (sd != null) {
878                    RuntimeException ex = sd.getUnbindLocation();
879                    throw new IllegalArgumentException(
880                            "Unbinding Service " + c
881                            + " that was already unbound", ex);
882                }
883            }
884            if (context == null) {
885                throw new IllegalStateException("Unbinding Service " + c
886                        + " from Context that is no longer in use: " + context);
887            } else {
888                throw new IllegalArgumentException("Service not registered: " + c);
889            }
890        }
891    }
892
893    static final class ServiceDispatcher {
894        private final ServiceDispatcher.InnerConnection mIServiceConnection;
895        private final ServiceConnection mConnection;
896        private final Context mContext;
897        private final Handler mActivityThread;
898        private final ServiceConnectionLeaked mLocation;
899        private final int mFlags;
900
901        private RuntimeException mUnbindLocation;
902
903        private boolean mDied;
904        private boolean mForgotten;
905
906        private static class ConnectionInfo {
907            IBinder binder;
908            IBinder.DeathRecipient deathMonitor;
909        }
910
911        private static class InnerConnection extends IServiceConnection.Stub {
912            final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
913
914            InnerConnection(LoadedApk.ServiceDispatcher sd) {
915                mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
916            }
917
918            public void connected(ComponentName name, IBinder service) throws RemoteException {
919                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
920                if (sd != null) {
921                    sd.connected(name, service);
922                }
923            }
924        }
925
926        private final HashMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
927            = new HashMap<ComponentName, ServiceDispatcher.ConnectionInfo>();
928
929        ServiceDispatcher(ServiceConnection conn,
930                Context context, Handler activityThread, int flags) {
931            mIServiceConnection = new InnerConnection(this);
932            mConnection = conn;
933            mContext = context;
934            mActivityThread = activityThread;
935            mLocation = new ServiceConnectionLeaked(null);
936            mLocation.fillInStackTrace();
937            mFlags = flags;
938        }
939
940        void validate(Context context, Handler activityThread) {
941            if (mContext != context) {
942                throw new RuntimeException(
943                    "ServiceConnection " + mConnection +
944                    " registered with differing Context (was " +
945                    mContext + " now " + context + ")");
946            }
947            if (mActivityThread != activityThread) {
948                throw new RuntimeException(
949                    "ServiceConnection " + mConnection +
950                    " registered with differing handler (was " +
951                    mActivityThread + " now " + activityThread + ")");
952            }
953        }
954
955        void doForget() {
956            synchronized(this) {
957                Iterator<ServiceDispatcher.ConnectionInfo> it = mActiveConnections.values().iterator();
958                while (it.hasNext()) {
959                    ServiceDispatcher.ConnectionInfo ci = it.next();
960                    ci.binder.unlinkToDeath(ci.deathMonitor, 0);
961                }
962                mActiveConnections.clear();
963                mForgotten = true;
964            }
965        }
966
967        ServiceConnectionLeaked getLocation() {
968            return mLocation;
969        }
970
971        ServiceConnection getServiceConnection() {
972            return mConnection;
973        }
974
975        IServiceConnection getIServiceConnection() {
976            return mIServiceConnection;
977        }
978
979        int getFlags() {
980            return mFlags;
981        }
982
983        void setUnbindLocation(RuntimeException ex) {
984            mUnbindLocation = ex;
985        }
986
987        RuntimeException getUnbindLocation() {
988            return mUnbindLocation;
989        }
990
991        public void connected(ComponentName name, IBinder service) {
992            if (mActivityThread != null) {
993                mActivityThread.post(new RunConnection(name, service, 0));
994            } else {
995                doConnected(name, service);
996            }
997        }
998
999        public void death(ComponentName name, IBinder service) {
1000            ServiceDispatcher.ConnectionInfo old;
1001
1002            synchronized (this) {
1003                mDied = true;
1004                old = mActiveConnections.remove(name);
1005                if (old == null || old.binder != service) {
1006                    // Death for someone different than who we last
1007                    // reported...  just ignore it.
1008                    return;
1009                }
1010                old.binder.unlinkToDeath(old.deathMonitor, 0);
1011            }
1012
1013            if (mActivityThread != null) {
1014                mActivityThread.post(new RunConnection(name, service, 1));
1015            } else {
1016                doDeath(name, service);
1017            }
1018        }
1019
1020        public void doConnected(ComponentName name, IBinder service) {
1021            ServiceDispatcher.ConnectionInfo old;
1022            ServiceDispatcher.ConnectionInfo info;
1023
1024            synchronized (this) {
1025                if (mForgotten) {
1026                    // We unbound before receiving the connection; ignore
1027                    // any connection received.
1028                    return;
1029                }
1030                old = mActiveConnections.get(name);
1031                if (old != null && old.binder == service) {
1032                    // Huh, already have this one.  Oh well!
1033                    return;
1034                }
1035
1036                if (service != null) {
1037                    // A new service is being connected... set it all up.
1038                    mDied = false;
1039                    info = new ConnectionInfo();
1040                    info.binder = service;
1041                    info.deathMonitor = new DeathMonitor(name, service);
1042                    try {
1043                        service.linkToDeath(info.deathMonitor, 0);
1044                        mActiveConnections.put(name, info);
1045                    } catch (RemoteException e) {
1046                        // This service was dead before we got it...  just
1047                        // don't do anything with it.
1048                        mActiveConnections.remove(name);
1049                        return;
1050                    }
1051
1052                } else {
1053                    // The named service is being disconnected... clean up.
1054                    mActiveConnections.remove(name);
1055                }
1056
1057                if (old != null) {
1058                    old.binder.unlinkToDeath(old.deathMonitor, 0);
1059                }
1060            }
1061
1062            // If there was an old service, it is not disconnected.
1063            if (old != null) {
1064                mConnection.onServiceDisconnected(name);
1065            }
1066            // If there is a new service, it is now connected.
1067            if (service != null) {
1068                mConnection.onServiceConnected(name, service);
1069            }
1070        }
1071
1072        public void doDeath(ComponentName name, IBinder service) {
1073            mConnection.onServiceDisconnected(name);
1074        }
1075
1076        private final class RunConnection implements Runnable {
1077            RunConnection(ComponentName name, IBinder service, int command) {
1078                mName = name;
1079                mService = service;
1080                mCommand = command;
1081            }
1082
1083            public void run() {
1084                if (mCommand == 0) {
1085                    doConnected(mName, mService);
1086                } else if (mCommand == 1) {
1087                    doDeath(mName, mService);
1088                }
1089            }
1090
1091            final ComponentName mName;
1092            final IBinder mService;
1093            final int mCommand;
1094        }
1095
1096        private final class DeathMonitor implements IBinder.DeathRecipient
1097        {
1098            DeathMonitor(ComponentName name, IBinder service) {
1099                mName = name;
1100                mService = service;
1101            }
1102
1103            public void binderDied() {
1104                death(mName, mService);
1105            }
1106
1107            final ComponentName mName;
1108            final IBinder mService;
1109        }
1110    }
1111}
1112