1 /* 2 * Copyright (C) 2012 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 package com.android.server.os; 18 19 import android.content.pm.PackageManager; 20 import android.os.Binder; 21 import android.os.IBinder; 22 import android.os.ISchedulingPolicyService; 23 import android.os.Process; 24 import android.util.Log; 25 26 import com.android.server.SystemServerInitThreadPool; 27 28 /** 29 * The implementation of the scheduling policy service interface. 30 * 31 * @hide 32 */ 33 public class SchedulingPolicyService extends ISchedulingPolicyService.Stub { 34 35 private static final String TAG = "SchedulingPolicyService"; 36 37 // Minimum and maximum values allowed for requestPriority parameter prio 38 private static final int PRIORITY_MIN = 1; 39 private static final int PRIORITY_MAX = 3; 40 41 private static final String[] MEDIA_PROCESS_NAMES = new String[] { 42 "media.swcodec", // /apex/com.android.media.swcodec/bin/mediaswcodec 43 }; 44 private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { 45 @Override 46 public void binderDied() { 47 requestCpusetBoost(false /*enable*/, null /*client*/); 48 } 49 }; 50 // Current process that received a cpuset boost 51 private int mBoostedPid = -1; 52 // Current client registered to the death recipient 53 private IBinder mClient; 54 SchedulingPolicyService()55 public SchedulingPolicyService() { 56 // system_server (our host) could have crashed before. The app may not survive 57 // it, but mediaserver/media.codec could have, and mediaserver probably tried 58 // to disable the boost while we were dead. 59 // We do a restore of media.codec to default cpuset upon service restart to 60 // catch this case. We can't leave media.codec in boosted state, because we've 61 // lost the death recipient of mClient from mediaserver after the restart, 62 // if mediaserver dies in the future we won't have a notification to reset. 63 // (Note that if mediaserver thinks we're in boosted state before the crash, 64 // the state could go out of sync temporarily until mediaserver enables/disable 65 // boost next time, but this won't be a big issue.) 66 SystemServerInitThreadPool.submit(() -> { 67 synchronized (mDeathRecipient) { 68 // only do this if we haven't already got a request to boost. 69 if (mBoostedPid == -1) { 70 int[] nativePids = Process.getPidsForCommands(MEDIA_PROCESS_NAMES); 71 if (nativePids != null && nativePids.length == 1) { 72 mBoostedPid = nativePids[0]; 73 disableCpusetBoost(nativePids[0]); 74 } 75 } 76 } 77 }, TAG + ".<init>"); 78 } 79 80 // TODO(b/35196900) We should pass the period in time units, rather 81 // than a fixed priority number. requestPriority(int pid, int tid, int prio, boolean isForApp)82 public int requestPriority(int pid, int tid, int prio, boolean isForApp) { 83 //Log.i(TAG, "requestPriority(pid=" + pid + ", tid=" + tid + ", prio=" + prio + ")"); 84 85 // Verify that the caller uid is permitted, priority is in range, 86 // and that the callback thread specified by app belongs to the app that 87 // called mediaserver or audioserver. 88 // Once we've verified that the caller uid is permitted, we can trust the pid but 89 // we can't trust the tid. No need to explicitly check for pid == 0 || tid == 0, 90 // since if not the case then the getThreadGroupLeader() test will also fail. 91 if (!isPermitted() || prio < PRIORITY_MIN || 92 prio > PRIORITY_MAX || Process.getThreadGroupLeader(tid) != pid) { 93 return PackageManager.PERMISSION_DENIED; 94 } 95 // If the calling UID is audio server, and this call is not for an app, 96 // then it must be for the audio HAL. Validate the UID of the thread. 97 if (Binder.getCallingUid() == Process.AUDIOSERVER_UID && !isForApp 98 && Process.getUidForPid(tid) != Process.AUDIOSERVER_UID) { 99 return PackageManager.PERMISSION_DENIED; 100 } 101 if (Binder.getCallingUid() != Process.BLUETOOTH_UID) { 102 try { 103 // make good use of our CAP_SYS_NICE capability 104 Process.setThreadGroup(tid, !isForApp ? 105 Process.THREAD_GROUP_AUDIO_SYS : Process.THREAD_GROUP_RT_APP); 106 } catch (RuntimeException e) { 107 Log.e(TAG, "Failed setThreadGroup: " + e); 108 return PackageManager.PERMISSION_DENIED; 109 } 110 } 111 try { 112 // must be in this order or it fails the schedulability constraint 113 Process.setThreadScheduler(tid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 114 prio); 115 } catch (RuntimeException e) { 116 Log.e(TAG, "Failed setThreadScheduler: " + e); 117 return PackageManager.PERMISSION_DENIED; 118 } 119 return PackageManager.PERMISSION_GRANTED; 120 } 121 122 // Request to move media.codec process between SP_FOREGROUND and SP_TOP_APP. requestCpusetBoost(boolean enable, IBinder client)123 public int requestCpusetBoost(boolean enable, IBinder client) { 124 // Can only allow mediaserver to call this. 125 if (Binder.getCallingPid() != Process.myPid() && 126 Binder.getCallingUid() != Process.MEDIA_UID) { 127 return PackageManager.PERMISSION_DENIED; 128 } 129 130 int[] nativePids = Process.getPidsForCommands(MEDIA_PROCESS_NAMES); 131 if (nativePids == null || nativePids.length != 1) { 132 Log.e(TAG, "requestCpusetBoost: can't find media.codec process"); 133 return PackageManager.PERMISSION_DENIED; 134 } 135 136 synchronized (mDeathRecipient) { 137 if (enable) { 138 return enableCpusetBoost(nativePids[0], client); 139 } else { 140 return disableCpusetBoost(nativePids[0]); 141 } 142 } 143 } 144 enableCpusetBoost(int pid, IBinder client)145 private int enableCpusetBoost(int pid, IBinder client) { 146 if (mBoostedPid == pid) { 147 return PackageManager.PERMISSION_GRANTED; 148 } 149 150 // The mediacodec process has changed, clean up the old pid and 151 // client before we boost the new process, so that the state 152 // is left clean if things go wrong. 153 mBoostedPid = -1; 154 if (mClient != null) { 155 try { 156 mClient.unlinkToDeath(mDeathRecipient, 0); 157 } catch (Exception e) { 158 } finally { 159 mClient = null; 160 } 161 } 162 163 try { 164 client.linkToDeath(mDeathRecipient, 0); 165 166 Log.i(TAG, "Moving " + pid + " to group " + Process.THREAD_GROUP_TOP_APP); 167 Process.setProcessGroup(pid, Process.THREAD_GROUP_TOP_APP); 168 169 mBoostedPid = pid; 170 mClient = client; 171 172 return PackageManager.PERMISSION_GRANTED; 173 } catch (Exception e) { 174 Log.e(TAG, "Failed enableCpusetBoost: " + e); 175 try { 176 // unlink if things go wrong and don't crash. 177 client.unlinkToDeath(mDeathRecipient, 0); 178 } catch (Exception e1) {} 179 } 180 181 return PackageManager.PERMISSION_DENIED; 182 } 183 disableCpusetBoost(int pid)184 private int disableCpusetBoost(int pid) { 185 int boostedPid = mBoostedPid; 186 187 // Clean up states first. 188 mBoostedPid = -1; 189 if (mClient != null) { 190 try { 191 mClient.unlinkToDeath(mDeathRecipient, 0); 192 } catch (Exception e) { 193 } finally { 194 mClient = null; 195 } 196 } 197 198 // Try restore the old thread group, no need to fail as the 199 // mediacodec process could be dead just now. 200 if (boostedPid == pid) { 201 try { 202 Log.i(TAG, "Moving " + pid + " back to group default"); 203 Process.setProcessGroup(pid, Process.THREAD_GROUP_DEFAULT); 204 } catch (Exception e) { 205 Log.w(TAG, "Couldn't move pid " + pid + " back to group default"); 206 } 207 } 208 209 return PackageManager.PERMISSION_GRANTED; 210 } 211 isPermitted()212 private boolean isPermitted() { 213 // schedulerservice hidl 214 if (Binder.getCallingPid() == Process.myPid()) { 215 return true; 216 } 217 218 switch (Binder.getCallingUid()) { 219 case Process.AUDIOSERVER_UID: // fastcapture, fastmixer 220 case Process.CAMERASERVER_UID: // camera high frame rate recording 221 case Process.BLUETOOTH_UID: // Bluetooth audio playback 222 case Process.PHONE_UID: // phone call 223 return true; 224 default: 225 return false; 226 } 227 } 228 } 229