• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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