android_util_Process.cpp revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
1/* //device/libs/android_runtime/android_util_Process.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#define LOG_TAG "Process"
19
20#include <utils/Log.h>
21#include <utils/IPCThreadState.h>
22#include <utils/ProcessState.h>
23#include <utils/IServiceManager.h>
24#include <utils/String8.h>
25#include <utils/Vector.h>
26
27#include <android_runtime/AndroidRuntime.h>
28
29#include "android_util_Binder.h"
30#include "JNIHelp.h"
31
32#include <sys/errno.h>
33#include <sys/resource.h>
34#include <sys/types.h>
35#include <dirent.h>
36#include <fcntl.h>
37#include <grp.h>
38#include <pwd.h>
39#include <signal.h>
40
41/* desktop Linux needs a little help with gettid() */
42#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
43#define __KERNEL__
44# include <linux/unistd.h>
45#ifdef _syscall0
46_syscall0(pid_t,gettid)
47#else
48pid_t gettid() { return syscall(__NR_gettid);}
49#endif
50#undef __KERNEL__
51#endif
52
53using namespace android;
54
55static void signalExceptionForPriorityError(JNIEnv* env, jobject obj, int err)
56{
57    switch (err) {
58        case EINVAL:
59            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
60            break;
61        case ESRCH:
62            jniThrowException(env, "java/lang/IllegalArgumentException", "Given thread does not exist");
63            break;
64        case EPERM:
65            jniThrowException(env, "java/lang/SecurityException", "No permission to modify given thread");
66            break;
67        case EACCES:
68            jniThrowException(env, "java/lang/SecurityException", "No permission to set to given priority");
69            break;
70        default:
71            jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
72            break;
73    }
74}
75
76static void fakeProcessEntry(void* arg)
77{
78    String8* cls = (String8*)arg;
79
80    AndroidRuntime* jr = AndroidRuntime::getRuntime();
81    jr->callMain(cls->string(), 0, NULL);
82
83    delete cls;
84}
85
86jint android_os_Process_myPid(JNIEnv* env, jobject clazz)
87{
88    return getpid();
89}
90
91jint android_os_Process_myTid(JNIEnv* env, jobject clazz)
92{
93#ifdef HAVE_GETTID
94    return gettid();
95#else
96    return getpid();
97#endif
98}
99
100jint android_os_Process_getUidForName(JNIEnv* env, jobject clazz, jstring name)
101{
102    if (name == NULL) {
103        jniThrowException(env, "java/lang/NullPointerException", NULL);
104        return -1;
105    }
106
107    const jchar* str16 = env->GetStringCritical(name, 0);
108    String8 name8;
109    if (str16) {
110        name8 = String8(str16, env->GetStringLength(name));
111        env->ReleaseStringCritical(name, str16);
112    }
113
114    const size_t N = name8.size();
115    if (N > 0) {
116        const char* str = name8.string();
117        for (size_t i=0; i<N; i++) {
118            if (str[i] < '0' || str[i] > '9') {
119                struct passwd* pwd = getpwnam(str);
120                if (pwd == NULL) {
121                    return -1;
122                }
123                return pwd->pw_uid;
124            }
125        }
126        return atoi(str);
127    }
128    return -1;
129}
130
131jint android_os_Process_getGidForName(JNIEnv* env, jobject clazz, jstring name)
132{
133    if (name == NULL) {
134        jniThrowException(env, "java/lang/NullPointerException", NULL);
135        return -1;
136    }
137
138    const jchar* str16 = env->GetStringCritical(name, 0);
139    String8 name8;
140    if (str16) {
141        name8 = String8(str16, env->GetStringLength(name));
142        env->ReleaseStringCritical(name, str16);
143    }
144
145    const size_t N = name8.size();
146    if (N > 0) {
147        const char* str = name8.string();
148        for (size_t i=0; i<N; i++) {
149            if (str[i] < '0' || str[i] > '9') {
150                struct group* grp = getgrnam(str);
151                if (grp == NULL) {
152                    return -1;
153                }
154                return grp->gr_gid;
155            }
156        }
157        return atoi(str);
158    }
159    return -1;
160}
161
162void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
163                                              jint pid, jint pri)
164{
165    if (setpriority(PRIO_PROCESS, pid, pri) < 0) {
166        signalExceptionForPriorityError(env, clazz, errno);
167    }
168    //LOGI("Setting priority of %d: %d, getpriority returns %d\n",
169    //     pid, pri, getpriority(PRIO_PROCESS, pid));
170}
171
172void android_os_Process_setCallingThreadPriority(JNIEnv* env, jobject clazz,
173                                                        jint pri)
174{
175    jint tid = android_os_Process_myTid(env, clazz);
176    android_os_Process_setThreadPriority(env, clazz, tid, pri);
177}
178
179jint android_os_Process_getThreadPriority(JNIEnv* env, jobject clazz,
180                                              jint pid)
181{
182    errno = 0;
183    jint pri = getpriority(PRIO_PROCESS, pid);
184    if (errno != 0) {
185        signalExceptionForPriorityError(env, clazz, errno);
186    }
187    //LOGI("Returning priority of %d: %d\n", pid, pri);
188    return pri;
189}
190
191jboolean android_os_Process_setOomAdj(JNIEnv* env, jobject clazz,
192                                      jint pid, jint adj)
193{
194#ifdef HAVE_OOM_ADJ
195    if (ProcessState::self()->supportsProcesses()) {
196        char text[64];
197        sprintf(text, "/proc/%d/oom_adj", pid);
198        int fd = open(text, O_WRONLY);
199        if (fd >= 0) {
200            sprintf(text, "%d", adj);
201            write(fd, text, strlen(text));
202            close(fd);
203            return true;
204        }
205    }
206#endif
207    return false;
208}
209
210void android_os_Process_setArgV0(JNIEnv* env, jobject clazz, jstring name)
211{
212    if (name == NULL) {
213        jniThrowException(env, "java/lang/NullPointerException", NULL);
214        return;
215    }
216
217    const jchar* str = env->GetStringCritical(name, 0);
218    String8 name8;
219    if (str) {
220        name8 = String8(str, env->GetStringLength(name));
221        env->ReleaseStringCritical(name, str);
222    }
223
224    if (name8.size() > 0) {
225        ProcessState::self()->setArgV0(name8.string());
226    }
227}
228
229jint android_os_Process_setUid(JNIEnv* env, jobject clazz, jint uid)
230{
231    #if HAVE_ANDROID_OS
232    return setuid(uid) == 0 ? 0 : errno;
233    #else
234    return ENOSYS;
235    #endif
236}
237
238jint android_os_Process_setGid(JNIEnv* env, jobject clazz, jint uid)
239{
240    #if HAVE_ANDROID_OS
241    return setgid(uid) == 0 ? 0 : errno;
242    #else
243    return ENOSYS;
244    #endif
245}
246
247jboolean android_os_Process_supportsProcesses(JNIEnv* env, jobject clazz)
248{
249    return ProcessState::self()->supportsProcesses();
250}
251
252static int pid_compare(const void* v1, const void* v2)
253{
254    //LOGI("Compare %d vs %d\n", *((const jint*)v1), *((const jint*)v2));
255    return *((const jint*)v1) - *((const jint*)v2);
256}
257
258jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
259{
260    int fd = open("/proc/meminfo", O_RDONLY);
261
262    if (fd < 0) {
263        LOGW("Unable to open /proc/meminfo");
264        return -1;
265    }
266
267    char buffer[256];
268    const int len = read(fd, buffer, sizeof(buffer)-1);
269    close(fd);
270
271    if (len < 0) {
272        LOGW("Unable to read /proc/meminfo");
273        return -1;
274    }
275    buffer[len] = 0;
276
277    int numFound = 0;
278    int mem = 0;
279
280    static const char* const sums[] = { "MemFree:", "Cached:", NULL };
281    static const int sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), NULL };
282
283    char* p = buffer;
284    while (*p && numFound < 2) {
285        int i = 0;
286        while (sums[i]) {
287            if (strncmp(p, sums[i], sumsLen[i]) == 0) {
288                p += sumsLen[i];
289                while (*p == ' ') p++;
290                char* num = p;
291                while (*p >= '0' && *p <= '9') p++;
292                if (*p != 0) {
293                    *p = 0;
294                    p++;
295                    if (*p == 0) p--;
296                }
297                mem += atoi(num) * 1024;
298                numFound++;
299                break;
300            }
301            i++;
302        }
303        p++;
304    }
305
306    return numFound > 0 ? mem : -1;
307}
308
309void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileStr,
310                                      jobjectArray reqFields, jlongArray outFields)
311{
312    //LOGI("getMemInfo: %p %p", reqFields, outFields);
313
314    if (fileStr == NULL || reqFields == NULL || outFields == NULL) {
315        jniThrowException(env, "java/lang/NullPointerException", NULL);
316        return;
317    }
318
319    const char* file8 = env->GetStringUTFChars(fileStr, NULL);
320    if (file8 == NULL) {
321        return;
322    }
323    String8 file(file8);
324    env->ReleaseStringUTFChars(fileStr, file8);
325
326    jsize count = env->GetArrayLength(reqFields);
327    if (count > env->GetArrayLength(outFields)) {
328        jniThrowException(env, "java/lang/IllegalArgumentException", "Array lengths differ");
329        return;
330    }
331
332    Vector<String8> fields;
333    int i;
334
335    for (i=0; i<count; i++) {
336        jobject obj = env->GetObjectArrayElement(reqFields, i);
337        if (obj != NULL) {
338            const char* str8 = env->GetStringUTFChars((jstring)obj, NULL);
339            //LOGI("String at %d: %p = %s", i, obj, str8);
340            if (str8 == NULL) {
341                jniThrowException(env, "java/lang/NullPointerException", "Element in reqFields");
342                return;
343            }
344            fields.add(String8(str8));
345            env->ReleaseStringUTFChars((jstring)obj, str8);
346        } else {
347            jniThrowException(env, "java/lang/NullPointerException", "Element in reqFields");
348            return;
349        }
350    }
351
352    jlong* sizesArray = env->GetLongArrayElements(outFields, 0);
353    if (sizesArray == NULL) {
354        return;
355    }
356
357    //LOGI("Clearing %d sizes", count);
358    for (i=0; i<count; i++) {
359        sizesArray[i] = 0;
360    }
361
362    int fd = open(file.string(), O_RDONLY);
363
364    if (fd >= 0) {
365        const size_t BUFFER_SIZE = 2048;
366        char* buffer = (char*)malloc(BUFFER_SIZE);
367        int len = read(fd, buffer, BUFFER_SIZE-1);
368        close(fd);
369
370        if (len < 0) {
371            LOGW("Unable to read %s", file.string());
372            len = 0;
373        }
374        buffer[len] = 0;
375
376        int foundCount = 0;
377
378        char* p = buffer;
379        while (*p && foundCount < count) {
380            bool skipToEol = true;
381            //LOGI("Parsing at: %s", p);
382            for (i=0; i<count; i++) {
383                const String8& field = fields[i];
384                if (strncmp(p, field.string(), field.length()) == 0) {
385                    p += field.length();
386                    while (*p == ' ') p++;
387                    char* num = p;
388                    while (*p >= '0' && *p <= '9') p++;
389                    skipToEol = *p != '\n';
390                    if (*p != 0) {
391                        *p = 0;
392                        p++;
393                    }
394                    char* end;
395                    sizesArray[i] = strtoll(num, &end, 10);
396                    //LOGI("Field %s = %d", field.string(), sizesArray[i]);
397                    foundCount++;
398                    break;
399                }
400            }
401            if (skipToEol) {
402                while (*p && *p != '\n') {
403                    p++;
404                }
405                if (*p == '\n') {
406                    p++;
407                }
408            }
409        }
410
411        free(buffer);
412    } else {
413        LOGW("Unable to open %s", file.string());
414    }
415
416    //LOGI("Done!");
417    env->ReleaseLongArrayElements(outFields, sizesArray, 0);
418}
419
420jintArray android_os_Process_getPids(JNIEnv* env, jobject clazz,
421                                     jstring file, jintArray lastArray)
422{
423    if (file == NULL) {
424        jniThrowException(env, "java/lang/NullPointerException", NULL);
425        return NULL;
426    }
427
428    const char* file8 = env->GetStringUTFChars(file, NULL);
429    if (file8 == NULL) {
430        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
431        return NULL;
432    }
433
434    DIR* dirp = opendir(file8);
435
436    env->ReleaseStringUTFChars(file, file8);
437
438    if(dirp == NULL) {
439        return NULL;
440    }
441
442    jsize curCount = 0;
443    jint* curData = NULL;
444    if (lastArray != NULL) {
445        curCount = env->GetArrayLength(lastArray);
446        curData = env->GetIntArrayElements(lastArray, 0);
447    }
448
449    jint curPos = 0;
450
451    struct dirent* entry;
452    while ((entry=readdir(dirp)) != NULL) {
453        const char* p = entry->d_name;
454        while (*p) {
455            if (*p < '0' || *p > '9') break;
456            p++;
457        }
458        if (*p != 0) continue;
459
460        char* end;
461        int pid = strtol(entry->d_name, &end, 10);
462        //LOGI("File %s pid=%d\n", entry->d_name, pid);
463        if (curPos >= curCount) {
464            jsize newCount = (curCount == 0) ? 10 : (curCount*2);
465            jintArray newArray = env->NewIntArray(newCount);
466            if (newArray == NULL) {
467                closedir(dirp);
468                jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
469                return NULL;
470            }
471            jint* newData = env->GetIntArrayElements(newArray, 0);
472            if (curData != NULL) {
473                memcpy(newData, curData, sizeof(jint)*curCount);
474                env->ReleaseIntArrayElements(lastArray, curData, 0);
475            }
476            lastArray = newArray;
477            curCount = newCount;
478            curData = newData;
479        }
480
481        curData[curPos] = pid;
482        curPos++;
483    }
484
485    closedir(dirp);
486
487    if (curData != NULL && curPos > 0) {
488        qsort(curData, curPos, sizeof(jint), pid_compare);
489    }
490
491    while (curPos < curCount) {
492        curData[curPos] = -1;
493        curPos++;
494    }
495
496    if (curData != NULL) {
497        env->ReleaseIntArrayElements(lastArray, curData, 0);
498    }
499
500    return lastArray;
501}
502
503enum {
504    PROC_TERM_MASK = 0xff,
505    PROC_ZERO_TERM = 0,
506    PROC_SPACE_TERM = ' ',
507    PROC_COMBINE = 0x100,
508    PROC_PARENS = 0x200,
509    PROC_OUT_STRING = 0x1000,
510    PROC_OUT_LONG = 0x2000,
511    PROC_OUT_FLOAT = 0x4000,
512};
513
514jboolean android_os_Process_readProcFile(JNIEnv* env, jobject clazz,
515        jstring file, jintArray format, jobjectArray outStrings,
516        jlongArray outLongs, jfloatArray outFloats)
517{
518    if (file == NULL || format == NULL) {
519        jniThrowException(env, "java/lang/NullPointerException", NULL);
520        return JNI_FALSE;
521    }
522
523    const char* file8 = env->GetStringUTFChars(file, NULL);
524    if (file8 == NULL) {
525        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
526        return JNI_FALSE;
527    }
528    int fd = open(file8, O_RDONLY);
529    env->ReleaseStringUTFChars(file, file8);
530
531    if (fd < 0) {
532        //LOGW("Unable to open process file: %s\n", file8);
533        return JNI_FALSE;
534    }
535
536    char buffer[256];
537    const int len = read(fd, buffer, sizeof(buffer)-1);
538    close(fd);
539
540    if (len < 0) {
541        //LOGW("Unable to open process file: %s fd=%d\n", file8, fd);
542        return JNI_FALSE;
543    }
544    buffer[len] = 0;
545
546    //LOGI("Process file %s: %s\n", file8, buffer);
547
548    const jsize NF = env->GetArrayLength(format);
549    const jsize NS = outStrings ? env->GetArrayLength(outStrings) : 0;
550    const jsize NL = outLongs ? env->GetArrayLength(outLongs) : 0;
551    const jsize NR = outFloats ? env->GetArrayLength(outFloats) : 0;
552
553    jint* formatData = env->GetIntArrayElements(format, 0);
554    jlong* longsData = outLongs ?
555        env->GetLongArrayElements(outLongs, 0) : NULL;
556    jfloat* floatsData = outFloats ?
557        env->GetFloatArrayElements(outFloats, 0) : NULL;
558    if (formatData == NULL || (NL > 0 && longsData == NULL)
559            || (NR > 0 && floatsData == NULL)) {
560        if (formatData != NULL) {
561            env->ReleaseIntArrayElements(format, formatData, 0);
562        }
563        if (longsData != NULL) {
564            env->ReleaseLongArrayElements(outLongs, longsData, 0);
565        }
566        if (floatsData != NULL) {
567            env->ReleaseFloatArrayElements(outFloats, floatsData, 0);
568        }
569        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
570        return JNI_FALSE;
571    }
572
573    jsize i = 0;
574    jsize di = 0;
575
576    jboolean res = JNI_TRUE;
577
578    for (jsize fi=0; fi<NF; fi++) {
579        const jint mode = formatData[fi];
580        if ((mode&PROC_PARENS) != 0) {
581            i++;
582        }
583        const char term = (char)(mode&PROC_TERM_MASK);
584        const jsize start = i;
585        if (i >= len) {
586            res = JNI_FALSE;
587            break;
588        }
589
590        jsize end = -1;
591        if ((mode&PROC_PARENS) != 0) {
592            while (buffer[i] != ')' && i < len) {
593                i++;
594            }
595            end = i;
596            i++;
597        }
598        while (buffer[i] != term && i < len) {
599            i++;
600        }
601        if (end < 0) {
602            end = i;
603        }
604
605        if (i < len) {
606            i++;
607            if ((mode&PROC_COMBINE) != 0) {
608                while (buffer[i] == term && i < len) {
609                    i++;
610                }
611            }
612        }
613
614        //LOGI("Field %d: %d-%d dest=%d mode=0x%x\n", i, start, end, di, mode);
615
616        if ((mode&(PROC_OUT_FLOAT|PROC_OUT_LONG|PROC_OUT_STRING)) != 0) {
617            char c = buffer[end];
618            buffer[end] = 0;
619            if ((mode&PROC_OUT_FLOAT) != 0 && di < NR) {
620                char* end;
621                floatsData[di] = strtof(buffer+start, &end);
622            }
623            if ((mode&PROC_OUT_LONG) != 0 && di < NL) {
624                char* end;
625                longsData[di] = strtoll(buffer+start, &end, 10);
626            }
627            if ((mode&PROC_OUT_STRING) != 0 && di < NS) {
628                jstring str = env->NewStringUTF(buffer+start);
629                env->SetObjectArrayElement(outStrings, di, str);
630            }
631            buffer[end] = c;
632            di++;
633        }
634    }
635
636    env->ReleaseIntArrayElements(format, formatData, 0);
637    if (longsData != NULL) {
638        env->ReleaseLongArrayElements(outLongs, longsData, 0);
639    }
640    if (floatsData != NULL) {
641        env->ReleaseFloatArrayElements(outFloats, floatsData, 0);
642    }
643
644    return res;
645}
646
647void android_os_Process_setApplicationObject(JNIEnv* env, jobject clazz,
648                                             jobject binderObject)
649{
650    if (binderObject == NULL) {
651        jniThrowException(env, "java/lang/NullPointerException", NULL);
652        return;
653    }
654
655    sp<IBinder> binder = ibinderForJavaObject(env, binderObject);
656}
657
658void android_os_Process_sendSignal(JNIEnv* env, jobject clazz, jint pid, jint sig)
659{
660    if (pid > 0) {
661        LOGI("Sending signal. PID: %d SIG: %d", pid, sig);
662        kill(pid, sig);
663    }
664}
665
666static jlong android_os_Process_getElapsedCpuTime(JNIEnv* env, jobject clazz)
667{
668    struct timespec ts;
669
670    int res = clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
671
672    if (res != 0) {
673        return (jlong) 0;
674    }
675
676    nsecs_t when = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec;
677    return (jlong) nanoseconds_to_milliseconds(when);
678}
679
680static jlong android_os_Process_getPss(JNIEnv* env, jobject clazz, jint pid)
681{
682    char filename[64];
683
684    snprintf(filename, sizeof(filename), "/proc/%d/smaps", pid);
685
686    FILE * file = fopen(filename, "r");
687    if (!file) {
688        return (jlong) -1;
689    }
690
691    // Tally up all of the Pss from the various maps
692    char line[256];
693    jlong pss = 0;
694    while (fgets(line, sizeof(line), file)) {
695        jlong v;
696        if (sscanf(line, "Pss: %lld kB", &v) == 1) {
697            pss += v;
698        }
699    }
700
701    fclose(file);
702
703    // Return the Pss value in bytes, not kilobytes
704    return pss * 1024;
705}
706
707static const JNINativeMethod methods[] = {
708    {"myPid",       "()I", (void*)android_os_Process_myPid},
709    {"myTid",       "()I", (void*)android_os_Process_myTid},
710    {"getUidForName",       "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName},
711    {"getGidForName",       "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName},
712    {"setThreadPriority",   "(II)V", (void*)android_os_Process_setThreadPriority},
713    {"setThreadPriority",   "(I)V", (void*)android_os_Process_setCallingThreadPriority},
714    {"getThreadPriority",   "(I)I", (void*)android_os_Process_getThreadPriority},
715    {"setOomAdj",   "(II)Z", (void*)android_os_Process_setOomAdj},
716    {"setArgV0",    "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
717    {"setUid", "(I)I", (void*)android_os_Process_setUid},
718    {"setGid", "(I)I", (void*)android_os_Process_setGid},
719    {"sendSignal", "(II)V", (void*)android_os_Process_sendSignal},
720    {"supportsProcesses", "()Z", (void*)android_os_Process_supportsProcesses},
721    {"getFreeMemory", "()I", (void*)android_os_Process_getFreeMemory},
722    {"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", (void*)android_os_Process_readProcLines},
723    {"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids},
724    {"readProcFile", "(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_readProcFile},
725    {"getElapsedCpuTime", "()J", (void*)android_os_Process_getElapsedCpuTime},
726    {"getPss", "(I)J", (void*)android_os_Process_getPss},
727    //{"setApplicationObject", "(Landroid/os/IBinder;)V", (void*)android_os_Process_setApplicationObject},
728};
729
730const char* const kProcessPathName = "android/os/Process";
731
732int register_android_os_Process(JNIEnv* env)
733{
734    jclass clazz;
735
736    clazz = env->FindClass(kProcessPathName);
737    LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Process");
738
739    return AndroidRuntime::registerNativeMethods(
740        env, kProcessPathName,
741        methods, NELEM(methods));
742}
743
744