/*
|
* Copyright (C) 2006 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
package com.android.server.am;
|
|
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
|
|
import android.app.ContentProviderHolder;
|
import android.content.ComponentName;
|
import android.content.IContentProvider;
|
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ProviderInfo;
|
import android.os.IBinder;
|
import android.os.IBinder.DeathRecipient;
|
import android.os.Process;
|
import android.os.RemoteException;
|
import android.os.UserHandle;
|
import android.util.ArrayMap;
|
import android.util.Slog;
|
|
import com.android.internal.app.procstats.AssociationState;
|
import com.android.internal.app.procstats.ProcessStats;
|
|
import java.io.PrintWriter;
|
import java.util.ArrayList;
|
|
final class ContentProviderRecord implements ComponentName.WithComponentName {
|
final ActivityManagerService service;
|
public final ProviderInfo info;
|
final int uid;
|
final ApplicationInfo appInfo;
|
final ComponentName name;
|
final boolean singleton;
|
public IContentProvider provider;
|
public boolean noReleaseNeeded;
|
// All attached clients
|
final ArrayList<ContentProviderConnection> connections
|
= new ArrayList<ContentProviderConnection>();
|
//final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
|
// Handles for non-framework processes supported by this provider
|
ArrayMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
|
// Count for external process for which we have no handles.
|
int externalProcessNoHandleCount;
|
ProcessRecord proc; // if non-null, hosting process.
|
ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
|
String stringName;
|
String shortStringName;
|
|
public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info,
|
ApplicationInfo ai, ComponentName _name, boolean _singleton) {
|
service = _service;
|
info = _info;
|
uid = ai.uid;
|
appInfo = ai;
|
name = _name;
|
singleton = _singleton;
|
noReleaseNeeded = (uid == 0 || uid == Process.SYSTEM_UID)
|
&& (_name == null || !"com.android.settings".equals(_name.getPackageName()));
|
}
|
|
public ContentProviderRecord(ContentProviderRecord cpr) {
|
service = cpr.service;
|
info = cpr.info;
|
uid = cpr.uid;
|
appInfo = cpr.appInfo;
|
name = cpr.name;
|
singleton = cpr.singleton;
|
noReleaseNeeded = cpr.noReleaseNeeded;
|
}
|
|
public ContentProviderHolder newHolder(ContentProviderConnection conn) {
|
ContentProviderHolder holder = new ContentProviderHolder(info);
|
holder.provider = provider;
|
holder.noReleaseNeeded = noReleaseNeeded;
|
holder.connection = conn;
|
return holder;
|
}
|
|
public void setProcess(ProcessRecord proc) {
|
this.proc = proc;
|
if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS) {
|
for (int iconn = connections.size() - 1; iconn >= 0; iconn--) {
|
final ContentProviderConnection conn = connections.get(iconn);
|
if (proc != null) {
|
conn.startAssociationIfNeeded();
|
} else {
|
conn.stopAssociation();
|
}
|
}
|
if (externalProcessTokenToHandle != null) {
|
for (int iext = externalProcessTokenToHandle.size() - 1; iext >= 0; iext--) {
|
final ExternalProcessHandle handle = externalProcessTokenToHandle.valueAt(iext);
|
if (proc != null) {
|
handle.startAssociationIfNeeded(this);
|
} else {
|
handle.stopAssociation();
|
}
|
}
|
}
|
}
|
}
|
|
public boolean canRunHere(ProcessRecord app) {
|
return (info.multiprocess || info.processName.equals(app.processName))
|
&& uid == app.info.uid;
|
}
|
|
public void addExternalProcessHandleLocked(IBinder token, int callingUid, String callingTag) {
|
if (token == null) {
|
externalProcessNoHandleCount++;
|
} else {
|
if (externalProcessTokenToHandle == null) {
|
externalProcessTokenToHandle = new ArrayMap<>();
|
}
|
ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
|
if (handle == null) {
|
handle = new ExternalProcessHandle(token, callingUid, callingTag);
|
externalProcessTokenToHandle.put(token, handle);
|
handle.startAssociationIfNeeded(this);
|
}
|
handle.mAcquisitionCount++;
|
}
|
}
|
|
public boolean removeExternalProcessHandleLocked(IBinder token) {
|
if (hasExternalProcessHandles()) {
|
boolean hasHandle = false;
|
if (externalProcessTokenToHandle != null) {
|
ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
|
if (handle != null) {
|
hasHandle = true;
|
handle.mAcquisitionCount--;
|
if (handle.mAcquisitionCount == 0) {
|
removeExternalProcessHandleInternalLocked(token);
|
return true;
|
}
|
}
|
}
|
if (!hasHandle) {
|
externalProcessNoHandleCount--;
|
return true;
|
}
|
}
|
return false;
|
}
|
|
private void removeExternalProcessHandleInternalLocked(IBinder token) {
|
ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
|
handle.unlinkFromOwnDeathLocked();
|
handle.stopAssociation();
|
externalProcessTokenToHandle.remove(token);
|
if (externalProcessTokenToHandle.size() == 0) {
|
externalProcessTokenToHandle = null;
|
}
|
}
|
|
public boolean hasExternalProcessHandles() {
|
return (externalProcessTokenToHandle != null || externalProcessNoHandleCount > 0);
|
}
|
|
public boolean hasConnectionOrHandle() {
|
return !connections.isEmpty() || hasExternalProcessHandles();
|
}
|
|
void dump(PrintWriter pw, String prefix, boolean full) {
|
if (full) {
|
pw.print(prefix); pw.print("package=");
|
pw.print(info.applicationInfo.packageName);
|
pw.print(" process="); pw.println(info.processName);
|
}
|
pw.print(prefix); pw.print("proc="); pw.println(proc);
|
if (launchingApp != null) {
|
pw.print(prefix); pw.print("launchingApp="); pw.println(launchingApp);
|
}
|
if (full) {
|
pw.print(prefix); pw.print("uid="); pw.print(uid);
|
pw.print(" provider="); pw.println(provider);
|
}
|
if (singleton) {
|
pw.print(prefix); pw.print("singleton="); pw.println(singleton);
|
}
|
pw.print(prefix); pw.print("authority="); pw.println(info.authority);
|
if (full) {
|
if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
|
pw.print(prefix); pw.print("isSyncable="); pw.print(info.isSyncable);
|
pw.print(" multiprocess="); pw.print(info.multiprocess);
|
pw.print(" initOrder="); pw.println(info.initOrder);
|
}
|
}
|
if (full) {
|
if (hasExternalProcessHandles()) {
|
pw.print(prefix); pw.print("externals:");
|
if (externalProcessTokenToHandle != null) {
|
pw.print(" w/token=");
|
pw.print(externalProcessTokenToHandle.size());
|
}
|
if (externalProcessNoHandleCount > 0) {
|
pw.print(" notoken=");
|
pw.print(externalProcessNoHandleCount);
|
}
|
pw.println();
|
}
|
} else {
|
if (connections.size() > 0 || externalProcessNoHandleCount > 0) {
|
pw.print(prefix); pw.print(connections.size());
|
pw.print(" connections, "); pw.print(externalProcessNoHandleCount);
|
pw.println(" external handles");
|
}
|
}
|
if (connections.size() > 0) {
|
if (full) {
|
pw.print(prefix); pw.println("Connections:");
|
}
|
for (int i=0; i<connections.size(); i++) {
|
ContentProviderConnection conn = connections.get(i);
|
pw.print(prefix); pw.print(" -> "); pw.println(conn.toClientString());
|
if (conn.provider != this) {
|
pw.print(prefix); pw.print(" *** WRONG PROVIDER: ");
|
pw.println(conn.provider);
|
}
|
}
|
}
|
}
|
|
@Override
|
public String toString() {
|
if (stringName != null) {
|
return stringName;
|
}
|
StringBuilder sb = new StringBuilder(128);
|
sb.append("ContentProviderRecord{");
|
sb.append(Integer.toHexString(System.identityHashCode(this)));
|
sb.append(" u");
|
sb.append(UserHandle.getUserId(uid));
|
sb.append(' ');
|
sb.append(name.flattenToShortString());
|
sb.append('}');
|
return stringName = sb.toString();
|
}
|
|
public String toShortString() {
|
if (shortStringName != null) {
|
return shortStringName;
|
}
|
StringBuilder sb = new StringBuilder(128);
|
sb.append(Integer.toHexString(System.identityHashCode(this)));
|
sb.append('/');
|
sb.append(name.flattenToShortString());
|
return shortStringName = sb.toString();
|
}
|
|
// This class represents a handle from an external process to a provider.
|
private class ExternalProcessHandle implements DeathRecipient {
|
private static final String LOG_TAG = "ExternalProcessHanldle";
|
|
final IBinder mToken;
|
final int mOwningUid;
|
final String mOwningProcessName;
|
int mAcquisitionCount;
|
AssociationState.SourceState mAssociation;
|
|
public ExternalProcessHandle(IBinder token, int owningUid, String owningProcessName) {
|
mToken = token;
|
mOwningUid = owningUid;
|
mOwningProcessName = owningProcessName;
|
try {
|
token.linkToDeath(this, 0);
|
} catch (RemoteException re) {
|
Slog.e(LOG_TAG, "Couldn't register for death for token: " + mToken, re);
|
}
|
}
|
|
public void unlinkFromOwnDeathLocked() {
|
mToken.unlinkToDeath(this, 0);
|
}
|
|
public void startAssociationIfNeeded(ContentProviderRecord provider) {
|
// If we don't already have an active association, create one... but only if this
|
// is an association between two different processes.
|
if (ActivityManagerService.TRACK_PROCSTATS_ASSOCIATIONS
|
&& mAssociation == null && provider.proc != null
|
&& (provider.appInfo.uid != mOwningUid
|
|| !provider.info.processName.equals(mOwningProcessName))) {
|
ProcessStats.ProcessStateHolder holder = provider.proc.pkgList.get(
|
provider.name.getPackageName());
|
if (holder == null) {
|
Slog.wtf(TAG_AM, "No package in referenced provider "
|
+ provider.name.toShortString() + ": proc=" + provider.proc);
|
} else if (holder.pkg == null) {
|
Slog.wtf(TAG_AM, "Inactive holder in referenced provider "
|
+ provider.name.toShortString() + ": proc=" + provider.proc);
|
} else {
|
mAssociation = holder.pkg.getAssociationStateLocked(holder.state,
|
provider.name.getClassName()).startSource(mOwningUid,
|
mOwningProcessName, null);
|
|
}
|
}
|
}
|
|
public void stopAssociation() {
|
if (mAssociation != null) {
|
mAssociation.stop();
|
mAssociation = null;
|
}
|
}
|
|
@Override
|
public void binderDied() {
|
synchronized (service) {
|
if (hasExternalProcessHandles() &&
|
externalProcessTokenToHandle.get(mToken) != null) {
|
removeExternalProcessHandleInternalLocked(mToken);
|
}
|
}
|
}
|
}
|
|
public ComponentName getComponentName() {
|
return name;
|
}
|
}
|