1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.am;
18 
19 import com.android.internal.app.procstats.ServiceState;
20 import com.android.internal.os.BatteryStatsImpl;
21 import com.android.server.LocalServices;
22 import com.android.server.notification.NotificationManagerInternal;
23 
24 import android.app.INotificationManager;
25 import android.app.Notification;
26 import android.app.NotificationManager;
27 import android.app.PendingIntent;
28 import android.content.ComponentName;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.content.pm.ApplicationInfo;
32 import android.content.pm.PackageManager;
33 import android.content.pm.ServiceInfo;
34 import android.net.Uri;
35 import android.os.Binder;
36 import android.os.IBinder;
37 import android.os.RemoteException;
38 import android.os.SystemClock;
39 import android.os.UserHandle;
40 import android.provider.Settings;
41 import android.util.ArrayMap;
42 import android.util.Slog;
43 import android.util.TimeUtils;
44 
45 import java.io.PrintWriter;
46 import java.util.ArrayList;
47 import java.util.List;
48 import java.util.Objects;
49 
50 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
51 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
52 
53 /**
54  * A running application service.
55  */
56 final class ServiceRecord extends Binder {
57     private static final String TAG = TAG_WITH_CLASS_NAME ? "ServiceRecord" : TAG_AM;
58 
59     // Maximum number of delivery attempts before giving up.
60     static final int MAX_DELIVERY_COUNT = 3;
61 
62     // Maximum number of times it can fail during execution before giving up.
63     static final int MAX_DONE_EXECUTING_COUNT = 6;
64 
65     final ActivityManagerService ams;
66     final BatteryStatsImpl.Uid.Pkg.Serv stats;
67     final ComponentName name; // service component.
68     final String shortName; // name.flattenToShortString().
69     final Intent.FilterComparison intent;
70                             // original intent used to find service.
71     final ServiceInfo serviceInfo;
72                             // all information about the service.
73     final ApplicationInfo appInfo;
74                             // information about service's app.
75     final int userId;       // user that this service is running as
76     final String packageName; // the package implementing intent's component
77     final String processName; // process where this component wants to run
78     final String permission;// permission needed to access service
79     final boolean exported; // from ServiceInfo.exported
80     final Runnable restarter; // used to schedule retries of starting the service
81     final long createTime;  // when this service was created
82     final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings
83             = new ArrayMap<Intent.FilterComparison, IntentBindRecord>();
84                             // All active bindings to the service.
85     final ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections
86             = new ArrayMap<IBinder, ArrayList<ConnectionRecord>>();
87                             // IBinder -> ConnectionRecord of all bound clients
88 
89     ProcessRecord app;      // where this service is running or null.
90     ProcessRecord isolatedProc; // keep track of isolated process, if requested
91     ServiceState tracker; // tracking service execution, may be null
92     ServiceState restartTracker; // tracking service restart
93     boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
94     boolean delayed;        // are we waiting to start this service in the background?
95     boolean isForeground;   // is service currently in foreground mode?
96     int foregroundId;       // Notification ID of last foreground req.
97     Notification foregroundNoti; // Notification record of foreground state.
98     long lastActivity;      // last time there was some activity on the service.
99     long startingBgTimeout;  // time at which we scheduled this for a delayed start.
100     boolean startRequested; // someone explicitly called start?
101     boolean delayedStop;    // service has been stopped but is in a delayed start?
102     boolean stopIfKilled;   // last onStart() said to stop if service killed?
103     boolean callStart;      // last onStart() has asked to alway be called on restart.
104     int executeNesting;     // number of outstanding operations keeping foreground.
105     boolean executeFg;      // should we be executing in the foreground?
106     long executingStart;    // start time of last execute request.
107     boolean createdFromFg;  // was this service last created due to a foreground process call?
108     int crashCount;         // number of times proc has crashed with service running
109     int totalRestartCount;  // number of times we have had to restart.
110     int restartCount;       // number of restarts performed in a row.
111     long restartDelay;      // delay until next restart attempt.
112     long restartTime;       // time of last restart.
113     long nextRestartTime;   // time when restartDelay will expire.
114     boolean destroying;     // set when we have started destroying the service
115     long destroyTime;       // time at which destory was initiated.
116 
117     String stringName;      // caching of toString
118 
119     private int lastStartId;    // identifier of most recent start request.
120 
121     static class StartItem {
122         final ServiceRecord sr;
123         final boolean taskRemoved;
124         final int id;
125         final Intent intent;
126         final ActivityManagerService.NeededUriGrants neededGrants;
127         long deliveredTime;
128         int deliveryCount;
129         int doneExecutingCount;
130         UriPermissionOwner uriPermissions;
131 
132         String stringName;      // caching of toString
133 
StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent, ActivityManagerService.NeededUriGrants _neededGrants)134         StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent,
135                 ActivityManagerService.NeededUriGrants _neededGrants) {
136             sr = _sr;
137             taskRemoved = _taskRemoved;
138             id = _id;
139             intent = _intent;
140             neededGrants = _neededGrants;
141         }
142 
getUriPermissionsLocked()143         UriPermissionOwner getUriPermissionsLocked() {
144             if (uriPermissions == null) {
145                 uriPermissions = new UriPermissionOwner(sr.ams, this);
146             }
147             return uriPermissions;
148         }
149 
removeUriPermissionsLocked()150         void removeUriPermissionsLocked() {
151             if (uriPermissions != null) {
152                 uriPermissions.removeUriPermissionsLocked();
153                 uriPermissions = null;
154             }
155         }
156 
toString()157         public String toString() {
158             if (stringName != null) {
159                 return stringName;
160             }
161             StringBuilder sb = new StringBuilder(128);
162             sb.append("ServiceRecord{")
163                 .append(Integer.toHexString(System.identityHashCode(sr)))
164                 .append(' ').append(sr.shortName)
165                 .append(" StartItem ")
166                 .append(Integer.toHexString(System.identityHashCode(this)))
167                 .append(" id=").append(id).append('}');
168             return stringName = sb.toString();
169         }
170     }
171 
172     final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
173                             // start() arguments which been delivered.
174     final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
175                             // start() arguments that haven't yet been delivered.
176 
dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now)177     void dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now) {
178         final int N = list.size();
179         for (int i=0; i<N; i++) {
180             StartItem si = list.get(i);
181             pw.print(prefix); pw.print("#"); pw.print(i);
182                     pw.print(" id="); pw.print(si.id);
183                     if (now != 0) {
184                         pw.print(" dur=");
185                         TimeUtils.formatDuration(si.deliveredTime, now, pw);
186                     }
187                     if (si.deliveryCount != 0) {
188                         pw.print(" dc="); pw.print(si.deliveryCount);
189                     }
190                     if (si.doneExecutingCount != 0) {
191                         pw.print(" dxc="); pw.print(si.doneExecutingCount);
192                     }
193                     pw.println("");
194             pw.print(prefix); pw.print("  intent=");
195                     if (si.intent != null) pw.println(si.intent.toString());
196                     else pw.println("null");
197             if (si.neededGrants != null) {
198                 pw.print(prefix); pw.print("  neededGrants=");
199                         pw.println(si.neededGrants);
200             }
201             if (si.uriPermissions != null) {
202                 si.uriPermissions.dump(pw, prefix);
203             }
204         }
205     }
206 
dump(PrintWriter pw, String prefix)207     void dump(PrintWriter pw, String prefix) {
208         pw.print(prefix); pw.print("intent={");
209                 pw.print(intent.getIntent().toShortString(false, true, false, true));
210                 pw.println('}');
211         pw.print(prefix); pw.print("packageName="); pw.println(packageName);
212         pw.print(prefix); pw.print("processName="); pw.println(processName);
213         if (permission != null) {
214             pw.print(prefix); pw.print("permission="); pw.println(permission);
215         }
216         long now = SystemClock.uptimeMillis();
217         long nowReal = SystemClock.elapsedRealtime();
218         if (appInfo != null) {
219             pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
220             if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
221                 pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
222             }
223             pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
224         }
225         pw.print(prefix); pw.print("app="); pw.println(app);
226         if (isolatedProc != null) {
227             pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
228         }
229         if (whitelistManager) {
230             pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager);
231         }
232         if (delayed) {
233             pw.print(prefix); pw.print("delayed="); pw.println(delayed);
234         }
235         if (isForeground || foregroundId != 0) {
236             pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
237                     pw.print(" foregroundId="); pw.print(foregroundId);
238                     pw.print(" foregroundNoti="); pw.println(foregroundNoti);
239         }
240         pw.print(prefix); pw.print("createTime=");
241                 TimeUtils.formatDuration(createTime, nowReal, pw);
242                 pw.print(" startingBgTimeout=");
243                 TimeUtils.formatDuration(startingBgTimeout, now, pw);
244                 pw.println();
245         pw.print(prefix); pw.print("lastActivity=");
246                 TimeUtils.formatDuration(lastActivity, now, pw);
247                 pw.print(" restartTime=");
248                 TimeUtils.formatDuration(restartTime, now, pw);
249                 pw.print(" createdFromFg="); pw.println(createdFromFg);
250         if (startRequested || delayedStop || lastStartId != 0) {
251             pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
252                     pw.print(" delayedStop="); pw.print(delayedStop);
253                     pw.print(" stopIfKilled="); pw.print(stopIfKilled);
254                     pw.print(" callStart="); pw.print(callStart);
255                     pw.print(" lastStartId="); pw.println(lastStartId);
256         }
257         if (executeNesting != 0) {
258             pw.print(prefix); pw.print("executeNesting="); pw.print(executeNesting);
259                     pw.print(" executeFg="); pw.print(executeFg);
260                     pw.print(" executingStart=");
261                     TimeUtils.formatDuration(executingStart, now, pw);
262                     pw.println();
263         }
264         if (destroying || destroyTime != 0) {
265             pw.print(prefix); pw.print("destroying="); pw.print(destroying);
266                     pw.print(" destroyTime=");
267                     TimeUtils.formatDuration(destroyTime, now, pw);
268                     pw.println();
269         }
270         if (crashCount != 0 || restartCount != 0
271                 || restartDelay != 0 || nextRestartTime != 0) {
272             pw.print(prefix); pw.print("restartCount="); pw.print(restartCount);
273                     pw.print(" restartDelay=");
274                     TimeUtils.formatDuration(restartDelay, now, pw);
275                     pw.print(" nextRestartTime=");
276                     TimeUtils.formatDuration(nextRestartTime, now, pw);
277                     pw.print(" crashCount="); pw.println(crashCount);
278         }
279         if (deliveredStarts.size() > 0) {
280             pw.print(prefix); pw.println("Delivered Starts:");
281             dumpStartList(pw, prefix, deliveredStarts, now);
282         }
283         if (pendingStarts.size() > 0) {
284             pw.print(prefix); pw.println("Pending Starts:");
285             dumpStartList(pw, prefix, pendingStarts, 0);
286         }
287         if (bindings.size() > 0) {
288             pw.print(prefix); pw.println("Bindings:");
289             for (int i=0; i<bindings.size(); i++) {
290                 IntentBindRecord b = bindings.valueAt(i);
291                 pw.print(prefix); pw.print("* IntentBindRecord{");
292                         pw.print(Integer.toHexString(System.identityHashCode(b)));
293                         if ((b.collectFlags()&Context.BIND_AUTO_CREATE) != 0) {
294                             pw.append(" CREATE");
295                         }
296                         pw.println("}:");
297                 b.dumpInService(pw, prefix + "  ");
298             }
299         }
300         if (connections.size() > 0) {
301             pw.print(prefix); pw.println("All Connections:");
302             for (int conni=0; conni<connections.size(); conni++) {
303                 ArrayList<ConnectionRecord> c = connections.valueAt(conni);
304                 for (int i=0; i<c.size(); i++) {
305                     pw.print(prefix); pw.print("  "); pw.println(c.get(i));
306                 }
307             }
308         }
309     }
310 
ServiceRecord(ActivityManagerService ams, BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name, Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg, Runnable restarter)311     ServiceRecord(ActivityManagerService ams,
312             BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name,
313             Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
314             Runnable restarter) {
315         this.ams = ams;
316         this.stats = servStats;
317         this.name = name;
318         shortName = name.flattenToShortString();
319         this.intent = intent;
320         serviceInfo = sInfo;
321         appInfo = sInfo.applicationInfo;
322         packageName = sInfo.applicationInfo.packageName;
323         processName = sInfo.processName;
324         permission = sInfo.permission;
325         exported = sInfo.exported;
326         this.restarter = restarter;
327         createTime = SystemClock.elapsedRealtime();
328         lastActivity = SystemClock.uptimeMillis();
329         userId = UserHandle.getUserId(appInfo.uid);
330         createdFromFg = callerIsFg;
331     }
332 
getTracker()333     public ServiceState getTracker() {
334         if (tracker != null) {
335             return tracker;
336         }
337         if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
338             tracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
339                     serviceInfo.applicationInfo.uid, serviceInfo.applicationInfo.versionCode,
340                     serviceInfo.processName, serviceInfo.name);
341             tracker.applyNewOwner(this);
342         }
343         return tracker;
344     }
345 
forceClearTracker()346     public void forceClearTracker() {
347         if (tracker != null) {
348             tracker.clearCurrentOwner(this, true);
349             tracker = null;
350         }
351     }
352 
makeRestarting(int memFactor, long now)353     public void makeRestarting(int memFactor, long now) {
354         if (restartTracker == null) {
355             if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
356                 restartTracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
357                         serviceInfo.applicationInfo.uid, serviceInfo.applicationInfo.versionCode,
358                         serviceInfo.processName, serviceInfo.name);
359             }
360             if (restartTracker == null) {
361                 return;
362             }
363         }
364         restartTracker.setRestarting(true, memFactor, now);
365     }
366 
retrieveAppBindingLocked(Intent intent, ProcessRecord app)367     public AppBindRecord retrieveAppBindingLocked(Intent intent,
368             ProcessRecord app) {
369         Intent.FilterComparison filter = new Intent.FilterComparison(intent);
370         IntentBindRecord i = bindings.get(filter);
371         if (i == null) {
372             i = new IntentBindRecord(this, filter);
373             bindings.put(filter, i);
374         }
375         AppBindRecord a = i.apps.get(app);
376         if (a != null) {
377             return a;
378         }
379         a = new AppBindRecord(this, i, app);
380         i.apps.put(app, a);
381         return a;
382     }
383 
hasAutoCreateConnections()384     public boolean hasAutoCreateConnections() {
385         // XXX should probably keep a count of the number of auto-create
386         // connections directly in the service.
387         for (int conni=connections.size()-1; conni>=0; conni--) {
388             ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
389             for (int i=0; i<cr.size(); i++) {
390                 if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
391                     return true;
392                 }
393             }
394         }
395         return false;
396     }
397 
updateWhitelistManager()398     public void updateWhitelistManager() {
399         whitelistManager = false;
400         for (int conni=connections.size()-1; conni>=0; conni--) {
401             ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
402             for (int i=0; i<cr.size(); i++) {
403                 if ((cr.get(i).flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
404                     whitelistManager = true;
405                     return;
406                 }
407             }
408         }
409     }
410 
resetRestartCounter()411     public void resetRestartCounter() {
412         restartCount = 0;
413         restartDelay = 0;
414         restartTime = 0;
415     }
416 
findDeliveredStart(int id, boolean remove)417     public StartItem findDeliveredStart(int id, boolean remove) {
418         final int N = deliveredStarts.size();
419         for (int i=0; i<N; i++) {
420             StartItem si = deliveredStarts.get(i);
421             if (si.id == id) {
422                 if (remove) deliveredStarts.remove(i);
423                 return si;
424             }
425         }
426 
427         return null;
428     }
429 
getLastStartId()430     public int getLastStartId() {
431         return lastStartId;
432     }
433 
makeNextStartId()434     public int makeNextStartId() {
435         lastStartId++;
436         if (lastStartId < 1) {
437             lastStartId = 1;
438         }
439         return lastStartId;
440     }
441 
postNotification()442     public void postNotification() {
443         final int appUid = appInfo.uid;
444         final int appPid = app.pid;
445         if (foregroundId != 0 && foregroundNoti != null) {
446             // Do asynchronous communication with notification manager to
447             // avoid deadlocks.
448             final String localPackageName = packageName;
449             final int localForegroundId = foregroundId;
450             final Notification _foregroundNoti = foregroundNoti;
451             ams.mHandler.post(new Runnable() {
452                 public void run() {
453                     NotificationManagerInternal nm = LocalServices.getService(
454                             NotificationManagerInternal.class);
455                     if (nm == null) {
456                         return;
457                     }
458                     Notification localForegroundNoti = _foregroundNoti;
459                     try {
460                         if (localForegroundNoti.getSmallIcon() == null) {
461                             // It is not correct for the caller to not supply a notification
462                             // icon, but this used to be able to slip through, so for
463                             // those dirty apps we will create a notification clearly
464                             // blaming the app.
465                             Slog.v(TAG, "Attempted to start a foreground service ("
466                                     + name
467                                     + ") with a broken notification (no icon: "
468                                     + localForegroundNoti
469                                     + ")");
470 
471                             CharSequence appName = appInfo.loadLabel(
472                                     ams.mContext.getPackageManager());
473                             if (appName == null) {
474                                 appName = appInfo.packageName;
475                             }
476                             Context ctx = null;
477                             try {
478                                 ctx = ams.mContext.createPackageContextAsUser(
479                                         appInfo.packageName, 0, new UserHandle(userId));
480 
481                                 Notification.Builder notiBuilder = new Notification.Builder(ctx);
482 
483                                 // it's ugly, but it clearly identifies the app
484                                 notiBuilder.setSmallIcon(appInfo.icon);
485 
486                                 // mark as foreground
487                                 notiBuilder.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true);
488 
489                                 // we are doing the app a kindness here
490                                 notiBuilder.setPriority(Notification.PRIORITY_MIN);
491 
492                                 Intent runningIntent = new Intent(
493                                         Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
494                                 runningIntent.setData(Uri.fromParts("package",
495                                         appInfo.packageName, null));
496                                 PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0,
497                                         runningIntent, PendingIntent.FLAG_UPDATE_CURRENT);
498                                 notiBuilder.setColor(ams.mContext.getColor(
499                                         com.android.internal
500                                                 .R.color.system_notification_accent_color));
501                                 notiBuilder.setContentTitle(
502                                         ams.mContext.getString(
503                                                 com.android.internal.R.string
504                                                         .app_running_notification_title,
505                                                 appName));
506                                 notiBuilder.setContentText(
507                                         ams.mContext.getString(
508                                                 com.android.internal.R.string
509                                                         .app_running_notification_text,
510                                                 appName));
511                                 notiBuilder.setContentIntent(pi);
512 
513                                 localForegroundNoti = notiBuilder.build();
514                             } catch (PackageManager.NameNotFoundException e) {
515                             }
516                         }
517                         if (localForegroundNoti.getSmallIcon() == null) {
518                             // Notifications whose icon is 0 are defined to not show
519                             // a notification, silently ignoring it.  We don't want to
520                             // just ignore it, we want to prevent the service from
521                             // being foreground.
522                             throw new RuntimeException("invalid service notification: "
523                                     + foregroundNoti);
524                         }
525                         int[] outId = new int[1];
526                         nm.enqueueNotification(localPackageName, localPackageName,
527                                 appUid, appPid, null, localForegroundId, localForegroundNoti,
528                                 outId, userId);
529 
530                         foregroundNoti = localForegroundNoti; // save it for amending next time
531                     } catch (RuntimeException e) {
532                         Slog.w(TAG, "Error showing notification for service", e);
533                         // If it gave us a garbage notification, it doesn't
534                         // get to be foreground.
535                         ams.setServiceForeground(name, ServiceRecord.this,
536                                 0, null, 0);
537                         ams.crashApplication(appUid, appPid, localPackageName,
538                                 "Bad notification for startForeground: " + e);
539                     }
540                 }
541             });
542         }
543     }
544 
cancelNotification()545     public void cancelNotification() {
546         if (foregroundId != 0) {
547             // Do asynchronous communication with notification manager to
548             // avoid deadlocks.
549             final String localPackageName = packageName;
550             final int localForegroundId = foregroundId;
551             ams.mHandler.post(new Runnable() {
552                 public void run() {
553                     INotificationManager inm = NotificationManager.getService();
554                     if (inm == null) {
555                         return;
556                     }
557                     try {
558                         inm.cancelNotificationWithTag(localPackageName, null,
559                                 localForegroundId, userId);
560                     } catch (RuntimeException e) {
561                         Slog.w(TAG, "Error canceling notification for service", e);
562                     } catch (RemoteException e) {
563                     }
564                 }
565             });
566         }
567     }
568 
stripForegroundServiceFlagFromNotification()569     public void stripForegroundServiceFlagFromNotification() {
570         if (foregroundId == 0) {
571             return;
572         }
573 
574         final int localForegroundId = foregroundId;
575         final int localUserId = userId;
576         final String localPackageName = packageName;
577 
578         // Do asynchronous communication with notification manager to
579         // avoid deadlocks.
580         ams.mHandler.post(new Runnable() {
581             @Override
582             public void run() {
583                 NotificationManagerInternal nmi = LocalServices.getService(
584                         NotificationManagerInternal.class);
585                 if (nmi == null) {
586                     return;
587                 }
588                 nmi.removeForegroundServiceFlagFromNotification(localPackageName, localForegroundId,
589                         localUserId);
590             }
591         });
592     }
593 
clearDeliveredStartsLocked()594     public void clearDeliveredStartsLocked() {
595         for (int i=deliveredStarts.size()-1; i>=0; i--) {
596             deliveredStarts.get(i).removeUriPermissionsLocked();
597         }
598         deliveredStarts.clear();
599     }
600 
toString()601     public String toString() {
602         if (stringName != null) {
603             return stringName;
604         }
605         StringBuilder sb = new StringBuilder(128);
606         sb.append("ServiceRecord{")
607             .append(Integer.toHexString(System.identityHashCode(this)))
608             .append(" u").append(userId)
609             .append(' ').append(shortName).append('}');
610         return stringName = sb.toString();
611     }
612 }
613