1 /*
2  * Copyright (C) 2021 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.car.admin;
18 
19 import static com.android.car.PermissionHelper.checkHasDumpPermissionGranted;
20 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
21 
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.UserIdInt;
26 import android.app.ActivityManager;
27 import android.app.admin.DevicePolicyManager;
28 import android.car.admin.CarDevicePolicyManager;
29 import android.car.admin.ICarDevicePolicyService;
30 import android.car.builtin.os.UserManagerHelper;
31 import android.car.builtin.util.Slogf;
32 import android.car.user.UserCreationRequest;
33 import android.car.user.UserCreationResult;
34 import android.car.user.UserRemovalResult;
35 import android.car.user.UserStartResult;
36 import android.car.user.UserStopResult;
37 import android.car.util.concurrent.AndroidFuture;
38 import android.content.BroadcastReceiver;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.IntentFilter;
42 import android.content.pm.PackageManager;
43 import android.os.UserHandle;
44 import android.os.UserManager;
45 import android.util.SparseIntArray;
46 import android.util.proto.ProtoOutputStream;
47 
48 import com.android.car.BuiltinPackageDependency;
49 import com.android.car.CarLog;
50 import com.android.car.CarServiceBase;
51 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
52 import com.android.car.internal.ResultCallbackImpl;
53 import com.android.car.internal.common.UserHelperLite;
54 import com.android.car.internal.os.CarSystemProperties;
55 import com.android.car.internal.util.DebugUtils;
56 import com.android.car.internal.util.IndentingPrintWriter;
57 import com.android.car.user.CarUserService;
58 import com.android.internal.annotations.GuardedBy;
59 import com.android.internal.annotations.VisibleForTesting;
60 
61 import java.lang.annotation.Retention;
62 import java.lang.annotation.RetentionPolicy;
63 
64 /**
65  * Service for device policy related features.
66  */
67 public final class CarDevicePolicyService extends ICarDevicePolicyService.Stub
68         implements CarServiceBase {
69 
70     @VisibleForTesting
71     static final String TAG = CarLog.tagFor(CarDevicePolicyService.class);
72 
73     private static final int HAL_TIMEOUT_MS = CarSystemProperties.getUserHalTimeout().orElse(5_000);
74     private static final String PREFIX_NEW_USER_DISCLAIMER_STATUS = "NEW_USER_DISCLAIMER_STATUS_";
75 
76     // TODO(b/175057848) must be public because of DebugUtils.constantToString()
77     public static final int NEW_USER_DISCLAIMER_STATUS_NEVER_RECEIVED = 0;
78     public static final int NEW_USER_DISCLAIMER_STATUS_RECEIVED = 1;
79     public static final int NEW_USER_DISCLAIMER_STATUS_NOTIFICATION_SENT = 2;
80     public static final int NEW_USER_DISCLAIMER_STATUS_SHOWN = 3;
81     public static final int NEW_USER_DISCLAIMER_STATUS_ACKED = 4;
82 
83     private final Object mLock = new Object();
84     private final CarUserService mCarUserService;
85     private final Context mContext;
86     private final Context mCarServiceBuiltinPackageContext;
87 
88     @Retention(RetentionPolicy.SOURCE)
89     @IntDef(flag = false, prefix = { PREFIX_NEW_USER_DISCLAIMER_STATUS }, value = {
90             NEW_USER_DISCLAIMER_STATUS_NEVER_RECEIVED,
91             NEW_USER_DISCLAIMER_STATUS_NOTIFICATION_SENT,
92             NEW_USER_DISCLAIMER_STATUS_RECEIVED,
93             NEW_USER_DISCLAIMER_STATUS_SHOWN,
94             NEW_USER_DISCLAIMER_STATUS_ACKED
95     })
96     public @interface NewUserDisclaimerStatus {}
97 
98     @GuardedBy("mLock")
99     private final SparseIntArray mUserDisclaimerStatusPerUser = new SparseIntArray();
100 
101     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
102         @Override
103         public void onReceive(Context context, Intent intent) {
104             int userId = ActivityManager.getCurrentUser();
105             Slogf.d(TAG, "Received intent for user " + userId + ": " + intent);
106             if (!mContext.getPackageManager()
107                     .hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
108                 Slogf.d(TAG, "Not handling ACTION_SHOW_NEW_USER_DISCLAIMER because device "
109                         + "doesn't have %s", PackageManager.FEATURE_DEVICE_ADMIN);
110                 return;
111             }
112             switch(intent.getAction()) {
113                 case DevicePolicyManager.ACTION_SHOW_NEW_USER_DISCLAIMER:
114                     Slogf.d(TAG, "Action show new user disclaimer");
115                     setUserDisclaimerStatus(userId, NEW_USER_DISCLAIMER_STATUS_RECEIVED);
116                     showNewUserDisclaimer(userId);
117                     break;
118                 default:
119                     Slogf.w(TAG, "received unexpected intent: %s" , intent);
120             }
121         }
122     };
123 
CarDevicePolicyService(@onNull Context context, @NonNull Context carServiceBuiltinPackageContext, @NonNull CarUserService carUserService)124     public CarDevicePolicyService(@NonNull Context context,
125             @NonNull Context carServiceBuiltinPackageContext,
126             @NonNull CarUserService carUserService) {
127         mCarUserService = carUserService;
128         mContext = context;
129         mCarServiceBuiltinPackageContext = carServiceBuiltinPackageContext;
130     }
131 
132     @Override
init()133     public void init() {
134         Slogf.d(TAG, "init()");
135         mContext.registerReceiverForAllUsers(mBroadcastReceiver,
136                 new IntentFilter(DevicePolicyManager.ACTION_SHOW_NEW_USER_DISCLAIMER),
137                 /* broadcastPermissions= */ null, /* scheduler= */ null,
138                 Context.RECEIVER_NOT_EXPORTED);
139     }
140 
141     @Override
release()142     public void release() {
143         Slogf.d(TAG, "release()");
144         mContext.unregisterReceiver(mBroadcastReceiver);
145     }
146 
147     @Override
removeUser(@serIdInt int userId, ResultCallbackImpl<UserRemovalResult> callback)148     public void removeUser(@UserIdInt int userId, ResultCallbackImpl<UserRemovalResult> callback) {
149         mCarUserService.removeUser(userId, /* hasCallerRestrictions= */ true, callback);
150     }
151 
152     @Override
createUser(@ullable String name, @CarDevicePolicyManager.UserType int type, ResultCallbackImpl<UserCreationResult> callback)153     public void createUser(@Nullable String name, @CarDevicePolicyManager.UserType int type,
154             ResultCallbackImpl<UserCreationResult> callback) {
155         UserCreationRequest.Builder userCreationRequestBuilder =
156                 new UserCreationRequest.Builder().setName(name);
157         int userInfoFlags = 0;
158         String userType = UserManager.USER_TYPE_FULL_SECONDARY;
159         switch(type) {
160             case CarDevicePolicyManager.USER_TYPE_REGULAR:
161                 break;
162             case CarDevicePolicyManager.USER_TYPE_ADMIN:
163                 userInfoFlags = UserManagerHelper.FLAG_ADMIN;
164                 userCreationRequestBuilder.setAdmin();
165                 break;
166             case CarDevicePolicyManager.USER_TYPE_GUEST:
167                 userType = UserManager.USER_TYPE_FULL_GUEST;
168                 userCreationRequestBuilder.setGuest();
169                 break;
170             default:
171                 Slogf.d(TAG, "createUser(): invalid userType (%s) / flags (%08x) "
172                         + "combination", userType, userInfoFlags);
173                 callback.complete(
174                         new UserCreationResult(UserCreationResult.STATUS_INVALID_REQUEST));
175                 return;
176         }
177 
178         Slogf.d(TAG, "calling createUser(%s, %s, %d, %d)",
179                 UserHelperLite.safeName(name), userType, userInfoFlags, HAL_TIMEOUT_MS);
180 
181         mCarUserService.createUser(userCreationRequestBuilder.build(), HAL_TIMEOUT_MS,
182                 callback);
183     }
184 
185     @Override
startUserInBackground(@serIdInt int userId, AndroidFuture<UserStartResult> receiver)186     public void startUserInBackground(@UserIdInt int userId,
187             AndroidFuture<UserStartResult> receiver) {
188         mCarUserService.startUserInBackground(userId, receiver);
189     }
190 
191     @Override
stopUser(@serIdInt int userId, AndroidFuture<UserStopResult> receiver)192     public void stopUser(@UserIdInt int userId, AndroidFuture<UserStopResult> receiver) {
193         mCarUserService.stopUser(userId, receiver);
194     }
195 
196     @Override
197     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(@onNull IndentingPrintWriter writer)198     public void dump(@NonNull IndentingPrintWriter writer) {
199         checkHasDumpPermissionGranted(mContext, "dump()");
200 
201         writer.println("*CarDevicePolicyService*");
202 
203         synchronized (mLock) {
204             int numUsers = mUserDisclaimerStatusPerUser.size();
205             writer.println("**mDisclaimerStatusPerUser**");
206             for (int i = 0; i < numUsers; i++) {
207                 int userId = mUserDisclaimerStatusPerUser.keyAt(i);
208                 int status = mUserDisclaimerStatusPerUser.get(userId);
209                 writer.printf("userId=%d disclaimerStatus=%s\n", userId,
210                         newUserDisclaimerStatusToString(status));
211             }
212         }
213 
214         writer.printf("HAL_TIMEOUT_MS: %d\n", HAL_TIMEOUT_MS);
215     }
216 
217     @Override
218     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dumpProto(ProtoOutputStream proto)219     public void dumpProto(ProtoOutputStream proto) {}
220 
221     /**
222      * Updates the internal state with the disclaimer status as shown.
223      */
224     @Override
setUserDisclaimerShown(int userId)225     public void setUserDisclaimerShown(int userId) {
226         setUserDisclaimerStatus(userId, NEW_USER_DISCLAIMER_STATUS_SHOWN);
227     }
228 
229     /**
230      * Updates the internal state with the disclaimer status as acknowledged.
231      */
232     @Override
setUserDisclaimerAcknowledged(int userId)233     public void setUserDisclaimerAcknowledged(int userId) {
234         setUserDisclaimerStatus(userId, NEW_USER_DISCLAIMER_STATUS_ACKED);
235         UserHandle user = UserHandle.of(userId);
236         BuiltinPackageDependency.createNotificationHelper(mCarServiceBuiltinPackageContext)
237                 .cancelUserDisclaimerNotification(user);
238 
239         DevicePolicyManager dpm = mContext.createContextAsUser(user, 0)
240                 .getSystemService(DevicePolicyManager.class);
241         dpm.acknowledgeNewUserDisclaimer();
242     }
243 
244     @VisibleForTesting
245     @NewUserDisclaimerStatus
getNewUserDisclaimerStatus(int userId)246     int getNewUserDisclaimerStatus(int userId) {
247         synchronized (mLock) {
248             return mUserDisclaimerStatusPerUser.get(userId,
249                     NEW_USER_DISCLAIMER_STATUS_NEVER_RECEIVED);
250         }
251     }
252 
showNewUserDisclaimer(@serIdInt int userId)253     private void showNewUserDisclaimer(@UserIdInt int userId) {
254         // TODO(b/175057848) persist status so it's shown again if car service crashes?
255 
256         BuiltinPackageDependency.createNotificationHelper(mCarServiceBuiltinPackageContext)
257                 .showUserDisclaimerNotification(UserHandle.of(userId));
258 
259         setUserDisclaimerStatus(userId, NEW_USER_DISCLAIMER_STATUS_NOTIFICATION_SENT);
260     }
261 
setUserDisclaimerStatus(@serIdInt int userId, @NewUserDisclaimerStatus int status)262     private void setUserDisclaimerStatus(@UserIdInt int userId,
263             @NewUserDisclaimerStatus int status) {
264         synchronized (mLock) {
265             Slogf.d(TAG, "Changing status from %s to %s",
266                     newUserDisclaimerStatusToString(
267                             mUserDisclaimerStatusPerUser.get(
268                                     userId, NEW_USER_DISCLAIMER_STATUS_NEVER_RECEIVED)),
269                     newUserDisclaimerStatusToString(status));
270             mUserDisclaimerStatusPerUser.put(userId, status);
271         }
272     }
273 
274     @VisibleForTesting
newUserDisclaimerStatusToString(@ewUserDisclaimerStatus int status)275     static String newUserDisclaimerStatusToString(@NewUserDisclaimerStatus int status) {
276         return DebugUtils.constantToString(CarDevicePolicyService.class,
277                 PREFIX_NEW_USER_DISCLAIMER_STATUS, status);
278     }
279 }
280