org.droid2droid.internal.AbstractRemoteAndroidImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.droid2droid.internal.AbstractRemoteAndroidImpl.java

Source

/******************************************************************************
 *
 * droid2droid - Distributed Android Framework
 * ==========================================
 *
 * Copyright (C) 2012 by Atos (http://www.http://atos.net)
 * http://www.droid2droid.org
 *
 ******************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
******************************************************************************/
package org.droid2droid.internal;

import static org.droid2droid.internal.Constants.BUFFER_SIZE_FOR_DOWNLOAD;
import static org.droid2droid.internal.Constants.D;
import static org.droid2droid.internal.Constants.E;
import static org.droid2droid.internal.Constants.PREFIX_LOG;
import static org.droid2droid.internal.Constants.TAG_CLIENT_BIND;
import static org.droid2droid.internal.Constants.TAG_INSTALL;
import static org.droid2droid.internal.Constants.TAG_SECURITY;
import static org.droid2droid.internal.Constants.UPDATE_PARCEL;
import static org.droid2droid.internal.Constants.V;
import static org.droid2droid.internal.Constants.W;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.UnknownHostException;
import java.util.WeakHashMap;
import java.util.concurrent.CancellationException;

import org.apache.http.MethodNotSupportedException;
import org.droid2droid.Droid2DroidManager;
import org.droid2droid.RemoteAndroid;
import org.droid2droid.RemoteAndroidInfo;
import org.droid2droid.internal.Messages.Type;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build.VERSION;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;

// NAT file:///home/pprados/Bureau/J%20ICE/index.html
/** @hide */
public abstract class AbstractRemoteAndroidImpl implements RemoteAndroid, IRemoteAndroid, IBinder {
    public static final int BIND_OID = 1;

    public static final int FINALIZE_OID = 2;

    public static final int PING_BINDER = 3;

    public static final int IS_BINDER_ALIVE = 4;

    public static final int UNBIND_SRV = 5;

    public static final int STATUS_REFUSE_CONNECTION_MODE = -1;
    public static final int STATUS_REFUSE_ANONYMOUS = -2;
    public static final int STATUS_REFUSE_NO_BOUND = -3;
    public static final int STATUS_INVALIDE_COOKIE = -4;
    public static final int STATUS_OK = 0;

    @SuppressWarnings("unused")
    final private WeakReference<IRemoteAndroid> mSelf = new WeakReference<IRemoteAndroid>(this);

    public Droid2DroidManager mManager;
    protected Uri mUri;

    protected AbstractRemoteAndroidImpl(Droid2DroidManager manager, Uri uri) {
        mManager = manager;
        mUri = uri;
    }

    protected abstract int getConnectionId();

    public RemoteAndroidInfoImpl mInfo;

    @Override
    public RemoteAndroidInfo getInfos() {
        return mInfo;
    }

    class Rebind {
        RemoteBinderProxy proxy;
        Intent service;
        ComponentName name;
        ServiceConnection conn;
        int flags;
    };

    @Override
    public void disconnect(int connid) {
        synchronized (mBinders) {
            for (Binded binded : mBinders.values()) {
                binded.conn.onServiceDisconnected(binded.name);
            }
            mBinders.clear();
        }
        if (mDeathRecipient != null)
            mDeathRecipient.binderDied();
    }

    @Override
    public boolean isClosed() {
        return mBinders.size() == 0;
    }

    // --- IBinder interface ----------------------------------------------
    protected DeathRecipient mDeathRecipient; // FIXME: Use mDeathRecipient 

    private IInterface mOwner;

    private String mDescriptor;

    @Override
    public void dump(FileDescriptor fd, String[] args) throws RemoteException {
        throw new UnsupportedOperationException();
    }

    //@Override
    @Override
    public void dumpAsync(FileDescriptor arg0, String[] arg1) throws RemoteException {
        RemoteException re = new RemoteException();
        re.initCause(new MethodNotSupportedException("dumpAsync"));
        throw re;
    }

    /**
     * Convenience method for associating a specific interface with the Binder.
     * After calling, queryLocalInterface() will be implemented for you to
     * return the given owner IInterface when the corresponding descriptor is
     * requested.
     */
    public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

    @Override
    public String getInterfaceDescriptor() throws RemoteException {
        return mDescriptor;
    }

    @Override
    public IInterface queryLocalInterface(String descriptor) {
        return mOwner;
    }

    @Override
    public void linkToDeath(DeathRecipient recipient, int flags) {
        // Ignore flags;
        mDeathRecipient = recipient;
    }

    @Override
    public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
        // Ignore flags
        assert (mDeathRecipient == recipient);
        mDeathRecipient = null;
        return true;
    }

    // -------------------------------------------------

    private long mExecuteTimeout = 10000L; // FIXME

    @Override
    public void setExecuteTimeout(long bindTimeout) {
        mExecuteTimeout = bindTimeout;
    }

    /** Send transactions to remote cloud. */
    @Override
    public int bindOID(int connid, int serviceConnectionOID, Intent intent, int flags, ComponentName[] name,
            long timeout) throws RemoteException {
        if (D)
            Log.d(TAG_CLIENT_BIND, PREFIX_LOG + "BindOID...");
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {

            data.writeInt(serviceConnectionOID);
            NormalizeIntent.writeIntent(intent, data, 0);
            data.writeInt(flags);
            transactRemoteAndroid(connid, BIND_OID, data, reply, 0, timeout);
            if (D)
                Log.d(TAG_CLIENT_BIND, PREFIX_LOG + "BindOID ok");
            name[0] = (ComponentName) reply.readParcelable(ComponentName.class.getClassLoader());
            return reply.readInt();
        } finally {
            if (data != null)
                data.recycle();
            if (reply != null)
                reply.recycle();
        }
    }

    @Override
    public boolean unbindService(ServiceConnection conn) {
        synchronized (mBinders) {
            Binded binder = mBinders.get(conn);
            if (binder != null) {
                if (D)
                    Log.d(TAG_CLIENT_BIND, PREFIX_LOG + "UnBindOID...");
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                try {

                    try {
                        data.writeInt(System.identityHashCode(conn));
                        transactRemoteAndroid(getConnectionId(), UNBIND_SRV, data, reply, 0, mExecuteTimeout);
                        if (D)
                            Log.d(TAG_CLIENT_BIND, PREFIX_LOG + "UnBindOID ok");
                    } catch (RemoteException e) {
                        if (W && !D)
                            Log.w(TAG_CLIENT_BIND, "Error when unbind (" + e.getMessage() + ")");
                        if (D)
                            Log.w(TAG_CLIENT_BIND, "Errer when unbind", e);
                    }
                } finally {
                    if (data != null)
                        data.recycle();
                    if (reply != null)
                        reply.recycle();
                }
                mBinders.remove(conn);
            } else
                return false;
        }

        return true;
    }

    @Override
    public void finalizeOID(int connid, int oid, long timeout) {
        if (oid == 0) {
            if (E)
                Log.e(TAG_CLIENT_BIND, PREFIX_LOG + "FinalizeOID with oid=0!");
            return;
        }
        if (!isBinderAlive())
            return;
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            data.writeInt(oid);
            transactRemoteAndroid(getConnectionId(), FINALIZE_OID, data, reply, 0, timeout);
            return;
        } catch (RemoteException e) {
            if (V)
                Log.v(TAG_CLIENT_BIND, PREFIX_LOG + "FinalizeOID", e);
        } finally {
            if (data != null)
                data.recycle();
            if (reply != null)
                reply.recycle();
        }

    }

    @Override
    public boolean isBinderAlive(int connid, int oid, long timeout) throws RemoteException {
        if (D)
            Log.d(TAG_CLIENT_BIND, PREFIX_LOG + "isBinderAlive...");
        if (!isBinderAlive()) {
            if (E)
                Log.e(TAG_CLIENT_BIND, PREFIX_LOG + "Binder is not alive");
            throw new RemoteException();
        }
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            data.writeInt(oid);
            transactRemoteAndroid(getConnectionId(), IS_BINDER_ALIVE, data, reply, 0, timeout);
            return reply.readByte() == 1;
        } finally {
            if (data != null)
                data.recycle();
            if (reply != null)
                reply.recycle();
        }
    }

    @Override
    public boolean pingBinder(int connid, int oid, long timeout) throws RemoteException {
        if (D)
            Log.d(TAG_CLIENT_BIND, PREFIX_LOG + "pingBinder...");
        if (!isBinderAlive())
            return false;
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            data.writeInt(oid);
            transactRemoteAndroid(getConnectionId(), PING_BINDER, data, reply, 0, timeout);
            return reply.readByte() == 1;
        } finally {
            if (data != null)
                data.recycle();
            if (reply != null)
                reply.recycle();
        }
    }

    static class Binded {
        ComponentName name;
        ServiceConnection conn;
        RemoteBinderProxy proxy;
    }

    private final WeakHashMap<ServiceConnection, Binded> mBinders = new WeakHashMap<ServiceConnection, Binded>();

    @Override
    public boolean bindService(final Intent service, final ServiceConnection conn, final int flags) {

        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... v) {
                try {
                    ComponentName[] name = new ComponentName[1];
                    int oid = bindOID(getConnectionId(), System.identityHashCode(conn), service, flags, name,
                            mExecuteTimeout);
                    if (oid == -1)
                        PostTools.postServiceDisconnected(conn, name[0]);
                    else {
                        RemoteBinderProxy proxy = new RemoteBinderProxy(AbstractRemoteAndroidImpl.this, oid);
                        Binded disconnect = new Binded();
                        disconnect.name = name[0];
                        disconnect.conn = conn;
                        disconnect.proxy = proxy;
                        synchronized (mBinders) {
                            mBinders.put(conn, disconnect);
                        }
                        PostTools.postServiceConnected(conn, name[0], proxy);
                    }
                } catch (SecurityException e) {
                    if (E)
                        Log.e(TAG_SECURITY, PREFIX_LOG + "Refuse connexion", e);
                    close();
                } catch (RemoteException e) {
                    PostTools.postServiceDisconnected(conn, null); // FIXME: null en ComponentName ?
                }
                return null;
            }
        }.execute();
        return true;
    }

    public abstract Pair<RemoteAndroidInfoImpl, Long> connectWithAuthent(Uri uri, Type type, int flags,
            long timeout) throws UnknownHostException, IOException, RemoteException;

    private static final void postPublishProgress(final PublishListener listener, final int progress) {
        Droid2DroidManagerImpl.sHandler.post(new Runnable() {
            @Override
            public void run() {
                listener.onProgress(progress);
            }
        });
    }

    private static final void postPublishError(final PublishListener listener, final Throwable error) {
        Droid2DroidManagerImpl.sHandler.post(new Runnable() {
            @Override
            public void run() {
                listener.onError(error);
            }
        });
    }

    private static final void postPublishFinish(final PublishListener listener, final int status) {
        Droid2DroidManagerImpl.sHandler.post(new Runnable() {
            @Override
            public void run() {
                listener.onFinish(status);
            }
        });
    }

    @Override
    public void pushMe(Context context, PublishListener listener, int flags, final long timeout)
            throws RemoteException, IOException {
        pushMe(context, listener, BUFFER_SIZE_FOR_DOWNLOAD, flags, timeout);
    }

    public void pushMe(final Context context, final PublishListener listener, final int bufsize, final int flags,
            final long timeout) throws RemoteException, IOException {
        new AsyncTask<PublishListener, Integer, Object>() {

            private PublishListener listener;

            @Override
            protected Object doInBackground(PublishListener... params) {
                listener = params[0];
                FileInputStream in = null;
                int fd = -1;
                int connid = -1;
                int s;
                byte[] buf = new byte[bufsize];
                byte[] signature = null;
                try {
                    PackageManager pm = context.getPackageManager();
                    ApplicationInfo info = Droid2DroidManagerImpl.sAppInfo;
                    String label = context.getString(info.labelRes);
                    PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
                    File filename = new File(info.publicSourceDir);

                    int versionCode = pi.versionCode;
                    signature = pi.signatures[0].toByteArray();

                    long length = filename.length();
                    connid = getConnectionId();
                    if (V)
                        Log.v(TAG_INSTALL, PREFIX_LOG + "CLI propose apk " + info.packageName);
                    fd = proposeApk(connid, label, info.packageName, versionCode, signature, length, flags,
                            timeout);
                    if (fd > 0) {
                        // TODO: ask sender before send datas
                        if (V)
                            Log.v(TAG_INSTALL, PREFIX_LOG + "CLI it's accepted");
                        in = new FileInputStream(filename);
                        StringBuilder prog = new StringBuilder();
                        // TODO: multi thread for optimize read latency ?
                        long timeoutSendFile = 30000; // FIXME: Time out pour send file en dur.
                        long pos = 0;
                        if (V)
                            Log.v(TAG_INSTALL, PREFIX_LOG + "CLI send file");
                        while ((s = in.read(buf, 0, buf.length)) > 0) {
                            if (V) {
                                prog.append('*');
                                if (V)
                                    Log.v(TAG_INSTALL, PREFIX_LOG + "" + prog.toString()); // TODO: A garder ?
                            }
                            if (!sendFileData(connid, fd, buf, s, pos, length, timeoutSendFile)) {
                                if (E)
                                    Log.e(TAG_INSTALL, PREFIX_LOG + "Impossible to send file data");
                                throw new RemoteException();
                            }
                            pos += s;
                            publishProgress((int) ((double) pos / length * 10000L));
                        }
                        if (V)
                            Log.v(TAG_INSTALL, PREFIX_LOG + "CLI send file done");
                        if (installApk(connid, label, fd, flags, timeout)) {
                            if (V)
                                Log.v(TAG_INSTALL, PREFIX_LOG + "CLI apk is installed");
                            return 1;
                        } else {
                            if (V)
                                Log.v(TAG_INSTALL, PREFIX_LOG + "CLI install apk is canceled");
                            return new CancellationException("Install apk " + pi.packageName + " is canceled");
                        }
                    } else {
                        if (V)
                            Log.v(TAG_INSTALL, PREFIX_LOG + "CLI install apk is accepted (" + fd + ")");
                        return fd;
                    }
                } catch (NameNotFoundException e) {
                    cancelCurrentUpload(timeout, fd, connid);
                    return e;
                } catch (IOException e) {
                    cancelCurrentUpload(timeout, fd, connid);
                    return e;
                } catch (RemoteException e) {
                    if (D)
                        Log.d(TAG_INSTALL, "Impossible to push apk (" + e.getMessage() + ")", e);
                    cancelCurrentUpload(timeout, fd, connid);
                    return e;
                } finally {
                    if (in != null) {
                        try {
                            in.close();
                        } catch (IOException e) {
                            // Ignore
                        }
                    }
                }
            }

            private void cancelCurrentUpload(final long timeout, int fd, int connid) {
                if (connid != -1 && fd != -1) {
                    try {
                        cancelFileData(connid, fd, timeout);
                    } catch (RemoteException e1) {
                        // Ignore
                    }
                }
            }

            @Override
            protected void onProgressUpdate(Integer... values) {
                postPublishProgress(listener, values[0]);
            }

            @Override
            protected void onPostExecute(Object result) {
                if (result instanceof Throwable) {
                    postPublishError(listener, (Throwable) result);
                } else
                    postPublishFinish(listener, ((Integer) result).intValue());
            }
        }.execute(listener);
    }

    protected final void updateData(Parcel data) // TODO: a placer plutot cot serveur
    {
        if (UPDATE_PARCEL) {
            int v = VERSION.SDK_INT;
            data.setDataPosition(0);
            if (v >= 10) // Gingerbread_MR1+
            {
                data.readInt();
            }
            String enforceInterfaceName = data.readString(); // Read the interface name (see Parcel.cpp)
            assert (enforceInterfaceName != null);
            byte[] bufDatas = data.marshall(); // Return all the buffer (with the specific enforceInterface
            int startDatas = data.dataPosition(); // Position after the first string

            // Create a new one with interface name + buffers
            Parcel p = Parcel.obtain();
            p.setDataPosition(0);
            p.writeString(enforceInterfaceName);
            int sizeInterface = p.dataPosition();
            byte[] bufInterface = p.marshall(); // Part of buffer only for the string
            p.recycle();

            // Extract the rest of the buffer
            byte[] result = new byte[sizeInterface + bufDatas.length - startDatas];
            System.arraycopy(bufInterface, 0, result, 0, sizeInterface);
            System.arraycopy(bufDatas, startDatas, result, sizeInterface, bufDatas.length - startDatas);
            data.unmarshall(result, 0, result.length);
        }
    }

    protected final void updateReply(Parcel reply) {
    }
    // -------------- Pairing
}