summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android/server/am/ServiceRecord.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/core/java/com/android/server/am/ServiceRecord.java')
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java544
1 files changed, 544 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
new file mode 100644
index 0000000..cb04835
--- /dev/null
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -0,0 +1,544 @@
+/*
+ * 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 com.android.internal.app.ProcessStats;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.LocalServices;
+import com.android.server.notification.NotificationManagerInternal;
+
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A running application service.
+ */
+final class ServiceRecord extends Binder {
+ // Maximum number of delivery attempts before giving up.
+ static final int MAX_DELIVERY_COUNT = 3;
+
+ // Maximum number of times it can fail during execution before giving up.
+ static final int MAX_DONE_EXECUTING_COUNT = 6;
+
+ final ActivityManagerService ams;
+ final BatteryStatsImpl.Uid.Pkg.Serv stats;
+ final ComponentName name; // service component.
+ final String shortName; // name.flattenToShortString().
+ final Intent.FilterComparison intent;
+ // original intent used to find service.
+ final ServiceInfo serviceInfo;
+ // all information about the service.
+ final ApplicationInfo appInfo;
+ // information about service's app.
+ final int userId; // user that this service is running as
+ final String packageName; // the package implementing intent's component
+ final String processName; // process where this component wants to run
+ final String permission;// permission needed to access service
+ final String baseDir; // where activity source (resources etc) located
+ final String resDir; // where public activity source (public resources etc) located
+ final String dataDir; // where activity data should go
+ final boolean exported; // from ServiceInfo.exported
+ final Runnable restarter; // used to schedule retries of starting the service
+ final long createTime; // when this service was created
+ final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings
+ = new ArrayMap<Intent.FilterComparison, IntentBindRecord>();
+ // All active bindings to the service.
+ final ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections
+ = new ArrayMap<IBinder, ArrayList<ConnectionRecord>>();
+ // IBinder -> ConnectionRecord of all bound clients
+
+ ProcessRecord app; // where this service is running or null.
+ ProcessRecord isolatedProc; // keep track of isolated process, if requested
+ ProcessStats.ServiceState tracker; // tracking service execution, may be null
+ ProcessStats.ServiceState restartTracker; // tracking service restart
+ boolean delayed; // are we waiting to start this service in the background?
+ boolean isForeground; // is service currently in foreground mode?
+ int foregroundId; // Notification ID of last foreground req.
+ Notification foregroundNoti; // Notification record of foreground state.
+ long lastActivity; // last time there was some activity on the service.
+ long startingBgTimeout; // time at which we scheduled this for a delayed start.
+ boolean startRequested; // someone explicitly called start?
+ boolean delayedStop; // service has been stopped but is in a delayed start?
+ boolean stopIfKilled; // last onStart() said to stop if service killed?
+ boolean callStart; // last onStart() has asked to alway be called on restart.
+ int executeNesting; // number of outstanding operations keeping foreground.
+ boolean executeFg; // should we be executing in the foreground?
+ long executingStart; // start time of last execute request.
+ boolean createdFromFg; // was this service last created due to a foreground process call?
+ int crashCount; // number of times proc has crashed with service running
+ int totalRestartCount; // number of times we have had to restart.
+ int restartCount; // number of restarts performed in a row.
+ long restartDelay; // delay until next restart attempt.
+ long restartTime; // time of last restart.
+ long nextRestartTime; // time when restartDelay will expire.
+
+ String stringName; // caching of toString
+
+ private int lastStartId; // identifier of most recent start request.
+
+ static class StartItem {
+ final ServiceRecord sr;
+ final boolean taskRemoved;
+ final int id;
+ final Intent intent;
+ final ActivityManagerService.NeededUriGrants neededGrants;
+ long deliveredTime;
+ int deliveryCount;
+ int doneExecutingCount;
+ UriPermissionOwner uriPermissions;
+
+ String stringName; // caching of toString
+
+ StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent,
+ ActivityManagerService.NeededUriGrants _neededGrants) {
+ sr = _sr;
+ taskRemoved = _taskRemoved;
+ id = _id;
+ intent = _intent;
+ neededGrants = _neededGrants;
+ }
+
+ UriPermissionOwner getUriPermissionsLocked() {
+ if (uriPermissions == null) {
+ uriPermissions = new UriPermissionOwner(sr.ams, this);
+ }
+ return uriPermissions;
+ }
+
+ void removeUriPermissionsLocked() {
+ if (uriPermissions != null) {
+ uriPermissions.removeUriPermissionsLocked();
+ uriPermissions = null;
+ }
+ }
+
+ public String toString() {
+ if (stringName != null) {
+ return stringName;
+ }
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("ServiceRecord{")
+ .append(Integer.toHexString(System.identityHashCode(sr)))
+ .append(' ').append(sr.shortName)
+ .append(" StartItem ")
+ .append(Integer.toHexString(System.identityHashCode(this)))
+ .append(" id=").append(id).append('}');
+ return stringName = sb.toString();
+ }
+ }
+
+ final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
+ // start() arguments which been delivered.
+ final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
+ // start() arguments that haven't yet been delivered.
+
+ void dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now) {
+ final int N = list.size();
+ for (int i=0; i<N; i++) {
+ StartItem si = list.get(i);
+ pw.print(prefix); pw.print("#"); pw.print(i);
+ pw.print(" id="); pw.print(si.id);
+ if (now != 0) {
+ pw.print(" dur=");
+ TimeUtils.formatDuration(si.deliveredTime, now, pw);
+ }
+ if (si.deliveryCount != 0) {
+ pw.print(" dc="); pw.print(si.deliveryCount);
+ }
+ if (si.doneExecutingCount != 0) {
+ pw.print(" dxc="); pw.print(si.doneExecutingCount);
+ }
+ pw.println("");
+ pw.print(prefix); pw.print(" intent=");
+ if (si.intent != null) pw.println(si.intent.toString());
+ else pw.println("null");
+ if (si.neededGrants != null) {
+ pw.print(prefix); pw.print(" neededGrants=");
+ pw.println(si.neededGrants);
+ }
+ if (si.uriPermissions != null) {
+ if (si.uriPermissions.readUriPermissions != null) {
+ pw.print(prefix); pw.print(" readUriPermissions=");
+ pw.println(si.uriPermissions.readUriPermissions);
+ }
+ if (si.uriPermissions.writeUriPermissions != null) {
+ pw.print(prefix); pw.print(" writeUriPermissions=");
+ pw.println(si.uriPermissions.writeUriPermissions);
+ }
+ }
+ }
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("intent={");
+ pw.print(intent.getIntent().toShortString(false, true, false, true));
+ pw.println('}');
+ pw.print(prefix); pw.print("packageName="); pw.println(packageName);
+ pw.print(prefix); pw.print("processName="); pw.println(processName);
+ if (permission != null) {
+ pw.print(prefix); pw.print("permission="); pw.println(permission);
+ }
+ long now = SystemClock.uptimeMillis();
+ long nowReal = SystemClock.elapsedRealtime();
+ pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
+ if (!resDir.equals(baseDir)) {
+ pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+ }
+ pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
+ pw.print(prefix); pw.print("app="); pw.println(app);
+ if (isolatedProc != null) {
+ pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
+ }
+ if (delayed) {
+ pw.print(prefix); pw.print("delayed="); pw.println(delayed);
+ }
+ if (isForeground || foregroundId != 0) {
+ pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
+ pw.print(" foregroundId="); pw.print(foregroundId);
+ pw.print(" foregroundNoti="); pw.println(foregroundNoti);
+ }
+ pw.print(prefix); pw.print("createTime=");
+ TimeUtils.formatDuration(createTime, nowReal, pw);
+ pw.print(" startingBgTimeout=");
+ TimeUtils.formatDuration(startingBgTimeout, now, pw);
+ pw.println();
+ pw.print(prefix); pw.print("lastActivity=");
+ TimeUtils.formatDuration(lastActivity, now, pw);
+ pw.print(" restartTime=");
+ TimeUtils.formatDuration(restartTime, now, pw);
+ pw.print(" createdFromFg="); pw.println(createdFromFg);
+ if (startRequested || delayedStop || lastStartId != 0) {
+ pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
+ pw.print(" delayedStop="); pw.print(delayedStop);
+ pw.print(" stopIfKilled="); pw.print(stopIfKilled);
+ pw.print(" callStart="); pw.print(callStart);
+ pw.print(" lastStartId="); pw.println(lastStartId);
+ }
+ if (executeNesting != 0) {
+ pw.print(prefix); pw.print("executeNesting="); pw.print(executeNesting);
+ pw.print(" executeFg="); pw.print(executeFg);
+ pw.print(" executingStart=");
+ TimeUtils.formatDuration(executingStart, now, pw);
+ pw.println();
+ }
+ if (crashCount != 0 || restartCount != 0
+ || restartDelay != 0 || nextRestartTime != 0) {
+ pw.print(prefix); pw.print("restartCount="); pw.print(restartCount);
+ pw.print(" restartDelay=");
+ TimeUtils.formatDuration(restartDelay, now, pw);
+ pw.print(" nextRestartTime=");
+ TimeUtils.formatDuration(nextRestartTime, now, pw);
+ pw.print(" crashCount="); pw.println(crashCount);
+ }
+ if (deliveredStarts.size() > 0) {
+ pw.print(prefix); pw.println("Delivered Starts:");
+ dumpStartList(pw, prefix, deliveredStarts, now);
+ }
+ if (pendingStarts.size() > 0) {
+ pw.print(prefix); pw.println("Pending Starts:");
+ dumpStartList(pw, prefix, pendingStarts, 0);
+ }
+ if (bindings.size() > 0) {
+ pw.print(prefix); pw.println("Bindings:");
+ for (int i=0; i<bindings.size(); i++) {
+ IntentBindRecord b = bindings.valueAt(i);
+ pw.print(prefix); pw.print("* IntentBindRecord{");
+ pw.print(Integer.toHexString(System.identityHashCode(b)));
+ if ((b.collectFlags()&Context.BIND_AUTO_CREATE) != 0) {
+ pw.append(" CREATE");
+ }
+ pw.println("}:");
+ b.dumpInService(pw, prefix + " ");
+ }
+ }
+ if (connections.size() > 0) {
+ pw.print(prefix); pw.println("All Connections:");
+ for (int conni=0; conni<connections.size(); conni++) {
+ ArrayList<ConnectionRecord> c = connections.valueAt(conni);
+ for (int i=0; i<c.size(); i++) {
+ pw.print(prefix); pw.print(" "); pw.println(c.get(i));
+ }
+ }
+ }
+ }
+
+ ServiceRecord(ActivityManagerService ams,
+ BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name,
+ Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
+ Runnable restarter) {
+ this.ams = ams;
+ this.stats = servStats;
+ this.name = name;
+ shortName = name.flattenToShortString();
+ this.intent = intent;
+ serviceInfo = sInfo;
+ appInfo = sInfo.applicationInfo;
+ packageName = sInfo.applicationInfo.packageName;
+ processName = sInfo.processName;
+ permission = sInfo.permission;
+ baseDir = sInfo.applicationInfo.sourceDir;
+ resDir = sInfo.applicationInfo.publicSourceDir;
+ dataDir = sInfo.applicationInfo.dataDir;
+ exported = sInfo.exported;
+ this.restarter = restarter;
+ createTime = SystemClock.elapsedRealtime();
+ lastActivity = SystemClock.uptimeMillis();
+ userId = UserHandle.getUserId(appInfo.uid);
+ createdFromFg = callerIsFg;
+ }
+
+ public ProcessStats.ServiceState getTracker() {
+ if (tracker != null) {
+ return tracker;
+ }
+ if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ tracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
+ serviceInfo.applicationInfo.uid, serviceInfo.processName, serviceInfo.name);
+ tracker.applyNewOwner(this);
+ }
+ return tracker;
+ }
+
+ public void forceClearTracker() {
+ if (tracker != null) {
+ tracker.clearCurrentOwner(this, true);
+ tracker = null;
+ }
+ }
+
+ public void makeRestarting(int memFactor, long now) {
+ if (restartTracker == null) {
+ if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ restartTracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
+ serviceInfo.applicationInfo.uid, serviceInfo.processName, serviceInfo.name);
+ }
+ if (restartTracker == null) {
+ return;
+ }
+ }
+ restartTracker.setRestarting(true, memFactor, now);
+ }
+
+ public AppBindRecord retrieveAppBindingLocked(Intent intent,
+ ProcessRecord app) {
+ Intent.FilterComparison filter = new Intent.FilterComparison(intent);
+ IntentBindRecord i = bindings.get(filter);
+ if (i == null) {
+ i = new IntentBindRecord(this, filter);
+ bindings.put(filter, i);
+ }
+ AppBindRecord a = i.apps.get(app);
+ if (a != null) {
+ return a;
+ }
+ a = new AppBindRecord(this, i, app);
+ i.apps.put(app, a);
+ return a;
+ }
+
+ public boolean hasAutoCreateConnections() {
+ // XXX should probably keep a count of the number of auto-create
+ // connections directly in the service.
+ for (int conni=connections.size()-1; conni>=0; conni--) {
+ ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
+ for (int i=0; i<cr.size(); i++) {
+ if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void resetRestartCounter() {
+ restartCount = 0;
+ restartDelay = 0;
+ restartTime = 0;
+ }
+
+ public StartItem findDeliveredStart(int id, boolean remove) {
+ final int N = deliveredStarts.size();
+ for (int i=0; i<N; i++) {
+ StartItem si = deliveredStarts.get(i);
+ if (si.id == id) {
+ if (remove) deliveredStarts.remove(i);
+ return si;
+ }
+ }
+
+ return null;
+ }
+
+ public int getLastStartId() {
+ return lastStartId;
+ }
+
+ public int makeNextStartId() {
+ lastStartId++;
+ if (lastStartId < 1) {
+ lastStartId = 1;
+ }
+ return lastStartId;
+ }
+
+ public void postNotification() {
+ final int appUid = appInfo.uid;
+ final int appPid = app.pid;
+ if (foregroundId != 0 && foregroundNoti != null) {
+ // Do asynchronous communication with notification manager to
+ // avoid deadlocks.
+ final String localPackageName = packageName;
+ final int localForegroundId = foregroundId;
+ final Notification localForegroundNoti = foregroundNoti;
+ ams.mHandler.post(new Runnable() {
+ public void run() {
+ NotificationManagerInternal nm = LocalServices.getService(
+ NotificationManagerInternal.class);
+ if (nm == null) {
+ return;
+ }
+ try {
+ if (localForegroundNoti.icon == 0) {
+ // It is not correct for the caller to supply a notification
+ // icon, but this used to be able to slip through, so for
+ // those dirty apps give it the app's icon.
+ localForegroundNoti.icon = appInfo.icon;
+
+ // Do not allow apps to present a sneaky invisible content view either.
+ localForegroundNoti.contentView = null;
+ localForegroundNoti.bigContentView = null;
+ CharSequence appName = appInfo.loadLabel(
+ ams.mContext.getPackageManager());
+ if (appName == null) {
+ appName = appInfo.packageName;
+ }
+ Context ctx = null;
+ try {
+ ctx = ams.mContext.createPackageContext(
+ appInfo.packageName, 0);
+ Intent runningIntent = new Intent(
+ Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ runningIntent.setData(Uri.fromParts("package",
+ appInfo.packageName, null));
+ PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0,
+ runningIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ localForegroundNoti.setLatestEventInfo(ctx,
+ ams.mContext.getString(
+ com.android.internal.R.string
+ .app_running_notification_title,
+ appName),
+ ams.mContext.getString(
+ com.android.internal.R.string
+ .app_running_notification_text,
+ appName),
+ pi);
+ } catch (PackageManager.NameNotFoundException e) {
+ localForegroundNoti.icon = 0;
+ }
+ }
+ if (localForegroundNoti.icon == 0) {
+ // Notifications whose icon is 0 are defined to not show
+ // a notification, silently ignoring it. We don't want to
+ // just ignore it, we want to prevent the service from
+ // being foreground.
+ throw new RuntimeException("icon must be non-zero");
+ }
+ int[] outId = new int[1];
+ nm.enqueueNotification(localPackageName, localPackageName,
+ appUid, appPid, null, localForegroundId, localForegroundNoti,
+ outId, userId);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error showing notification for service", e);
+ // If it gave us a garbage notification, it doesn't
+ // get to be foreground.
+ ams.setServiceForeground(name, ServiceRecord.this,
+ 0, null, true);
+ ams.crashApplication(appUid, appPid, localPackageName,
+ "Bad notification for startForeground: " + e);
+ }
+ }
+ });
+ }
+ }
+
+ public void cancelNotification() {
+ if (foregroundId != 0) {
+ // Do asynchronous communication with notification manager to
+ // avoid deadlocks.
+ final String localPackageName = packageName;
+ final int localForegroundId = foregroundId;
+ ams.mHandler.post(new Runnable() {
+ public void run() {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ inm.cancelNotificationWithTag(localPackageName, null,
+ localForegroundId, userId);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+ }
+ });
+ }
+ }
+
+ public void clearDeliveredStartsLocked() {
+ for (int i=deliveredStarts.size()-1; i>=0; i--) {
+ deliveredStarts.get(i).removeUriPermissionsLocked();
+ }
+ deliveredStarts.clear();
+ }
+
+ public String toString() {
+ if (stringName != null) {
+ return stringName;
+ }
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("ServiceRecord{")
+ .append(Integer.toHexString(System.identityHashCode(this)))
+ .append(" u").append(userId)
+ .append(' ').append(shortName).append('}');
+ return stringName = sb.toString();
+ }
+}