0

I have a strange issue going on, I have created an Android service, and a widget that consumes it. I have some complex objects that are sent between the two via AIDL. I have no problems registering my callbacks, and the service is populating the data correctly and sending it to the callbacks, but when I put a breakpoint on the writeToParcel method, and activate my debugger on the widget, I notice that the method is serializing the default class values, and not the ones the service populated it with. Does anyone have any suggestions for figuring out what is going on.

Here is my object:

Node.java:

package my.unique.package.name.service;


import java.util.ArrayList;
import java.util.HashMap;

import android.os.Parcel;
import android.os.Parcelable;

public class Node implements Parcelable {
    protected int mNodeId;
    protected double mBatteryLevel = -1;
    protected double mBatteryTemp;
    protected String mRadioName;
    protected String mIPAddress;
    protected String mFirmwareVersion;
    protected String mModel;
    protected String mSerialNumber;
    protected String mUptime;
    protected double mTemp;
    protected double mInputVoltage;
    protected double mBatteryClockVoltage;
    protected String mSource;
    protected double mLatitude;
    protected double mLongitude;
    protected double mAltitude;
    protected String mFixType;
    protected int    mSatellites;
    protected String mNTPServer;

    protected ArrayList<Neighbor> mNeighbors;
    protected HashMap<String, String> mManagedNodes;
    protected HashMap<String, String> mOtherNodes;

    public Node()
    {

    }
    /**
     * Node Constructor for Parcelable Interface
     * 
     * @param in
     *            Parcel in
     */
    public Node(Parcel in) {
        String[] data = new String[19];
        in.readStringArray(data);
        mNodeId = Integer.parseInt(data[0]);
        mBatteryLevel = Double.parseDouble(data[1]);
        mBatteryTemp = Double.parseDouble(data[2]);
        mRadioName = data[3];
        mIPAddress = data[4];
        mFirmwareVersion = data[5];
        mModel = data[6];
        mSerialNumber = data[7];
        mUptime = data[8];
        mTemp = Double.parseDouble(data[9]);
        mInputVoltage = Double.parseDouble(data[10]);
        mBatteryClockVoltage = Double.parseDouble(data[11]);
        mSource = data[12];
        mLatitude= Double.valueOf(data[13]);
        mLongitude = Double.valueOf(data[14]);
        mAltitude = Double.valueOf(data[15]);
        mFixType = data[16];
        mSatellites = Integer.valueOf(data[17]);
        mNTPServer = data[18];
    }

    //Removed public get/set methods...

    public static final Parcelable.Creator<Node> CREATOR = new Parcelable.Creator<Node>() {
        public Node createFromParcel(Parcel in) {
            return new Node(in);
        }

        public Node[] newArray(int size) {
            return new Node[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeStringArray(new String[] { String.valueOf(mNodeId),
                String.valueOf(mBatteryLevel), String.valueOf(mBatteryTemp),
                mRadioName, mIPAddress, mFirmwareVersion, mModel,
                mSerialNumber, mUptime, String.valueOf(mTemp),
                String.valueOf(mInputVoltage),
                String.valueOf(mBatteryClockVoltage),
                mSource, String.valueOf(mLatitude), String.valueOf(mLongitude), 
                String.valueOf(mAltitude), mFixType, 
                String.valueOf(mSatellites), mNTPServer});
    }
}

Node.aidl:

package my.unique.package.namespace.service;

parcelable Node;

This is how the service notifies callbacks

private void notifyCallbacksOfUpdate(Node updated) {
        final int N = mNodeUpdatedCallbacks.beginBroadcast();
        for (int i = 0; i < N; i++) {
            try {
                mNodeUpdatedCallbacks.getBroadcastItem(i).NodeUpdated(updated);
            } catch (RemoteException e) {
                // The RemoteCallbackList will take care of removing
                // the dead object for us.
            }
        }
        mNodeUpdatedCallbacks.finishBroadcast();
    }

EDIT: Additional Code from consumer

MyApplication.java

public void bindConnection()
    {
        if (mServiceConnection == null) {
            // create connection to service
            mServiceConnection = new MyServiceConnection(this);
            Intent i = new Intent("my.unique.package.IService");
            boolean bound = bindService(i, mConnection,
                    Context.BIND_AUTO_CREATE);
            Log.v("Service Bound: ", String.valueOf(bound));
        }
    }

Connection Class

import my.unique.package.service.IService;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;

public class MyServiceConnection implements ServiceConnection {

    private IService service;
    private Context mContext;
    public MyServiceConnection(Context context)
    {
        mContext = context;
    }

    public void onServiceConnected(ComponentName name, IBinder boundService) {
      service = IService.Stub.asInterface(boundService);
      if(service != null)
      {
          mContext.sendBroadcast(new Intent(MyApplication.BOUND_TO_SERVICE));
      }
    }

    public void onServiceDisconnected(ComponentName name) {
      service = null;
    }

    protected IService getService()
    {
        return service;
    }
  }

In here is where I get notified of updates to the node, service sends correct info, but here is where it receives the default values. The node object class is in the same jar as the service and widget, just different packages, does the AIDL need to be copied to all of the packages, or can it reference it cross packages.

private INodeUpdatedCallback nodeUpdated = new INodeUpdatedCallback.Stub() {

@Override
public void NodeUpdated(Node nodeInfo) throws RemoteException {
    Log.v("Node Updated: ", nodeInfo.getRadioIP());
    if (nodeInfo.getIP().equalsIgnoreCase(mIp)
            && requestedLogin) {
        requestedLogin = false;
        ViewSwitcher vs = (ViewSwitcher) findViewById(R.id.vsSettings);
        if (vs.getDisplayedChild() != VIEW_SETTINGS) {
            vs.setInAnimation(AnimationUtils.loadAnimation(
                    WaveRelaySettingsActivity.this, R.anim.fade_in));
            vs.setOutAnimation(AnimationUtils.loadAnimation(
                    WaveRelaySettingsActivity.this, R.anim.fade_out));
            vs.setDisplayedChild(VIEW_SETTINGS);

        }
    }
}
};

Aidl for stub of callback

package my.unique.namespace.service;
import my.unique.namespace.service.Node;

oneway interface INodeUpdatedCallback {
    void NodeUpdated(out Node nodeInfo);
}
sergej shafarenka
  • 20,071
  • 7
  • 67
  • 86
bhawkins
  • 326
  • 2
  • 11

1 Answers1

3

I believe you need to change out to in in your AIDL.

package my.unique.namespace.service;
import my.unique.namespace.service.Node;

oneway interface INodeUpdatedCallback {
    void NodeUpdated(in Node nodeInfo);
}
sergej shafarenka
  • 20,071
  • 7
  • 67
  • 86
  • 1
    The prefix is used as a hint for performance optimization. "In" means this is just an input parameter for the method, and it won't be modified inside this method. "Out" mean it won't be read, but modified. That's why it was initialized with default values, because system does not expect it to be read anyway. "Inout" means it will be both read and modified. Modified values will be sent back to the service. – sergej shafarenka Jul 07 '14 at 17:51