1 /*
2  * Copyright (C) 2018 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.role;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.role.IRoleManager;
22 import android.os.Build;
23 import android.os.RemoteCallback;
24 import android.os.RemoteException;
25 
26 import androidx.annotation.RequiresApi;
27 
28 import com.android.modules.utils.BasicShellCommandHandler;
29 import com.android.permission.compat.UserHandleCompat;
30 
31 import java.io.PrintWriter;
32 import java.util.concurrent.CompletableFuture;
33 import java.util.concurrent.TimeUnit;
34 
35 @RequiresApi(Build.VERSION_CODES.S)
36 class RoleShellCommand extends BasicShellCommandHandler {
37     @NonNull
38     private final IRoleManager mRoleManager;
39 
RoleShellCommand(@onNull IRoleManager roleManager)40     RoleShellCommand(@NonNull IRoleManager roleManager) {
41         mRoleManager = roleManager;
42     }
43 
44     private class CallbackFuture extends CompletableFuture<Void> {
45         @NonNull
createCallback()46         public RemoteCallback createCallback() {
47             return new RemoteCallback(result -> {
48                 boolean successful = result != null;
49                 if (successful) {
50                     complete(null);
51                 } else {
52                     completeExceptionally(new RuntimeException("Failed"));
53                 }
54             });
55         }
56 
waitForResult()57         public int waitForResult() {
58             try {
59                 get(5, TimeUnit.SECONDS);
60                 return 0;
61             } catch (Exception e) {
62                 getErrPrintWriter().println("Error: see logcat for details.\n" + e);
63                 return -1;
64             }
65         }
66     }
67 
68     @Override
onCommand(@ullable String cmd)69     public int onCommand(@Nullable String cmd) {
70         if (cmd == null) {
71             return handleDefaultCommands(cmd);
72         }
73 
74         PrintWriter pw = getOutPrintWriter();
75         try {
76             switch (cmd) {
77                 case "add-role-holder":
78                     return runAddRoleHolder();
79                 case "remove-role-holder":
80                     return runRemoveRoleHolder();
81                 case "clear-role-holders":
82                     return runClearRoleHolders();
83                 default:
84                     return handleDefaultCommands(cmd);
85             }
86         } catch (RemoteException e) {
87             pw.println("Remote exception: " + e);
88         }
89         return -1;
90     }
91 
getUserIdMaybe()92     private int getUserIdMaybe() {
93         int userId = UserHandleCompat.USER_SYSTEM;
94         String option = getNextOption();
95         if (option != null && option.equals("--user")) {
96             userId = Integer.parseInt(getNextArgRequired());
97         }
98         return userId;
99     }
100 
getFlagsMaybe()101     private int getFlagsMaybe() {
102         String flags = getNextArg();
103         if (flags == null) {
104             return 0;
105         }
106         return Integer.parseInt(flags);
107     }
108 
runAddRoleHolder()109     private int runAddRoleHolder() throws RemoteException {
110         int userId = getUserIdMaybe();
111         String roleName = getNextArgRequired();
112         String packageName = getNextArgRequired();
113         int flags = getFlagsMaybe();
114 
115         CallbackFuture future = new CallbackFuture();
116         mRoleManager.addRoleHolderAsUser(roleName, packageName, flags, userId,
117                 future.createCallback());
118         return future.waitForResult();
119     }
120 
runRemoveRoleHolder()121     private int runRemoveRoleHolder() throws RemoteException {
122         int userId = getUserIdMaybe();
123         String roleName = getNextArgRequired();
124         String packageName = getNextArgRequired();
125         int flags = getFlagsMaybe();
126 
127         CallbackFuture future = new CallbackFuture();
128         mRoleManager.removeRoleHolderAsUser(roleName, packageName, flags, userId,
129                 future.createCallback());
130         return future.waitForResult();
131     }
132 
runClearRoleHolders()133     private int runClearRoleHolders() throws RemoteException {
134         int userId = getUserIdMaybe();
135         String roleName = getNextArgRequired();
136         int flags = getFlagsMaybe();
137 
138         CallbackFuture future = new CallbackFuture();
139         mRoleManager.clearRoleHoldersAsUser(roleName, flags, userId, future.createCallback());
140         return future.waitForResult();
141     }
142 
143     @Override
onHelp()144     public void onHelp() {
145         PrintWriter pw = getOutPrintWriter();
146         pw.println("Role (role) commands:");
147         pw.println("  help or -h");
148         pw.println("    Print this help text.");
149         pw.println();
150         pw.println("  add-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]");
151         pw.println("  remove-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]");
152         pw.println("  clear-role-holders [--user USER_ID] ROLE [FLAGS]");
153         pw.println();
154     }
155 }
156