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