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