本文仅讨论ContentProvider的Publish过程。
在Android启动后,会自动launch application,如果Application用到ContentProvider则会在handleBindApplication()时publish该ContentProvider。另外如果ContentProvider进程中途死掉,则在有调用ContentProvider接口如query,update等时创建。本文即专注于此流程。
Application通过在AndroidManifest.xml中说明<provider android:name="MediaProvider" android.authority="media" android.multiprocess="false" />,则PackageManager可以知道对应ContentProvider的apk文件,从而加载该apk完成Contentprovider publish.
步骤:
通过ContentResolver先查找对应给定Uri的ContentProvider,返回对应的BinderProxy
a.通过ServiceManager查找activity service得到ActivityManagerService对应BinderProxy
b.调用BinderProxy的transcat方法发送GET_CONTENT_PROVIDER_TRANSACTION命令,得到对应ContentProvider的BinderProxy
一旦得到ActivityManager对应的binder 代理对象,则使用如下代码可以发送请求得到名字为name的Contentprovider。
调用顺序图:
源代码调用路径:
frameworks/base/core/java/android/app/ActivityManagerNative.java
- class ActivityManagerProxy implements IActivityManager
- {
- ...
- public ContentProviderHolder getContentProvider(IApplicationThread caller,
- String name) throws RemoteException
- {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeStrongBinder(caller != null ? caller.asBinder() : null);
- data.writeString(name); //ContentProvider名字
- mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0); //取得ContentProvider
- reply.readException();
- int res = reply.readInt();
- ContentProviderHolder cph = null;
- if (res != 0) {
- cph = ContentProviderHolder.CREATOR.createFromParcel(reply);
- }
- data.recycle();
- reply.recycle();
- return cph;
- }
class ActivityManagerProxy implements IActivityManager
{
...
public ContentProviderHolder getContentProvider(IApplicationThread caller,
String name) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(name); //ContentProvider名字
mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0); //取得ContentProvider
reply.readException();
int res = reply.readInt();
ContentProviderHolder cph = null;
if (res != 0) {
cph = ContentProviderHolder.CREATOR.createFromParcel(reply);
}
data.recycle();
reply.recycle();
return cph;
}
frameworks/base/core/java/android/app/ActivityManagerService.java
- public final ContentProviderHolder getContentProvider(
- IApplicationThread caller, String name) {
- if (caller == null) {
- String msg = "null IApplicationThread when getting content provider "
- + name;
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
- return getContentProviderImpl(caller, name);
- }
- private final ContentProviderHolder getContentProviderImpl(
- IApplicationThread caller, String name) {
- ContentProviderRecord cpr;
- ProviderInfo cpi = null;
- synchronized(this) {
- ProcessRecord r = null;
- if (caller != null) {
- r = getRecordForAppLocked(caller);
- if (r == null) {
- throw new SecurityException(
- "Unable to find app for caller " + caller
- + " (pid=" + Binder.getCallingPid()
- + ") when getting content provider " + name);
- }
- }
- // First check if this content provider has been published...
- cpr = mProvidersByName.get(name);
- if (cpr != null) {
- ...
- } else {
- try {
- cpi = AppGlobals.getPackageManager().
- resolveContentProvider(name,
- STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
- } catch (RemoteException ex) {
- }
- if (cpi == null) {
- return null;
- }
- String msg;
- if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
- throw new SecurityException(msg);
- }
- if (!mProcessesReady && !mDidUpdate && !mWaitingUpdate
- && !cpi.processName.equals("system")) {
- // If this content provider does not run in the system
- // process, and the system is not yet ready to run other
- // processes, then fail fast instead of hanging.
- throw new IllegalArgumentException(
- "Attempt to launch content provider before system ready");
- }
- cpr = mProvidersByClass.get(cpi.name);
- final boolean firstClass = cpr == null;
- if (firstClass) {
- try {
- ApplicationInfo ai =
- AppGlobals.getPackageManager().
- getApplicationInfo(
- cpi.applicationInfo.packageName,
- STOCK_PM_FLAGS);
- if (ai == null) {
- Slog.w(TAG, "No package info for content provider "
- + cpi.name);
- return null;
- }
- cpr = new ContentProviderRecord(cpi, ai);
- } catch (RemoteException ex) {
- // pm is in same process, this will never happen.
- }
- }
- if (r != null && cpr.canRunHere(r)) {
- // If this is a multiprocess provider, then just return its
- // info and allow the caller to instantiate it. Only do
- // this if the provider is the same user as the caller's
- // process, or can run as root (so can be in any process).
- return cpr;
- }
- if (DEBUG_PROVIDER) {
- RuntimeException e = new RuntimeException("here");
- Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.info.uid
- + " pruid " + cpr.appInfo.uid + "): " + cpr.info.name, e);
- }
- // This is single process, and our app is now connecting to it.
- // See if we are already in the process of launching this
- // provider.
- final int N = mLaunchingProviders.size();
- int i;
- for (i=0; i<N; i++) {
- if (mLaunchingProviders.get(i) == cpr) {
- break;
- }
- }
- // If the provider is not already being launched, then get it
- // started.
- if (i >= N) {
- final long origId = Binder.clearCallingIdentity();
- ProcessRecord proc = startProcessLocked(cpi.processName,
- cpr.appInfo, false, 0, "content provider",
- new ComponentName(cpi.applicationInfo.packageName,
- cpi.name), false);
- if (proc == null) {
- Slog.w(TAG, "Unable to launch app "
- + cpi.applicationInfo.packageName + "/"
- + cpi.applicationInfo.uid + " for provider "
- + name + ": process is bad");
- return null;
- }
- cpr.launchingApp = proc;
- mLaunchingProviders.add(cpr);
- Binder.restoreCallingIdentity(origId);
- }
- // Make sure the provider is published (the same provider class
- // may be published under multiple names).
- if (firstClass) {
- mProvidersByClass.put(cpi.name, cpr);
- }
- mProvidersByName.put(name, cpr);
- if (r != null) {
- if (DEBUG_PROVIDER) Slog.v(TAG,
- "Adding provider requested by "
- + r.processName + " from process "
- + cpr.info.processName);
- Integer cnt = r.conProviders.get(cpr);
- if (cnt == null) {
- r.conProviders.put(cpr, new Integer(1));
- } else {
- r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
- }
- cpr.clients.add(r);
- } else {
- cpr.externals++;
- }
- }
- }
- // Wait for the provider to be published...
- synchronized (cpr) {
- while (cpr.provider == null) {
- if (cpr.launchingApp == null) {
- Slog.w(TAG, "Unable to launch app "
- + cpi.applicationInfo.packageName + "/"
- + cpi.applicationInfo.uid + " for provider "
- + name + ": launching app became null");
- EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,
- cpi.applicationInfo.packageName,
- cpi.applicationInfo.uid, name);
- return null;
- }
- try {
- cpr.wait();
- } catch (InterruptedException ex) {
- }
- }
- }
- return cpr;
- }
- final ProcessRecord startProcessLocked(String processName,
- ApplicationInfo info, boolean knownToBeDead, int intentFlags,
- String hostingType, ComponentName hostingName, boolean allowWhileBooting) {
- ProcessRecord app = getProcessRecordLocked(processName, info.uid);
- // We don't have to do anything more if:
- // (1) There is an existing application record; and
- // (2) The caller doesn't think it is dead, OR there is no thread
- // object attached to it so we know it couldn't have crashed; and
- // (3) There is a pid assigned to it, so it is either starting or
- // already running.
- if (DEBUG_PROCESSES) Slog.v(TAG, "startProcess: name=" + processName
- + " app=" + app + " knownToBeDead=" + knownToBeDead
- + " thread=" + (app != null ? app.thread : null)
- + " pid=" + (app != null ? app.pid : -1));
- if (app != null && app.pid > 0) {
- if (!knownToBeDead || app.thread == null) {
- // We already have the app running, or are waiting for it to
- // come up (we have a pid but not yet its thread), so keep it.
- if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app);
- return app;
- } else {
- // An application record is attached to a previous process,
- // clean it up now.
- if (DEBUG_PROCESSES) Slog.v(TAG, "App died: " + app);
- handleAppDiedLocked(app, true);
- }
- }
- String hostingNameStr = hostingName != null
- ? hostingName.flattenToShortString() : null;
- if ((intentFlags&Intent.FLAG_FROM_BACKGROUND) != 0) {
- // If we are in the background, then check to see if this process
- // is bad. If so, we will just silently fail.
- if (mBadProcesses.get(info.processName, info.uid) != null) {
- if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
- + "/" + info.processName);
- return null;
- }
- } else {
- // When the user is explicitly starting a process, then clear its
- // crash count so that we won't make it bad until they see at
- // least one crash dialog again, and make the process good again
- // if it had been bad.
- if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
- + "/" + info.processName);
- mProcessCrashTimes.remove(info.processName, info.uid);
- if (mBadProcesses.get(info.processName, info.uid) != null) {
- EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, info.uid,
- info.processName);
- mBadProcesses.remove(info.processName, info.uid);
- if (app != null) {
- app.bad = false;
- }
- }
- }
- if (app == null) {
- app = newProcessRecordLocked(null, info, processName);
- mProcessNames.put(processName, info.uid, app);
- } else {
- // If this is a new package in the process, add the package to the list
- app.addPackage(info.packageName);
- }
- // If the system is not ready yet, then hold off on starting this
- // process until it is.
- if (!mProcessesReady
- && !isAllowedWhileBooting(info)
- && !allowWhileBooting) {
- if (!mProcessesOnHold.contains(app)) {
- mProcessesOnHold.add(app);
- }
- if (DEBUG_PROCESSES) Slog.v(TAG, "System not ready, putting on hold: " + app);
- return app;
- }
- startProcessLocked(app, hostingType, hostingNameStr);
- return (app.pid != 0) ? app : null;
- }
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name) {
if (caller == null) {
String msg = "null IApplicationThread when getting content provider "
+ name;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
return getContentProviderImpl(caller, name);
}
private final ContentProviderHolder getContentProviderImpl(
IApplicationThread caller, String name) {
ContentProviderRecord cpr;
ProviderInfo cpi = null;
synchronized(this) {
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when getting content provider " + name);
}
}
// First check if this content provider has been published...
cpr = mProvidersByName.get(name);
if (cpr != null) {
...
} else {
try {
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
} catch (RemoteException ex) {
}
if (cpi == null) {
return null;
}
String msg;
if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
throw new SecurityException(msg);
}
if (!mProcessesReady && !mDidUpdate && !mWaitingUpdate
&& !cpi.processName.equals("system")) {
// If this content provider does not run in the system
// process, and the system is not yet ready to run other
// processes, then fail fast instead of hanging.
throw new IllegalArgumentException(
"Attempt to launch content provider before system ready");
}
cpr = mProvidersByClass.get(cpi.name);
final boolean firstClass = cpr == null;
if (firstClass) {
try {
ApplicationInfo ai =
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS);
if (ai == null) {
Slog.w(TAG, "No package info for content provider "
+ cpi.name);
return null;
}
cpr = new ContentProviderRecord(cpi, ai);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
}
if (r != null && cpr.canRunHere(r)) {
// If this is a multiprocess provider, then just return its
// info and allow the caller to instantiate it. Only do
// this if the provider is the same user as the caller's
// process, or can run as root (so can be in any process).
return cpr;
}
if (DEBUG_PROVIDER) {
RuntimeException e = new RuntimeException("here");
Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.info.uid
+ " pruid " + cpr.appInfo.uid + "): " + cpr.info.name, e);
}
// This is single process, and our app is now connecting to it.
// See if we are already in the process of launching this
// provider.
final int N = mLaunchingProviders.size();
int i;
for (i=0; i<N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
// If the provider is not already being launched, then get it
// started.
if (i >= N) {
final long origId = Binder.clearCallingIdentity();
ProcessRecord proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false);
if (proc == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": process is bad");
return null;
}
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
Binder.restoreCallingIdentity(origId);
}
// Make sure the provider is published (the same provider class
// may be published under multiple names).
if (firstClass) {
mProvidersByClass.put(cpi.name, cpr);
}
mProvidersByName.put(name, cpr);
if (r != null) {
if (DEBUG_PROVIDER) Slog.v(TAG,
"Adding provider requested by "
+ r.processName + " from process "
+ cpr.info.processName);
Integer cnt = r.conProviders.get(cpr);
if (cnt == null) {
r.conProviders.put(cpr, new Integer(1));
} else {
r.conProviders.put(cpr, new Integer(cnt.intValue()+1));
}
cpr.clients.add(r);
} else {
cpr.externals++;
}
}
}
// Wait for the provider to be published...
synchronized (cpr) {
while (cpr.provider == null) {
if (cpr.launchingApp == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": launching app became null");
EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,
cpi.applicationInfo.packageName,
cpi.applicationInfo.uid, name);
return null;
}
try {
cpr.wait();
} catch (InterruptedException ex) {
}
}
}
return cpr;
}
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting) {
ProcessRecord app = getProcessRecordLocked(processName, info.uid);
// We don't have to do anything more if:
// (1) There is an existing application record; and
// (2) The caller doesn't think it is dead, OR there is no thread
// object attached to it so we know it couldn't have crashed; and
// (3) There is a pid assigned to it, so it is either starting or
// already running.
if (DEBUG_PROCESSES) Slog.v(TAG, "startProcess: name=" + processName
+ " app=" + app + " knownToBeDead=" + knownToBeDead
+ " thread=" + (app != null ? app.thread : null)
+ " pid=" + (app != null ? app.pid : -1));
if (app != null && app.pid > 0) {
if (!knownToBeDead || app.thread == null) {
// We already have the app running, or are waiting for it to
// come up (we have a pid but not yet its thread), so keep it.
if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app);
return app;
} else {
// An application record is attached to a previous process,
// clean it up now.
if (DEBUG_PROCESSES) Slog.v(TAG, "App died: " + app);
handleAppDiedLocked(app, true);
}
}
String hostingNameStr = hostingName != null
? hostingName.flattenToShortString() : null;
if ((intentFlags&Intent.FLAG_FROM_BACKGROUND) != 0) {
// If we are in the background, then check to see if this process
// is bad. If so, we will just silently fail.
if (mBadProcesses.get(info.processName, info.uid) != null) {
if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+ "/" + info.processName);
return null;
}
} else {
// When the user is explicitly starting a process, then clear its
// crash count so that we won't make it bad until they see at
// least one crash dialog again, and make the process good again
// if it had been bad.
if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
+ "/" + info.processName);
mProcessCrashTimes.remove(info.processName, info.uid);
if (mBadProcesses.get(info.processName, info.uid) != null) {
EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, info.uid,
info.processName);
mBadProcesses.remove(info.processName, info.uid);
if (app != null) {
app.bad = false;
}
}
}
if (app == null) {
app = newProcessRecordLocked(null, info, processName);
mProcessNames.put(processName, info.uid, app);
} else {
// If this is a new package in the process, add the package to the list
app.addPackage(info.packageName);
}
// If the system is not ready yet, then hold off on starting this
// process until it is.
if (!mProcessesReady
&& !isAllowedWhileBooting(info)
&& !allowWhileBooting) {
if (!mProcessesOnHold.contains(app)) {
mProcessesOnHold.add(app);
}
if (DEBUG_PROCESSES) Slog.v(TAG, "System not ready, putting on hold: " + app);
return app;
}
startProcessLocked(app, hostingType, hostingNameStr);
return (app.pid != 0) ? app : null;
}
startProcessLocked通过调用Process.start()用来创建新的进程,其随后调用startViaZygote@Process.java, 通过socket通知runSelectLoopMode@ZygoteInit.java调用runOnce@ZygoteConnection.java,最后调用Zygote.forkAndSpecialize()来创建进程并调用handleChildProc,在其中调用ZygoteInit.invokeStaticMain。 详见http://blog.csdn.net/windskier/article/details/6417061。
frameworks/base/core/java/com/android/os/Process.java
- public static final int start(final String processClass,
- final String niceName,
- int uid, int gid, int[] gids,
- int debugFlags,
- String[] zygoteArgs)
- {
- if (supportsProcesses()) {
- try {
- return startViaZygote(processClass, niceName, uid, gid, gids,
- debugFlags, zygoteArgs);
- } catch (ZygoteStartFailedEx ex) {
- Log.e(LOG_TAG,
- "Starting VM process through Zygote failed");
- throw new RuntimeException(
- "Starting VM process through Zygote failed", ex);
- }
- } ...
- }
- private static int startViaZygote(final String processClass,
- final String niceName,
- final int uid, final int gid,
- final int[] gids,
- int debugFlags,
- String[] extraArgs)
- throws ZygoteStartFailedEx {
- int pid;
- synchronized(Process.class) {
- ArrayList<String> argsForZygote = new ArrayList<String>();
- // --runtime-init, --setuid=, --setgid=,
- // and --setgroups= must go first
- argsForZygote.add("--runtime-init");
- argsForZygote.add("--setuid=" + uid);
- argsForZygote.add("--setgid=" + gid);
- ...
- // --setgroups is a comma-separated list
- if (gids != null && gids.length > 0) {
- StringBuilder sb = new StringBuilder();
- sb.append("--setgroups=");
- int sz = gids.length;
- for (int i = 0; i < sz; i++) {
- if (i != 0) {
- sb.append(',');
- }
- sb.append(gids[i]);
- }
- argsForZygote.add(sb.toString());
- }
- if (niceName != null) {
- argsForZygote.add("--nice-name=" + niceName);
- }
- argsForZygote.add(processClass);
- if (extraArgs != null) {
- for (String arg : extraArgs) {
- argsForZygote.add(arg);
- }
- }
- pid = zygoteSendArgsAndGetPid(argsForZygote); //写入到Zygote端口,触发创建进程
- }
- private static int zygoteSendArgsAndGetPid(ArrayList<String> args)
- throws ZygoteStartFailedEx {
- int pid;
- openZygoteSocketIfNeeded(); //打开Zygote端口
- try {
- /**
- * See com.android.internal.os.ZygoteInit.readArgumentList()
- * Presently the wire format to the zygote process is:
- * a) a count of arguments (argc, in essence)
- * b) a number of newline-separated argument strings equal to count
- *
- * After the zygote process reads these it will write the pid of
- * the child or -1 on failure.
- */
- sZygoteWriter.write(Integer.toString(args.size()));
- sZygoteWriter.newLine();
- int sz = args.size();
- for (int i = 0; i < sz; i++) {
- String arg = args.get(i);
- if (arg.indexOf('\n') >= 0) {
- throw new ZygoteStartFailedEx(
- "embedded newlines not allowed");
- }
- sZygoteWriter.write(arg);
- sZygoteWriter.newLine();
- }
- sZygoteWriter.flush(); //写入数据到端口
- // Should there be a timeout on this?
- pid = sZygoteInputStream.readInt(); //等待读端口数据,即进程完成创建
- if (pid < 0) {
- throw new ZygoteStartFailedEx("fork() failed");
- }
- } catch (IOException ex) {
- try {
- if (sZygoteSocket != null) {
- sZygoteSocket.close();
- }
- } catch (IOException ex2) {
- // we're going to fail anyway
- Log.e(LOG_TAG,"I/O exception on routine close", ex2);
- }
- sZygoteSocket = null;
- throw new ZygoteStartFailedEx(ex);
- }
- return pid;
- }
public static final int start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags,
String[] zygoteArgs)
{
if (supportsProcesses()) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
} ...
}
private static int startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags,
String[] extraArgs)
throws ZygoteStartFailedEx {
int pid;
synchronized(Process.class) {
ArrayList<String> argsForZygote = new ArrayList<String>();
// --runtime-init, --setuid=, --setgid=,
// and --setgroups= must go first
argsForZygote.add("--runtime-init");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
...
// --setgroups is a comma-separated list
if (gids != null && gids.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--setgroups=");
int sz = gids.length;
for (int i = 0; i < sz; i++) {
if (i != 0) {
sb.append(',');
}
sb.append(gids[i]);
}
argsForZygote.add(sb.toString());
}
if (niceName != null) {
argsForZygote.add("--nice-name=" + niceName);
}
argsForZygote.add(processClass);
if (extraArgs != null) {
for (String arg : extraArgs) {
argsForZygote.add(arg);
}
}
pid = zygoteSendArgsAndGetPid(argsForZygote); //写入到Zygote端口,触发创建进程
}
private static int zygoteSendArgsAndGetPid(ArrayList<String> args)
throws ZygoteStartFailedEx {
int pid;
openZygoteSocketIfNeeded(); //打开Zygote端口
try {
/**
* See com.android.internal.os.ZygoteInit.readArgumentList()
* Presently the wire format to the zygote process is:
* a) a count of arguments (argc, in essence)
* b) a number of newline-separated argument strings equal to count
*
* After the zygote process reads these it will write the pid of
* the child or -1 on failure.
*/
sZygoteWriter.write(Integer.toString(args.size()));
sZygoteWriter.newLine();
int sz = args.size();
for (int i = 0; i < sz; i++) {
String arg = args.get(i);
if (arg.indexOf('\n') >= 0) {
throw new ZygoteStartFailedEx(
"embedded newlines not allowed");
}
sZygoteWriter.write(arg);
sZygoteWriter.newLine();
}
sZygoteWriter.flush(); //写入数据到端口
// Should there be a timeout on this?
pid = sZygoteInputStream.readInt(); //等待读端口数据,即进程完成创建
if (pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
} catch (IOException ex) {
try {
if (sZygoteSocket != null) {
sZygoteSocket.close();
}
} catch (IOException ex2) {
// we're going to fail anyway
Log.e(LOG_TAG,"I/O exception on routine close", ex2);
}
sZygoteSocket = null;
throw new ZygoteStartFailedEx(ex);
}
return pid;
}
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
- class ZygoteConnection {
- void run() throws ZygoteInit.MethodAndArgsCaller {
- int loopCount = ZygoteInit.GC_LOOP_COUNT;
- while (true) {
- ...
- if (runOnce()) {
- break;
- }
- }
- }
- boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
- String args[];
- Arguments parsedArgs = null;
- FileDescriptor[] descriptors;
- try {
- args = readArgumentList(); //监听Zygote端口等待创建进程命令,如无则阻塞
- descriptors = mSocket.getAncillaryFileDescriptors();
- } catch (IOException ex) {
- Log.w(TAG, "IOException on command socket " + ex.getMessage());
- closeSocket();
- return true;
- }
- ...
- pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
- parsedArgs.gids, parsedArgs.debugFlags, rlimits); //这里调用fork创建进程
- ...
- if (pid == 0) {
- // in child
- handleChildProc(parsedArgs, descriptors, newStderr);
- // should never happen
- return true;
- } else { /* pid != 0 */
- // in parent...pid of < 0 means failure
- return handleParentProc(pid, descriptors, parsedArgs);
- }
- }
- private void handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, PrintStream newStderr) throws ZygoteInit.MethodAndArgsCaller {...
- if (parsedArgs.runtimeInit) {
- RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
- }
- ... }
class ZygoteConnection {
void run() throws ZygoteInit.MethodAndArgsCaller {
int loopCount = ZygoteInit.GC_LOOP_COUNT;
while (true) {
...
if (runOnce()) {
break;
}
}
}
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
args = readArgumentList(); //监听Zygote端口等待创建进程命令,如无则阻塞
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}
...
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids, parsedArgs.debugFlags, rlimits); //这里调用fork创建进程
...
if (pid == 0) {
// in child
handleChildProc(parsedArgs, descriptors, newStderr);
// should never happen
return true;
} else { /* pid != 0 */
// in parent...pid of < 0 means failure
return handleParentProc(pid, descriptors, parsedArgs);
}
}
private void handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, PrintStream newStderr) throws ZygoteInit.MethodAndArgsCaller {...
if (parsedArgs.runtimeInit) {
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
}
... }
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
- public static final void zygoteInit(String[] argv)
- throws ZygoteInit.MethodAndArgsCaller {
- commonInit();
- zygoteInitNative();
- ...
- invokeStaticMain(startClass, startArgs); //调用main方法
- }
public static final void zygoteInit(String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
commonInit();
zygoteInitNative();
...
invokeStaticMain(startClass, startArgs); //调用main方法
}
frameworks/base/core/jni/AndroidRuntime.cpp
- static void com_android_internal_os_RuntimeInit_zygoteInit(JNIEnv* env, jobject clazz)
- {
- gCurRuntime->onZygoteInit();
- }
static void com_android_internal_os_RuntimeInit_zygoteInit(JNIEnv* env, jobject clazz)
{
gCurRuntime->onZygoteInit();
}
frameworks/base/cmds/app_process/App_main.cpp
- class AppRuntime : public AndroidRuntime{
- virtual void onZygoteInit()
- {
- sp<ProcessState> proc = ProcessState::self();
- if (proc->supportsProcesses()) {
- proc->startThreadPool(); //对于应用来讲,此处创建1个线程池以处理Binder事件。注意此处只有一个Loop,而Native mediaserver进程则创建了两个线程循环。
- }
- }
class AppRuntime : public AndroidRuntime{
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
if (proc->supportsProcesses()) {
proc->startThreadPool(); //对于应用来讲,此处创建1个线程池以处理Binder事件。注意此处只有一个Loop,而Native mediaserver进程则创建了两个线程循环。
}
}
frameworks/base/core/java/android/app/ActivityThread.java
- public static final void main(String[] args) {
- SamplingProfilerIntegration.start();
- Process.setArgV0("<pre-initialized>");
- Looper.prepareMainLooper();
- if (sMainThreadHandler == null) {
- sMainThreadHandler = new Handler();
- }
- ActivityThread thread = new ActivityThread();
- thread.attach(false);
- if (false) {
- Looper.myLooper().setMessageLogging(new
- LogPrinter(Log.DEBUG, "ActivityThread"));
- }
- Looper.loop();
- private final void attach(boolean system) {
- sThreadLocal.set(this);
- mSystemThread = system;
- if (!system) {
- ViewRoot.addFirstDrawHandler(new Runnable() {
- public void run() {
- ensureJitEnabled();
- }
- });
- android.ddm.DdmHandleAppName.setAppName("<pre-initialized>");
- RuntimeInit.setApplicationObject(mAppThread.asBinder());
- IActivityManager mgr = ActivityManagerNative.getDefault();
- try {
- mgr.attachApplication(mAppThread);
- } catch (RemoteException ex) {
- }
- ...
- }
public static final void main(String[] args) {
SamplingProfilerIntegration.start();
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
private final void attach(boolean system) {
sThreadLocal.set(this);
mSystemThread = system;
if (!system) {
ViewRoot.addFirstDrawHandler(new Runnable() {
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>");
RuntimeInit.setApplicationObject(mAppThread.asBinder());
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
}
...
}
frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
- public final void attachApplication(IApplicationThread thread) {
- synchronized (this) {
- int callingPid = Binder.getCallingPid();
- final long origId = Binder.clearCallingIdentity();
- attachApplicationLocked(thread, callingPid);
- Binder.restoreCallingIdentity(origId);
- }
- }
- private final boolean attachApplicationLocked(IApplicationThread thread,
- int pid) {
- // Find the application record that is being attached... either via
- // the pid if we are running in multiple processes, or just pull the
- // next app record if we are emulating process with anonymous threads.
- ProcessRecord app;
- if (pid != MY_PID && pid >= 0) {
- synchronized (mPidsSelfLocked) {
- app = mPidsSelfLocked.get(pid);
- }
- } else if (mStartingProcesses.size() > 0) {
- app = mStartingProcesses.remove(0);
- app.setPid(pid);
- } else {
- app = null;
- }
- if (app == null) {
- Slog.w(TAG, "No pending application record for pid " + pid
- + " (IApplicationThread " + thread + "); dropping process");
- EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
- if (pid > 0 && pid != MY_PID) {
- Process.killProcess(pid);
- } else {
- try {
- thread.scheduleExit();
- } catch (Exception e) {
- // Ignore exceptions.
- }
- }
- return false;
- }
- // If this application record is still attached to a previous
- // process, clean it up now.
- if (app.thread != null) {
- handleAppDiedLocked(app, true);
- }
- // Tell the process all about itself.
- if (localLOGV) Slog.v(
- TAG, "Binding process pid " + pid + " to record " + app);
- String processName = app.processName;
- try {
- thread.asBinder().linkToDeath(new AppDeathRecipient(
- app, pid, thread), 0);
- } catch (RemoteException e) {
- app.resetPackageList();
- startProcessLocked(app, "link fail", processName);
- return false;
- }
- EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.pid, app.processName);
- app.thread = thread;
- app.curAdj = app.setAdj = -100;
- app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
- app.setSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
- app.forcingToForeground = null;
- app.foregroundServices = false;
- app.debugging = false;
- mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
- List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
- if (!normalMode) {
- Slog.i(TAG, "Launching preboot mode app: " + app);
- }
- if (localLOGV) Slog.v(
- TAG, "New app record " + app
- + " thread=" + thread.asBinder() + " pid=" + pid);
- try {
- int testMode = IApplicationThread.DEBUG_OFF;
- if (mDebugApp != null && mDebugApp.equals(processName)) {
- testMode = mWaitForDebugger
- ? IApplicationThread.DEBUG_WAIT
- : IApplicationThread.DEBUG_ON;
- app.debugging = true;
- if (mDebugTransient) {
- mDebugApp = mOrigDebugApp;
- mWaitForDebugger = mOrigWaitForDebugger;
- }
- }
- // If the app is being launched for restore or full backup, set it up specially
- boolean isRestrictedBackupMode = false;
- if (mBackupTarget != null && mBackupAppName.equals(processName)) {
- isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)
- || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
- }
- ensurePackageDexOpt(app.instrumentationInfo != null
- ? app.instrumentationInfo.packageName
- : app.info.packageName);
- if (app.instrumentationClass != null) {
- ensurePackageDexOpt(app.instrumentationClass.getPackageName());
- }
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Binding proc "
- + processName + " with config " + mConfiguration);
- thread.bindApplication(processName, app.instrumentationInfo != null
- ? app.instrumentationInfo : app.info, providers,
- app.instrumentationClass, app.instrumentationProfileFile,
- app.instrumentationArguments, app.instrumentationWatcher, testMode,
- isRestrictedBackupMode || !normalMode,
- mConfiguration, getCommonServicesLocked());
- updateLruProcessLocked(app, false, true);
- app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
- } catch (Exception e) {
- // todo: Yikes! What should we do? For now we will try to
- // start another process, but that could easily get us in
- // an infinite loop of restarting processes...
- Slog.w(TAG, "Exception thrown during bind!", e);
- app.resetPackageList();
- startProcessLocked(app, "bind fail", processName);
- return false;
- }
- // Remove this record from the list of starting applications.
- mPersistentStartingProcesses.remove(app);
- if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG,
- "Attach application locked removing on hold: " + app);
- mProcessesOnHold.remove(app);
- boolean badApp = false;
- boolean didSomething = false;
- // See if the top visible activity is waiting to run in this process...
- ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
- if (hr != null && normalMode) {
- if (hr.app == null && app.info.uid == hr.info.applicationInfo.uid
- && processName.equals(hr.processName)) {
- try {
- if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
- didSomething = true;
- }
- } catch (Exception e) {
- Slog.w(TAG, "Exception in new application when starting activity "
- + hr.intent.getComponent().flattenToShortString(), e);
- badApp = true;
- }
- } else {
- mMainStack.ensureActivitiesVisibleLocked(hr, null, processName, 0);
- }
- }
- // Find any services that should be running in this process...
- if (!badApp && mPendingServices.size() > 0) {
- ServiceRecord sr = null;
- try {
- for (int i=0; i<mPendingServices.size(); i++) {
- sr = mPendingServices.get(i);
- if (app.info.uid != sr.appInfo.uid
- || !processName.equals(sr.processName)) {
- continue;
- }
- mPendingServices.remove(i);
- i--;
- realStartServiceLocked(sr, app);
- didSomething = true;
- }
- } catch (Exception e) {
- Slog.w(TAG, "Exception in new application when starting service "
- + sr.shortName, e);
- badApp = true;
- }
- }
- // Check if the next broadcast receiver is in this process...
- BroadcastRecord br = mPendingBroadcast;
- if (!badApp && br != null && br.curApp == app) {
- try {
- mPendingBroadcast = null;
- processCurBroadcastLocked(br, app);
- didSomething = true;
- } catch (Exception e) {
- Slog.w(TAG, "Exception in new application when starting receiver "
- + br.curComponent.flattenToShortString(), e);
- badApp = true;
- logBroadcastReceiverDiscardLocked(br);
- finishReceiverLocked(br.receiver, br.resultCode, br.resultData,
- br.resultExtras, br.resultAbort, true);
- scheduleBroadcastsLocked();
- // We need to reset the state if we fails to start the receiver.
- br.state = BroadcastRecord.IDLE;
- }
- }
- // Check whether the next backup agent is in this process...
- if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) {
- if (DEBUG_BACKUP) Slog.v(TAG, "New app is backup target, launching agent for " + app);
- ensurePackageDexOpt(mBackupTarget.appInfo.packageName);
- try {
- thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode);
- } catch (Exception e) {
- Slog.w(TAG, "Exception scheduling backup agent creation: ");
- e.printStackTrace();
- }
- }
- if (badApp) {
- // todo: Also need to kill application to deal with all
- // kinds of exceptions.
- handleAppDiedLocked(app, false);
- return false;
- }
- if (!didSomething) {
- updateOomAdjLocked();
- }
- return true;
- }
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
// Find the application record that is being attached... either via
// the pid if we are running in multiple processes, or just pull the
// next app record if we are emulating process with anonymous threads.
ProcessRecord app;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid);
}
} else if (mStartingProcesses.size() > 0) {
app = mStartingProcesses.remove(0);
app.setPid(pid);
} else {
app = null;
}
if (app == null) {
Slog.w(TAG, "No pending application record for pid " + pid
+ " (IApplicationThread " + thread + "); dropping process");
EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
if (pid > 0 && pid != MY_PID) {
Process.killProcess(pid);
} else {
try {
thread.scheduleExit();
} catch (Exception e) {
// Ignore exceptions.
}
}
return false;
}
// If this application record is still attached to a previous
// process, clean it up now.
if (app.thread != null) {
handleAppDiedLocked(app, true);
}
// Tell the process all about itself.
if (localLOGV) Slog.v(
TAG, "Binding process pid " + pid + " to record " + app);
String processName = app.processName;
try {
thread.asBinder().linkToDeath(new AppDeathRecipient(
app, pid, thread), 0);
} catch (RemoteException e) {
app.resetPackageList();
startProcessLocked(app, "link fail", processName);
return false;
}
EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.pid, app.processName);
app.thread = thread;
app.curAdj = app.setAdj = -100;
app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
app.setSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.forcingToForeground = null;
app.foregroundServices = false;
app.debugging = false;
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
if (!normalMode) {
Slog.i(TAG, "Launching preboot mode app: " + app);
}
if (localLOGV) Slog.v(
TAG, "New app record " + app
+ " thread=" + thread.asBinder() + " pid=" + pid);
try {
int testMode = IApplicationThread.DEBUG_OFF;
if (mDebugApp != null && mDebugApp.equals(processName)) {
testMode = mWaitForDebugger
? IApplicationThread.DEBUG_WAIT
: IApplicationThread.DEBUG_ON;
app.debugging = true;
if (mDebugTransient) {
mDebugApp = mOrigDebugApp;
mWaitForDebugger = mOrigWaitForDebugger;
}
}
// If the app is being launched for restore or full backup, set it up specially
boolean isRestrictedBackupMode = false;
if (mBackupTarget != null && mBackupAppName.equals(processName)) {
isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)
|| (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
}
ensurePackageDexOpt(app.instrumentationInfo != null
? app.instrumentationInfo.packageName
: app.info.packageName);
if (app.instrumentationClass != null) {
ensurePackageDexOpt(app.instrumentationClass.getPackageName());
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Binding proc "
+ processName + " with config " + mConfiguration);
thread.bindApplication(processName, app.instrumentationInfo != null
? app.instrumentationInfo : app.info, providers,
app.instrumentationClass, app.instrumentationProfileFile,
app.instrumentationArguments, app.instrumentationWatcher, testMode,
isRestrictedBackupMode || !normalMode,
mConfiguration, getCommonServicesLocked());
updateLruProcessLocked(app, false, true);
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
} catch (Exception e) {
// todo: Yikes! What should we do? For now we will try to
// start another process, but that could easily get us in
// an infinite loop of restarting processes...
Slog.w(TAG, "Exception thrown during bind!", e);
app.resetPackageList();
startProcessLocked(app, "bind fail", processName);
return false;
}
// Remove this record from the list of starting applications.
mPersistentStartingProcesses.remove(app);
if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG,
"Attach application locked removing on hold: " + app);
mProcessesOnHold.remove(app);
boolean badApp = false;
boolean didSomething = false;
// See if the top visible activity is waiting to run in this process...
ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
if (hr != null && normalMode) {
if (hr.app == null && app.info.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
try {
if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
didSomething = true;
}
} catch (Exception e) {
Slog.w(TAG, "Exception in new application when starting activity "
+ hr.intent.getComponent().flattenToShortString(), e);
badApp = true;
}
} else {
mMainStack.ensureActivitiesVisibleLocked(hr, null, processName, 0);
}
}
// Find any services that should be running in this process...
if (!badApp && mPendingServices.size() > 0) {
ServiceRecord sr = null;
try {
for (int i=0; i<mPendingServices.size(); i++) {
sr = mPendingServices.get(i);
if (app.info.uid != sr.appInfo.uid
|| !processName.equals(sr.processName)) {
continue;
}
mPendingServices.remove(i);
i--;
realStartServiceLocked(sr, app);
didSomething = true;
}
} catch (Exception e) {
Slog.w(TAG, "Exception in new application when starting service "
+ sr.shortName, e);
badApp = true;
}
}
// Check if the next broadcast receiver is in this process...
BroadcastRecord br = mPendingBroadcast;
if (!badApp && br != null && br.curApp == app) {
try {
mPendingBroadcast = null;
processCurBroadcastLocked(br, app);
didSomething = true;
} catch (Exception e) {
Slog.w(TAG, "Exception in new application when starting receiver "
+ br.curComponent.flattenToShortString(), e);
badApp = true;
logBroadcastReceiverDiscardLocked(br);
finishReceiverLocked(br.receiver, br.resultCode, br.resultData,
br.resultExtras, br.resultAbort, true);
scheduleBroadcastsLocked();
// We need to reset the state if we fails to start the receiver.
br.state = BroadcastRecord.IDLE;
}
}
// Check whether the next backup agent is in this process...
if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) {
if (DEBUG_BACKUP) Slog.v(TAG, "New app is backup target, launching agent for " + app);
ensurePackageDexOpt(mBackupTarget.appInfo.packageName);
try {
thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode);
} catch (Exception e) {
Slog.w(TAG, "Exception scheduling backup agent creation: ");
e.printStackTrace();
}
}
if (badApp) {
// todo: Also need to kill application to deal with all
// kinds of exceptions.
handleAppDiedLocked(app, false);
return false;
}
if (!didSomething) {
updateOomAdjLocked();
}
return true;
}
frameworks/base/core/java/android/app/ActivityThread.java
- private final class ApplicationThread extends ApplicationThreadNative {
- public final void bindApplication(String processName,
- ApplicationInfo appInfo, List<ProviderInfo> providers,
- ComponentName instrumentationName, String profileFile,
- Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
- int debugMode, boolean isRestrictedBackupMode, Configuration config,
- Map<String, IBinder> services) {
- if (services != null) {
- // Setup the service cache in the ServiceManager
- ServiceManager.initServiceCache(services);
- }
- AppBindData data = new AppBindData();
- data.processName = processName;
- data.appInfo = appInfo;
- data.providers = providers;
- data.instrumentationName = instrumentationName;
- data.profileFile = profileFile;
- data.instrumentationArgs = instrumentationArgs;
- data.instrumentationWatcher = instrumentationWatcher;
- data.debugMode = debugMode;
- data.restrictedBackupMode = isRestrictedBackupMode;
- data.config = config;
- queueOrSendMessage(H.BIND_APPLICATION, data);
- }
private final class ApplicationThread extends ApplicationThreadNative {
public final void bindApplication(String processName,
ApplicationInfo appInfo, List<ProviderInfo> providers,
ComponentName instrumentationName, String profileFile,
Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
int debugMode, boolean isRestrictedBackupMode, Configuration config,
Map<String, IBinder> services) {
if (services != null) {
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.profileFile = profileFile;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.debugMode = debugMode;
data.restrictedBackupMode = isRestrictedBackupMode;
data.config = config;
queueOrSendMessage(H.BIND_APPLICATION, data);
}
- public void handleMessage(Message msg) {
- case BIND_APPLICATION:
- AppBindData data = (AppBindData)msg.obj;
- handleBindApplication(data);
- break;
public void handleMessage(Message msg) {
...
case BIND_APPLICATION:
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
break;
- private final void handleBindApplication(AppBindData data) {
- mBoundApplication = data;
- mConfiguration = new Configuration(data.config);
- // send up app name; do this *before* waiting for debugger
- Process.setArgV0(data.processName);
- android.ddm.DdmHandleAppName.setAppName(data.processName);
- /*
- * Before spawning a new process, reset the time zone to be the system time zone.
- * This needs to be done because the system time zone could have changed after the
- * the spawning of this process. Without doing this this process would have the incorrect
- * system time zone.
- */
- TimeZone.setDefault(null);
- /*
- * Initialize the default locale in this process for the reasons we set the time zone.
- */
- Locale.setDefault(data.config.locale);
- /*
- * Update the system configuration since its preloaded and might not
- * reflect configuration changes. The configuration object passed
- * in AppBindData can be safely assumed to be up to date
- */
- Resources.getSystem().updateConfiguration(mConfiguration, null);
- data.info = getPackageInfoNoCheck(data.appInfo);
- /**
- * For system applications on userdebug/eng builds, log stack
- * traces of disk and network access to dropbox for analysis.
- */
- if ((data.appInfo.flags &
- (ApplicationInfo.FLAG_SYSTEM |
- ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0) {
- StrictMode.conditionallyEnableDebugLogging();
- }
- /**
- * Switch this process to density compatibility mode if needed.
- */
- if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
- == 0) {
- Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);
- }
- if (data.debugMode != IApplicationThread.DEBUG_OFF) {
- // XXX should have option to change the port.
- Debug.changeDebugPort(8100);
- if (data.debugMode == IApplicationThread.DEBUG_WAIT) {
- Slog.w(TAG, "Application " + data.info.getPackageName()
- + " is waiting for the debugger on port 8100...");
- IActivityManager mgr = ActivityManagerNative.getDefault();
- try {
- mgr.showWaitingForDebugger(mAppThread, true);
- } catch (RemoteException ex) {
- }
- Debug.waitForDebugger();
- try {
- mgr.showWaitingForDebugger(mAppThread, false);
- } catch (RemoteException ex) {
- }
- } else {
- Slog.w(TAG, "Application " + data.info.getPackageName()
- + " can be debugged on port 8100...");
- }
- }
- if (data.instrumentationName != null) {
- ContextImpl appContext = new ContextImpl();
- appContext.init(data.info, null, this);
- InstrumentationInfo ii = null;
- try {
- ii = appContext.getPackageManager().
- getInstrumentationInfo(data.instrumentationName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- }
- if (ii == null) {
- throw new RuntimeException(
- "Unable to find instrumentation info for: "
- + data.instrumentationName);
- }
- mInstrumentationAppDir = ii.sourceDir;
- mInstrumentationAppPackage = ii.packageName;
- mInstrumentedAppDir = data.info.getAppDir();
- ApplicationInfo instrApp = new ApplicationInfo();
- instrApp.packageName = ii.packageName;
- instrApp.sourceDir = ii.sourceDir;
- instrApp.publicSourceDir = ii.publicSourceDir;
- instrApp.dataDir = ii.dataDir;
- instrApp.nativeLibraryDir = ii.nativeLibraryDir;
- LoadedApk pi = getPackageInfo(instrApp,
- appContext.getClassLoader(), false, true);
- ContextImpl instrContext = new ContextImpl();
- instrContext.init(pi, null, this);
- try {
- java.lang.ClassLoader cl = instrContext.getClassLoader();
- mInstrumentation = (Instrumentation)
- cl.loadClass(data.instrumentationName.getClassName()).newInstance();
- } catch (Exception e) {
- throw new RuntimeException(
- "Unable to instantiate instrumentation "
- + data.instrumentationName + ": " + e.toString(), e);
- }
- mInstrumentation.init(this, instrContext, appContext,
- new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher);
- if (data.profileFile != null && !ii.handleProfiling) {
- data.handlingProfiling = true;
- File file = new File(data.profileFile);
- file.getParentFile().mkdirs();
- Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
- }
- try {
- mInstrumentation.onCreate(data.instrumentationArgs);
- }
- catch (Exception e) {
- throw new RuntimeException(
- "Exception thrown in onCreate() of "
- + data.instrumentationName + ": " + e.toString(), e);
- }
- } else {
- mInstrumentation = new Instrumentation();
- }
- // If the app is being launched for full backup or restore, bring it up in
- // a restricted environment with the base application class.
- Application app = data.info.makeApplication(data.restrictedBackupMode, null);
- mInitialApplication = app;
- List<ProviderInfo> providers = data.providers;
- if (providers != null) {
- installContentProviders(app, providers);
- // For process that contain content providers, we want to
- // ensure that the JIT is enabled "at some point".
- mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
- }
- try {
- mInstrumentation.callApplicationOnCreate(app);
- } catch (Exception e) {
- if (!mInstrumentation.onException(app, e)) {
- throw new RuntimeException(
- "Unable to create application " + app.getClass().getName()
- + ": " + e.toString(), e);
- }
- }
- }
private final void handleBindApplication(AppBindData data) {
mBoundApplication = data;
mConfiguration = new Configuration(data.config);
// send up app name; do this *before* waiting for debugger
Process.setArgV0(data.processName);
android.ddm.DdmHandleAppName.setAppName(data.processName);
/*
* Before spawning a new process, reset the time zone to be the system time zone.
* This needs to be done because the system time zone could have changed after the
* the spawning of this process. Without doing this this process would have the incorrect
* system time zone.
*/
TimeZone.setDefault(null);
/*
* Initialize the default locale in this process for the reasons we set the time zone.
*/
Locale.setDefault(data.config.locale);
/*
* Update the system configuration since its preloaded and might not
* reflect configuration changes. The configuration object passed
* in AppBindData can be safely assumed to be up to date
*/
Resources.getSystem().updateConfiguration(mConfiguration, null);
data.info = getPackageInfoNoCheck(data.appInfo);
/**
* For system applications on userdebug/eng builds, log stack
* traces of disk and network access to dropbox for analysis.
*/
if ((data.appInfo.flags &
(ApplicationInfo.FLAG_SYSTEM |
ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0) {
StrictMode.conditionallyEnableDebugLogging();
}
/**
* Switch this process to density compatibility mode if needed.
*/
if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
== 0) {
Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);
}
if (data.debugMode != IApplicationThread.DEBUG_OFF) {
// XXX should have option to change the port.
Debug.changeDebugPort(8100);
if (data.debugMode == IApplicationThread.DEBUG_WAIT) {
Slog.w(TAG, "Application " + data.info.getPackageName()
+ " is waiting for the debugger on port 8100...");
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.showWaitingForDebugger(mAppThread, true);
} catch (RemoteException ex) {
}
Debug.waitForDebugger();
try {
mgr.showWaitingForDebugger(mAppThread, false);
} catch (RemoteException ex) {
}
} else {
Slog.w(TAG, "Application " + data.info.getPackageName()
+ " can be debugged on port 8100...");
}
}
if (data.instrumentationName != null) {
ContextImpl appContext = new ContextImpl();
appContext.init(data.info, null, this);
InstrumentationInfo ii = null;
try {
ii = appContext.getPackageManager().
getInstrumentationInfo(data.instrumentationName, 0);
} catch (PackageManager.NameNotFoundException e) {
}
if (ii == null) {
throw new RuntimeException(
"Unable to find instrumentation info for: "
+ data.instrumentationName);
}
mInstrumentationAppDir = ii.sourceDir;
mInstrumentationAppPackage = ii.packageName;
mInstrumentedAppDir = data.info.getAppDir();
ApplicationInfo instrApp = new ApplicationInfo();
instrApp.packageName = ii.packageName;
instrApp.sourceDir = ii.sourceDir;
instrApp.publicSourceDir = ii.publicSourceDir;
instrApp.dataDir = ii.dataDir;
instrApp.nativeLibraryDir = ii.nativeLibraryDir;
LoadedApk pi = getPackageInfo(instrApp,
appContext.getClassLoader(), false, true);
ContextImpl instrContext = new ContextImpl();
instrContext.init(pi, null, this);
try {
java.lang.ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate instrumentation "
+ data.instrumentationName + ": " + e.toString(), e);
}
mInstrumentation.init(this, instrContext, appContext,
new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher);
if (data.profileFile != null && !ii.handleProfiling) {
data.handlingProfiling = true;
File file = new File(data.profileFile);
file.getParentFile().mkdirs();
Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
}
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
} else {
mInstrumentation = new Instrumentation();
}
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
List<ProviderInfo> providers = data.providers;
if (providers != null) {
installContentProviders(app, providers);
// For process that contain content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}
frameworks/base/core/java/android/content/ContentProvider.java
- <PRE class=java name="code"> public void attachInfo(Context context, ProviderInfo info) {
- /*
- * Only allow it to be set once, so after the content service gives
- * this to us clients can't change it.
- */
- if (mContext == null) {
- mContext = context;
- mMyUid = Process.myUid();
- if (info != null) {
- setReadPermission(info.readPermission);
- setWritePermission(info.writePermission);
- setPathPermissions(info.pathPermissions);
- mExported = info.exported;
- }
- ContentProvider.this.onCreate();
- }
- }</PRE>
- <PRE></PRE>
- <P></P>
- <PRE></PRE>
- <P></P>
- <P>frameworks/base/services/java/com/android/server/am/ActivityManagerService.java </P>
- <P></P>
- <P></P>
- <PRE class=cpp name="code"> public final void publishContentProviders(IApplicationThread caller,
- List<ContentProviderHolder> providers) {
- if (providers == null) {
- return;
- }
- synchronized(this) {
- final ProcessRecord r = getRecordForAppLocked(caller);
- if (r == null) {
- throw new SecurityException(
- "Unable to find app for caller " + caller
- + " (pid=" + Binder.getCallingPid()
- + ") when publishing content providers");
- }
- final long origId = Binder.clearCallingIdentity();
- final int N = providers.size();
- for (int i=0; i<N; i++) {
- ContentProviderHolder src = providers.get(i);
- if (src == null || src.info == null || src.provider == null) {
- continue;
- }
- ContentProviderRecord dst = r.pubProviders.get(src.info.name);
- if (dst != null) {
- mProvidersByClass.put(dst.info.name, dst);
- String names[] = dst.info.authority.split(";");
- for (int j = 0; j < names.length; j++) {
- mProvidersByName.put(names[j], dst);
- }
- int NL = mLaunchingProviders.size();
- int j;
- for (j=0; j<NL; j++) {
- if (mLaunchingProviders.get(j) == dst) {
- mLaunchingProviders.remove(j);
- j--;
- NL--;
- }
- }
- synchronized (dst) {
- dst.provider = src.provider;
- dst.app = r;
- dst.notifyAll();
- }
- updateOomAdjLocked(r);
- }
- }
- Binder.restoreCallingIdentity(origId);
- }
- }
- </PRE><BR>
- <P></P>
- <P>frameworks/base/core/java/android/app/ActivityThread.java</P>
- <P></P>
- <P><BR>
- </P>
- <PRE class=cpp name="code"><PRE class=java name="code"> private final void installContentProviders(
- Context context, List<ProviderInfo> providers) {
- final ArrayList<IActivityManager.ContentProviderHolder> results =
- new ArrayList<IActivityManager.ContentProviderHolder>();
- Iterator<ProviderInfo> i = providers.iterator();
- while (i.hasNext()) {
- ProviderInfo cpi = i.next();
- StringBuilder buf = new StringBuilder(128);
- buf.append("Pub ");
- buf.append(cpi.authority);
- buf.append(": ");
- buf.append(cpi.name);
- Log.i(TAG, buf.toString());
- IContentProvider cp = installProvider(context, null, cpi, false);
- if (cp != null) {
- IActivityManager.ContentProviderHolder cph =
- new IActivityManager.ContentProviderHolder(cpi);
- cph.provider = cp;
- results.add(cph);
- // Don't ever unload this provider from the process.
- synchronized(mProviderMap) {
- mProviderRefCountMap.put(cp.asBinder(), new ProviderRefCount(10000));
- }
- }
- }
- try {
- ActivityManagerNative.getDefault().publishContentProviders(
- getApplicationThread(), results);
- } catch (RemoteException ex) {
- }
- }
- </PRE>
- <PRE></PRE>
- <P>In the following code,ContentProvider is instantiated.</P>
- <P></P>
- <PRE class=cpp name="code"><PRE class=java name="code"> private final IContentProvider installProvider(Context context,
- IContentProvider provider, ProviderInfo info, boolean noisy) {
- ContentProvider localProvider = null;
- if (provider == null) {
- if (noisy) {
- Slog.d(TAG, "Loading provider " + info.authority + ": "
- + info.name);
- }
- Context c = null;
- ApplicationInfo ai = info.applicationInfo;
- if (context.getPackageName().equals(ai.packageName)) {
- c = context;
- } else if (mInitialApplication != null &&
- mInitialApplication.getPackageName().equals(ai.packageName)) {
- c = mInitialApplication;
- } else {
- try {
- c = context.createPackageContext(ai.packageName,
- Context.CONTEXT_INCLUDE_CODE);
- } catch (PackageManager.NameNotFoundException e) {
- }
- }
- if (c == null) {
- Slog.w(TAG, "Unable to get context for package " +
- ai.packageName +
- " while loading content provider " +
- info.name);
- return null;
- }
- try {
- final java.lang.ClassLoader cl = c.getClassLoader();
- localProvider = (ContentProvider)cl.
- loadClass(info.name).newInstance(); //这里构造ContentProvider实例,例如:new MediaProvider
- provider = localProvider.getIContentProvider(); //得到对应的Binder操作实例
- if (provider == null) {
- Slog.e(TAG, "Failed to instantiate class " +
- info.name + " from sourceDir " +
- info.applicationInfo.sourceDir);
- return null;
- }
- if (Config.LOGV) Slog.v(
- TAG, "Instantiating local provider " + info.name);
- // XXX Need to create the correct context for this provider.
- localProvider.attachInfo(c, info); //在这里调用ContentProvider.attachInfo,那里调用onCreate开始初始化Provider
- } catch (java.lang.Exception e) {
- if (!mInstrumentation.onException(null, e)) {
- throw new RuntimeException(
- "Unable to get provider " + info.name
- + ": " + e.toString(), e);
- }
- return null;
- }
- } else if (localLOGV) {
- Slog.v(TAG, "Installing external provider " + info.authority + ": "
- + info.name);
- }
- synchronized (mProviderMap) {
- // Cache the pointer for the remote provider.
- String names[] = PATTERN_SEMICOLON.split(info.authority); //同一个ContentProvider可能支持多个authority
- for (int i=0; i<names.length; i++) { //对每一个authority,构建对应的ProviderClientRecord
- ProviderClientRecord pr = new ProviderClientRecord(names[i], provider,
- localProvider);
- try {
- provider.asBinder().linkToDeath(pr, 0);
- mProviderMap.put(names[i], pr);
- } catch (RemoteException e) {
- return null;
- }
- }
- if (localProvider != null) { //如果provider早已创建出来了
- mLocalProviders.put(provider.asBinder(),
- new ProviderClientRecord(null, provider, localProvider));
- }
- }
- return provider;
- }</PRE>
- <PRE></PRE>
- <P></P>
- <PRE></PRE>
- frameworks/base/services/java/com/android/server/am/ActivityManagerService.java
- <P></P>
- <P></P>
- <PRE class=cpp name="code"><PRE class=java name="code"> public final void publishContentProviders(IApplicationThread caller,
- List<ContentProviderHolder> providers) {
- if (providers == null) {
- return;
- }
- synchronized(this) {
- final ProcessRecord r = getRecordForAppLocked(caller);
- if (r == null) {
- throw new SecurityException(
- "Unable to find app for caller " + caller
- + " (pid=" + Binder.getCallingPid()
- + ") when publishing content providers");
- }
- final long origId = Binder.clearCallingIdentity();
- final int N = providers.size();
- for (int i=0; i<N; i++) {
- ContentProviderHolder src = providers.get(i);
- if (src == null || src.info == null || src.provider == null) {
- continue;
- }
- ContentProviderRecord dst = r.pubProviders.get(src.info.name);
- if (dst != null) {
- mProvidersByClass.put(dst.info.name, dst);
- String names[] = dst.info.authority.split(";");
- for (int j = 0; j < names.length; j++) {
- mProvidersByName.put(names[j], dst);
- }
- int NL = mLaunchingProviders.size();
- int j;
- for (j=0; j<NL; j++) {
- if (mLaunchingProviders.get(j) == dst) {
- mLaunchingProviders.remove(j);
- j--;
- NL--;
- }
- }
- synchronized (dst) {
- dst.provider = src.provider;
- dst.app = r;
- dst.notifyAll();
- }
- updateOomAdjLocked(r);
- }
- }
- Binder.restoreCallingIdentity(origId);
- }
- }</PRE>
- <PRE></PRE>
- <P></P>
- <PRE></PRE>
- <P></P>
- <P><BR>
- </P>
- <P><BR>
- </P>
- <P><BR>
- </P>
- <P><BR>
- </P>
- <P><BR>
- </P>
- <BR>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- <PRE></PRE>
- </PRE></PRE></PRE>