/* * Copyright (C) 2022 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.common.DeviceLockConstants.EXTRA_ALLOW_DEBUGGING; import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_DISALLOW_INSTALLING_FROM_UNKNOWN_SOURCES; import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_KIOSK_ALLOWLIST; import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_KIOSK_APP_PROVIDER_NAME; import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_KIOSK_DISABLE_OUTGOING_CALLS; import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_KIOSK_ENABLE_NOTIFICATIONS_IN_LOCK_TASK_MODE; import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_KIOSK_PACKAGE; import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_MANDATORY_PROVISION; import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_PROVISIONING_TYPE; import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_SUPPORT_URL; import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_TERMS_AND_CONDITIONS_URL; import static com.android.devicelockcontroller.common.DeviceLockConstants.ProvisioningType.TYPE_UNDEFINED; import android.content.Context; import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; import android.util.ArraySet; import androidx.annotation.Nullable; import com.android.devicelockcontroller.common.DeviceLockConstants.ProvisioningType; import com.android.devicelockcontroller.util.LogUtil; import com.google.common.collect.ImmutableList; import java.util.Locale; import java.util.Set; /** * Stores setup parameters. *

* Note that these parameters are created by the system user at the setup time and must not be * written afterwards. *

* Also, these parameters are accessed globally by all users and must be accessed all the time via * the {@link SetupParametersClient}. */ final class SetupParameters { private static final String TAG = "SetupParameters"; private static final String FILENAME = "setup-prefs"; private static final String KEY_KIOSK_PACKAGE = "kiosk-package-name"; private static final String KEY_KIOSK_ALLOWLIST = "kiosk-allowlist"; private static final String KEY_KIOSK_DISABLE_OUTGOING_CALLS = "kiosk-disable-outgoing-calls"; private static final String KEY_KIOSK_ENABLE_NOTIFICATIONS_IN_LOCK_TASK_MODE = "kiosk-enable-notifications-in-lock-task-mode"; private static final String KEY_ALLOW_DEBUGGING = "allow-debugging"; private static final String KEY_PROVISIONING_TYPE = "provisioning-type"; private static final String KEY_MANDATORY_PROVISION = "mandatory-provision"; private static final String KEY_KIOSK_APP_PROVIDER_NAME = "kiosk-app-provider-name"; private static final String KEY_DISALLOW_INSTALLING_FROM_UNKNOWN_SOURCES = "disallow-installing-from-unknown-sources"; private static final String KEY_TERMS_AND_CONDITIONS_URL = "terms-and-conditions-url"; private static final String KEY_SUPPORT_URL = "support-url"; private SetupParameters() { } private static SharedPreferences getSharedPreferences(Context context) { Context deviceContext = context.createDeviceProtectedStorageContext(); return deviceContext.getSharedPreferences(FILENAME, Context.MODE_PRIVATE); } // Note that this API is only used for debugging purpose and should only be called in // debuggable build. static synchronized void overridePrefs(Context context, Bundle bundle) { if (!Build.isDebuggable()) { throw new SecurityException( "Setup parameters is not allowed to be override in non-debuggable build!"); } populatePreferencesLocked(getSharedPreferences(context), bundle); dumpParameters(context); } static void dumpParameters(Context context) { LogUtil.d(TAG, String.format(Locale.US, "Dumping SetupParameters ...\n" + "%s: %s\n" // kiosk-package-name: + "%s: %s\n" // kiosk-allowlist: + "%s: %s\n" // kiosk-disable-outgoing-calls: + "%s: %s\n" // kiosk-enable-notifications-in-lock-task-mode: + "%s: %s\n" // allow-debugging: + "%s: %d\n" // provisioning-type: + "%s: %s\n" // mandatory-provision: + "%s: %s\n" // kiosk-app-provider-name: + "%s: %s\n" // disallow-installing-from-unknown-sources: + "%s: %s\n" // terms-and-conditions-url: + "%s: %s\n", // support-url: KEY_KIOSK_PACKAGE, getKioskPackage(context), KEY_KIOSK_ALLOWLIST, getKioskAllowlist(context), KEY_KIOSK_DISABLE_OUTGOING_CALLS, getOutgoingCallsDisabled(context), KEY_KIOSK_ENABLE_NOTIFICATIONS_IN_LOCK_TASK_MODE, isNotificationsInLockTaskModeEnabled(context), KEY_ALLOW_DEBUGGING, isDebuggingAllowed(context), KEY_PROVISIONING_TYPE, getProvisioningType(context), KEY_MANDATORY_PROVISION, isProvisionMandatory(context), KEY_KIOSK_APP_PROVIDER_NAME, getKioskAppProviderName(context), KEY_DISALLOW_INSTALLING_FROM_UNKNOWN_SOURCES, isInstallingFromUnknownSourcesDisallowed(context), KEY_TERMS_AND_CONDITIONS_URL, getTermsAndConditionsUrl(context), KEY_SUPPORT_URL, getSupportUrl(context) )); } /** * Parse setup parameters from the extras bundle. * * @param context Application context * @param bundle Bundle with provisioning parameters. */ static synchronized void createPrefs(Context context, Bundle bundle) { SharedPreferences sharedPreferences = getSharedPreferences(context); if (sharedPreferences.contains(KEY_KIOSK_PACKAGE)) { LogUtil.i(TAG, "Setup parameters are already populated"); return; } populatePreferencesLocked(sharedPreferences, bundle); } private static void populatePreferencesLocked(SharedPreferences sharedPreferences, Bundle bundle) { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString(KEY_KIOSK_PACKAGE, bundle.getString(EXTRA_KIOSK_PACKAGE)); editor.putBoolean(KEY_KIOSK_DISABLE_OUTGOING_CALLS, bundle.getBoolean(EXTRA_KIOSK_DISABLE_OUTGOING_CALLS)); editor.putBoolean(KEY_KIOSK_ENABLE_NOTIFICATIONS_IN_LOCK_TASK_MODE, bundle.getBoolean(EXTRA_KIOSK_ENABLE_NOTIFICATIONS_IN_LOCK_TASK_MODE)); editor.putStringSet(KEY_KIOSK_ALLOWLIST, new ArraySet<>(bundle.getStringArrayList(EXTRA_KIOSK_ALLOWLIST))); editor.putInt(KEY_PROVISIONING_TYPE, bundle.getInt(EXTRA_PROVISIONING_TYPE)); editor.putBoolean(KEY_MANDATORY_PROVISION, bundle.getBoolean(EXTRA_MANDATORY_PROVISION)); editor.putBoolean(KEY_ALLOW_DEBUGGING, bundle.getBoolean(EXTRA_ALLOW_DEBUGGING)); editor.putString(KEY_KIOSK_APP_PROVIDER_NAME, bundle.getString(EXTRA_KIOSK_APP_PROVIDER_NAME)); editor.putBoolean(KEY_DISALLOW_INSTALLING_FROM_UNKNOWN_SOURCES, bundle.getBoolean(EXTRA_DISALLOW_INSTALLING_FROM_UNKNOWN_SOURCES)); editor.putString(KEY_TERMS_AND_CONDITIONS_URL, bundle.getString(EXTRA_TERMS_AND_CONDITIONS_URL)); editor.putString(KEY_SUPPORT_URL, bundle.getString(EXTRA_SUPPORT_URL)); editor.apply(); } /** * Get the name of the package implementing the kiosk app. * * @param context Context used to get the shared preferences. * @return kiosk app package name. */ @Nullable static String getKioskPackage(Context context) { return getSharedPreferences(context).getString(KEY_KIOSK_PACKAGE, null /* defValue */); } /** * Check if the configuration disables outgoing calls. * * @param context Context used to get the shared preferences. * @return True if outgoing calls are disabled. */ static boolean getOutgoingCallsDisabled(Context context) { return getSharedPreferences(context) .getBoolean(KEY_KIOSK_DISABLE_OUTGOING_CALLS, false /* defValue */); } /** * Get package allowlist provisioned by the server. * * @param context Context used to get the shared preferences. * @return List of allowed packages. */ static ImmutableList getKioskAllowlist(Context context) { SharedPreferences sharedPreferences = getSharedPreferences(context); Set allowlistSet = sharedPreferences.getStringSet(KEY_KIOSK_ALLOWLIST, null /* defValue */); return allowlistSet == null ? ImmutableList.of() : ImmutableList.copyOf(allowlistSet); } /** * Check if notifications are enabled in lock task mode. * * @param context Context used to get the shared preferences. * @return True if notification are enabled. */ static boolean isNotificationsInLockTaskModeEnabled(Context context) { return getSharedPreferences(context) .getBoolean(KEY_KIOSK_ENABLE_NOTIFICATIONS_IN_LOCK_TASK_MODE, false /* defValue */); } /** * Check if adb debugging is allowed even on prod devices. * * @param context Context used to get the shared preferences. * @return True if adb debugging is allowed. */ static boolean isDebuggingAllowed(Context context) { return getSharedPreferences(context).getBoolean(KEY_ALLOW_DEBUGGING, false /* defValue */); } /** * Get the provisioning type of this configuration. * * @param context Context used to get the shared preferences. * @return The type of provisioning which could be one of {@link ProvisioningType}. */ @ProvisioningType static int getProvisioningType(Context context) { return getSharedPreferences(context).getInt(KEY_PROVISIONING_TYPE, TYPE_UNDEFINED); } /** * Check if provision is mandatory. * * @param context Context used to get the shared preferences. * @return True if the provision should be mandatory. */ static boolean isProvisionMandatory(Context context) { return getSharedPreferences(context).getBoolean(KEY_MANDATORY_PROVISION, false); } /** * Get the name of the provider of the kiosk app. * * @param context Context used to get the shared preferences. * @return the name of the provider. */ @Nullable static String getKioskAppProviderName(Context context) { return getSharedPreferences(context).getString(KEY_KIOSK_APP_PROVIDER_NAME, null /* defValue */); } /** * Check if installing from unknown sources should be disallowed on this device after provision * * @param context Context used to get the shared preferences. * @return True if installing from unknown sources is disallowed. */ static boolean isInstallingFromUnknownSourcesDisallowed(Context context) { return getSharedPreferences(context).getBoolean( KEY_DISALLOW_INSTALLING_FROM_UNKNOWN_SOURCES, /* defValue= */ false); } /** * Get the URL to the terms and conditions of the partner for enrolling in a Device Lock * program. * * @param context Context used to get the shared preferences. * @return The URL to the terms and conditions. */ @Nullable static String getTermsAndConditionsUrl(Context context) { return getSharedPreferences(context).getString( KEY_TERMS_AND_CONDITIONS_URL, /* defValue= */ null); } /** * The URL to the support page the user can use to get help. * * @param context Context used to get the shared preferences. * @return The URL to the support page. */ @Nullable static String getSupportUrl(Context context) { return getSharedPreferences(context).getString( KEY_SUPPORT_URL, /* defValue= */ null); } static void clear(Context context) { if (!Build.isDebuggable()) { throw new SecurityException("Clear is not allowed in non-debuggable build!"); } getSharedPreferences(context).edit().clear().commit(); } }