1 /*
2 * Copyright (C) 2019 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 "CachedAppOptimizer"
18 //#define LOG_NDEBUG 0
19
20 #include <dirent.h>
21 #include <stddef.h>
22 #include <stdio.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 #include <android-base/stringprintf.h>
28 #include <android-base/file.h>
29
30 #include <nativehelper/JNIHelp.h>
31 #include <android_runtime/AndroidRuntime.h>
32 #include <binder/IPCThreadState.h>
33 #include <jni.h>
34 #include <processgroup/processgroup.h>
35
36 using android::base::StringPrintf;
37 using android::base::WriteStringToFile;
38
39 #define SYNC_RECEIVED_WHILE_FROZEN (1)
40 #define ASYNC_RECEIVED_WHILE_FROZEN (2)
41
42 namespace android {
43
44 // This performs per-process reclaim on all processes belonging to non-app UIDs.
45 // For the most part, these are non-zygote processes like Treble HALs, but it
46 // also includes zygote-derived processes that run in system UIDs, like bluetooth
47 // or potentially some mainline modules. The only process that should definitely
48 // not be compacted is system_server, since compacting system_server around the
49 // time of BOOT_COMPLETE could result in perceptible issues.
com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *,jobject)50 static void com_android_server_am_CachedAppOptimizer_compactSystem(JNIEnv *, jobject) {
51 std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir);
52 struct dirent* current;
53 while ((current = readdir(proc.get()))) {
54 if (current->d_type != DT_DIR) {
55 continue;
56 }
57
58 // don't compact system_server, rely on persistent compaction during screen off
59 // in order to avoid mmap_sem-related stalls
60 if (atoi(current->d_name) == getpid()) {
61 continue;
62 }
63
64 std::string status_name = StringPrintf("/proc/%s/status", current->d_name);
65 struct stat status_info;
66
67 if (stat(status_name.c_str(), &status_info) != 0) {
68 // must be some other directory that isn't a pid
69 continue;
70 }
71
72 // android.os.Process.FIRST_APPLICATION_UID
73 if (status_info.st_uid >= 10000) {
74 continue;
75 }
76
77 std::string reclaim_path = StringPrintf("/proc/%s/reclaim", current->d_name);
78 WriteStringToFile(std::string("all"), reclaim_path);
79 }
80 }
81
com_android_server_am_CachedAppOptimizer_enableFreezerInternal(JNIEnv * env,jobject clazz,jboolean enable)82 static void com_android_server_am_CachedAppOptimizer_enableFreezerInternal(
83 JNIEnv *env, jobject clazz, jboolean enable) {
84 bool success = true;
85
86 if (enable) {
87 success = SetTaskProfiles(0, {"FreezerEnabled"}, true);
88 } else {
89 success = SetTaskProfiles(0, {"FreezerDisabled"}, true);
90 }
91
92 if (!success) {
93 jniThrowException(env, "java/lang/RuntimeException", "Unknown error");
94 }
95 }
96
com_android_server_am_CachedAppOptimizer_freezeBinder(JNIEnv * env,jobject clazz,jint pid,jboolean freeze)97 static void com_android_server_am_CachedAppOptimizer_freezeBinder(
98 JNIEnv *env, jobject clazz, jint pid, jboolean freeze) {
99
100 if (IPCThreadState::freeze(pid, freeze, 100 /* timeout [ms] */) != 0) {
101 jniThrowException(env, "java/lang/RuntimeException", "Unable to freeze/unfreeze binder");
102 }
103 }
104
com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv * env,jobject clazz,jint pid)105 static jint com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo(JNIEnv *env,
106 jobject clazz, jint pid) {
107 bool syncReceived = false, asyncReceived = false;
108
109 int error = IPCThreadState::getProcessFreezeInfo(pid, &syncReceived, &asyncReceived);
110
111 if (error < 0) {
112 jniThrowException(env, "java/lang/RuntimeException", strerror(error));
113 }
114
115 jint retVal = 0;
116
117 if(syncReceived) {
118 retVal |= SYNC_RECEIVED_WHILE_FROZEN;;
119 }
120
121 if(asyncReceived) {
122 retVal |= ASYNC_RECEIVED_WHILE_FROZEN;
123 }
124
125 return retVal;
126 }
127
128 static const JNINativeMethod sMethods[] = {
129 /* name, signature, funcPtr */
130 {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
131 {"enableFreezerInternal", "(Z)V",
132 (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
133 {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
134 {"getBinderFreezeInfo", "(I)I",
135 (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}
136 };
137
register_android_server_am_CachedAppOptimizer(JNIEnv * env)138 int register_android_server_am_CachedAppOptimizer(JNIEnv* env)
139 {
140 return jniRegisterNativeMethods(env, "com/android/server/am/CachedAppOptimizer",
141 sMethods, NELEM(sMethods));
142 }
143
144 }
145