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 fgRequired;     // is the service required to go foreground after starting?
96     boolean fgWaiting;      // is a timeout for going foreground already scheduled?
97     boolean isForeground;   // is service currently in foreground mode?
98     int foregroundId;       // Notification ID of last foreground req.
99     Notification foregroundNoti; // Notification record of foreground state.
100     long lastActivity;      // last time there was some activity on the service.
101     long startingBgTimeout;  // time at which we scheduled this for a delayed start.
102     boolean startRequested; // someone explicitly called start?
103     boolean delayedStop;    // service has been stopped but is in a delayed start?
104     boolean stopIfKilled;   // last onStart() said to stop if service killed?
105     boolean callStart;      // last onStart() has asked to alway be called on restart.
106     int executeNesting;     // number of outstanding operations keeping foreground.
107     boolean executeFg;      // should we be executing in the foreground?
108     long executingStart;    // start time of last execute request.
109     boolean createdFromFg;  // was this service last created due to a foreground process call?
110     int crashCount;         // number of times proc has crashed with service running
111     int totalRestartCount;  // number of times we have had to restart.
112     int restartCount;       // number of restarts performed in a row.
113     long restartDelay;      // delay until next restart attempt.
114     long restartTime;       // time of last restart.
115     long nextRestartTime;   // time when restartDelay will expire.
116     boolean destroying;     // set when we have started destroying the service
117     long destroyTime;       // time at which destory was initiated.
118 
119     String stringName;      // caching of toString
120 
121     private int lastStartId;    // identifier of most recent start request.
122 
123     static class StartItem {
124         final ServiceRecord sr;
125         final boolean taskRemoved;
126         final int id;
127         final int callingId;
128         final Intent intent;
129         final ActivityManagerService.NeededUriGrants neededGrants;
130         long deliveredTime;
131         int deliveryCount;
132         int doneExecutingCount;
133         UriPermissionOwner uriPermissions;
134 
135         String stringName;      // caching of toString
136 
StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent, ActivityManagerService.NeededUriGrants _neededGrants, int _callingId)137         StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent,
138                 ActivityManagerService.NeededUriGrants _neededGrants, int _callingId) {
139             sr = _sr;
140             taskRemoved = _taskRemoved;
141             id = _id;
142             intent = _intent;
143             neededGrants = _neededGrants;
144             callingId = _callingId;
145         }
146 
getUriPermissionsLocked()147         UriPermissionOwner getUriPermissionsLocked() {
148             if (uriPermissions == null) {
149                 uriPermissions = new UriPermissionOwner(sr.ams, this);
150             }
151             return uriPermissions;
152         }
153 
removeUriPermissionsLocked()154         void removeUriPermissionsLocked() {
155             if (uriPermissions != null) {
156                 uriPermissions.removeUriPermissionsLocked();
157                 uriPermissions = null;
158             }
159         }
160 
toString()161         public String toString() {
162             if (stringName != null) {
163                 return stringName;
164             }
165             StringBuilder sb = new StringBuilder(128);
166             sb.append("ServiceRecord{")
167                 .append(Integer.toHexString(System.identityHashCode(sr)))
168                 .append(' ').append(sr.shortName)
169                 .append(" StartItem ")
170                 .append(Integer.toHexString(System.identityHashCode(this)))
171                 .append(" id=").append(id).append('}');
172             return stringName = sb.toString();
173         }
174     }
175 
176     final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
177                             // start() arguments which been delivered.
178     final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
179                             // start() arguments that haven't yet been delivered.
180 
dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now)181     void dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now) {
182         final int N = list.size();
183         for (int i=0; i<N; i++) {
184             StartItem si = list.get(i);
185             pw.print(prefix); pw.print("#"); pw.print(i);
186                     pw.print(" id="); pw.print(si.id);
187                     if (now != 0) {
188                         pw.print(" dur=");
189                         TimeUtils.formatDuration(si.deliveredTime, now, pw);
190                     }
191                     if (si.deliveryCount != 0) {
192                         pw.print(" dc="); pw.print(si.deliveryCount);
193                     }
194                     if (si.doneExecutingCount != 0) {
195                         pw.print(" dxc="); pw.print(si.doneExecutingCount);
196                     }
197                     pw.println("");
198             pw.print(prefix); pw.print("  intent=");
199                     if (si.intent != null) pw.println(si.intent.toString());
200                     else pw.println("null");
201             if (si.neededGrants != null) {
202                 pw.print(prefix); pw.print("  neededGrants=");
203                         pw.println(si.neededGrants);
204             }
205             if (si.uriPermissions != null) {
206                 si.uriPermissions.dump(pw, prefix);
207             }
208         }
209     }
210 
dump(PrintWriter pw, String prefix)211     void dump(PrintWriter pw, String prefix) {
212         pw.print(prefix); pw.print("intent={");
213                 pw.print(intent.getIntent().toShortString(false, true, false, true));
214                 pw.println('}');
215         pw.print(prefix); pw.print("packageName="); pw.println(packageName);
216         pw.print(prefix); pw.print("processName="); pw.println(processName);
217         if (permission != null) {
218             pw.print(prefix); pw.print("permission="); pw.println(permission);
219         }
220         long now = SystemClock.uptimeMillis();
221         long nowReal = SystemClock.elapsedRealtime();
222         if (appInfo != null) {
223             pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
224             if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
225                 pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
226             }
227             pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
228         }
229         pw.print(prefix); pw.print("app="); pw.println(app);
230         if (isolatedProc != null) {
231             pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
232         }
233         if (whitelistManager) {
234             pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager);
235         }
236         if (delayed) {
237             pw.print(prefix); pw.print("delayed="); pw.println(delayed);
238         }
239         if (isForeground || foregroundId != 0) {
240             pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
241                     pw.print(" foregroundId="); pw.print(foregroundId);
242                     pw.print(" foregroundNoti="); pw.println(foregroundNoti);
243         }
244         pw.print(prefix); pw.print("createTime=");
245                 TimeUtils.formatDuration(createTime, nowReal, pw);
246                 pw.print(" startingBgTimeout=");
247                 TimeUtils.formatDuration(startingBgTimeout, now, pw);
248                 pw.println();
249         pw.print(prefix); pw.print("lastActivity=");
250                 TimeUtils.formatDuration(lastActivity, now, pw);
251                 pw.print(" restartTime=");
252                 TimeUtils.formatDuration(restartTime, now, pw);
253                 pw.print(" createdFromFg="); pw.println(createdFromFg);
254         if (startRequested || delayedStop || lastStartId != 0) {
255             pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
256                     pw.print(" delayedStop="); pw.print(delayedStop);
257                     pw.print(" stopIfKilled="); pw.print(stopIfKilled);
258                     pw.print(" callStart="); pw.print(callStart);
259                     pw.print(" lastStartId="); pw.println(lastStartId);
260         }
261         if (executeNesting != 0) {
262             pw.print(prefix); pw.print("executeNesting="); pw.print(executeNesting);
263                     pw.print(" executeFg="); pw.print(executeFg);
264                     pw.print(" executingStart=");
265                     TimeUtils.formatDuration(executingStart, now, pw);
266                     pw.println();
267         }
268         if (destroying || destroyTime != 0) {
269             pw.print(prefix); pw.print("destroying="); pw.print(destroying);
270                     pw.print(" destroyTime=");
271                     TimeUtils.formatDuration(destroyTime, now, pw);
272                     pw.println();
273         }
274         if (crashCount != 0 || restartCount != 0
275                 || restartDelay != 0 || nextRestartTime != 0) {
276             pw.print(prefix); pw.print("restartCount="); pw.print(restartCount);
277                     pw.print(" restartDelay=");
278                     TimeUtils.formatDuration(restartDelay, now, pw);
279                     pw.print(" nextRestartTime=");
280                     TimeUtils.formatDuration(nextRestartTime, now, pw);
281                     pw.print(" crashCount="); pw.println(crashCount);
282         }
283         if (deliveredStarts.size() > 0) {
284             pw.print(prefix); pw.println("Delivered Starts:");
285             dumpStartList(pw, prefix, deliveredStarts, now);
286         }
287         if (pendingStarts.size() > 0) {
288             pw.print(prefix); pw.println("Pending Starts:");
289             dumpStartList(pw, prefix, pendingStarts, 0);
290         }
291         if (bindings.size() > 0) {
292             pw.print(prefix); pw.println("Bindings:");
293             for (int i=0; i<bindings.size(); i++) {
294                 IntentBindRecord b = bindings.valueAt(i);
295                 pw.print(prefix); pw.print("* IntentBindRecord{");
296                         pw.print(Integer.toHexString(System.identityHashCode(b)));
297                         if ((b.collectFlags()&Context.BIND_AUTO_CREATE) != 0) {
298                             pw.append(" CREATE");
299                         }
300                         pw.println("}:");
301                 b.dumpInService(pw, prefix + "  ");
302             }
303         }
304         if (connections.size() > 0) {
305             pw.print(prefix); pw.println("All Connections:");
306             for (int conni=0; conni<connections.size(); conni++) {
307                 ArrayList<ConnectionRecord> c = connections.valueAt(conni);
308                 for (int i=0; i<c.size(); i++) {
309                     pw.print(prefix); pw.print("  "); pw.println(c.get(i));
310                 }
311             }
312         }
313     }
314 
ServiceRecord(ActivityManagerService ams, BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name, Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg, Runnable restarter)315     ServiceRecord(ActivityManagerService ams,
316             BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name,
317             Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
318             Runnable restarter) {
319         this.ams = ams;
320         this.stats = servStats;
321         this.name = name;
322         shortName = name.flattenToShortString();
323         this.intent = intent;
324         serviceInfo = sInfo;
325         appInfo = sInfo.applicationInfo;
326         packageName = sInfo.applicationInfo.packageName;
327         processName = sInfo.processName;
328         permission = sInfo.permission;
329         exported = sInfo.exported;
330         this.restarter = restarter;
331         createTime = SystemClock.elapsedRealtime();
332         lastActivity = SystemClock.uptimeMillis();
333         userId = UserHandle.getUserId(appInfo.uid);
334         createdFromFg = callerIsFg;
335     }
336 
getTracker()337     public ServiceState getTracker() {
338         if (tracker != null) {
339             return tracker;
340         }
341         if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
342             tracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
343                     serviceInfo.applicationInfo.uid, serviceInfo.applicationInfo.versionCode,
344                     serviceInfo.processName, serviceInfo.name);
345             tracker.applyNewOwner(this);
346         }
347         return tracker;
348     }
349 
forceClearTracker()350     public void forceClearTracker() {
351         if (tracker != null) {
352             tracker.clearCurrentOwner(this, true);
353             tracker = null;
354         }
355     }
356 
makeRestarting(int memFactor, long now)357     public void makeRestarting(int memFactor, long now) {
358         if (restartTracker == null) {
359             if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
360                 restartTracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
361                         serviceInfo.applicationInfo.uid, serviceInfo.applicationInfo.versionCode,
362                         serviceInfo.processName, serviceInfo.name);
363             }
364             if (restartTracker == null) {
365                 return;
366             }
367         }
368         restartTracker.setRestarting(true, memFactor, now);
369     }
370 
retrieveAppBindingLocked(Intent intent, ProcessRecord app)371     public AppBindRecord retrieveAppBindingLocked(Intent intent,
372             ProcessRecord app) {
373         Intent.FilterComparison filter = new Intent.FilterComparison(intent);
374         IntentBindRecord i = bindings.get(filter);
375         if (i == null) {
376             i = new IntentBindRecord(this, filter);
377             bindings.put(filter, i);
378         }
379         AppBindRecord a = i.apps.get(app);
380         if (a != null) {
381             return a;
382         }
383         a = new AppBindRecord(this, i, app);
384         i.apps.put(app, a);
385         return a;
386     }
387 
hasAutoCreateConnections()388     public boolean hasAutoCreateConnections() {
389         // XXX should probably keep a count of the number of auto-create
390         // connections directly in the service.
391         for (int conni=connections.size()-1; conni>=0; conni--) {
392             ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
393             for (int i=0; i<cr.size(); i++) {
394                 if ((cr.get(i).flags&Context.BIND_AUTO_CREATE) != 0) {
395                     return true;
396                 }
397             }
398         }
399         return false;
400     }
401 
updateWhitelistManager()402     public void updateWhitelistManager() {
403         whitelistManager = false;
404         for (int conni=connections.size()-1; conni>=0; conni--) {
405             ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
406             for (int i=0; i<cr.size(); i++) {
407                 if ((cr.get(i).flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
408                     whitelistManager = true;
409                     return;
410                 }
411             }
412         }
413     }
414 
resetRestartCounter()415     public void resetRestartCounter() {
416         restartCount = 0;
417         restartDelay = 0;
418         restartTime = 0;
419     }
420 
findDeliveredStart(int id, boolean remove)421     public StartItem findDeliveredStart(int id, boolean remove) {
422         final int N = deliveredStarts.size();
423         for (int i=0; i<N; i++) {
424             StartItem si = deliveredStarts.get(i);
425             if (si.id == id) {
426                 if (remove) deliveredStarts.remove(i);
427                 return si;
428             }
429         }
430 
431         return null;
432     }
433 
getLastStartId()434     public int getLastStartId() {
435         return lastStartId;
436     }
437 
makeNextStartId()438     public int makeNextStartId() {
439         lastStartId++;
440         if (lastStartId < 1) {
441             lastStartId = 1;
442         }
443         return lastStartId;
444     }
445 
postNotification()446     public void postNotification() {
447         final int appUid = appInfo.uid;
448         final int appPid = app.pid;
449         if (foregroundId != 0 && foregroundNoti != null) {
450             // Do asynchronous communication with notification manager to
451             // avoid deadlocks.
452             final String localPackageName = packageName;
453             final int localForegroundId = foregroundId;
454             final Notification _foregroundNoti = foregroundNoti;
455             ams.mHandler.post(new Runnable() {
456                 public void run() {
457                     NotificationManagerInternal nm = LocalServices.getService(
458                             NotificationManagerInternal.class);
459                     if (nm == null) {
460                         return;
461                     }
462                     Notification localForegroundNoti = _foregroundNoti;
463                     try {
464                         if (localForegroundNoti.getSmallIcon() == null) {
465                             // It is not correct for the caller to not supply a notification
466                             // icon, but this used to be able to slip through, so for
467                             // those dirty apps we will create a notification clearly
468                             // blaming the app.
469                             Slog.v(TAG, "Attempted to start a foreground service ("
470                                     + name
471                                     + ") with a broken notification (no icon: "
472                                     + localForegroundNoti
473                                     + ")");
474 
475                             CharSequence appName = appInfo.loadLabel(
476                                     ams.mContext.getPackageManager());
477                             if (appName == null) {
478                                 appName = appInfo.packageName;
479                             }
480                             Context ctx = null;
481                             try {
482                                 ctx = ams.mContext.createPackageContextAsUser(
483                                         appInfo.packageName, 0, new UserHandle(userId));
484 
485                                 Notification.Builder notiBuilder = new Notification.Builder(ctx,
486                                         localForegroundNoti.getChannelId());
487 
488                                 // it's ugly, but it clearly identifies the app
489                                 notiBuilder.setSmallIcon(appInfo.icon);
490 
491                                 // mark as foreground
492                                 notiBuilder.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true);
493 
494                                 Intent runningIntent = new Intent(
495                                         Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
496                                 runningIntent.setData(Uri.fromParts("package",
497                                         appInfo.packageName, null));
498                                 PendingIntent pi = PendingIntent.getActivityAsUser(ams.mContext, 0,
499                                         runningIntent, PendingIntent.FLAG_UPDATE_CURRENT, null,
500                                         UserHandle.of(userId));
501                                 notiBuilder.setColor(ams.mContext.getColor(
502                                         com.android.internal
503                                                 .R.color.system_notification_accent_color));
504                                 notiBuilder.setContentTitle(
505                                         ams.mContext.getString(
506                                                 com.android.internal.R.string
507                                                         .app_running_notification_title,
508                                                 appName));
509                                 notiBuilder.setContentText(
510                                         ams.mContext.getString(
511                                                 com.android.internal.R.string
512                                                         .app_running_notification_text,
513                                                 appName));
514                                 notiBuilder.setContentIntent(pi);
515 
516                                 localForegroundNoti = notiBuilder.build();
517                             } catch (PackageManager.NameNotFoundException e) {
518                             }
519                         }
520                         if (localForegroundNoti.getSmallIcon() == null) {
521                             // Notifications whose icon is 0 are defined to not show
522                             // a notification, silently ignoring it.  We don't want to
523                             // just ignore it, we want to prevent the service from
524                             // being foreground.
525                             throw new RuntimeException("invalid service notification: "
526                                     + foregroundNoti);
527                         }
528                         nm.enqueueNotification(localPackageName, localPackageName,
529                                 appUid, appPid, null, localForegroundId, localForegroundNoti,
530                                 userId);
531 
532                         foregroundNoti = localForegroundNoti; // save it for amending next time
533                     } catch (RuntimeException e) {
534                         Slog.w(TAG, "Error showing notification for service", e);
535                         // If it gave us a garbage notification, it doesn't
536                         // get to be foreground.
537                         ams.setServiceForeground(name, ServiceRecord.this,
538                                 0, null, 0);
539                         ams.crashApplication(appUid, appPid, localPackageName, -1,
540                                 "Bad notification for startForeground: " + e);
541                     }
542                 }
543             });
544         }
545     }
546 
cancelNotification()547     public void cancelNotification() {
548         // Do asynchronous communication with notification manager to
549         // avoid deadlocks.
550         final String localPackageName = packageName;
551         final int localForegroundId = foregroundId;
552         ams.mHandler.post(new Runnable() {
553             public void run() {
554                 INotificationManager inm = NotificationManager.getService();
555                 if (inm == null) {
556                     return;
557                 }
558                 try {
559                     inm.cancelNotificationWithTag(localPackageName, null,
560                             localForegroundId, userId);
561                 } catch (RuntimeException e) {
562                     Slog.w(TAG, "Error canceling notification for service", e);
563                 } catch (RemoteException e) {
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