1 /*
2  * Copyright (C) 2022 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.devicelockcontroller;
18 
19 import android.app.Service;
20 import android.app.admin.DevicePolicyManager;
21 import android.content.Intent;
22 import android.content.pm.PackageManager;
23 import android.devicelock.ParcelableException;
24 import android.os.Bundle;
25 import android.os.IBinder;
26 import android.os.RemoteCallback;
27 
28 import androidx.annotation.NonNull;
29 import androidx.annotation.Nullable;
30 
31 import com.android.devicelockcontroller.policy.DevicePolicyController;
32 import com.android.devicelockcontroller.policy.DeviceStateController;
33 import com.android.devicelockcontroller.policy.FinalizationController;
34 import com.android.devicelockcontroller.policy.PolicyObjectsProvider;
35 import com.android.devicelockcontroller.stats.StatsLogger;
36 import com.android.devicelockcontroller.stats.StatsLoggerProvider;
37 import com.android.devicelockcontroller.storage.GlobalParametersClient;
38 import com.android.devicelockcontroller.storage.SetupParametersClient;
39 import com.android.devicelockcontroller.util.LogUtil;
40 
41 import com.google.common.util.concurrent.FutureCallback;
42 import com.google.common.util.concurrent.Futures;
43 import com.google.common.util.concurrent.ListenableFuture;
44 import com.google.common.util.concurrent.MoreExecutors;
45 
46 import java.util.List;
47 import java.util.Objects;
48 
49 /**
50  * Device Lock Controller Service. This is hosted in an APK and is bound
51  * by the Device Lock System Service.
52  */
53 public final class DeviceLockControllerService extends Service {
54     private static final String TAG = "DeviceLockControllerService";
55     private DeviceStateController mDeviceStateController;
56     private DevicePolicyController mPolicyController;
57     private FinalizationController mFinalizationController;
58     private PackageManager mPackageManager;
59     private StatsLogger mStatsLogger;
60 
61     private final IDeviceLockControllerService.Stub mBinder =
62             new IDeviceLockControllerService.Stub() {
63                 @Override
64                 public void lockDevice(RemoteCallback remoteCallback) {
65                     logKioskAppRequest();
66                     ListenableFuture<Void> lockDeviceFuture = mDeviceStateController.lockDevice();
67                     Futures.addCallback(lockDeviceFuture,
68                             remoteCallbackWrapper(remoteCallback),
69                             MoreExecutors.directExecutor());
70                     Futures.addCallback(lockDeviceFuture,
71                             logLockUnlockDeviceCallback(/* isLockDevice = */ true),
72                             MoreExecutors.directExecutor());
73                 }
74 
75                 @Override
76                 public void unlockDevice(RemoteCallback remoteCallback) {
77                     logKioskAppRequest();
78                     ListenableFuture<Void> unlockDeviceFuture =
79                             mDeviceStateController.unlockDevice();
80                     Futures.addCallback(unlockDeviceFuture,
81                             remoteCallbackWrapper(remoteCallback),
82                             MoreExecutors.directExecutor());
83                     Futures.addCallback(unlockDeviceFuture,
84                             logLockUnlockDeviceCallback(/* isLockDevice = */ false),
85                             MoreExecutors.directExecutor());
86                 }
87 
88                 @Override
89                 public void isDeviceLocked(RemoteCallback remoteCallback) {
90                     logKioskAppRequest();
91                     Futures.addCallback(mDeviceStateController.isLocked(),
92                             remoteCallbackWrapper(remoteCallback, KEY_RESULT),
93                             MoreExecutors.directExecutor());
94                 }
95 
96                 @Override
97                 public void getDeviceIdentifier(RemoteCallback remoteCallback) {
98                     logKioskAppRequest();
99                     Futures.addCallback(
100                             GlobalParametersClient.getInstance().getRegisteredDeviceId(),
101                             remoteCallbackWrapper(remoteCallback, KEY_RESULT),
102                             MoreExecutors.directExecutor());
103                 }
104 
105                 @Override
106                 public void clearDeviceRestrictions(RemoteCallback remoteCallback) {
107                     logKioskAppRequest();
108                     Futures.addCallback(
109                             Futures.transformAsync(mDeviceStateController.clearDevice(),
110                                     unused -> mFinalizationController.notifyRestrictionsCleared(),
111                                     MoreExecutors.directExecutor()),
112                             remoteCallbackWrapper(remoteCallback),
113                             MoreExecutors.directExecutor());
114                 }
115 
116                 @Override
117                 public void onUserSwitching(RemoteCallback remoteCallback) {
118                     Futures.addCallback(
119                             Futures.transformAsync(mPolicyController.enforceCurrentPolicies(),
120                                     // Force read from disk in case it progressed on the other user
121                                     unused -> mFinalizationController.enforceDiskState(
122                                             /* force= */ true),
123                                     MoreExecutors.directExecutor()),
124                             remoteCallbackWrapper(remoteCallback),
125                             MoreExecutors.directExecutor());
126                 }
127 
128                 @Override
129                 public void onUserUnlocked(RemoteCallback remoteCallback) {
130                     DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
131                     Objects.requireNonNull(dpm).setUserControlDisabledPackages(
132                             /* admin= */ null,
133                             List.of(getPackageName()));
134                     Futures.addCallback(mPolicyController.onUserUnlocked(),
135                             remoteCallbackWrapper(remoteCallback),
136                             MoreExecutors.directExecutor());
137                 }
138 
139                 @Override
140                 public void onUserSetupCompleted(RemoteCallback remoteCallback) {
141                     Futures.addCallback(mPolicyController.onUserSetupCompleted(),
142                             remoteCallbackWrapper(remoteCallback),
143                             MoreExecutors.directExecutor());
144                 }
145 
146                 @Override
147                 public void onAppCrashed(boolean isKiosk, RemoteCallback remoteCallback) {
148                     Futures.addCallback(mPolicyController.onAppCrashed(isKiosk),
149                             remoteCallbackWrapper(remoteCallback),
150                             MoreExecutors.directExecutor());
151                 }
152 
153                 private void logKioskAppRequest() {
154                     Futures.addCallback(SetupParametersClient.getInstance().getKioskPackage(),
155                             new FutureCallback<>() {
156                                 @Override
157                                 public void onSuccess(String result) {
158                                     try {
159                                         final int uid = mPackageManager.getPackageUid(
160                                                 result, /* flags= */ 0);
161                                         mStatsLogger.logKioskAppRequest(uid);
162                                     } catch (PackageManager.NameNotFoundException e) {
163                                         LogUtil.e(TAG, "Kiosk App package name not found", e);
164                                     }
165                                 }
166 
167                                 @Override
168                                 public void onFailure(Throwable t) {
169                                     LogUtil.e(TAG, "Failed to get Kiosk app package name", t);
170                                 }
171                             },
172                             MoreExecutors.directExecutor());
173 
174                 }
175             };
176 
177     @NonNull
remoteCallbackWrapper(RemoteCallback remoteCallback, @Nullable final String key)178     private static FutureCallback<Object> remoteCallbackWrapper(RemoteCallback remoteCallback,
179             @Nullable final String key) {
180         return new FutureCallback<>() {
181             @Override
182             public void onSuccess(Object result) {
183                 sendResult(key, remoteCallback, result);
184             }
185 
186             @Override
187             public void onFailure(Throwable t) {
188                 LogUtil.e(TAG, "Failed to perform the request", t);
189                 sendFailure(t, remoteCallback);
190             }
191         };
192     }
193 
194     @NonNull
195     private static FutureCallback<Object> remoteCallbackWrapper(RemoteCallback remoteCallback) {
196         return remoteCallbackWrapper(remoteCallback, /* key= */ null);
197     }
198 
199     /**
200      * Send result to caller.
201      *
202      * @param key Key to use in bundle for result. null if no result is needed
203      * @param remoteCallback remote callback used to send the result.
204      * @param result Value to return in bundle.
205      */
206     private static void sendResult(@Nullable String key, RemoteCallback remoteCallback,
207             Object result) {
208         final Bundle bundle = new Bundle();
209         if (key != null) {
210             if (result instanceof Boolean) {
211                 bundle.putBoolean(key, (Boolean) result);
212             } else if (result instanceof String) {
213                 bundle.putString(key, (String) result);
214             }
215         }
216         remoteCallback.sendResult(bundle);
217     }
218 
219     private static void sendFailure(Throwable t, RemoteCallback remoteCallback) {
220         final Bundle bundle = new Bundle();
221         bundle.putParcelable(IDeviceLockControllerService.KEY_PARCELABLE_EXCEPTION,
222                 new ParcelableException(t instanceof Exception ? (Exception) t : new Exception(t)));
223         remoteCallback.sendResult(bundle);
224     }
225 
226     private FutureCallback<Void> logLockUnlockDeviceCallback(boolean isLockDevice) {
227         return new FutureCallback<Void>() {
228             @Override
229             public void onSuccess(Void result) {
230                 if (isLockDevice) {
231                     mStatsLogger.logSuccessfulLockingDevice();
232                 } else {
233                     mStatsLogger.logSuccessfulUnlockingDevice();
234                 }
235             }
236 
237             @Override
238             public void onFailure(Throwable t) {
239                 Futures.addCallback(mDeviceStateController.getDeviceState(),
240                         new FutureCallback<Integer>() {
241                             @Override
242                             public void onSuccess(Integer result) {
243                                 int deviceStatePostCommand;
244                                 switch (result) {
245                                     case DeviceStateController.DeviceState.UNLOCKED ->
246                                             deviceStatePostCommand =
247                                                     StatsLogger.DeviceStateStats.UNLOCKED;
248                                     case DeviceStateController.DeviceState.LOCKED ->
249                                             deviceStatePostCommand =
250                                                     StatsLogger.DeviceStateStats.LOCKED;
251                                     case DeviceStateController.DeviceState.CLEARED ->
252                                             deviceStatePostCommand =
253                                                     StatsLogger.DeviceStateStats.CLEARED;
254                                     case DeviceStateController.DeviceState.UNDEFINED ->
255                                             deviceStatePostCommand =
256                                                     StatsLogger.DeviceStateStats.UNDEFINED;
257                                     default -> deviceStatePostCommand =
258                                             StatsLogger.DeviceStateStats.UNDEFINED;
259                                 }
260                                 if (isLockDevice) {
261                                     mStatsLogger.logLockDeviceFailure(deviceStatePostCommand);
262                                 } else {
263                                     mStatsLogger.logUnlockDeviceFailure(deviceStatePostCommand);
264                                 }
265                             }
266 
267                             // We don't expect this to be reached
268                             @Override
269                             public void onFailure(Throwable t) {
270                                 LogUtil.e(TAG, "Failed to get device State", t);
271                                 throw new RuntimeException(t);
272                             }
273                         }, MoreExecutors.directExecutor());
274             }
275         };
276     }
277 
278     @Override
279     public void onCreate() {
280         LogUtil.d(TAG, "onCreate");
281 
282         final PolicyObjectsProvider policyObjects = (PolicyObjectsProvider) getApplication();
283         final StatsLoggerProvider statsLoggerProvider = (StatsLoggerProvider) getApplication();
284         mDeviceStateController = policyObjects.getDeviceStateController();
285         mPolicyController = policyObjects.getPolicyController();
286         mFinalizationController = policyObjects.getFinalizationController();
287         mPackageManager = getPackageManager();
288         mStatsLogger = statsLoggerProvider.getStatsLogger();
289     }
290 
291     @Override
292     public IBinder onBind(Intent intent) {
293         return mBinder;
294     }
295 }
296