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 package android.app.role;
18 
19 import android.Manifest;
20 import android.annotation.FlaggedApi;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.annotation.WorkerThread;
25 import android.app.Service;
26 import android.content.Intent;
27 import android.os.Binder;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.HandlerThread;
31 import android.os.IBinder;
32 import android.os.Process;
33 import android.os.RemoteCallback;
34 import android.os.UserHandle;
35 import android.permission.flags.Flags;
36 import android.util.Log;
37 
38 import com.android.internal.util.Preconditions;
39 
40 import java.util.ArrayList;
41 import java.util.List;
42 import java.util.Objects;
43 import java.util.concurrent.Executor;
44 
45 /**
46  * Abstract base class for the role controller service.
47  * <p>
48  * Subclass should implement the business logic for role management, including enforcing role
49  * requirements and granting or revoking relevant privileges of roles. This class can only be
50  * implemented by the permission controller app which is registered in {@code PackageManager}.
51  *
52  * @deprecated The role controller service is an internal implementation detail inside role, and it
53  *             may be replaced by other mechanisms in the future and no longer be called.
54  *
55  * @hide
56  */
57 @Deprecated
58 @SystemApi
59 public abstract class RoleControllerService extends Service {
60     private static final String LOG_TAG = RoleControllerService.class.getSimpleName();
61 
62     /**
63      * The {@link Intent} that must be declared as handled by the service.
64      */
65     public static final String SERVICE_INTERFACE = "android.app.role.RoleControllerService";
66 
67     private HandlerThread mWorkerThread;
68     private Handler mWorkerHandler;
69 
70     @Override
onCreate()71     public void onCreate() {
72         super.onCreate();
73 
74         mWorkerThread = new HandlerThread(RoleControllerService.class.getSimpleName());
75         mWorkerThread.start();
76         mWorkerHandler = new Handler(mWorkerThread.getLooper());
77     }
78 
79     @Override
onDestroy()80     public void onDestroy() {
81         super.onDestroy();
82 
83         mWorkerThread.quitSafely();
84     }
85 
86     @Nullable
87     @Override
onBind(@ullable Intent intent)88     public final IBinder onBind(@Nullable Intent intent) {
89         return new IRoleController.Stub() {
90 
91             @Override
92             public void grantDefaultRoles(RemoteCallback callback) {
93                 enforceCallerSystemUid("grantDefaultRoles");
94                 Objects.requireNonNull(callback, "callback cannot be null");
95 
96                 mWorkerHandler.post(() -> RoleControllerService.this.grantDefaultRoles(callback));
97             }
98 
99             @Override
100             public void onAddRoleHolder(String roleName, String packageName, int flags,
101                     RemoteCallback callback) {
102                 enforceCallerSystemUid("onAddRoleHolder");
103                 Objects.requireNonNull(callback, "callback cannot be null");
104 
105                 mWorkerHandler.post(() -> RoleControllerService.this.onAddRoleHolder(roleName,
106                         packageName, flags, callback));
107             }
108 
109             @Override
110             public void onRemoveRoleHolder(String roleName, String packageName, int flags,
111                     RemoteCallback callback) {
112                 enforceCallerSystemUid("onRemoveRoleHolder");
113                 Objects.requireNonNull(callback, "callback cannot be null");
114 
115                 mWorkerHandler.post(() -> RoleControllerService.this.onRemoveRoleHolder(roleName,
116                         packageName, flags, callback));
117             }
118 
119             @Override
120             public void onClearRoleHolders(String roleName, int flags, RemoteCallback callback) {
121                 enforceCallerSystemUid("onClearRoleHolders");
122                 Objects.requireNonNull(callback, "callback cannot be null");
123 
124                 mWorkerHandler.post(() -> RoleControllerService.this.onClearRoleHolders(roleName,
125                         flags, callback));
126             }
127 
128             private void enforceCallerSystemUid(@NonNull String methodName) {
129                 if (Binder.getCallingUid() != Process.SYSTEM_UID) {
130                     throw new SecurityException("Only the system process can call " + methodName
131                             + "()");
132                 }
133             }
134 
135             @Override
136             public void isApplicationQualifiedForRole(String roleName, String packageName,
137                     RemoteCallback callback) {
138                 enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
139                 Objects.requireNonNull(callback, "callback cannot be null");
140 
141                 Bundle result = new Bundle();
142                 try {
143                     Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
144                     Preconditions.checkStringNotEmpty(packageName,
145                             "packageName cannot be null or empty");
146                     boolean qualified = onIsApplicationQualifiedForRole(roleName, packageName);
147                     result.putBoolean(RoleControllerManager.KEY_RESULT, qualified);
148                 } catch (Exception e) {
149                     result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
150                 }
151                 callback.sendResult(result);
152             }
153 
154             @Override
155             public void isApplicationVisibleForRole(String roleName, String packageName,
156                     RemoteCallback callback) {
157                 enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
158                 Objects.requireNonNull(callback, "callback cannot be null");
159 
160                 Bundle result = new Bundle();
161                 try {
162                     Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
163                     Preconditions.checkStringNotEmpty(packageName,
164                             "packageName cannot be null or empty");
165                     boolean visible = onIsApplicationVisibleForRole(roleName, packageName);
166                     result.putBoolean(RoleControllerManager.KEY_RESULT, visible);
167                 } catch (Exception e) {
168                     result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
169                 }
170                 callback.sendResult(result);
171             }
172 
173             @Override
174             public void isRoleVisible(String roleName, RemoteCallback callback) {
175                 enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
176                 Objects.requireNonNull(callback, "callback cannot be null");
177 
178                 Bundle result = new Bundle();
179                 try {
180                     Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
181                     boolean visible = onIsRoleVisible(roleName);
182                     result.putBoolean(RoleControllerManager.KEY_RESULT, visible);
183                 } catch (Exception e) {
184                     result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
185                 }
186                 callback.sendResult(result);
187             }
188 
189             @Override
190             public void getLegacyFallbackDisabledRoles(RemoteCallback callback) {
191                 enforceCallerSystemUid("getLegacyFallbackDisabledRoles");
192 
193                 Objects.requireNonNull(callback, "callback cannot be null");
194 
195                 Bundle result = new Bundle();
196                 try {
197                     List<String> legacyFallbackDisabledRoles = onGetLegacyFallbackDisabledRoles();
198                     result.putStringArrayList(RoleControllerManager.KEY_RESULT,
199                             new ArrayList<>(legacyFallbackDisabledRoles));
200                 } catch (Exception e) {
201                     result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
202                 }
203                 callback.sendResult(result);
204             }
205         };
206     }
207 
208     private void grantDefaultRoles(RemoteCallback callback) {
209         Bundle result = new Bundle();
210         try {
211             boolean successful = onGrantDefaultRoles();
212             result.putBoolean(RoleControllerManager.KEY_RESULT, successful);
213         } catch (Exception e) {
214             result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
215         }
216         callback.sendResult(result);
217     }
218 
219     private void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
220             @RoleManager.ManageHoldersFlags int flags, RemoteCallback callback) {
221         Bundle result = new Bundle();
222         try {
223             Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
224             Preconditions.checkStringNotEmpty(packageName,
225                     "packageName cannot be null or empty");
226             boolean successful = onAddRoleHolder(roleName, packageName, flags);
227             result.putBoolean(RoleControllerManager.KEY_RESULT, successful);
228         } catch (Exception e) {
229             result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
230         }
231         callback.sendResult(result);
232     }
233 
234     private void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
235             @RoleManager.ManageHoldersFlags int flags, RemoteCallback callback) {
236         Bundle result = new Bundle();
237         try {
238             Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
239             Preconditions.checkStringNotEmpty(packageName,
240                     "packageName cannot be null or empty");
241             boolean successful = onRemoveRoleHolder(roleName, packageName, flags);
242             result.putBoolean(RoleControllerManager.KEY_RESULT, successful);
243         } catch (Exception e) {
244             result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
245         }
246         callback.sendResult(result);
247     }
248 
249     private void onClearRoleHolders(@NonNull String roleName,
250             @RoleManager.ManageHoldersFlags int flags, RemoteCallback callback) {
251         Bundle result = new Bundle();
252         try {
253             Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
254             boolean successful = onClearRoleHolders(roleName, flags);
255             result.putBoolean(RoleControllerManager.KEY_RESULT, successful);
256         } catch (Exception e) {
257             result.putSerializable(RoleControllerManager.KEY_EXCEPTION, e);
258         }
259         callback.sendResult(result);
260     }
261 
262     /**
263      * Called by system to grant default permissions and roles.
264      * <p>
265      * This is typically when creating a new user or upgrading either system or
266      * permission controller package
267      *
268      * @return whether this call was successful
269      */
270     @WorkerThread
271     public abstract boolean onGrantDefaultRoles();
272 
273     /**
274      * Add a specific application to the holders of a role. If the role is exclusive, the previous
275      * holder will be replaced.
276      * <p>
277      * Implementation should enforce the role requirements and grant or revoke the relevant
278      * privileges of roles.
279      *
280      * @param roleName the name of the role to add the role holder for
281      * @param packageName the package name of the application to add to the role holders
282      * @param flags optional behavior flags
283      *
284      * @return whether this call was successful
285      *
286      * @see RoleManager#addRoleHolderAsUser(String, String, int, UserHandle, Executor,
287      *      RemoteCallback)
288      */
289     @WorkerThread
290     public abstract boolean onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
291             @RoleManager.ManageHoldersFlags int flags);
292 
293     /**
294      * Remove a specific application from the holders of a role.
295      *
296      * @param roleName the name of the role to remove the role holder for
297      * @param packageName the package name of the application to remove from the role holders
298      * @param flags optional behavior flags
299      *
300      * @return whether this call was successful
301      *
302      * @see RoleManager#removeRoleHolderAsUser(String, String, int, UserHandle, Executor,
303      *      RemoteCallback)
304      */
305     @WorkerThread
306     public abstract boolean onRemoveRoleHolder(@NonNull String roleName,
307             @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags);
308 
309     /**
310      * Remove all holders of a role.
311      *
312      * @param roleName the name of the role to remove role holders for
313      * @param flags optional behavior flags
314      *
315      * @return whether this call was successful
316      *
317      * @see RoleManager#clearRoleHoldersAsUser(String, int, UserHandle, Executor, RemoteCallback)
318      */
319     @WorkerThread
320     public abstract boolean onClearRoleHolders(@NonNull String roleName,
321             @RoleManager.ManageHoldersFlags int flags);
322 
323     /**
324      * Check whether an application is qualified for a role.
325      *
326      * @param roleName name of the role to check for
327      * @param packageName package name of the application to check for
328      *
329      * @return whether the application is qualified for the role
330      *
331      * @deprecated Implement {@link #onIsApplicationVisibleForRole(String, String)} instead.
332      */
333     @Deprecated
334     public abstract boolean onIsApplicationQualifiedForRole(@NonNull String roleName,
335             @NonNull String packageName);
336 
337     /**
338      * Check whether an application is visible for a role.
339      *
340      * While an application can be qualified for a role, it can still stay hidden from user (thus
341      * not visible). If an application is visible for a role, we may show things related to the role
342      * for it, e.g. showing an entry pointing to the role settings in its application info page.
343      *
344      * @param roleName name of the role to check for
345      * @param packageName package name of the application to check for
346      *
347      * @return whether the application is visible for the role
348      */
349     public boolean onIsApplicationVisibleForRole(@NonNull String roleName,
350             @NonNull String packageName) {
351         return onIsApplicationQualifiedForRole(roleName, packageName);
352     }
353 
354     /**
355      * Check whether a role should be visible to user.
356      *
357      * @param roleName name of the role to check for
358      *
359      * @return whether the role should be visible to user
360      */
361     public abstract boolean onIsRoleVisible(@NonNull String roleName);
362 
363     /**
364      * Get the legacy fallback disabled state.
365      *
366      * @return A list of role names with disabled fallback state.
367      */
368     @FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED)
369     @NonNull
370     public List<String> onGetLegacyFallbackDisabledRoles() {
371         Log.wtf(LOG_TAG, "onGetLegacyFallbackDisabledRoles is unsupported by this version of"
372                 + " PermissionController");
373         throw new UnsupportedOperationException();
374     }
375 }
376