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