/*
|
* 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.IServiceConnection;
|
import android.app.PendingIntent;
|
import android.content.Context;
|
import android.util.Slog;
|
import android.util.proto.ProtoOutputStream;
|
import android.util.proto.ProtoUtils;
|
|
import com.android.internal.app.procstats.AssociationState;
|
import com.android.internal.app.procstats.ProcessStats;
|
import com.android.server.wm.ActivityServiceConnectionsHolder;
|
|
import java.io.PrintWriter;
|
|
/**
|
* Description of a single binding to a service.
|
*/
|
final class ConnectionRecord {
|
final AppBindRecord binding; // The application/service binding.
|
final ActivityServiceConnectionsHolder<ConnectionRecord> activity; // If non-null, the owning activity.
|
final IServiceConnection conn; // The client connection.
|
final int flags; // Binding options.
|
final int clientLabel; // String resource labeling this client.
|
final PendingIntent clientIntent; // How to launch the client.
|
final int clientUid; // The identity of this connection's client
|
final String clientProcessName; // The source process of this connection's client
|
final String clientPackageName; // The source package of this connection's client
|
public AssociationState.SourceState association; // Association tracking
|
String stringName; // Caching of toString.
|
boolean serviceDead; // Well is it?
|
|
// Please keep the following two enum list synced.
|
private static final int[] BIND_ORIG_ENUMS = new int[] {
|
Context.BIND_AUTO_CREATE,
|
Context.BIND_DEBUG_UNBIND,
|
Context.BIND_NOT_FOREGROUND,
|
Context.BIND_IMPORTANT_BACKGROUND,
|
Context.BIND_ABOVE_CLIENT,
|
Context.BIND_ALLOW_OOM_MANAGEMENT,
|
Context.BIND_WAIVE_PRIORITY,
|
Context.BIND_IMPORTANT,
|
Context.BIND_ADJUST_WITH_ACTIVITY,
|
Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
|
Context.BIND_FOREGROUND_SERVICE,
|
Context.BIND_TREAT_LIKE_ACTIVITY,
|
Context.BIND_VISIBLE,
|
Context.BIND_SHOWING_UI,
|
Context.BIND_NOT_VISIBLE,
|
Context.BIND_NOT_PERCEPTIBLE,
|
Context.BIND_INCLUDE_CAPABILITIES,
|
};
|
private static final int[] BIND_PROTO_ENUMS = new int[] {
|
ConnectionRecordProto.AUTO_CREATE,
|
ConnectionRecordProto.DEBUG_UNBIND,
|
ConnectionRecordProto.NOT_FG,
|
ConnectionRecordProto.IMPORTANT_BG,
|
ConnectionRecordProto.ABOVE_CLIENT,
|
ConnectionRecordProto.ALLOW_OOM_MANAGEMENT,
|
ConnectionRecordProto.WAIVE_PRIORITY,
|
ConnectionRecordProto.IMPORTANT,
|
ConnectionRecordProto.ADJUST_WITH_ACTIVITY,
|
ConnectionRecordProto.FG_SERVICE_WHILE_AWAKE,
|
ConnectionRecordProto.FG_SERVICE,
|
ConnectionRecordProto.TREAT_LIKE_ACTIVITY,
|
ConnectionRecordProto.VISIBLE,
|
ConnectionRecordProto.SHOWING_UI,
|
ConnectionRecordProto.NOT_VISIBLE,
|
ConnectionRecordProto.NOT_PERCEPTIBLE,
|
ConnectionRecordProto.INCLUDE_CAPABILITIES,
|
};
|
|
void dump(PrintWriter pw, String prefix) {
|
pw.println(prefix + "binding=" + binding);
|
if (activity != null) {
|
activity.dump(pw, prefix);
|
}
|
pw.println(prefix + "conn=" + conn.asBinder()
|
+ " flags=0x" + Integer.toHexString(flags));
|
}
|
|
ConnectionRecord(AppBindRecord _binding,
|
ActivityServiceConnectionsHolder<ConnectionRecord> _activity,
|
IServiceConnection _conn, int _flags,
|
int _clientLabel, PendingIntent _clientIntent,
|
int _clientUid, String _clientProcessName, String _clientPackageName) {
|
binding = _binding;
|
activity = _activity;
|
conn = _conn;
|
flags = _flags;
|
clientLabel = _clientLabel;
|
clientIntent = _clientIntent;
|
clientUid = _clientUid;
|
clientProcessName = _clientProcessName;
|
clientPackageName = _clientPackageName;
|
}
|
|
public boolean hasFlag(final int flag) {
|
return (flags & flag) != 0;
|
}
|
|
public boolean notHasFlag(final int flag) {
|
return (flags & flag) == 0;
|
}
|
|
public void startAssociationIfNeeded() {
|
// 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
|
&& association == null && binding.service.app != null
|
&& (binding.service.appInfo.uid != clientUid
|
|| !binding.service.processName.equals(clientProcessName))) {
|
ProcessStats.ProcessStateHolder holder = binding.service.app.pkgList.get(
|
binding.service.instanceName.getPackageName());
|
if (holder == null) {
|
Slog.wtf(TAG_AM, "No package in referenced service "
|
+ binding.service.shortInstanceName + ": proc=" + binding.service.app);
|
} else if (holder.pkg == null) {
|
Slog.wtf(TAG_AM, "Inactive holder in referenced service "
|
+ binding.service.shortInstanceName + ": proc=" + binding.service.app);
|
} else {
|
association = holder.pkg.getAssociationStateLocked(holder.state,
|
binding.service.instanceName.getClassName()).startSource(clientUid,
|
clientProcessName, clientPackageName);
|
|
}
|
}
|
}
|
|
public void trackProcState(int procState, int seq, long now) {
|
if (association != null) {
|
association.trackProcState(procState, seq, now);
|
}
|
}
|
|
public void stopAssociation() {
|
if (association != null) {
|
association.stop();
|
association = null;
|
}
|
}
|
|
public String toString() {
|
if (stringName != null) {
|
return stringName;
|
}
|
StringBuilder sb = new StringBuilder(128);
|
sb.append("ConnectionRecord{");
|
sb.append(Integer.toHexString(System.identityHashCode(this)));
|
sb.append(" u");
|
sb.append(binding.client.userId);
|
sb.append(' ');
|
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
|
sb.append("CR ");
|
}
|
if ((flags&Context.BIND_DEBUG_UNBIND) != 0) {
|
sb.append("DBG ");
|
}
|
if ((flags&Context.BIND_NOT_FOREGROUND) != 0) {
|
sb.append("!FG ");
|
}
|
if ((flags&Context.BIND_IMPORTANT_BACKGROUND) != 0) {
|
sb.append("IMPB ");
|
}
|
if ((flags&Context.BIND_ABOVE_CLIENT) != 0) {
|
sb.append("ABCLT ");
|
}
|
if ((flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
|
sb.append("OOM ");
|
}
|
if ((flags&Context.BIND_WAIVE_PRIORITY) != 0) {
|
sb.append("WPRI ");
|
}
|
if ((flags&Context.BIND_IMPORTANT) != 0) {
|
sb.append("IMP ");
|
}
|
if ((flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
|
sb.append("WACT ");
|
}
|
if ((flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE) != 0) {
|
sb.append("FGSA ");
|
}
|
if ((flags&Context.BIND_FOREGROUND_SERVICE) != 0) {
|
sb.append("FGS ");
|
}
|
if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
|
sb.append("LACT ");
|
}
|
if ((flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0) {
|
sb.append("SLTA ");
|
}
|
if ((flags&Context.BIND_VISIBLE) != 0) {
|
sb.append("VIS ");
|
}
|
if ((flags&Context.BIND_SHOWING_UI) != 0) {
|
sb.append("UI ");
|
}
|
if ((flags&Context.BIND_NOT_VISIBLE) != 0) {
|
sb.append("!VIS ");
|
}
|
if ((flags & Context.BIND_NOT_PERCEPTIBLE) != 0) {
|
sb.append("!PRCP ");
|
}
|
if ((flags & Context.BIND_INCLUDE_CAPABILITIES) != 0) {
|
sb.append("CAPS ");
|
}
|
if (serviceDead) {
|
sb.append("DEAD ");
|
}
|
sb.append(binding.service.shortInstanceName);
|
sb.append(":@");
|
sb.append(Integer.toHexString(System.identityHashCode(conn.asBinder())));
|
sb.append('}');
|
return stringName = sb.toString();
|
}
|
|
public void writeToProto(ProtoOutputStream proto, long fieldId) {
|
if (binding == null) return; // if binding is null, don't write data, something is wrong.
|
long token = proto.start(fieldId);
|
proto.write(ConnectionRecordProto.HEX_HASH,
|
Integer.toHexString(System.identityHashCode(this)));
|
if (binding.client != null) {
|
proto.write(ConnectionRecordProto.USER_ID, binding.client.userId);
|
}
|
ProtoUtils.writeBitWiseFlagsToProtoEnum(proto, ConnectionRecordProto.FLAGS,
|
flags, BIND_ORIG_ENUMS, BIND_PROTO_ENUMS);
|
if (serviceDead) {
|
proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.DEAD);
|
}
|
if (binding.service != null) {
|
proto.write(ConnectionRecordProto.SERVICE_NAME, binding.service.shortInstanceName);
|
}
|
proto.end(token);
|
}
|
}
|