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