/**
 * Processes ancillary data, handling only
 * SCM_RIGHTS. Creates appropriate objects and sets appropriate
 * fields in the LocalSocketImpl object. Returns 0 on success
 * or -1 if an exception was thrown.
 */
static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg)
{
    struct cmsghdr *cmsgptr;

    for (cmsgptr = CMSG_FIRSTHDR(pMsg);
            cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(pMsg, cmsgptr)) {

        if (cmsgptr->cmsg_level != SOL_SOCKET) {
            continue;
        }

        if (cmsgptr->cmsg_type == SCM_RIGHTS) {
            int *pDescriptors = (int *)CMSG_DATA(cmsgptr);
            jobjectArray fdArray;
            int count
                = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int));

            if (count < 0) {
                jniThrowException(env, "java/io/IOException",
                    "invalid cmsg length");
                return -1;
            }

            fdArray = env->NewObjectArray(count, class_FileDescriptor, NULL);

            if (fdArray == NULL) {
                return -1;
            }

            for (int i = 0; i < count; i++) {
                jobject fdObject
                        = jniCreateFileDescriptor(env, pDescriptors[i]);

                if (env->ExceptionCheck()) {
                    return -1;
                }

                env->SetObjectArrayElement(fdArray, i, fdObject);

                if (env->ExceptionCheck()) {
                    return -1;
                }
            }

            env->SetObjectField(thisJ, field_inboundFileDescriptors, fdArray);

            if (env->ExceptionCheck()) {
                return -1;
            }
        }
    }

    return 0;
}
static jobject android_os_Parcel_readFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        int fd = parcel->readFileDescriptor();
        if (fd < 0) return NULL;
        fd = dup(fd);
        if (fd < 0) return NULL;
        return jniCreateFileDescriptor(env, fd);
    }
    return NULL;
}
static jobjectArray
android_server_UsbMidiDevice_open(JNIEnv *env, jobject thiz, jint card, jint device,
        jint subdevice_count)
{
    char    path[100];

    snprintf(path, sizeof(path), "/dev/snd/midiC%dD%d", card, device);

    // allocate one extra file descriptor for close pipe
    jobjectArray fds = env->NewObjectArray(subdevice_count + 1, sFileDescriptorClass, NULL);
    if (!fds) {
        return NULL;
    }

    // to support multiple subdevices we open the same file multiple times
    for (int i = 0; i < subdevice_count; i++) {
        int fd = open(path, O_RDWR);
        if (fd < 0) {
            ALOGE("open failed on %s for index %d", path, i);
            return NULL;
        }

        jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
        env->SetObjectArrayElement(fds, i, fileDescriptor);
        env->DeleteLocalRef(fileDescriptor);
    }

    // create a pipe to use for unblocking our input thread
    int pipeFD[2];
    pipe(pipeFD);
    jobject fileDescriptor = jniCreateFileDescriptor(env, pipeFD[0]);
    env->SetObjectArrayElement(fds, subdevice_count, fileDescriptor);
    env->DeleteLocalRef(fileDescriptor);
    // store our end of the pipe in mPipeFD
    env->SetIntField(thiz, sPipeFDField, pipeFD[1]);

    return fds;
}
/*
 * private native FileDescriptor
 * create_native(boolean stream)
 *               throws IOException;
 */
static jobject
socket_create (JNIEnv *env, jobject object, jboolean stream)
{
    int ret;

    ret = socket(PF_LOCAL, stream ? SOCK_STREAM : SOCK_DGRAM, 0);

    if (ret < 0) {
        jniThrowIOException(env, errno);
        return NULL;
    }

    return jniCreateFileDescriptor(env,ret);
}
static jobject android_server_UsbDeviceManager_openAccessory(JNIEnv *env, jobject /* thiz */)
{
    int fd = open(DRIVER_NAME, O_RDWR);
    if (fd < 0) {
        ALOGE("could not open %s", DRIVER_NAME);
        return NULL;
    }
    jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
    if (fileDescriptor == NULL) {
        return NULL;
    }
    return env->NewObject(gParcelFileDescriptorOffsets.mClass,
        gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
}
static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jclass clazz,
                                                    jstring name, jint mode)
{
    if (name == NULL) {
        jniThrowNullPointerException(env, NULL);
        return NULL;
    }
    const jchar* str = env->GetStringCritical(name, 0);
    if (str == NULL) {
        // Whatever, whatever.
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return NULL;
    }
    String8 name8((const char16_t*)str, env->GetStringLength(name));
    env->ReleaseStringCritical(name, str);
    int flags=0;
    switch (mode&0x30000000) {
        case 0:
        case 0x10000000:
            flags = O_RDONLY;
            break;
        case 0x20000000:
            flags = O_WRONLY;
            break;
        case 0x30000000:
            flags = O_RDWR;
            break;
    }

    if (mode&0x08000000) flags |= O_CREAT;
    if (mode&0x04000000) flags |= O_TRUNC;
    if (mode&0x02000000) flags |= O_APPEND;

    int realMode = S_IRWXU|S_IRWXG;
    if (mode&0x00000001) realMode |= S_IROTH;
    if (mode&0x00000002) realMode |= S_IWOTH;

    int fd = open(name8.string(), flags, realMode);
    if (fd < 0) {
        jniThrowException(env, "java/io/FileNotFoundException", strerror(errno));
        return NULL;
    }
    jobject object = jniCreateFileDescriptor(env, fd);
    if (object == NULL) {
        close(fd);
    }
    return object;
}
static jobject android_os_Parcel_openFileDescriptor(JNIEnv* env, jclass clazz,
                                                    jstring name, jint mode)
{
    if (name == NULL) {
        jniThrowNullPointerException(env, NULL);
        return NULL;
    }
    ScopedUtfChars name8(env, name);
    if (name8.c_str() == NULL) {
        return NULL;
    }

    int flags=0;
    switch (mode&0x30000000) {
        case 0:
        case 0x10000000:
            flags = O_RDONLY;
            break;
        case 0x20000000:
            flags = O_WRONLY;
            break;
        case 0x30000000:
            flags = O_RDWR;
            break;
    }

    if (mode&0x08000000) flags |= O_CREAT;
    if (mode&0x04000000) flags |= O_TRUNC;
    if (mode&0x02000000) flags |= O_APPEND;

    int realMode = S_IRWXU|S_IRWXG;
    if (mode&0x00000001) realMode |= S_IROTH;
    if (mode&0x00000002) realMode |= S_IWOTH;

    int fd = open(name8.c_str(), flags, realMode);
    if (fd < 0) {
        jniThrowException(env, "java/io/FileNotFoundException", strerror(errno));
        return NULL;
    }
    jobject object = jniCreateFileDescriptor(env, fd);
    if (object == NULL) {
        close(fd);
    }
    return object;
}
static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length)
{
    const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL);

    // round up length to page boundary
    length = (((length - 1) / getpagesize()) + 1) * getpagesize();
    int result = ashmem_create_region(namestr, length);

    if (name)
        env->ReleaseStringUTFChars(name, namestr);

    if (result < 0) {
        jniThrowException(env, "java/io/IOException", "ashmem_create_region failed");
        return NULL;
    }

    return jniCreateFileDescriptor(env, result);
}
static jobject android_server_SerialService_open(JNIEnv *env, jobject /* thiz */, jstring path)
{
    const char *pathStr = env->GetStringUTFChars(path, NULL);

    int fd = open(pathStr, O_RDWR | O_NOCTTY);
    if (fd < 0) {
        ALOGE("could not open %s", pathStr);
        env->ReleaseStringUTFChars(path, pathStr);
        return NULL;
    }
    env->ReleaseStringUTFChars(path, pathStr);

    jobject fileDescriptor = jniCreateFileDescriptor(env, fd);
    if (fileDescriptor == NULL) {
        return NULL;
    }
    return env->NewObject(gParcelFileDescriptorOffsets.mClass,
        gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
}
/*    private native FileDescriptor
**    accept (FileDescriptor fd, LocalSocketImpl s)
**                                   throws IOException;
*/
static jobject
socket_accept (JNIEnv *env, jobject object, jobject fileDescriptor, jobject s)
{
    union {
        struct sockaddr address;
        struct sockaddr_un un_address;
    } sa;

    int ret;
    int retFD;
    int fd;
    socklen_t addrlen;

    if (s == NULL) {
        jniThrowNullPointerException(env, NULL);
        return NULL;
    }

    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);

    if (env->ExceptionOccurred() != NULL) {
        return NULL;
    }

    do {
        addrlen = sizeof(sa);
        ret = accept(fd, &(sa.address), &addrlen);
    } while (ret < 0 && errno == EINTR);

    if (ret < 0) {
        jniThrowIOException(env, errno);
        return NULL;
    }

    retFD = ret;

    return jniCreateFileDescriptor(env, retFD);
}
static jobject android_os_Parcel_dupFileDescriptor(JNIEnv* env, jclass clazz, jobject orig)
{
    if (orig == NULL) {
        jniThrowNullPointerException(env, NULL);
        return NULL;
    }
    int origfd = jniGetFDFromFileDescriptor(env, orig);
    if (origfd < 0) {
        jniThrowException(env, "java/lang/IllegalArgumentException", "bad FileDescriptor");
        return NULL;
    }

    int fd = dup(origfd);
    if (fd < 0) {
        jniThrowIOException(env, errno);
        return NULL;
    }
    jobject object = jniCreateFileDescriptor(env, fd);
    if (object == NULL) {
        close(fd);
    }
    return object;
}
static jobject com_android_internal_os_ZygoteInit_createFileDescriptor (
        JNIEnv *env, jobject clazz, jint fd)
{
    return jniCreateFileDescriptor(env, fd);
}