/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.devicelockcontroller; import android.annotation.CallbackExecutor; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManager.PackageInfoFlags; import android.devicelock.DeviceLockManager; import android.devicelock.IDeviceLockService; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.OutcomeReceiver; import android.os.RemoteCallback; import android.os.RemoteException; import androidx.annotation.NonNull; import java.util.Objects; import java.util.concurrent.Executor; /** * Implementation for SystemDeviceLockManager. */ public final class SystemDeviceLockManagerImpl implements SystemDeviceLockManager { private static final String TAG = "SystemDeviceLockManagerImpl"; private final IDeviceLockService mIDeviceLockService; private final Context mContext; private SystemDeviceLockManagerImpl(Context context) { mContext = context; final DeviceLockManager deviceLockManager = context.getSystemService( DeviceLockManager.class); mIDeviceLockService = deviceLockManager.getService(); } private SystemDeviceLockManagerImpl() { this(DeviceLockControllerApplication.getAppContext()); } // Initialization-on-demand holder. private static final class SystemDeviceLockManagerHolder { static final SystemDeviceLockManagerImpl sSystemDeviceLockManager = new SystemDeviceLockManagerImpl(); } /** * Returns the lazily initialized singleton instance of the SystemDeviceLockManager. */ public static SystemDeviceLockManager getInstance() { return SystemDeviceLockManagerHolder.sSystemDeviceLockManager; } @Override public void addFinancedDeviceKioskRole(@NonNull String packageName, @CallbackExecutor Executor executor, @NonNull OutcomeReceiver callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { mIDeviceLockService.addFinancedDeviceKioskRole(packageName, new RemoteCallback(result -> executor.execute(() -> { processResult(result, callback, "Failed to add financed role to: " + packageName); }), new Handler(Looper.getMainLooper()))); } catch (RemoteException e) { executor.execute(() -> callback.onError(new RuntimeException(e))); } } @Override public void removeFinancedDeviceKioskRole(@NonNull String packageName, @CallbackExecutor Executor executor, @NonNull OutcomeReceiver callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { mIDeviceLockService.removeFinancedDeviceKioskRole(packageName, new RemoteCallback(result -> executor.execute(() -> { processResult(result, callback, "Failed to remove financed role from: " + packageName); }), new Handler(Looper.getMainLooper()))); } catch (RemoteException e) { executor.execute(() -> callback.onError(new RuntimeException(e))); } } @Override public void setDlcExemptFromActivityBgStartRestrictionState(boolean exempt, @CallbackExecutor Executor executor, @NonNull OutcomeReceiver callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { mIDeviceLockService.setCallerExemptFromActivityBgStartRestrictionState(exempt, new RemoteCallback(result -> executor.execute(() -> { processResult(result, callback, "Failed to change exempt from activity background start to: " + (exempt ? "exempt" : "non exempt")); }), new Handler(Looper.getMainLooper()))); } catch (RemoteException e) { executor.execute(() -> callback.onError(new RuntimeException(e))); } } @Override public void setDlcAllowedToSendUndismissibleNotifications(boolean allowed, @CallbackExecutor Executor executor, @NonNull OutcomeReceiver callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { mIDeviceLockService.setCallerAllowedToSendUndismissibleNotifications(allowed, new RemoteCallback(result -> executor.execute(() -> { processResult(result, callback, "Failed to change undismissible notifs allowed to: " + (allowed ? "allowed" : "not allowed")); }), new Handler(Looper.getMainLooper()))); } catch (RemoteException e) { executor.execute(() -> callback.onError(new RuntimeException(e))); } } @Override public void setKioskAppExemptFromRestrictionsState(String packageName, boolean exempt, Executor executor, @NonNull OutcomeReceiver callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { int kioskUid = mContext.getPackageManager().getPackageUid(packageName, PackageInfoFlags.of(0)); mIDeviceLockService.setUidExemptFromRestrictionsState(kioskUid, exempt, new RemoteCallback(result -> executor.execute(() -> { processResult(result, callback, "Failed to exempt for UID: " + kioskUid); }), new Handler(Looper.getMainLooper()))); } catch (RemoteException | PackageManager.NameNotFoundException e) { executor.execute(() -> callback.onError(new RuntimeException(e))); } } @Override public void enableKioskKeepalive(String packageName, Executor executor, @NonNull OutcomeReceiver callback) { Objects.requireNonNull(packageName); Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { mIDeviceLockService.enableKioskKeepalive(packageName, new RemoteCallback(result -> executor.execute(() -> { processResult(result, callback, "Failed to enable kiosk keep-alive for package: " + packageName); }), new Handler(Looper.getMainLooper()))); } catch (RemoteException e) { executor.execute(() -> callback.onError(new RuntimeException(e))); } } @Override public void disableKioskKeepalive(Executor executor, @NonNull OutcomeReceiver callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { mIDeviceLockService.disableKioskKeepalive( new RemoteCallback(result -> executor.execute(() -> { processResult(result, callback, "Failed to disable kiosk keep-alive"); }), new Handler(Looper.getMainLooper()))); } catch (RemoteException e) { executor.execute(() -> callback.onError(new RuntimeException(e))); } } @Override public void enableControllerKeepalive(Executor executor, @NonNull OutcomeReceiver callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { mIDeviceLockService.enableControllerKeepalive( new RemoteCallback(result -> executor.execute(() -> { processResult(result, callback, "Failed to enable controller keep-alive"); }), new Handler(Looper.getMainLooper()))); } catch (RemoteException e) { executor.execute(() -> callback.onError(new RuntimeException(e))); } } @Override public void disableControllerKeepalive(Executor executor, @NonNull OutcomeReceiver callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { mIDeviceLockService.disableControllerKeepalive( new RemoteCallback(result -> executor.execute(() -> { processResult(result, callback, "Failed to disable controller keep-alive"); }), new Handler(Looper.getMainLooper()))); } catch (RemoteException e) { executor.execute(() -> callback.onError(new RuntimeException(e))); } } @Override public void setDeviceFinalized(boolean finalized, Executor executor, @NonNull OutcomeReceiver callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { mIDeviceLockService.setDeviceFinalized(finalized, new RemoteCallback(result -> executor.execute(() -> { processResult(result, callback, "Failed to set device finalized"); }), new Handler(Looper.getMainLooper()))); } catch (RemoteException e) { executor.execute(() -> callback.onError(new RuntimeException(e))); } } @Override public void setPostNotificationsSystemFixed(boolean systemFixed, Executor executor, @NonNull OutcomeReceiver callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { mIDeviceLockService.setPostNotificationsSystemFixed(systemFixed, new RemoteCallback(result -> executor.execute(() -> { processResult(result, callback, "Failed to change POST_NOTIFICATIONS " + "SYSTEM_FIXED flag to: " + systemFixed); }), new Handler(Looper.getMainLooper()))); } catch (RemoteException e) { executor.execute(() -> callback.onError(new RuntimeException(e))); } } private static void processResult(Bundle result, @NonNull OutcomeReceiver callback, String message) { final boolean remoteCallbackResult = result.getBoolean( IDeviceLockService.KEY_REMOTE_CALLBACK_RESULT); if (remoteCallbackResult) { callback.onResult(null /* result */); } else { callback.onError(new Exception(message)); } } }