1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "LocalSocketImpl"
18
19#include "JNIHelp.h"
20#include "jni.h"
21#include "utils/Log.h"
22#include "utils/misc.h"
23
24#include <stdio.h>
25#include <string.h>
26#include <sys/types.h>
27#include <sys/socket.h>
28#include <sys/un.h>
29#include <arpa/inet.h>
30#include <netinet/in.h>
31#include <stdlib.h>
32#include <errno.h>
33#include <unistd.h>
34#include <sys/ioctl.h>
35
36#include <cutils/sockets.h>
37#include <netinet/tcp.h>
38#include <ScopedUtfChars.h>
39
40namespace android {
41
42static jfieldID field_inboundFileDescriptors;
43static jfieldID field_outboundFileDescriptors;
44static jclass class_Credentials;
45static jclass class_FileDescriptor;
46static jmethodID method_CredentialsInit;
47
48/* private native void connectLocal(FileDescriptor fd,
49 * String name, int namespace) throws IOException
50 */
51static void
52socket_connect_local(JNIEnv *env, jobject object,
53                        jobject fileDescriptor, jstring name, jint namespaceId)
54{
55    int ret;
56    int fd;
57
58    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
59
60    if (env->ExceptionOccurred() != NULL) {
61        return;
62    }
63
64    ScopedUtfChars nameUtf8(env, name);
65
66    ret = socket_local_client_connect(
67                fd,
68                nameUtf8.c_str(),
69                namespaceId,
70                SOCK_STREAM);
71
72    if (ret < 0) {
73        jniThrowIOException(env, errno);
74        return;
75    }
76}
77
78#define DEFAULT_BACKLOG 4
79
80/* private native void bindLocal(FileDescriptor fd, String name, namespace)
81 * throws IOException;
82 */
83
84static void
85socket_bind_local (JNIEnv *env, jobject object, jobject fileDescriptor,
86                jstring name, jint namespaceId)
87{
88    int ret;
89    int fd;
90
91    if (name == NULL) {
92        jniThrowNullPointerException(env, NULL);
93        return;
94    }
95
96    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
97
98    if (env->ExceptionOccurred() != NULL) {
99        return;
100    }
101
102    ScopedUtfChars nameUtf8(env, name);
103
104    ret = socket_local_server_bind(fd, nameUtf8.c_str(), namespaceId);
105
106    if (ret < 0) {
107        jniThrowIOException(env, errno);
108        return;
109    }
110}
111
112/* private native void listen_native(int fd, int backlog) throws IOException; */
113static void
114socket_listen (JNIEnv *env, jobject object, jobject fileDescriptor, jint backlog)
115{
116    int ret;
117    int fd;
118
119    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
120
121    if (env->ExceptionOccurred() != NULL) {
122        return;
123    }
124
125    ret = listen(fd, backlog);
126
127    if (ret < 0) {
128        jniThrowIOException(env, errno);
129        return;
130    }
131}
132
133/*    private native FileDescriptor
134**    accept (FileDescriptor fd, LocalSocketImpl s)
135**                                   throws IOException;
136*/
137static jobject
138socket_accept (JNIEnv *env, jobject object, jobject fileDescriptor, jobject s)
139{
140    union {
141        struct sockaddr address;
142        struct sockaddr_un un_address;
143    } sa;
144
145    int ret;
146    int retFD;
147    int fd;
148    socklen_t addrlen;
149
150    if (s == NULL) {
151        jniThrowNullPointerException(env, NULL);
152        return NULL;
153    }
154
155    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
156
157    if (env->ExceptionOccurred() != NULL) {
158        return NULL;
159    }
160
161    do {
162        addrlen = sizeof(sa);
163        ret = accept(fd, &(sa.address), &addrlen);
164    } while (ret < 0 && errno == EINTR);
165
166    if (ret < 0) {
167        jniThrowIOException(env, errno);
168        return NULL;
169    }
170
171    retFD = ret;
172
173    return jniCreateFileDescriptor(env, retFD);
174}
175
176/* private native void shutdown(FileDescriptor fd, boolean shutdownInput) */
177
178static void
179socket_shutdown (JNIEnv *env, jobject object, jobject fileDescriptor,
180                    jboolean shutdownInput)
181{
182    int ret;
183    int fd;
184
185    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
186
187    if (env->ExceptionOccurred() != NULL) {
188        return;
189    }
190
191    ret = shutdown(fd, shutdownInput ? SHUT_RD : SHUT_WR);
192
193    if (ret < 0) {
194        jniThrowIOException(env, errno);
195        return;
196    }
197}
198
199static bool
200java_opt_to_real(int optID, int* opt, int* level)
201{
202    switch (optID)
203    {
204        case 4098:
205            *opt = SO_RCVBUF;
206            *level = SOL_SOCKET;
207            return true;
208        case 4097:
209            *opt = SO_SNDBUF;
210            *level = SOL_SOCKET;
211            return true;
212        case 4102:
213            *opt = SO_SNDTIMEO;
214            *level = SOL_SOCKET;
215            return true;
216        case 128:
217            *opt = SO_LINGER;
218            *level = SOL_SOCKET;
219            return true;
220        case 1:
221            *opt = TCP_NODELAY;
222            *level = IPPROTO_TCP;
223            return true;
224        case 4:
225            *opt = SO_REUSEADDR;
226            *level = SOL_SOCKET;
227            return true;
228
229    }
230    return false;
231}
232
233static jint
234socket_getOption(JNIEnv *env, jobject object, jobject fileDescriptor, jint optID)
235{
236    int ret, value;
237    int opt, level;
238    int fd;
239
240    socklen_t size = sizeof(int);
241
242    if (!java_opt_to_real(optID, &opt, &level)) {
243        jniThrowIOException(env, -1);
244        return 0;
245    }
246
247    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
248
249    if (env->ExceptionOccurred() != NULL) {
250        return 0;
251    }
252
253    switch (opt)
254    {
255        case SO_LINGER:
256        {
257            struct linger lingr;
258            size = sizeof(lingr);
259            ret = getsockopt(fd, level, opt, &lingr, &size);
260            if (!lingr.l_onoff) {
261                value = -1;
262            } else {
263                value = lingr.l_linger;
264            }
265            break;
266        }
267        default:
268            ret = getsockopt(fd, level, opt, &value, &size);
269            break;
270    }
271
272
273    if (ret != 0) {
274        jniThrowIOException(env, errno);
275        return 0;
276    }
277
278    return value;
279}
280
281static void socket_setOption(
282        JNIEnv *env, jobject object, jobject fileDescriptor, jint optID,
283        jint boolValue, jint intValue) {
284    int ret;
285    int optname;
286    int level;
287    int fd;
288
289    if (!java_opt_to_real(optID, &optname, &level)) {
290        jniThrowIOException(env, -1);
291        return;
292    }
293
294    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
295
296    if (env->ExceptionOccurred() != NULL) {
297        return;
298    }
299
300    switch (optname) {
301        case SO_LINGER: {
302            /*
303             * SO_LINGER is special because it needs to use a special
304             * "linger" struct as well as use the incoming boolean
305             * argument specially.
306             */
307            struct linger lingr;
308            lingr.l_onoff = boolValue ? 1 : 0; // Force it to be 0 or 1.
309            lingr.l_linger = intValue;
310            ret = setsockopt(fd, level, optname, &lingr, sizeof(lingr));
311            break;
312        }
313        case SO_SNDTIMEO: {
314            /*
315             * SO_TIMEOUT from the core library gets converted to
316             * SO_SNDTIMEO, but the option is supposed to set both
317             * send and receive timeouts. Note: The incoming timeout
318             * value is in milliseconds.
319             */
320            struct timeval timeout;
321            timeout.tv_sec = intValue / 1000;
322            timeout.tv_usec = (intValue % 1000) * 1000;
323
324            ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,
325                    (void *)&timeout, sizeof(timeout));
326
327            if (ret == 0) {
328                ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO,
329                        (void *)&timeout, sizeof(timeout));
330            }
331
332            break;
333        }
334        default: {
335            /*
336             * In all other cases, the translated option level and
337             * optname may be used directly for a call to setsockopt().
338             */
339            ret = setsockopt(fd, level, optname, &intValue, sizeof(intValue));
340            break;
341        }
342    }
343
344    if (ret != 0) {
345        jniThrowIOException(env, errno);
346        return;
347    }
348}
349static jint socket_pending (JNIEnv *env, jobject object,
350        jobject fileDescriptor)
351{
352    int fd;
353
354    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
355
356    if (env->ExceptionOccurred() != NULL) {
357        return (jint)-1;
358    }
359
360    int pending;
361    int ret = ioctl(fd, TIOCOUTQ, &pending);
362
363    // If this were a non-socket fd, there would be other cases to worry
364    // about...
365
366    //ALOGD("socket_pending, ioctl ret:%d, pending:%d", ret, pending);
367    if (ret < 0) {
368        jniThrowIOException(env, errno);
369        return (jint) 0;
370    }
371
372    return (jint)pending;
373}
374static jint socket_available (JNIEnv *env, jobject object,
375        jobject fileDescriptor)
376{
377    int fd;
378
379    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
380
381    if (env->ExceptionOccurred() != NULL) {
382        return (jint)-1;
383    }
384
385#if 1
386    int avail;
387    int ret = ioctl(fd, FIONREAD, &avail);
388
389    // If this were a non-socket fd, there would be other cases to worry
390    // about...
391
392    if (ret < 0) {
393        jniThrowIOException(env, errno);
394        return (jint) 0;
395    }
396
397    return (jint)avail;
398#else
399// there appears to be a bionic bug that prevents this version from working.
400
401    ssize_t ret;
402    struct msghdr msg;
403
404    memset(&msg, 0, sizeof(msg));
405
406    do {
407        ret = recvmsg(fd, &msg, MSG_PEEK | MSG_DONTWAIT | MSG_NOSIGNAL);
408    } while (ret < 0 && errno == EINTR);
409
410
411    // MSG_PEEK returns 0 on EOF and EWOULDBLOCK on none available
412    if (ret < 0 && errno == EWOULDBLOCK) {
413        return 0;
414    } if (ret < 0) {
415        jniThrowIOException(env, errno);
416        return -1;
417    }
418
419    return (jint)ret;
420#endif
421}
422
423/**
424 * Processes ancillary data, handling only
425 * SCM_RIGHTS. Creates appropriate objects and sets appropriate
426 * fields in the LocalSocketImpl object. Returns 0 on success
427 * or -1 if an exception was thrown.
428 */
429static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg)
430{
431    struct cmsghdr *cmsgptr;
432
433    for (cmsgptr = CMSG_FIRSTHDR(pMsg);
434            cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(pMsg, cmsgptr)) {
435
436        if (cmsgptr->cmsg_level != SOL_SOCKET) {
437            continue;
438        }
439
440        if (cmsgptr->cmsg_type == SCM_RIGHTS) {
441            int *pDescriptors = (int *)CMSG_DATA(cmsgptr);
442            jobjectArray fdArray;
443            int count
444                = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int));
445
446            if (count < 0) {
447                jniThrowException(env, "java/io/IOException",
448                    "invalid cmsg length");
449                return -1;
450            }
451
452            fdArray = env->NewObjectArray(count, class_FileDescriptor, NULL);
453
454            if (fdArray == NULL) {
455                return -1;
456            }
457
458            for (int i = 0; i < count; i++) {
459                jobject fdObject
460                        = jniCreateFileDescriptor(env, pDescriptors[i]);
461
462                if (env->ExceptionOccurred() != NULL) {
463                    return -1;
464                }
465
466                env->SetObjectArrayElement(fdArray, i, fdObject);
467
468                if (env->ExceptionOccurred() != NULL) {
469                    return -1;
470                }
471            }
472
473            env->SetObjectField(thisJ, field_inboundFileDescriptors, fdArray);
474
475            if (env->ExceptionOccurred() != NULL) {
476                return -1;
477            }
478        }
479    }
480
481    return 0;
482}
483
484/**
485 * Reads data from a socket into buf, processing any ancillary data
486 * and adding it to thisJ.
487 *
488 * Returns the length of normal data read, or -1 if an exception has
489 * been thrown in this function.
490 */
491static ssize_t socket_read_all(JNIEnv *env, jobject thisJ, int fd,
492        void *buffer, size_t len)
493{
494    ssize_t ret;
495    ssize_t bytesread = 0;
496    struct msghdr msg;
497    struct iovec iv;
498    unsigned char *buf = (unsigned char *)buffer;
499    // Enough buffer for a pile of fd's. We throw an exception if
500    // this buffer is too small.
501    struct cmsghdr cmsgbuf[2*sizeof(cmsghdr) + 0x100];
502
503    memset(&msg, 0, sizeof(msg));
504    memset(&iv, 0, sizeof(iv));
505
506    iv.iov_base = buf;
507    iv.iov_len = len;
508
509    msg.msg_iov = &iv;
510    msg.msg_iovlen = 1;
511    msg.msg_control = cmsgbuf;
512    msg.msg_controllen = sizeof(cmsgbuf);
513
514    do {
515        ret = recvmsg(fd, &msg, MSG_NOSIGNAL);
516    } while (ret < 0 && errno == EINTR);
517
518    if (ret < 0 && errno == EPIPE) {
519        // Treat this as an end of stream
520        return 0;
521    }
522
523    if (ret < 0) {
524        jniThrowIOException(env, errno);
525        return -1;
526    }
527
528    if ((msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) {
529        // To us, any of the above flags are a fatal error
530
531        jniThrowException(env, "java/io/IOException",
532                "Unexpected error or truncation during recvmsg()");
533
534        return -1;
535    }
536
537    if (ret >= 0) {
538        socket_process_cmsg(env, thisJ, &msg);
539    }
540
541    return ret;
542}
543
544/**
545 * Writes all the data in the specified buffer to the specified socket.
546 *
547 * Returns 0 on success or -1 if an exception was thrown.
548 */
549static int socket_write_all(JNIEnv *env, jobject object, int fd,
550        void *buf, size_t len)
551{
552    ssize_t ret;
553    struct msghdr msg;
554    unsigned char *buffer = (unsigned char *)buf;
555    memset(&msg, 0, sizeof(msg));
556
557    jobjectArray outboundFds
558            = (jobjectArray)env->GetObjectField(
559                object, field_outboundFileDescriptors);
560
561    if (env->ExceptionOccurred() != NULL) {
562        return -1;
563    }
564
565    struct cmsghdr *cmsg;
566    int countFds = outboundFds == NULL ? 0 : env->GetArrayLength(outboundFds);
567    int fds[countFds];
568    char msgbuf[CMSG_SPACE(countFds)];
569
570    // Add any pending outbound file descriptors to the message
571    if (outboundFds != NULL) {
572
573        if (env->ExceptionOccurred() != NULL) {
574            return -1;
575        }
576
577        for (int i = 0; i < countFds; i++) {
578            jobject fdObject = env->GetObjectArrayElement(outboundFds, i);
579            if (env->ExceptionOccurred() != NULL) {
580                return -1;
581            }
582
583            fds[i] = jniGetFDFromFileDescriptor(env, fdObject);
584            if (env->ExceptionOccurred() != NULL) {
585                return -1;
586            }
587        }
588
589        // See "man cmsg" really
590        msg.msg_control = msgbuf;
591        msg.msg_controllen = sizeof msgbuf;
592        cmsg = CMSG_FIRSTHDR(&msg);
593        cmsg->cmsg_level = SOL_SOCKET;
594        cmsg->cmsg_type = SCM_RIGHTS;
595        cmsg->cmsg_len = CMSG_LEN(sizeof fds);
596        memcpy(CMSG_DATA(cmsg), fds, sizeof fds);
597    }
598
599    // We only write our msg_control during the first write
600    while (len > 0) {
601        struct iovec iv;
602        memset(&iv, 0, sizeof(iv));
603
604        iv.iov_base = buffer;
605        iv.iov_len = len;
606
607        msg.msg_iov = &iv;
608        msg.msg_iovlen = 1;
609
610        do {
611            ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
612        } while (ret < 0 && errno == EINTR);
613
614        if (ret < 0) {
615            jniThrowIOException(env, errno);
616            return -1;
617        }
618
619        buffer += ret;
620        len -= ret;
621
622        // Wipes out any msg_control too
623        memset(&msg, 0, sizeof(msg));
624    }
625
626    return 0;
627}
628
629static jint socket_read (JNIEnv *env, jobject object, jobject fileDescriptor)
630{
631    int fd;
632    int err;
633
634    if (fileDescriptor == NULL) {
635        jniThrowNullPointerException(env, NULL);
636        return (jint)-1;
637    }
638
639    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
640
641    if (env->ExceptionOccurred() != NULL) {
642        return (jint)0;
643    }
644
645    unsigned char buf;
646
647    err = socket_read_all(env, object, fd, &buf, 1);
648
649    if (err < 0) {
650        jniThrowIOException(env, errno);
651        return (jint)0;
652    }
653
654    if (err == 0) {
655        // end of file
656        return (jint)-1;
657    }
658
659    return (jint)buf;
660}
661
662static jint socket_readba (JNIEnv *env, jobject object,
663        jbyteArray buffer, jint off, jint len, jobject fileDescriptor)
664{
665    int fd;
666    jbyte* byteBuffer;
667    int ret;
668
669    if (fileDescriptor == NULL || buffer == NULL) {
670        jniThrowNullPointerException(env, NULL);
671        return (jint)-1;
672    }
673
674    if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) {
675        jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
676        return (jint)-1;
677    }
678
679    if (len == 0) {
680        // because socket_read_all returns 0 on EOF
681        return 0;
682    }
683
684    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
685
686    if (env->ExceptionOccurred() != NULL) {
687        return (jint)-1;
688    }
689
690    byteBuffer = env->GetByteArrayElements(buffer, NULL);
691
692    if (NULL == byteBuffer) {
693        // an exception will have been thrown
694        return (jint)-1;
695    }
696
697    ret = socket_read_all(env, object,
698            fd, byteBuffer + off, len);
699
700    // A return of -1 above means an exception is pending
701
702    env->ReleaseByteArrayElements(buffer, byteBuffer, 0);
703
704    return (jint) ((ret == 0) ? -1 : ret);
705}
706
707static void socket_write (JNIEnv *env, jobject object,
708        jint b, jobject fileDescriptor)
709{
710    int fd;
711    int err;
712
713    if (fileDescriptor == NULL) {
714        jniThrowNullPointerException(env, NULL);
715        return;
716    }
717
718    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
719
720    if (env->ExceptionOccurred() != NULL) {
721        return;
722    }
723
724    err = socket_write_all(env, object, fd, &b, 1);
725
726    // A return of -1 above means an exception is pending
727}
728
729static void socket_writeba (JNIEnv *env, jobject object,
730        jbyteArray buffer, jint off, jint len, jobject fileDescriptor)
731{
732    int fd;
733    int err;
734    jbyte* byteBuffer;
735
736    if (fileDescriptor == NULL || buffer == NULL) {
737        jniThrowNullPointerException(env, NULL);
738        return;
739    }
740
741    if (off < 0 || len < 0 || (off + len) > env->GetArrayLength(buffer)) {
742        jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
743        return;
744    }
745
746    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
747
748    if (env->ExceptionOccurred() != NULL) {
749        return;
750    }
751
752    byteBuffer = env->GetByteArrayElements(buffer,NULL);
753
754    if (NULL == byteBuffer) {
755        // an exception will have been thrown
756        return;
757    }
758
759    err = socket_write_all(env, object, fd,
760            byteBuffer + off, len);
761
762    // A return of -1 above means an exception is pending
763
764    env->ReleaseByteArrayElements(buffer, byteBuffer, JNI_ABORT);
765}
766
767static jobject socket_get_peer_credentials(JNIEnv *env,
768        jobject object, jobject fileDescriptor)
769{
770    int err;
771    int fd;
772
773    if (fileDescriptor == NULL) {
774        jniThrowNullPointerException(env, NULL);
775        return NULL;
776    }
777
778    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
779
780    if (env->ExceptionOccurred() != NULL) {
781        return NULL;
782    }
783
784    struct ucred creds;
785
786    memset(&creds, 0, sizeof(creds));
787    socklen_t szCreds = sizeof(creds);
788
789    err = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
790
791    if (err < 0) {
792        jniThrowIOException(env, errno);
793        return NULL;
794    }
795
796    if (szCreds == 0) {
797        return NULL;
798    }
799
800    return env->NewObject(class_Credentials, method_CredentialsInit,
801            creds.pid, creds.uid, creds.gid);
802}
803
804#if 0
805//TODO change this to return an instance of LocalSocketAddress
806static jobject socket_getSockName(JNIEnv *env,
807        jobject object, jobject fileDescriptor)
808{
809    int err;
810    int fd;
811
812    if (fileDescriptor == NULL) {
813        jniThrowNullPointerException(env, NULL);
814        return NULL;
815    }
816
817    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
818
819    if (env->ExceptionOccurred() != NULL) {
820        return NULL;
821    }
822
823    union {
824        struct sockaddr address;
825        struct sockaddr_un un_address;
826    } sa;
827
828    memset(&sa, 0, sizeof(sa));
829
830    socklen_t namelen = sizeof(sa);
831    err = getsockname(fd, &(sa.address), &namelen);
832
833    if (err < 0) {
834        jniThrowIOException(env, errno);
835        return NULL;
836    }
837
838    if (sa.address.sa_family != AF_UNIX) {
839        // We think we're an impl only for AF_UNIX, so this should never happen.
840
841        jniThrowIOException(env, EINVAL);
842        return NULL;
843    }
844
845    if (sa.un_address.sun_path[0] == '\0') {
846    } else {
847    }
848
849
850
851
852}
853#endif
854
855/*
856 * JNI registration.
857 */
858static JNINativeMethod gMethods[] = {
859     /* name, signature, funcPtr */
860    {"getOption_native", "(Ljava/io/FileDescriptor;I)I", (void*)socket_getOption},
861    {"setOption_native", "(Ljava/io/FileDescriptor;III)V", (void*)socket_setOption},
862    {"connectLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
863                                                (void*)socket_connect_local},
864    {"bindLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V", (void*)socket_bind_local},
865    {"listen_native", "(Ljava/io/FileDescriptor;I)V", (void*)socket_listen},
866    {"accept", "(Ljava/io/FileDescriptor;Landroid/net/LocalSocketImpl;)Ljava/io/FileDescriptor;", (void*)socket_accept},
867    {"shutdown", "(Ljava/io/FileDescriptor;Z)V", (void*)socket_shutdown},
868    {"available_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_available},
869    {"pending_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_pending},
870    {"read_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_read},
871    {"readba_native", "([BIILjava/io/FileDescriptor;)I", (void*) socket_readba},
872    {"writeba_native", "([BIILjava/io/FileDescriptor;)V", (void*) socket_writeba},
873    {"write_native", "(ILjava/io/FileDescriptor;)V", (void*) socket_write},
874    {"getPeerCredentials_native",
875            "(Ljava/io/FileDescriptor;)Landroid/net/Credentials;",
876            (void*) socket_get_peer_credentials}
877    //,{"getSockName_native", "(Ljava/io/FileDescriptor;)Ljava/lang/String;",
878    //        (void *) socket_getSockName}
879
880};
881
882int register_android_net_LocalSocketImpl(JNIEnv *env)
883{
884    jclass clazz;
885
886    clazz = env->FindClass("android/net/LocalSocketImpl");
887
888    if (clazz == NULL) {
889        goto error;
890    }
891
892    field_inboundFileDescriptors = env->GetFieldID(clazz,
893            "inboundFileDescriptors", "[Ljava/io/FileDescriptor;");
894
895    if (field_inboundFileDescriptors == NULL) {
896        goto error;
897    }
898
899    field_outboundFileDescriptors = env->GetFieldID(clazz,
900            "outboundFileDescriptors", "[Ljava/io/FileDescriptor;");
901
902    if (field_outboundFileDescriptors == NULL) {
903        goto error;
904    }
905
906    class_Credentials = env->FindClass("android/net/Credentials");
907
908    if (class_Credentials == NULL) {
909        goto error;
910    }
911
912    class_Credentials = (jclass)env->NewGlobalRef(class_Credentials);
913
914    class_FileDescriptor = env->FindClass("java/io/FileDescriptor");
915
916    if (class_FileDescriptor == NULL) {
917        goto error;
918    }
919
920    class_FileDescriptor = (jclass)env->NewGlobalRef(class_FileDescriptor);
921
922    method_CredentialsInit
923            = env->GetMethodID(class_Credentials, "<init>", "(III)V");
924
925    if (method_CredentialsInit == NULL) {
926        goto error;
927    }
928
929    return jniRegisterNativeMethods(env,
930        "android/net/LocalSocketImpl", gMethods, NELEM(gMethods));
931
932error:
933    ALOGE("Error registering android.net.LocalSocketImpl");
934    return -1;
935}
936
937};
938