/* * 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.storage; import static com.android.devicelockcontroller.storage.ISetupParametersService.Stub.asInterface; import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.os.Bundle; import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.devicelockcontroller.DeviceLockControllerApplication; import com.android.devicelockcontroller.common.DeviceLockConstants.ProvisioningType; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import java.util.List; import java.util.concurrent.Executors; /** * Class used to access Setup Parameters from any users. * Storage is hosted by user 0 and is accessed indirectly using a service. */ public final class SetupParametersClient extends DlcClient implements SetupParametersClientInterface { private static final Object sInstanceLock = new Object(); @SuppressLint("StaticFieldLeak") // Only holds application context. @GuardedBy("sInstanceLock") private static SetupParametersClient sClient; private SetupParametersClient(@NonNull Context context, ListeningExecutorService executorService) { super(context, new ComponentName(context, SetupParametersService.class), executorService); } /** * Get the SetupParametersClient singleton instance. */ public static SetupParametersClient getInstance() { return getInstance(DeviceLockControllerApplication.getAppContext(), /* executorService= */ null); } /** * Get the SetupParametersClient singleton instance. */ @VisibleForTesting public static SetupParametersClient getInstance(Context appContext, @Nullable ListeningExecutorService executorService) { synchronized (sInstanceLock) { if (sClient == null) { sClient = new SetupParametersClient( appContext, executorService == null ? MoreExecutors.listeningDecorator(Executors.newCachedThreadPool()) : executorService); } return sClient; } } /** * Reset the Client singleton instance */ @VisibleForTesting public static void reset() { synchronized (sInstanceLock) { if (sClient != null) { sClient.tearDown(); sClient = null; } } } /** * Override setup parameters if there exists any; otherwise create new parameters. * Note that this API can only be called in debuggable build for debugging purpose. */ @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture overridePrefs(Bundle bundle) { return call(() -> { asInterface(getService()).overridePrefs(bundle); return null; }); } /** * Dump current values of SetupParameters to logcat. * Note that this API can only be called in debuggable build for debugging purpose. */ @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture dump() { return call(() -> { asInterface(getService()).dump(); return null; }); } /** * Clear any existing setup parameters. * Note that this API can only be called in debuggable build for debugging purpose. */ @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture clear() { return call(() -> { asInterface(getService()).clear(); return null; }); } /** * Parse setup parameters from the extras bundle. * * @param bundle Bundle with provisioning parameters. */ @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture createPrefs(Bundle bundle) { return call(() -> { asInterface(getService()).createPrefs(bundle); return null; }); } /** * Get the name of the package implementing the kiosk app. * * @return kiosk app package name. */ @Override @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture getKioskPackage() { return call(() -> asInterface(getService()).getKioskPackage()); } /** * Check if the configuration disables outgoing calls. * * @return True if outgoign calls are disabled. */ @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture getOutgoingCallsDisabled() { return call(() -> asInterface(getService()).getOutgoingCallsDisabled()); } /** * Get package allowlist provisioned by the server. * * @return List of allowed packages. */ @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture> getKioskAllowlist() { return call(() -> asInterface(getService()).getKioskAllowlist()); } /** * Check if notifications are enabled in lock task mode. * * @return True if notification are enabled. */ @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture isNotificationsInLockTaskModeEnabled() { return call(() -> asInterface(getService()).isNotificationsInLockTaskModeEnabled()); } /** * Check if adb debugging is allowed even on prod devices. * * @return True if adb debugging is allowed */ @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture isDebuggingAllowed() { return call(() -> asInterface(getService()).isDebuggingAllowed()); } /** * Get the provisioning type of this configuration. * * @return The type of provisioning which could be one of {@link ProvisioningType}. */ @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture<@ProvisioningType Integer> getProvisioningType() { return call(() -> asInterface(getService()).getProvisioningType()); } /** * Check if provision is mandatory. * * @return True if the provision should be mandatory. */ @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture isProvisionMandatory() { return call(() -> asInterface(getService()).isProvisionMandatory()); } /** * Get the name of the provider of the kiosk app. * * @return the name of the provider. */ @Nullable @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture getKioskAppProviderName() { return call(() -> asInterface(getService()).getKioskAppProviderName()); } /** * Check if installing from unknown sources should be disallowed on this device after * provision * * @return True if installing from unknown sources is disallowed. */ @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture isInstallingFromUnknownSourcesDisallowed() { return call(() -> asInterface(getService()).isInstallingFromUnknownSourcesDisallowed()); } /** * Get the Terms and Conditions URL of the partner for enrolling in a Device Lock program. * * @return The URL to the terms and conditions. */ @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture getTermsAndConditionsUrl() { return call(() -> asInterface(getService()).getTermsAndConditionsUrl()); } /** * The URL to the support page the user can use to get help. * * @return The URL to the support page. */ @SuppressWarnings("GuardedBy") // mLock already held in "call" (error prone). public ListenableFuture getSupportUrl() { return call(() -> asInterface(getService()).getSupportUrl()); } }