Just An Application

June 6, 2012

Adventures In Distributed Garbage Collection: Part Eighteen – The Great Service Record Mystery Revisited

Filed under: Android, Android Internals, Java, Mobile Java, Services — Tags: , , , , — Simon Lewis @ 9:31 am

1.0 Is The ServiceRecord Part Of A Binder Reference Cycle ?

As we have seen it is possible for a Java Binder object to be part of a Binder reference cycle which will prevent it from being garbage collected, and we know that a ServiceRecord is a Java Binder object, so does this explain what is happening in this case ?

To be sure we need to find a Binder reference cycle which starts and ends at the ServiceRecord object [0x406f89b8]

1.1 The ServiceRecord

We know that there is a JNI Global reference to the ServiceRecord object [0x406f89b8] which means that there exists at least one BinderProxy somewhere that references it.

1.2 The ServiceRecord BinderProxy

We know

  1. that the ServiceRecord object representing a Service is passed to the process running that Service via a remote procedure call to the scheduleCreateService() method of the ApplicationThread

  2. that during the the unmarshalling of the arguments to this method a BinderProxy object which references the ServiceRecord object is created

  3. that this BinderProxy object is passed to the Service.attach() method as the token argument

  4. that it is then assigned to the mToken instance variable of the Service Component object that implements the Service

  5. that in this case the Service Component object is the FastnetService object [0x0x40517518].

From this and in conjunction with the object graph we can identify the Java object in the Service process which references the ServiceRecord object [0x0x0x406f89b8] in the System process as the BinderProxy object [0x40513580].

Direct inspection of the Java heap dump confirms that the only effective reference to the BinderProxy object [0x40513580] is from the FastnetService object [0x0x40517518].

We also know that the process running the Service is the only one to which the ServiceRecord object representing that Service is sent, so this is the only BinderProxy which references the ServiceRecord object.

1.3 The FastnetService Object

We can see from this object graph that there are two references to the FastnetService object [0x40517518]

  • one from the ContextImpl object [0x40517548], and

  • one from the FastnetServiceBinder object [0x40517fc0].

We can ignore the reference from the ContextImpl object [0x40517548] as it in turn is only referenced by the FastnetService object [0x40517518] and an inner class object which only it in turn references.

The reference that is keeping the FastnetService object [0x40517518] in existence is the one from the FastnetServiceBinder object [0x40517fc0].

1.4 The FastnetServiceBinder Object

We know

  1. that the FastnetServiceBinder [0x40517fc0] object is a Java Binder object

  2. that there is a JNI Global reference to it

  3. that the JNI Global reference is the only effective reference to it, that is, it is the one which is preventing it from being garbage collected

  4. that it was returned from a call to the FastnetService implementation of the onBind() method

We can infer from the Service process runtime Binder information below

    binder proc state:
    proc 301
      thread 301: l 00
      thread 307: l 12
      thread 308: l 11
      node 4335: u0014c9a0 c00094f88 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 proc 60
      node 4358: u001568a8 c00156888 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 proc 60
      ref 4330: desc 0 node 1 s 1 w 1 d (null)
      ref 4333: desc 1 node 147 s 1 w 1 d (null)
      ref 4338: desc 2 node 209 s 1 w 1 d (null)
      ref 4339: desc 3 node 143 s 1 w 1 d (null)
      ref 4340: desc 4 node 196 s 1 w 1 d (null)
      ref 4343: desc 5 node 4342 s 1 w 1 d (null)
      ref 4350: desc 6 node 23 s 1 w 1 d (null)
      ref 4353: desc 7 node 44 s 1 w 1 d (null)

that there can be only one BinderProxy referencing it since there are only two Binders associated with the process and both of them are only referenced from one other process (60) which is in fact the System process.

1.5 The FastnetServiceBinder BinderProxy

We know

  1. that the Java Binder object returned from the call to a Service component’s implementation of the onBind() method is passed to the
    System process via a remote procedure call to the publishService() method of the ActivityManagerService.

  2. that during the unmarshalling of the arguments to this method a BinderProxy object is created which references the original Binder object

  3. that this BinderProxy object is then assigned to the binder instance variable of the IntentBindRecord associated with the ServiceRecord which represents the Service which created the original Binder object

From this and in conjunction with the object graph we can identify the Java object corresponding to the FastnetServiceBinder object [0x40517fc0] in the Service process as being BinderProxy object [0x4069ce20] which is referenced by the IntentBindRecord object [0x40704020].

Direct inspection of the Java heap dump confirms that this is the only reference.

1.6 The IntentBindRecord

We can see from the object graph that there is a single reference to the IntentBindRecord object [0x40704020] from the HashMap object [0x406f8a80].

Direct inspection of the Java heap dump confirms that this the only reference.

We can also see that there is a single reference to the HashMap object [0x406f8a80] from the ServiceRecord object [0x0x406f89b8].

Direct inspection of the Java heap dump confirms this the only reference.

1.7 The ServiceRecord

We are back at the ServiceRecord object [0x0x406f89b8] so there is indeed a Binder reference cycle and it looks like this

2.0 Does It Matter ?

Any Android Service implementation based on the example in the documentation can end up causing a Binder reference cycle.

If it does, then depending upon the exact implementation of the Service and its usage patterns it is possible that the process running the Service will crash when it runs out of memory.This is especially true if the Service component directly or indirectly uses a large amount of memory and a number of Service components are leaked as clients repeatedly bind and unbind.

Given enough different Service implementations all with the same problem then it is possible that the System process will crash as eventually it will run out of JNI Global references.

So it does matter, especially since if a given Service implementation does run into this problem it is not at all obvious what the underlying cause is.

3.0 Bug Fix

One of the problems associated with ServiceRecord reference cycles, namely the Service process runnning out of memory, was reported as an issue two years ago.

There is a fix for it in the Android code base but it is not clear when it actually turned up in shipping products. The submission date was just after the initial release of 2.3 so it probably did not start to appear until later revisions of 2.3 which may mean that there are still many devices on which this is potentially a problem.

The fix is specific to the ServiceRecord reference cycle. It is simply to break the reference cycle in the System process by clearing the HashMap referenced by the bindings instance variable of the ServiceRecord. This is done in the bringDownServiceLocked() method of the ActivityManagerService. Doing this breaks the reference between the ServiceRecord and the associated IntentBindRecord(s).

4.0 A Workaround

It is possble to work around the ServiceRecord reference cycle problem specifically by ensuring that the Java Binder object returned from a call to a Service component’s implementation of the onBind() method, only holds a weak reference to the Service component. This prevents the establishment of a Binder reference cycle.

The Java Binder object should either be an instance of a separate class, as in the Fastnet Service example, or a static inner class of the Service and it should use an instance of java.lang.WeakReference to hold the reference to the Service component.

For example, the Service class

    package xper.service.hebrides;

    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.util.Log;

    public final class HebridesService 
                       extends 
                           Service 
    {
        public HebridesService()
        {
        }
	
        @Override
        public void onCreate() 
        {
            super.onCreate();
            binder = new HebridesServiceBinder(this);
        }

        @Override
        public IBinder onBind(Intent intent) 
        {
            Log.d(TAG, "onBind(" + intent + ")");
            return binder;
        }
	
        //
	
        @Override
        public boolean onUnbind(Intent intent) 
        {
            Log.d(TAG, "onUnbind(" + intent + ")");
            return super.onUnbind(intent);
        }

        void doNothingInParticular()
        {
		
        }

        //
	
        private HebridesServiceBinder	binder;
	
        //
	
        private static final String TAG = "HebridesService";
    }

and the Binder class

    package xper.service.hebrides;

    import java.lang.ref.WeakReference;

    import android.os.RemoteException;
    import xper.service.hebrides.Hebrides.Stub;

    final class HebridesServiceBinder 
                extends 
                    Stub 
    {
        public void doNothingInParticular() 
                    throws 
                        RemoteException 
        {
            HebridesService hs = service.get();
		
            if (hs != null)
            {
                hs.doNothingInParticular();
            }
        }

        //
	
        HebridesServiceBinder(HebridesService theService)
        {
            service = new WeakReference<HebridesService>(theService);
        }
	
        //
	
        private WeakReference<HebridesService>	service;
    }

5.0 An Alternative Design Pattern

By convention, the documentation, implication, a Service Component is a sub-class of a class called Service, and it is declared in the Android manifest in an element called service, and probably, though not necessarily intention, the Service Component implements the functionality of the Service, that is the methods defined in the AIDL definition of that Service.

If the Service Component implements the functionality of the Service then a Binder created to enable access to that Service necessarily has a reference to the Service Component and it is that reference that results in a Binder reference cycle.

However, as a sub-class of the Service class the only method the Service Component has to implement is onBind(), that is, the only function it is required to perform is that of a Binder Factory.

As the example below shows it is possible to adopt a different design pattern where the Service Component does nothing other than create Binder objects when necessary. Another object, in the example, the Application, implements the Service functionality.

One consequence of this is that the Binder objects do not need to have a reference to the Service Component, but another is that the lifetime of the implementation of the Service is decoupled from that of the Service Component. This can be useful if the setting up and/or tearing down of the implementation of the Service functionality is computationally expensive.

5.1. Example

5.1.1 An Interface Defining The Service

    package xper.service.faeroes;

    public interface FaeroesService 
    {
        public void doNothingInParticular();
    }

5.1.2 The Application Class

    package xper.service.faeroes;

    import android.app.Application;

    public class FaeroesServiceApplication 
                 extends 
                     Application 
                 implements
                     FaeroesService
    {
        public void doNothingInParticular() 
        {		
        }

        //
	
        FaeroesService getService()
        {
            return this;
        }
    }

5.1.3 The Binder Factory Implementation

    package xper.service.faeroes;

    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.util.Log;

    public class FaeroesServiceBinderFactory 
                 extends 
                     Service 
    {
        @Override
        public void onCreate() 
        {
            super.onCreate();
		
            FaeroesServiceApplication app = (FaeroesServiceApplication)getApplication();
		
            binder = new FaeroesServiceBinder(app.getService());
        }

        //

        @Override
        public IBinder onBind(Intent intent) 
        {
            Log.d(TAG, "onBind(" + intent + ")");
            return binder;
        }

        @Override
        public boolean onUnbind(Intent intent) 
        {
            Log.d(TAG, "onUnind(" + intent + ")");
            return super.onUnbind(intent);
        }
	
        //
	
        private FaeroesServiceBinder binder;
	
        //
	
        private static final String TAG = "FaeroesServiceBinderFactory";
    }

5.1.4 The Binder implementation

    package xper.service.faeroes;

    import android.os.RemoteException;

    final class FaeroesServiceBinder 
                extends
                    Faeroes.Stub
    {
        @Override
        public void doNothingInParticular() 
                    throws 
                        RemoteException 
        {
            service.doNothingInParticular();
        }
	
        //
	
        FaeroesServiceBinder(FaeroesService theService)
        {
            service = theService;
        }

	    //
	
        private FaeroesService service;
    }

5.1.5 The Android Manifest

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="xper.service.faeroes"
        android:versionCode="1"
        android:versionName="1.0" >

        <uses-sdk android:minSdkVersion="9" />

        <application
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name" android:name="FaeroesServiceApplication">
            <service android:name="FaeroesServiceBinderFactory">
                <intent-filter>
                    <action android:name="xper.service.faeroes.ACTION_FAEROES"/>
                </intent-filter>
            </service>
        </application>
    </manifest>

Copyright (c) 2012 By Simon Lewis. All Rights Reserved.

Advertisements

June 1, 2012

Adventures In Distributed Garbage Collection: Part Seventeen – Binders And Garbage Collection

Filed under: Android, Android Internals, Java, Mobile Java, Services — Tags: , , , , — Simon Lewis @ 9:33 am

1.0 An Alternative Service implementation

Unlike the previous example the following Android Service implementation creates the Java Binder object returned from the onBind() method on demand and it does not retain a reference to it.

    package xper.service.malin;

    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.util.Log;

    public final class MalinService 
                       extends 
                           Service 
    {
        public MalinService()
        {
        }
	

        @Override
        public IBinder onBind(Intent intent) 
        {
            Log.d(TAG, "onBind(" + intent + ")");
            return new MalinServiceBinder(this);
        }
	
        //
	
        @Override
        public boolean onUnbind(Intent intent) 
        {
            Log.d(TAG, "onUnbind(" + intent + ")");
            return super.onUnbind(intent);
        }

        void doSomethingInParticular()
        {
        }

        //
	
        private static final String TAG = "MalinService";
    }

The MarlinServiceBinder object retains a reference to the MalinService object but that is all.

    package xper.service.malin;

    import android.os.RemoteException;
    import xper.service.malin.Malin.Stub;

    final class MalinServiceBinder 
                extends 
                    Stub 
    {
        public void doSomethingInParticular() 
                    throws 
                        RemoteException 
        {
            service.doSomethingInParticular();
        }

        //
	
        MalinServiceBinder(MalinService theService)
        {
            service = theService;
        }
	
        //
	
        private MalinService	service;
    }

Once it has been created there are no references to the MarlinServiceBinder object from any other Java objects.

We can show this using MAT and by examining the heap dump directly.

As in the ServiceRecord case MAT erroneously reports that the MalinServiceBinder object is referenced by a Native Stack GC Root but examination of the heap dump shows that, as expected, it is actually a JNI Global reference.

In this example, if the JNI Global reference did not exist then the MarlinServiceBinder object would be eligible for garbage collection. This is of course precisely why the Binder C++ code creates the JNI Global reference. It is to ensure that a Java Binder object that is otherwise unreferenced in the process in which it was created is not garbage collected whilest there are references to it from other processes.

2.0 Binder References

Explaining exactly how the Binder reference mechanism works more or less requires explaining exactly how Binders work full-stop, which would take a lot more space than is available in the margin of this post, so what follows is a somewhat simplified explanation.

2.2 Reference Counting

Binders are reference counted. Each BinderProxy which references a Binder constitutes a reference to that Binder.

The Binder reference counting mechanism is, necessarily, cross-process and entirely orthogonal to the per-process Java garbage collection mechanism.

2.2.1 Incrementing The Reference Count

Effectively each time a Binder is sent to another process and a BinderProxy is created for that Binder the reference count is incremented.

2.2.2 Decrementing The Reference Count

The finalize() method of a Java BinderProxy object, that is an instance of the class android.os.BinderProxy, calls the native method destroy() which decrements the reference count of the associated Binder object.

In other words, once a Java BinderProxy object has been created in a process then the reference from it to the associated Binder will exist until that Java BinderProxy object has both been garbage collected and finalized.

2.2 Interaction With Java GC

As we have seen the Binder reference mechanism creates a JNI Global reference to a Java Binder object to prevent it from being garbage collected whilest there remain BinderProxy objects in other processes that reference it.

The JNI Global reference is created the first time the Binder is referenced by a BinderProxy.

The JNI Global reference is destroyed when there are no longer any BinderProxies referencing the Binder as determined by the reference count.

That is the only interaction between the Binder reference mechanism and the Java garbage collection mechanism of the process in which the Binder object was created and it is completely opaque. That is the Binder reference mechanism creates a vanilla JNI Global reference that is indistinguishable from a JNI Global reference created by any other piece of native code.

2.4 Inspecting Per-Process Binder Related State At Runtime

It is possible to inspect the Binders and BinderProxies in use in a given process by looking at the contents of the file

    /proc/binder/proc/<PID>

where <PID> is the id of the process.

For example, entering

 cat /proc/binder/proc/368

in a shell running on the device after the client has bound to the Malin Service results in the following output

    binder proc state:
    proc 368
      thread 368: l 00
      thread 374: l 12
      thread 375: l 11
      node 3879: u0007ccf0 c00094f88 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 proc 60
      node 3904: u001a5bb8 c001a5b98 hs 1 hw 1 ls 0 lw 0 is 2 iw 2 proc 357 60
      ref 3874: desc 0 node 1 s 1 w 1 d (null)
      ref 3877: desc 1 node 135 s 1 w 1 d (null)
      ref 3882: desc 2 node 196 s 1 w 1 d (null)
      ref 3883: desc 3 node 131 s 1 w 1 d (null)
      ref 3884: desc 4 node 183 s 1 w 1 d (null)
      ref 3887: desc 5 node 3886 s 1 w 1 d (null)
      ref 3896: desc 6 node 23 s 1 w 1 d (null)
      ref 3899: desc 7 node 44 s 1 w 1 d (null)

In this case 368 is the id of the process running the Malin Service, 357 is the id of the process running the client, and 60 is the id of the System process.

The corresponding output for the process running the client looks like this

    binder proc state:
    proc 357
      thread 357: l 00
      thread 366: l 12
      thread 367: l 11
      node 3830: u000a5168 c001ad3c0 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 proc 60
      node 3754: u0014c9a0 c00094f88 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 proc 60
      node 3785: u001ad4f0 c001ae468 hs 1 hw 1 ls 0 lw 0 is 2 iw 2 proc 113 60
      node 3783: u001ae450 c001ae430 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 proc 60
      node 3791: u001ae6d0 c001ae6b0 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 proc 60
      node 3870: u001b3ba8 c001b3b88 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 proc 60
      ref 3749: desc 0 node 1 s 1 w 1 d (null)
      ref 3752: desc 1 node 135 s 1 w 1 d (null)
      ref 3757: desc 2 node 196 s 1 w 1 d (null)
      ref 3758: desc 3 node 131 s 1 w 1 d (null)
      ref 3759: desc 4 node 183 s 1 w 1 d (null)
      ref 3762: desc 5 node 3761 s 1 w 1 d (null)
      ref 3772: desc 6 node 23 s 1 w 1 d (null)
      ref 3775: desc 7 node 44 s 1 w 1 d (null)
      ref 3781: desc 8 node 219 s 1 w 1 d (null)
      ref 3789: desc 9 node 3788 s 1 w 1 d (null)
      ref 3799: desc 10 node 3798 s 1 w 1 d (null)
      ref 3803: desc 11 node 3802 s 1 w 1 d (null)
      ref 3807: desc 12 node 3806 s 1 w 1 d (null)
      ref 3828: desc 13 node 272 s 1 w 1 d (null)
      ref 3843: desc 14 node 3821 s 1 w 1 d (null)
      ref 3866: desc 15 node 765 s 1 w 1 d c5ff8940
      ref 3907: desc 16 node 3904 s 1 w 1 d c469be40

2.4.1 Binder Entries

The lines with the prefix node describe Binder objects that have been created in the process.

The numbers after the proc keyword are the ids of the processes which hold references to that Binder, that is, there
are BinderProxy objects in those processes which reference the Binder.

For example, the entry

      node 3904: u001a5bb8 c001a5b98 hs 1 hw 1 ls 0 lw 0 is 2 iw 2 proc 357 60

from the first example above from the Service process, 368, specifies that the System process (60) and the client process (357) both have BinderProxy objects which reference this Binder in the service process.

By inference this entry must be for the MalinServiceBinder Java Binder object.

2.4.2 BinderProxy Entries

The lines with the prefix ref describe the BinderProxy objects that have been created in the process

The number after the node keyword is the id of the Binder the BinderProxy references.

For example, the entry

      ref 3907: desc 16 node 3904 s 1 w 1 d c469be40

from the second example above from the client process, 357, specfies the BinderProxy which references the Binder with id 3904 which as we have seen is a Binder in the service process, 368.

3.0 A Binder Cycle Made For Two

The way the Binder reference mechanism works means that it possible to create cross-process reference cycles such that none of the objects in the cycle can be garbage collected if they are Java objects or freed if they are C++ objects despite the fact that none of them are in use. In short a memory leak.

The simplest cycle requires just two Java Binder objects in two separate processes each of which holds a Java BinderProxy object referencing the other, like so


Copyright (c) 2012 By Simon Lewis. All Rights Reserved.

May 30, 2012

Adventures In Distributed Garbage Collection: Part Sixteen – The Great ServiceRecord Mystery

1.0 The Mystery

Why does the ServiceRecord object representing a non-existent Service still exist in the Java heap of the System process ?

2.0 The Object Graph: A Recap

The object graph shows two references to the ServiceRecord object [0x406f89b8] from other objects in the graph but they are themselves only referenced from the ServiceRecord object itself and therefore cannot be responsible for its continued existence.

3.0 MAT: A Recap

Using the MAT

 Path To GC Roots > with all references

option on the errant ServiceRecord object produces this

There is a reference to the ServiceRecord object from a Native Stack GC Root.

The MAT Garbage Collection Roots documentation says

Native Stack

In or out parameters in native code, such as user defined JNI code or JVM internal code. This is often the case as many methods have native parts and the objects handled as method parameters become GC roots. For example, parameters used for file/network I/O methods or reflection.

The implication is that the ServiceRecord object has been passed to a native method as an argument which is executing at the point the Java heap dump was taken. This is unlikely but not impossible. However we have seen exactly the same thing in a different process. Again this is not impossible but it is suspicious and it does suggest that it might be a good idea to try and find out what exactly is going on in this particular case. One way to find out is to look at the contents of the hprof file directly.

4.0 The Heap Dump vs. MAT

Looking directly at the contents of the hprof file containing the dump of the Java heap turns up a single JNI Global reference (HPROF_GC_ROOT_JNI_GLOBAL) rather than a native stack reference (HPROF_GC_ROOT_NATIVE_STACK) which is odd.

A JNI Global reference is not the same as a reference from a native stack frame and in fact there is a separate entry for JNI Global in the MAT documentation for Garbage Collection Roots which reads

Global variable in native code, such as user defined JNI code or JVM internal code.

so why has MAT decided to conflate the two ?

The answer appears to lie in the following method which is defined in the class org.eclipse.mat.hprof.Pass1Parser (code slightly re-formatted)

    private void readDumpSegments(long length) throws IOException, SnapshotException
    {
        long segmentStartPos = in.position();
        long segmentsEndPos = segmentStartPos + length;

        while (segmentStartPos < segmentsEndPos)
        {
            long workDone = segmentStartPos / 1000;
            if (this.monitor.getWorkDone() < workDone)
            {
                if (this.monitor.isProbablyCanceled())
                    throw new IProgressListener.OperationCanceledException();
                this.monitor.totalWorkDone(workDone);
            }

            int segmentType = in.readUnsignedByte();
            if (verbose)
                System.out.println("    Read heap sub-record type "+segmentType+" at position 0x"+ \
                   Long.toHexString(segmentStartPos)); //$NON-NLS-1$ //$NON-NLS-2$
            switch (segmentType)
            {
                case Constants.DumpSegment.ROOT_UNKNOWN:
                    readGC(GCRootInfo.Type.UNKNOWN, 0);
                    break;
                case Constants.DumpSegment.ROOT_THREAD_OBJECT:
                    readGCThreadObject(GCRootInfo.Type.THREAD_OBJ);
                    break;
				
                case Constants.DumpSegment.ROOT_JNI_GLOBAL:
                    readGC(GCRootInfo.Type.NATIVE_STACK, idSize);
                    break;
				
                case Constants.DumpSegment.ROOT_JNI_LOCAL:
                    readGCWithThreadContext(GCRootInfo.Type.NATIVE_LOCAL, true);
                    break;
                case Constants.DumpSegment.ROOT_JAVA_FRAME:
                    readGCWithThreadContext(GCRootInfo.Type.JAVA_LOCAL, true);
                    break;
                case Constants.DumpSegment.ROOT_NATIVE_STACK:
                    readGCWithThreadContext(GCRootInfo.Type.NATIVE_STACK, false);
                    break;
                case Constants.DumpSegment.ROOT_STICKY_CLASS:
                    readGC(GCRootInfo.Type.SYSTEM_CLASS, 0);
                    break;
                case Constants.DumpSegment.ROOT_THREAD_BLOCK:
                    readGC(GCRootInfo.Type.THREAD_BLOCK, 4);
                    break;
                case Constants.DumpSegment.ROOT_MONITOR_USED:
                    readGC(GCRootInfo.Type.BUSY_MONITOR, 0);
                    break;
                case Constants.DumpSegment.CLASS_DUMP:
                    readClassDump(segmentStartPos);
                    break;
                case Constants.DumpSegment.INSTANCE_DUMP:
                    readInstanceDump(segmentStartPos);
                    break;
                case Constants.DumpSegment.OBJECT_ARRAY_DUMP:
                    readObjectArrayDump(segmentStartPos);
                    break;
                case Constants.DumpSegment.PRIMITIVE_ARRAY_DUMP:
                    readPrimitiveArrayDump(segmentStartPos);
                    break;
                default:
                    throw new SnapshotException(MessageUtil.format( \
                                  Messages.Pass1Parser_Error_InvalidHeapDumpFile,
                                    segmentType, segmentStartPos));
            }

            segmentStartPos = in.position();
        }
       
        ...
    }

Curiously the class org.eclipse.mat.snapshot.model.GCRootInfo does not define a NATIVE_GLOBAL constant for use in this case which would seem to imply that this is intentional if rather unhelpful behaviour.

5.0 Hunt The JNI Global Reference

So the ServiceRecord object is still in the Java heap because there is a JNI Global reference to it. Why ?

At this point it looks as though we may be stuck. There is obviously no record in the heap dump of how or why individual JNI Global references were created by native code nor could there be.

On the other hand for native code to have created a JNI Global reference to the ServiceRecord object, it must have been passed to a native method at some point. None of the ActivityManagerService methods discussed to date have been native. What about the ServiceRecord class ? It does not have any native methods, on the other hand its super class android.os.Binder does so what about them ?

6.0 android.os.Binder Native Methods

The first native method to be invoked in the lifecycle of a Binder object is the init() method which is invoked at the very start of the default constructor

    public Binder()

It is declared as follows

    private native final void init();

The actual implementation can be found in the file frameworks/base/core/jni/android_util_Binder.cpp, lines 633-643, and it looks like this

    static void android_os_Binder_init(JNIEnv* env, jobject clazz)
    {
        JavaBBinderHolder* jbh = new JavaBBinderHolder(env, clazz);
        if (jbh == NULL) {
            jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
            return;
        }
        LOGV("Java Binder %p: acquiring first ref on holder %p", clazz, jbh);
        jbh->incStrong(clazz);
        env->SetIntField(clazz, gBinderOffsets.mObject, (int)jbh);
    }

Note that init() is an instance method, so the jobject argument clazz is the Java Binder object itself which is neither a class nor a clazz.

At first glance it does not look very promising. Certainly there is no direct call to the NewGlobalRef() JNI function which would result in the creation of a JNI Global reference. On the other hand the Java Binder object is being passed to the constructor of the C++ class JavaBBinderHolder. Maybe that creates one.

The C++ class JavaBBinderHolder is defined in the file frameworks/base/core/jni/android_util_Binder.cpp, lines 322-359.

Although none of the JavaBBinderHolder methods invoke NewGlobalRef() a JavaBBinderHolder object does retain the pointer to the Java Binder object passed to the constructor, so there is still hope.

In the get() method the pointer to the Java Binder object is passed to the constructor of the C++ class JavaBBinderr.

The C++ class JavaBBinder is also defined in the file frameworks/base/core/jni/android_util_Binder.cpp, lines 234-318.

The constructor is defined like this

   JavaBBinder(JNIEnv* env, jobject object)
        : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object))
    {
        LOGV("Creating JavaBBinder %p\n", this);
        android_atomic_inc(&gNumLocalRefs);
        incRefsCreated(env);
    }

and as we can see it duly creates a JNI Global reference to the Java Binder object.

Note

NewGlobalRef() is defined to return NULL if the system runs out of memory, so in general the usage in this method is bad practice. However the Dalvik implementation will blow up the VM if it cannot allocate the reference (see dalvik/vm/Jni.c NewGlobalRef() lines 2195-2207 and addGlobalReference() lines 742-866) so this code is safe for some value of safe.


Copyright (c) 2012 By Simon Lewis. All Rights Reserved.

May 28, 2012

Adventures In Distributed Garbage Collection: Part Fifteen – Post Unbind System State

Filed under: Android, Android Internals, Java, Mobile Java, Services — Tags: , , , , — Simon Lewis @ 8:41 am

1.0 Object State

Once the client has unbound from the Fastnet Service then on the basis of the source code and given that it was the only client and that it specified the BIND_AUTO_CREATE flag when binding to the Fastnet Service, we would expect that

  • the ActivityManagerService would no longer hold a reference to the ServiceRecord representing the Fastnet Service via the mServices instance variable,

  • the ProcessRecord representing the Service process would no longer hold a reference to the ServiceRecord representing the Fastnet Service via
    services instance variable,

  • the ProcessRecord representing the Service client process would no longer hold a reference to the ConnectionRecord representing the connection to the Fastnet Service via the connections instance variable,

  • the ActivityRecord representing the Service client Activity would no longer hold a reference to the ConnectionRecord representing the connection to the Fastnet Service via the connections instance variable

  • the ServiceRecord object representing the Fastnet Service would no longer be present in the Java heap, nor would the associated IntentBindRecord and AppBindRecord objects

  • the ConnectionRecord object represnting the binding to the Fastnet Service from the Service client process would no longer be present in the Java heap.

2.0 dumpsys Output

Below are excerpts from the output of the dumpsys command run immediately after the client has unbound from the Fastnet Service.
In some cases the output has been slightly re-formatted for clarity.

2.1 ProcessRecords

2.1.1 Service Client ProcessRecord

    ...

    *APP* UID 10054 ProcessRecord{4067a100 293:xper.service.fastnet.client/10054}
      dir=/data/app/xper.service.fastnet.client-2.apk \
	  publicDir=/data/app/xper.service.fastnet.client-2.apk \
          data=/data/data/xper.service.fastnet.client
      packageList=[xper.service.fastnet.client]
      thread=android.app.ApplicationThreadProxy@406c1890 curReceiver=null
      pid=293 starting=false lastPss=0
      lastActivityTime=-6m18s641ms lruWeight=253489 keeping=true hidden=false empty=false
      oom: max=15 hidden=7 curRaw=0 setRaw=0 cur=0 set=0
      curSchedGroup=0 setSchedGroup=0
      setIsForeground=false foregroundServices=false forcingToForeground=null
      persistent=false removed=false
      adjSeq=248 lruSeq=57
      lastRequestedGc=-6m18s642ms lastLowMemory=-6m18s642ms reportLowMemory=false
      activities=[HistoryRecord{407ad2d8 xper.service.fastnet.client/.FastnetClientActivity}]
      conProviders={ContentProviderRecord{4051bc78 com.android.providers.settings.SettingsProvider}=1}
	
    ...

As expected the ProcessRecord [0x4067a100] representing the Service client process has no associated connections.

2.1.2 Service ProcessRecord

    ...

    *APP* UID 10053 ProcessRecord{4076e000 301:xper.service.fastnet/10053}
      dir=/data/app/xper.service.fastnet-2.apk \
	      publicDir=/data/app/xper.service.fastnet-2.apk data=/data/data/xper.service.fastnet
      packageList=[xper.service.fastnet]
      thread=android.app.ApplicationThreadProxy@4077e2a0 curReceiver=null
      pid=301 starting=false lastPss=0
      lastActivityTime=-6m11s99ms lruWeight=141059 keeping=false hidden=true empty=true
      oom: max=15 hidden=7 curRaw=7 setRaw=7 cur=7 set=7
      curSchedGroup=1 setSchedGroup=1
      setIsForeground=false foregroundServices=false forcingToForeground=null
      persistent=false removed=false
      adjSeq=248 lruSeq=60
      lastWakeTime=0 time used=0
      lastCpuTime=0 time used=0
      lastRequestedGc=-29s98ms lastLowMemory=-6m11s10ms reportLowMemory=false
	  
    ...

As expected the ProcessRecord [0x4076e000] representing the Service process has no associated services.

2.2 ActivityRecord

    Activities in Current Activity Manager State:
      * TaskRecord{4070aa90 #3 A xper.service.fastnet.client}
        clearOnBackground=false numActivities=1 rootWasReset=true
        affinity=xper.service.fastnet.client
        intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] \
            flg=0x10200000 cmp=xper.service.fastnet.client/.FastnetClientActivity bnds=[83,316][157,395]}
        realActivity=xper.service.fastnet.client/.FastnetClientActivity
        lastActiveTime=253499 (inactive for 378s)
        * Hist #1: HistoryRecord{407ad2d8 xper.service.fastnet.client/.FastnetClientActivity}
            packageName=xper.service.fastnet.client processName=xper.service.fastnet.client
            launchedFromUid=10001 app=ProcessRecord{4067a100 293:xper.service.fastnet.client/10054}
            Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 \
			    cmp=xper.service.fastnet.client/.FastnetClientActivity bnds=[83,316][157,395] }
            frontOfTask=true task=TaskRecord{4070aa90 #3 A xper.service.fastnet.client}
            taskAffinity=xper.service.fastnet.client
            realActivity=xper.service.fastnet.client/.FastnetClientActivity
            base=/data/app/xper.service.fastnet.client-2.apk/data/app/xper.service.fastnet.client-2.apk \
            data=/data/data/xper.service.fastnet.client
            labelRes=0x7f040001 icon=0x7f020000 theme=0x0
            stateNotNeeded=false componentSpecified=true isHomeActivity=false
                configuration={ scale=1.0 imsi=310/260 loc=en_US touch=3 keys=2/1/2 nav=3/1 orien=1 \
                layout=18 uiMode=17 seq=4}
            launchFailed=false haveState=false icicle=null
            state=RESUMED stopped=false delayedResume=false finishing=false
            keysPaused=false inHistory=true launchMode=0
            fullscreen=true visible=true frozenBeforeDestroy=false thumbnailNeeded=false idle=true
            waitingVisible=false nowVisible=true
            connections=[]
       ...

As expected the ActivityRecord [0x4070aa90] representing the Activity running in the Service client process has no associated connections.

3.0 Java Heap State

As before we already have the dumpsys output, but again we can use the the state of the Java heap of the System process to double-check our understanding of the source code.

This graph was generated from a Java heap dump of the System process taken after the Service client has unbound from the Fastnet Service .

Like its counterpart here it was generated using the ServiceRecord object [0x406f89b8] representing the Fastnet Service as the root. As before greyed out nodes have been filtered, that is, one or more instance variables have been omitted for clarity.

This is another problematic graph. If the ServiceRecord object [0x406f89b8] is no longer in the Java heap it should not be possible to generate a graph with it as the root.

Evidently the graph exists therefore the assumption is wrong. So what is referencing it ? According to MAT there is a single GC Root of type Native Stack.

This is an identical situation to the previous one and whilest it is possible that on one occasion there was a transient reference from native code at exactly the point the Java heap dump was taken, two occasions in two separate processes is beginning to look like carelessness on somebody’s part !


Copyright (c) 2012 By Simon Lewis. All Rights Reserved.

May 25, 2012

Adventures In Distributed Garbage Collection: Part Fourteen – Post Unbind Service State

Filed under: Android, Android Internals, Java, Mobile Java, Services — Tags: , , , , — Simon Lewis @ 6:00 am

1.0 Object State

Once the client has unbound from the Fastnet Service then on the basis of the source code we would expect that in the Service process

  • there should no longer be a reference to the FastnetService object from the ActivityThread via its mServices instance variable

  • the FastnetService object should no longer exist

  • the FastnetServiceBinder object should no longer exist

The production of a diagram showing the absence of these things is left as an exercise for the reader.

2.0 Java Heap State

This graph was generated from a Java heap dump of the Service process taken after the client has unbound from the Fastnet Service.

Like its counterpart here it was generated using the FastnetService object [0x40517518] as the root. As before greyed out nodes have been filtered, that is, one or more instance variables have been omitted for clarity.

The existence of this graph is problematic. If the FastnetService object is no longer in the Java heap it should not be possible to generate
a graph with it as the root.

Evidently the graph does exist therefore the assumption that the FastnetService object no longer existing in the Java heap is wrong.

The graph shows two references to the FastnetService object

  • one from the FastnetServiceBinder object [0x40517fc0], and

  • one from the ContextImpl object [0x40517548]

However none of these objects are referenced from any other objects in the graph so this does not explain why the FastnetService object still exists. Either it, or something which references it, is being referenced by something else which is not reachable from any object in the graph.

According to MAT there are no other references to the FastnetService object but there is another reference to the FastnetServiceBinder object.

The reference is identified as Native Stack and it is a GC Root, that is, it is a reference that will ensure the FastnetServiceBinder object will not be garbage collected as long as the GC Root continues to exist.

The Native Stack GC Root is described in the MAT documentation as follows.

In or out parameters in native code, such as user defined JNI code or JVM internal code. This is often the case as many methods have native parts and the objects handled as method parameters become GC roots. For example, parameters used for file/network I/O methods or reflection.

It is not obvious what this reference from the native code might be but presumably it is transient. Possibly the continued existence of the FastnetService object is simply an anomaly that happens to have occurred on this occasion.


Copyright (c) 2012 By Simon Lewis. All Rights Reserved.

May 24, 2012

Adventures In Distributed Garbage Collection: Part Thirteen – Post Unbind Service Client State

Filed under: Android, Android Internals, Java, Mobile Java, Services — Tags: , , , , — Simon Lewis @ 6:00 am

1.0 Object State

Once the client has unbound from the Fastnet Service then on the basis of the source code we would expect that in the Service client process

2.0 Java Heap State

This graph was generated from a Java heap dump of the Service client process taken after the client Activity has unbound from the Fastnet Service by calling the unbindService() method.

Like its counterpart here it was generated using the LoadedApk object [0x40513e90] as the root. As before greyed out nodes have been filtered, that is, one or more instance variables have been omitted for clarity.

The graph shows that as expected the LoadedApk object does not reference the ServiceDispatcher object [0x405213b8]

In addition the following objects no longer appear in the graph

  • the ServiceDispatcher object [0x405213b8]

  • the ConnectionInfo object [0x40521b40]

  • the InnerConection object [0x405216b8]

  • the FastnetServiceConnection object [0x405211f0]

This on its own does not prove conclusively that these objects are no longer in the Java heap, just that they are not reachable via the LoadedApk any more.

However, after loading the Java heap dump into MAT none of the objects can be found either which should mean that they have been garbage collected.

Inspecting the contents of the Java heap dump file directly confirms this.


Copyright (c) 2012 By Simon Lewis. All Rights Reserved.

May 23, 2012

Adventures In Distributed Garbage Collection: Part Twelve – Unbinding From A Service

1.0 Process: Service Client

The following code in the Service Client calls the Activity’s implementation of the method

    public abstract void unbindService(ServiceConnection conn)

passing the ServiceConnection object that was originally passed to the bindService() method.

        ...

        unbindService(connection);
        connection = null;

        ...

This eventually results in a call to the ContextImpl implementation of unbindService().

1.1 unbindService(…)

Object: ContextImpl

Definition: ContextImpl.java [888-899]

    public void unbindService(ServiceConnection conn)

Argument: ServiceConnection conn

  • The ServiceConnection object identifying the connection(s) to disconnect.

Method

The method begins by obtaining the IServiceConnection object corresponding to the ServiceConnection argument by calling the method forgetServiceDispatcher() on the LoadedApk object (mPackageInfo).

The static method ActivityManagerNative.getDefault() is then called and the method unbindService() is called on the ActivityManagerProxy object returned, with the IServiceConnection object as the argument

This results in a synchronous remote procedure call to the ActivityManagerService implementation of the unbindService() method.

1.2 forgetServiceDispatcher(…)

Object: LoadedApk

Definition: LoadedApk.java [846-894]

    public final IServiceConnection forgetServiceDispatcher(Context context, ServiceConnection c)

Argument: Context context

Argument: ServiceConnection c

Method

The method begins by getting the ServiceConnection to ServiceDispatcher map for the given Context from the mServices map.

If the map is not null then the ServiceConnection argument c is used as the key to obtain the corresponding ServiceDispatcher object.

If there is a corresponding ServiceDispatcher it is removed from the ServiceConnection to ServiceDispatcher map for the given Context.

The doForget() method is then called on the ServiceDispatcher object.

If the BIND_DEBUG_UNBIND flag is set in the mFlags instance variable of the ServiceDispatcher then

  • The method generates a stack-trace for this method call via a RuntimeException object which is then stored in the mUnbindLocation instance variable of the ServiceDispatcher.

  • The ServiceDispatcher is then added to the ServiceConnection to ServiceDispatcher map for the given Context in the mUnboundServices map.

The method then returns the result of calling the method getIServiceConnection() on the ServiceDispatcher.

If a corresponding ServiceDispatcher is not found the method throws an IllegalArgumentException.

2.0 Process: System

Entry Point: Remote procedure call to the ActivityManagerService implementation of the IActivityManager method unbindService().

2.1 unbindService(…)

Caller: Service client via remote procedure call

Actor: ActivityManagerService

Definition: ActivityManagerService.java [9598-9625]

    public boolean unbindService(IServiceConnection connection)

Argument: IServiceConnection connection

  • A proxy for the object implementating the IServiceConnection in the process in which the Service client is running.

Method

The method starts by calling the asBinder() method on the IServiceConnection connection argument.

It uses the resulting IBinder object as a key into the mServiceConnections map to obtain the ConnectionRecord list associated with the given IServiceConnection.

If the list is null the method returns false.

Otherwise it iterates over the ConnectionRecord list calling the method removeConnectionLocked() on each ConnectionRecord.

2.2 removeConnectionLocked(…)

Definition: ActivityManagerService.java [9542-9596]

    void removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct)

Argument: ConnectionRecord c

  • The ConnectionRecord which represents the connection to remove.

Argument: ProcessRecord skipApp

  • The ProcessRecord to ignore, or null.

Argument: ActivityRecord skipAct

  • The ActivityRecord to ignore, or null.

Overview

The method removes references to the given ConnectionRecord from data structures held by a number of different objects.

If the connection represented by the ConnectionRecord was the only one using the Intent/IBinder binding then it will also remove that binding.

Method

The method begins by calling asBinder() on the IServiceConnection object held by the ConnectionRecord (conn) to convert it to an IBinder.

It then gets the AppBindRecord held by the ConnectionRecord (binding) and the ServiceRecord held by AppBindRecord (service).

The IBinder object obtained at the beginning of the method is then used as a key into the connections map of the ServiceRecord to obtain the associated ConnectionRecord list. If the list is not null then the ConnectionRecord is removed from it. If the list is now empty it is removed from ServiceRecord‘s connections map.

The ConnectionRecord is then removed from the connections list of the AppBindRecord.

If the ActivityRecord held by the ConnectionRecord (activity) is not null and it is not identical to the skipAct argument, then the ConnectionRecord is removed from its connections list.

If the ProcessRecord held by the AppBindRecord (client) is not identical to the skipApp argument then the ConnectionRecord is removed from its connections list.

The IBinder object obtained at the beginning of the method is then used as a key into the mServiceConnections map to obtain the associated ConnectionRecord list. If the list is not null then the ConnectionRecord is removed from it. If the list is now empty it is removed from mServiceConnections map.

If the AppBindRecord connections list is empty, then the AppBindRecord is removed from the apps list of the IntentBindRecord that it holds (intent).

If the Service is running and the IntentBindRecord held by the AppBindRecord no longer has any associated connections then

Finally if the BIND_AUTO_CREATE flag was set when the connection represented by the ConnectionRecord was created the method calls bringDownServiceLocked() passing it the ServiceRecord and with the force argument set to false.

3.0 Process: Service

Entry Point: remote procedure call call to the ApplicationThread implementation of the IApplicationThread method scheduleUnbindService().

3.1 scheduleUnbindService(…)

Caller: ActivityManagerService via remote procedure call.

Actor: ApplicationThread

Definition: ActivityThread.java [512-518]

    public final void scheduleUnbindService(IBinder token, Intent intent)

Argument: IBinder token

  • The proxy for the ServiceRecord which represents the Service the operation should be invoked upon.

Argument: Intent intent

  • The Intent specifying the binding which should be undone.

Method

The method encapsulates its arguments in an instance of the ActivityThread inner class BindServiceData and then sends it to the ActivityThread as the payload of a message.

This message is handled by the method ActivityThread.handleUnbindService().

3.2 handleUnbindService(…)

Actor: ActivityThread

Definition: ActivityThread.java [1990-2014]

    private final void handleUnbindService(BindServiceData data)

Argument: BindServiceData data

  • The encapsulated arguments passed to the ApplicationThread.scheduleUnbindService() method.

Method

The method starts by retrieving the Service Component from the mServices map using the IBinder object data.token as the key.

If there is no corresponding Service Component the method returns.

Otherwise the onUnbind() method is called on the Service Component passing in the Intent data.intent.

If the call to onUnbind() returns true then the method unbindFinished() is called on the ActivityManagerProxy object returned by a call to the static method ActivityManagerNative.getDefault()

This results in a synchronous remote procedure call to the implementation of the method unbindFinished() in the ActivityManagerService.

If the call to onUnbind() returns true then the method serviceDoneExecuting() is called on the ActivityManagerProxy object returned by a call to the static method ActivityManagerNative.getDefault()

This results in an asynchronous remote procedure call to the implementation of the method serviceDoneExecuting() in the ActivityManagerService running in the System process.

4.0 Process: System

Entry Point: Remote procedure call to the ActivityManagerService implementation of the IActivityManager method unbindFinished()

4.1 unbindFinished(…)

Caller: The ActivityThread in the Service process via remote procedure call.

Actor: ActivityManagerService

Definition: ActivityManagerService.java [9687-9725]

    public void unbindFinished(IBinder token, Intent intent, boolean doRebind)

Argument: IBinder token

  • The ServiceRecord which represents the Service which has finished performing the unbind.

Argument: Intent intent

  • The Intent identifying the binding which has been undone.

Argument: boolean doRebind

  • Not used.

Method

The method begins by explicitly checking whether the token argument is actually a ServiceRecord and throws an IllegalArgumentException if it is not.

It then casts the the token argument to a ServiceRecord.

If the resulting ServiceRecord is null the method does nothing more.

If the resulting ServiceRecord is not null then the method wraps the intent argument in an Intent.FilterComparison instance which is then used as a key into the bindings map of the ServiceRecord.

If a corresponding IntentBindRecord exists then

  • If there are now processes associated with the IntentBindRecord the method calls requestServiceBindingLocked(), otherwise it sets the doRebind instance variable of the IntentBindRecord to true.

The method then calls serviceDoneExecutingLocked()


Copyright (c) 2012 By Simon Lewis. All Rights Reserved.

May 22, 2012

Adventures In Distributed Garbage Collection: Part Eleven – Post Bind System Process State

Filed under: Android, Android Internals, Java, Mobile Java, Services — Tags: , , , , — Simon Lewis @ 6:00 am

1.0 Object State

As in the Service client and Service cases we can use the source code to derive a diagram which shows the Service related objects that should exist in the System process and their relationships after a client has bound to the Service.

2.0 dumpsys Output

We can use the output from the Android dumpsys command to check whether the ActivityManagerService is doing what we think it is when the client binds to the Fastnet Service.

Below are excerpts from the output of the dumpsys command run immediately after the client has successfully bound to the Fastnet Service.
In some cases the output has been slightly re-formatted for clarity.

2.1 ProcessRecords

2.1.1 Service Client ProcessRecord

    ...

    *APP* UID 10054 ProcessRecord{4067a100 293:xper.service.fastnet.client/10054}
      dir=/data/app/xper.service.fastnet.client-2.apk \
          publicDir=/data/app/xper.service.fastnet.client-2.apk data=/data/data/xper.service.fastnet.client
      packageList=[xper.service.fastnet.client]
      thread=android.app.ApplicationThreadProxy@406c1890 curReceiver=null
      pid=293 starting=false lastPss=0
      lastActivityTime=-1m6s575ms lruWeight=253489 keeping=true hidden=false empty=false
      oom: max=15 hidden=7 curRaw=0 setRaw=0 cur=0 set=0
      curSchedGroup=0 setSchedGroup=0
      setIsForeground=false foregroundServices=false forcingToForeground=null
      persistent=false removed=false
      adjSeq=198 lruSeq=57
      lastRequestedGc=-1m6s576ms lastLowMemory=-1m6s576ms reportLowMemory=false
      activities=[HistoryRecord{407ad2d8 xper.service.fastnet.client/.FastnetClientActivity}]
      connections=[ConnectionRecord{40706ac0 xper.service.fastnet/.FastnetService:@40721f08}]
      conProviders={ContentProviderRecord{4051bc78 com.android.providers.settings.SettingsProvider}=1}
    
    ...

The ProcessRecord [0x4067a100] for the Service client process is present and has a reference to a single ConnectionRecord [0x40706ac0] as expected.

2.1.1 Service ProcessRecord

    ...

    *APP* UID 10053 ProcessRecord{4076e000 301:xper.service.fastnet/10053}
      dir=/data/app/xper.service.fastnet-2.apk \
          publicDir=/data/app/xper.service.fastnet-2.apk data=/data/data/xper.service.fastnet
      packageList=[xper.service.fastnet]
      thread=android.app.ApplicationThreadProxy@4077e2a0 curReceiver=null
      pid=301 starting=false lastPss=0
      lastActivityTime=-59s120ms lruWeight=141059 keeping=true hidden=false empty=true
      oom: max=15 hidden=7 curRaw=0 setRaw=0 cur=0 set=0
      curSchedGroup=0 setSchedGroup=0
      setIsForeground=false foregroundServices=false forcingToForeground=null
      persistent=false removed=false
      adjSeq=198 lruSeq=60
      lastRequestedGc=-59s121ms lastLowMemory=-59s121ms reportLowMemory=false
      services=[ServiceRecord{406f89b8 xper.service.fastnet/.FastnetService}]
    
    ...

The ProcessRecord [0x4076e000] for the Service process is present and has a reference to a single ServiceRecord [0x406f89b8] as expected.

2.2 ActivityRecord

    Activities in Current Activity Manager State:
      * TaskRecord{4070aa90 #3 A xper.service.fastnet.client}
        clearOnBackground=false numActivities=1 rootWasReset=true
        affinity=xper.service.fastnet.client
        intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] \
            flg=0x10200000 cmp=xper.service.fastnet.client/.FastnetClientActivity bnds=[83,316][157,395]}
        realActivity=xper.service.fastnet.client/.FastnetClientActivity
        lastActiveTime=253499 (inactive for 66s)
        * Hist #1: HistoryRecord{407ad2d8 xper.service.fastnet.client/.FastnetClientActivity}
            packageName=xper.service.fastnet.client processName=xper.service.fastnet.client
            launchedFromUid=10001 app=ProcessRecord{4067a100 293:xper.service.fastnet.client/10054}
            Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] \
                flg=0x10200000 cmp=xper.service.fastnet.client/.FastnetClientActivity bnds=[83,316][157,395] }
            frontOfTask=true task=TaskRecord{4070aa90 #3 A xper.service.fastnet.client}
            taskAffinity=xper.service.fastnet.client
            realActivity=xper.service.fastnet.client/.FastnetClientActivity
            base=/data/app/xper.service.fastnet.client-2.apk/data/app/xper.service.fastnet.client-2.apk \
                data=/data/data/xper.service.fastnet.client
            labelRes=0x7f040001 icon=0x7f020000 theme=0x0
            stateNotNeeded=false componentSpecified=true isHomeActivity=false
            configuration={ scale=1.0 imsi=310/260 loc=en_US touch=3 keys=2/1/2 nav=3/1 orien=1 layout=18 uiMode=17 seq=4}
            launchFailed=false haveState=false icicle=null
            state=RESUMED stopped=false delayedResume=false finishing=false
            keysPaused=false inHistory=true launchMode=0
            fullscreen=true visible=true frozenBeforeDestroy=false thumbnailNeeded=false idle=true
            waitingVisible=false nowVisible=true
            connections=[ConnectionRecord{40706ac0 xper.service.fastnet/.FastnetService:@40721f08}]
		
    ...

The ActivityRecord [0x4070aa90] representing the Activity running in the Service client process has a reference to a ConnectionRecord
[0x40706ac0] as expected.

Note

When printed an ActivityRecord identifies itself as a HistoryRecord for reasons best known to itself.

2.3 ServiceRecord

    ...

      * ServiceRecord{406f89b8 xper.service.fastnet/.FastnetService}
        intent={act=xper.service.fastnet.ACTION_FASTNET}
        packageName=xper.service.fastnet
        processName=xper.service.fastnet
        baseDir=/data/app/xper.service.fastnet-2.apk
    resDir=/data/app/xper.service.fastnet-2.apk
        dataDir=/data/data/xper.service.fastnet
        app=ProcessRecord{4076e000 301:xper.service.fastnet/10053}
        createTime=-58s92ms lastActivity=-58s696ms
         executingStart=-58s690ms restartTime=-58s696ms
        Bindings:
        * IntentBindRecord{40704020}:
          intent={act=xper.service.fastnet.ACTION_FASTNET}
          binder=android.os.BinderProxy@4069ce20
          requested=true received=true hasBound=true doRebind=false
          * Client AppBindRecord{407040d0 ProcessRecord{4067a100 293:xper.service.fastnet.client/10054}}
            Per-process Connections:
              ConnectionRecord{40706ac0 xper.service.fastnet/.FastnetService:@40721f08}
        All Connections:
          ConnectionRecord{40706ac0 xper.service.fastnet/.FastnetService:@40721f08}

The ServiceRecord [0x406f89b8] representing the Fastnet Service is present and it references a single IntentBindRecord [0x4067a100] as expected.

The IntentBindRecord [0x4067a100] references a single AppBindRecord [0x407040d0] as expected.

The AppBindRecord [0x407040d0] references the ProcessRecord [0x4067a100] which represents the Fastnet Service client and has a single reference to a ConnectionRecord [0x40706ac0] as expected,

2.4 ConnectionRecord

    Connection bindings to services:
    * ConnectionRecord{40706ac0 xper.service.fastnet/.FastnetService:@40721f08}
      binding=AppBindRecord{407040d0 xper.service.fastnet/.FastnetService:xper.service.fastnet.client}
      activity=HistoryRecord{407ad2d8 xper.service.fastnet.client/.FastnetClientActivity}
      conn=android.os.BinderProxy@40721f08 flags=0x1
	
    ...

The ConnectionRecord [0x40706ac0] references the ActivityRecord [0x407040d0] for the FastnetClientActivity and the AppBindRecord [0x407040d0] as expected.

3.0 Java Heap State

In this case we already have the dumpsys output but we can, as in the Service client and Service cases, also use the state of the Java heap of the System process after the client has bound to the Fastnet Service to double-check.

As in the two previous case we can automatically generate a graph which shows the relationships between the objects in the heap.

This graph was generated using the ServiceRecord object [0x406f89b8] representing the Fastnet Service as the root.

The greyed out nodes have been filtered, that is, one or more instance variables have been omitted. This makes the graph only marginally less complicated in this case.

The complexity of the graph makes it difficult to see but it does confirm the output from dumpsys.


Copyright (c) 2012 By Simon Lewis. All Rights Reserved.

May 21, 2012

Adventures In Distributed Garbage Collection: Part Ten – Post Bind Service State

Filed under: Android, Android Internals, Java, Mobile Java, Services — Tags: , , , , — Simon Lewis @ 6:00 am

1.0 Object State

As in the Service client case we can use the source code to derive a diagram which shows the Service related objects which should exist in the Service process and their relationships once the Service client has bound to the Service.

2.0 Java Heap State

We can use the state of the Java heap of the Service process once the client has bound to the Service to check whether the code that actually runs in this case is actually doing what we think it is.

We can obtain a dump of the Java heap state and examine it interactively using a tool like MAT.

Alternatively we can automatically generate a graph which shows the relationships between the objects in the heap graphically.

This graph was generated using the FastnetService object [0x40517518] as the root.

The greyed out nodes have been filtered, that is, one or more instance variables have been omitted. This makes the graph a bit less complicated.

Again, although it is not exactly obvious the heap does contain the Service related objects and state we would expect.


Copyright (c) 2012 By Simon Lewis. All Rights Reserved.

May 20, 2012

Adventures In Distributed Garbage Collection: Part Nine – Post Bind Service Client State

Filed under: Android, Android Internals, Java, Mobile Java, Services — Tags: , , , , — Simon Lewis @ 6:00 am

1.0 Object State

We can use the source code to derive a diagram which shows the Service related objects which should exist in the Service client process and their
relationships following a successful call to bindService().

2.0 Java Heap State

We can use the state of the Java heap of the Service client process following the call to bindService() to check whether the code that runs
is actually doing what we think it is.

We can obtain a dump of the Java heap state and examine it interactively using a tool like MAT.

Alternatively we can generate a graph from the dump of the Java heap state which shows the relationships between the objects graphically.

This graph was generated using the LoadedApk object [0x40513e90] as the root.

The greyed out nodes have been filtered, that is, one or more instance variables have been omitted. This makes the graph a bit less complicated.

Although it is not exactly obvious the following objects are present in the generated object graph as expected

  • ServiceDispatcher [0x405213b8]

  • ConnectionInfo [0x40521b40]

  • InnerConnection [0x405216b8]

  • FastnetServiceConnection [0x405211f0]

and the relationships between them are also as expected.


Copyright (c) 2012 By Simon Lewis. All Rights Reserved.

Older Posts »

Blog at WordPress.com.

%d bloggers like this: