summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOleg Kibirev <olegk@google.com>2023-12-07 00:46:49 +0000
committerOleg Kibirev <olegk@google.com>2023-12-08 19:45:50 +0000
commite6920c7ede6abcf134750582b732cba13c11993b (patch)
tree782335eb43a1eadf8d04d3c6bc2f467fb04edaf2
parentdc52b5a3021574014c0fd0b9058751e454856abe (diff)
downloadTvSettings-e6920c7ede6abcf134750582b732cba13c11993b.tar.gz
Provide Watson TV Settings intents to create or enter restricted profile
This allows setupwraith to trigger these operations for regulatory compliance. Bug: 312260906 To test: adb shell am start -a com.android.tv.settings.action.CREATE_RESTRICTED_PROFILE adb shell am start -a com.android.tv.settings.action.ENTER_RESTRICTED_PROFILE Programmatic callers can use startActivityForResult and check for RESULT_OK to ensure user completed requested operation. Change-Id: If96a078be35295fe09168393d1782592e0ebce70
-rw-r--r--Settings/AndroidManifest.xml24
-rw-r--r--Settings/res/values/strings.xml18
-rw-r--r--Settings/res/xml/create_restricted_profile.xml31
-rw-r--r--Settings/res/xml/enter_restricted_profile.xml35
-rw-r--r--Settings/src/com/android/tv/settings/system/BaseSecurityFragment.java403
-rw-r--r--Settings/src/com/android/tv/settings/system/CreateRestrictedProfileActivity.java52
-rw-r--r--Settings/src/com/android/tv/settings/system/CreateRestrictedProfileFragment.java89
-rw-r--r--Settings/src/com/android/tv/settings/system/EnterRestrictedProfileActivity.java49
-rw-r--r--Settings/src/com/android/tv/settings/system/EnterRestrictedProfileFragment.java97
-rw-r--r--Settings/src/com/android/tv/settings/system/SecurityFragment.java373
-rw-r--r--Settings/src/com/android/tv/settings/users/AppRestrictionsFragment.java28
11 files changed, 833 insertions, 366 deletions
diff --git a/Settings/AndroidManifest.xml b/Settings/AndroidManifest.xml
index c3d386cf4..5cf7e6dd4 100644
--- a/Settings/AndroidManifest.xml
+++ b/Settings/AndroidManifest.xml
@@ -507,6 +507,30 @@
</activity-alias>
<activity
+ android:name=".system.CreateRestrictedProfileActivity"
+ android:configChanges="keyboard|keyboardHidden|navigation"
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/TvSettingsActivityTheme">
+ <intent-filter>
+ <action android:name="com.android.tv.settings.action.CREATE_RESTRICTED_PROFILE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name=".system.EnterRestrictedProfileActivity"
+ android:configChanges="keyboard|keyboardHidden|navigation"
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:theme="@style/TvSettingsActivityTheme">
+ <intent-filter>
+ <action android:name="com.android.tv.settings.action.ENTER_RESTRICTED_PROFILE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <activity
android:name=".device.displaysound.DisplaySoundActivity"
android:configChanges="keyboard|keyboardHidden|navigation"
android:excludeFromRecents="true"
diff --git a/Settings/res/values/strings.xml b/Settings/res/values/strings.xml
index 93f8dcea2..c89af6377 100644
--- a/Settings/res/values/strings.xml
+++ b/Settings/res/values/strings.xml
@@ -2222,11 +2222,27 @@
<string name="restricted_profile_configure_apps_description_loading" >One moment\u2026</string>
<!-- Title for action to change the PIN code to use when exiting restricted profile [CHAR LIMIT=100] -->
<string name="restricted_profile_change_password_title" >Change pin</string>
+
+ <!-- Title of preference screen to create a restricted profile. [CHAR LIMIT=30] -->
+ <string name="restricted_profile_create_preference_screen">Create restricted profile?</string>
+ <!-- Placeholder to indicate that restricted profile is already created. [CHAR LIMIT=30] -->
+ <string name="restricted_profile_already_created_placeholder">Already Created</string>
+ <!-- Action to skip creating a restricted profile. [CHAR LIMIT=30] -->
+ <string name="restricted_profile_skip_action">Skip</string>
+ <!-- Action to create pin for a restricted profile. [CHAR LIMIT=30] -->
+ <string name="restricted_profile_create_pin_action">Create PIN</string>
+
+ <!-- Title of preference screen to create a restricted profile. [CHAR LIMIT=30] -->
+ <string name="restricted_profile_enter_preference_screen">Enter restricted profile?</string>
+ <!-- Placeholder to indicated restricted profile is not found. [CHAR LIMIT=30] -->
+ <string name="restricted_profile_enter_not_found_placeholder">No restricted profile</string>
+ <!-- Placeholder to indicated restricted profile is already entered. [CHAR LIMIT=30] -->
+ <string name="restricted_profile_enter_already_entered_placeholder">Already entered</string>
+
<!-- Description for action to configure an application's restriction [CHAR LIMIT=200] -->
<string name="restriction_description" ><xliff:g id="description">%1$s</xliff:g>\n<xliff:g id="value">%2$s</xliff:g></string>
<!-- Summary for a case when app entries that are controlled by another entry and app can access user accounts [CHAR LIMIT=none] -->
<string name="app_sees_restricted_accounts_and_controlled_by">This app can access your accounts. Controlled by <xliff:g id="app">%1$s</xliff:g></string>
-
<!-- PIN UX -->
<eat-comment />
<string name="font" translatable="false">sans-serif</string>
diff --git a/Settings/res/xml/create_restricted_profile.xml b/Settings/res/xml/create_restricted_profile.xml
new file mode 100644
index 000000000..b45c95f65
--- /dev/null
+++ b/Settings/res/xml/create_restricted_profile.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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
+ -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ android:title="@string/restricted_profile_create_preference_screen">
+ <Preference
+ android:key="restricted_profile_already_created"
+ android:title="@string/restricted_profile_already_created_placeholder"
+ android:enabled="false" />
+ <Preference
+ android:key="restricted_profile_skip"
+ android:title="@string/restricted_profile_skip_action" />
+ <Preference
+ android:key="restricted_profile_create"
+ android:title="@string/restricted_profile_create_pin_action" />
+
+</PreferenceScreen>
diff --git a/Settings/res/xml/enter_restricted_profile.xml b/Settings/res/xml/enter_restricted_profile.xml
new file mode 100644
index 000000000..1e0c78ef9
--- /dev/null
+++ b/Settings/res/xml/enter_restricted_profile.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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
+ -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ android:title="@string/restricted_profile_enter_preference_screen">
+ <Preference
+ android:key="restricted_profile_not_found"
+ android:title="@string/restricted_profile_enter_not_found_placeholder"
+ android:enabled="false" />
+ <Preference
+ android:key="restricted_profile_already_entered"
+ android:title="@string/restricted_profile_enter_already_entered_placeholder"
+ android:enabled="false" />
+ <Preference
+ android:key="restricted_profile_skip"
+ android:title="@string/restricted_profile_skip_action" />
+ <Preference
+ android:key="restricted_profile_enter"
+ android:title="@string/restricted_profile_switch_to" />
+
+</PreferenceScreen>
diff --git a/Settings/src/com/android/tv/settings/system/BaseSecurityFragment.java b/Settings/src/com/android/tv/settings/system/BaseSecurityFragment.java
new file mode 100644
index 000000000..f5e3a186c
--- /dev/null
+++ b/Settings/src/com/android/tv/settings/system/BaseSecurityFragment.java
@@ -0,0 +1,403 @@
+/*
+ * 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.tv.settings.system;
+
+import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected;
+
+import android.accounts.AccountManager;
+import android.annotation.SuppressLint;
+import android.app.admin.DevicePolicyManager;
+import android.app.tvsettings.TvSettingsEnums;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.IntDef;
+import androidx.annotation.Keep;
+import androidx.fragment.app.Fragment;
+import androidx.leanback.preference.LeanbackSettingsFragmentCompat;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceGroup;
+
+import com.android.tv.settings.R;
+import com.android.tv.settings.SettingsPreferenceFragment;
+import com.android.tv.settings.dialog.PinDialogFragment;
+import com.android.tv.settings.users.AppRestrictionsFragment;
+import com.android.tv.settings.users.RestrictedProfileModel;
+import com.android.tv.settings.users.RestrictedProfilePinDialogFragment;
+import com.android.tv.settings.users.RestrictedProfilePinStorage;
+import com.android.tv.settings.users.UserSwitchListenerService;
+import com.android.tv.twopanelsettings.TwoPanelSettingsFragment;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Base fragment for security settings.
+ */
+abstract class BaseSecurityFragment extends SettingsPreferenceFragment
+ implements PinDialogFragment.ResultListener {
+
+ private static final String TAG = "BaseSecurityFragment";
+
+ protected static final String KEY_UNKNOWN_SOURCES = "unknown_sources";
+ protected static final String KEY_RESTRICTED_PROFILE_GROUP = "restricted_profile_group";
+ protected static final String KEY_RESTRICTED_PROFILE_ENTER = "restricted_profile_enter";
+ protected static final String KEY_RESTRICTED_PROFILE_EXIT = "restricted_profile_exit";
+ protected static final String KEY_RESTRICTED_PROFILE_APPS = "restricted_profile_apps";
+ protected static final String KEY_RESTRICTED_PROFILE_PIN = "restricted_profile_pin";
+ protected static final String KEY_RESTRICTED_PROFILE_CREATE = "restricted_profile_create";
+ protected static final String KEY_RESTRICTED_PROFILE_DELETE = "restricted_profile_delete";
+ protected static final String KEY_RESTRICTED_PROFILE_SKIP = "restricted_profile_skip";
+ protected static final String KEY_MANAGE_DEVICE_ADMIN = "manage_device_admin";
+ protected static final String KEY_ENTERPRISE_PRIVACY = "enterprise_privacy";
+
+ private static final String ACTION_RESTRICTED_PROFILE_CREATED =
+ "SecurityFragment.RESTRICTED_PROFILE_CREATED";
+ private static final String EXTRA_RESTRICTED_PROFILE_INFO =
+ "SecurityFragment.RESTRICTED_PROFILE_INFO";
+ private static final String SAVESTATE_CREATING_RESTRICTED_PROFILE =
+ "SecurityFragment.CREATING_RESTRICTED_PROFILE";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({PIN_MODE_CHOOSE_LOCKSCREEN,
+ PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT,
+ PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD,
+ PIN_MODE_RESTRICTED_PROFILE_DELETE})
+ private @interface PinMode {}
+ private static final int PIN_MODE_CHOOSE_LOCKSCREEN = 1;
+ private static final int PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT = 2;
+ private static final int PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD = 3;
+ private static final int PIN_MODE_RESTRICTED_PROFILE_DELETE = 4;
+
+
+ protected RestrictedProfileModel mRestrictedProfile;
+
+ private boolean mCreatingRestrictedProfile;
+ private RestrictedProfilePinStorage mRestrictedProfilePinStorage;
+
+ @SuppressLint("StaticFieldLeak")
+ private static CreateRestrictedProfileTask sCreateRestrictedProfileTask;
+ private final BroadcastReceiver mRestrictedProfileReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ UserInfo result = intent.getParcelableExtra(EXTRA_RESTRICTED_PROFILE_INFO);
+ if (isResumed()) {
+ onRestrictedUserCreated(result);
+ }
+ }
+ };
+
+ private Handler mUiThreadHandler;
+ private HandlerThread mBackgroundHandlerThread;
+ private Handler mBackgroundHandler;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mRestrictedProfile = new RestrictedProfileModel(getContext());
+
+ super.onCreate(savedInstanceState);
+ mCreatingRestrictedProfile = savedInstanceState != null
+ && savedInstanceState.getBoolean(SAVESTATE_CREATING_RESTRICTED_PROFILE);
+
+ mUiThreadHandler = new Handler();
+ mBackgroundHandlerThread = new HandlerThread("SecurityFragmentBackgroundThread");
+ mBackgroundHandlerThread.start();
+ mBackgroundHandler = new Handler(mBackgroundHandlerThread.getLooper());
+ }
+
+ @Override
+ public void onDestroy() {
+ mBackgroundHandler = null;
+ mBackgroundHandlerThread.quitSafely();
+ mBackgroundHandlerThread = null;
+ mUiThreadHandler = null;
+
+ super.onDestroy();
+
+ mRestrictedProfile = null;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ refresh();
+ LocalBroadcastManager.getInstance(getActivity())
+ .registerReceiver(mRestrictedProfileReceiver,
+ new IntentFilter(ACTION_RESTRICTED_PROFILE_CREATED));
+ if (mCreatingRestrictedProfile) {
+ UserInfo userInfo = mRestrictedProfile.getUser();
+ if (userInfo != null) {
+ onRestrictedUserCreated(userInfo);
+ }
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ LocalBroadcastManager.getInstance(getActivity())
+ .unregisterReceiver(mRestrictedProfileReceiver);
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mRestrictedProfilePinStorage = RestrictedProfilePinStorage.newInstance(getContext());
+ mRestrictedProfilePinStorage.bind();
+ }
+
+ @Override
+ public void onDetach() {
+ mRestrictedProfilePinStorage.unbind();
+ mRestrictedProfilePinStorage = null;
+ super.onDetach();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVESTATE_CREATING_RESTRICTED_PROFILE, mCreatingRestrictedProfile);
+ }
+
+
+ abstract protected void refresh();
+
+ @Override
+ public boolean onPreferenceTreeClick(Preference preference) {
+ final String key = preference.getKey();
+ if (TextUtils.isEmpty(key)) {
+ return super.onPreferenceTreeClick(preference);
+ }
+ switch (key) {
+ case KEY_RESTRICTED_PROFILE_ENTER:
+ logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_ENTER_PROFILE);
+ if (mRestrictedProfile.enterUser()) {
+ getActivity().finish();
+ }
+ return true;
+ case KEY_RESTRICTED_PROFILE_EXIT:
+ logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_EXIT_PROFILE);
+ launchPinDialog(PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT);
+ return true;
+ case KEY_RESTRICTED_PROFILE_PIN:
+ logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_PROFILE_CHANGE_PIN);
+ launchPinDialog(PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD);
+ return true;
+ case KEY_RESTRICTED_PROFILE_CREATE:
+ logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_CREATE_PROFILE);
+ createRestrictedProfile();
+ return true;
+ case KEY_RESTRICTED_PROFILE_DELETE:
+ logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_DELETE_PROFILE);
+ launchPinDialog(PIN_MODE_RESTRICTED_PROFILE_DELETE);
+ return true;
+ }
+ return super.onPreferenceTreeClick(preference);
+ }
+
+ private void createRestrictedProfile() {
+ mBackgroundHandler.post(() -> {
+ boolean pinIsSet = mRestrictedProfilePinStorage.isPinSet();
+
+ mUiThreadHandler.post(() -> {
+ if (pinIsSet) {
+ addRestrictedUser();
+ } else {
+ launchPinDialog(PIN_MODE_CHOOSE_LOCKSCREEN);
+ }
+ });
+ });
+ }
+
+ private void launchPinDialog(@PinMode int pinMode) {
+ @PinDialogFragment.PinDialogType
+ int pinDialogMode;
+
+ switch (pinMode) {
+ case PIN_MODE_CHOOSE_LOCKSCREEN:
+ pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN;
+ break;
+ case PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT:
+ pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN;
+ break;
+ case PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD:
+ pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN;
+ break;
+ case PIN_MODE_RESTRICTED_PROFILE_DELETE:
+ pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_DELETE_PIN;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown pin mode: " + pinMode);
+ }
+
+ RestrictedProfilePinDialogFragment restrictedProfilePinDialogFragment =
+ RestrictedProfilePinDialogFragment.newInstance(pinDialogMode);
+ restrictedProfilePinDialogFragment.setTargetFragment(this, pinMode);
+ restrictedProfilePinDialogFragment.show(getFragmentManager(),
+ PinDialogFragment.DIALOG_TAG);
+ }
+
+ @Override
+ public void pinFragmentDone(int requestCode, boolean success) {
+ if (!success) {
+ Log.d(TAG, "Request " + requestCode + " unsuccessful.");
+ return;
+ }
+
+ switch (requestCode) {
+ case PIN_MODE_CHOOSE_LOCKSCREEN:
+ addRestrictedUser();
+ break;
+ case PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT:
+ mRestrictedProfile.exitUser();
+ mUiThreadHandler.post(() -> getActivity().finish());
+ break;
+ case PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD:
+ // do nothing
+ break;
+ case PIN_MODE_RESTRICTED_PROFILE_DELETE:
+ mUiThreadHandler.post(() -> {
+ mRestrictedProfile.removeUser();
+ UserSwitchListenerService.onUserCreatedOrDeleted(getActivity());
+ refresh();
+ });
+ break;
+ default:
+ Log.d(TAG, "Pin request code not recognised: " + requestCode);
+ }
+ }
+
+ private void addRestrictedUser() {
+ if (sCreateRestrictedProfileTask == null) {
+ sCreateRestrictedProfileTask = new CreateRestrictedProfileTask(getContext());
+ sCreateRestrictedProfileTask.execute();
+ mCreatingRestrictedProfile = true;
+ }
+ refresh();
+ }
+
+ private void onRestrictedUserCreated(UserInfo result) {
+ int userId = result.id;
+ if (result.isRestricted()
+ && result.restrictedProfileParentId == UserHandle.myUserId()) {
+ final AppRestrictionsFragment restrictionsFragment =
+ AppRestrictionsFragment.newInstance(userId, true,
+ shouldExitAfterUpdatingApps());
+ final Fragment settingsFragment = getCallbackFragment();
+ if (settingsFragment instanceof LeanbackSettingsFragmentCompat) {
+ ((LeanbackSettingsFragmentCompat) settingsFragment)
+ .startPreferenceFragment(restrictionsFragment);
+ } else if (settingsFragment instanceof TwoPanelSettingsFragment) {
+ ((TwoPanelSettingsFragment) settingsFragment)
+ .startPreferenceFragment(restrictionsFragment);
+ } else {
+ throw new IllegalStateException("Didn't find fragment of expected type: "
+ + settingsFragment);
+ }
+ }
+ mCreatingRestrictedProfile = false;
+ refresh();
+ }
+
+ protected boolean shouldExitAfterUpdatingApps() {
+ return false;
+ }
+
+ private static class CreateRestrictedProfileTask extends AsyncTask<Void, Void, UserInfo> {
+ private final Context mContext;
+ private final UserManager mUserManager;
+
+ CreateRestrictedProfileTask(Context context) {
+ mContext = context.getApplicationContext();
+ mUserManager = mContext.getSystemService(UserManager.class);
+ }
+
+ @Override
+ protected UserInfo doInBackground(Void... params) {
+ UserInfo restrictedUserInfo = mUserManager.createProfileForUser(
+ mContext.getString(R.string.user_new_profile_name),
+ UserManager.USER_TYPE_FULL_RESTRICTED, /* flags */ 0, UserHandle.myUserId());
+ if (restrictedUserInfo == null) {
+ final UserInfo existingUserInfo = new RestrictedProfileModel(mContext).getUser();
+ if (existingUserInfo == null) {
+ Log.wtf(TAG, "Got back a null user handle!");
+ }
+ return existingUserInfo;
+ }
+ int userId = restrictedUserInfo.id;
+ UserHandle user = new UserHandle(userId);
+ mUserManager.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user);
+ Bitmap bitmap = createBitmapFromDrawable(R.drawable.ic_avatar_default);
+ mUserManager.setUserIcon(userId, bitmap);
+ // Add shared accounts
+ AccountManager.get(mContext).addSharedAccountsFromParentUser(
+ UserHandle.of(UserHandle.myUserId()), user);
+ return restrictedUserInfo;
+ }
+
+ @Override
+ protected void onPostExecute(UserInfo result) {
+ sCreateRestrictedProfileTask = null;
+ if (result == null) {
+ return;
+ }
+ UserSwitchListenerService.onUserCreatedOrDeleted(mContext);
+ LocalBroadcastManager.getInstance(mContext).sendBroadcast(
+ new Intent(ACTION_RESTRICTED_PROFILE_CREATED)
+ .putExtra(EXTRA_RESTRICTED_PROFILE_INFO, result));
+ }
+
+ private Bitmap createBitmapFromDrawable(@DrawableRes int resId) {
+ Drawable icon = mContext.getDrawable(resId);
+ if (icon == null) {
+ throw new IllegalArgumentException("Drawable is missing!");
+ }
+ icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
+ Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(),
+ Bitmap.Config.ARGB_8888);
+ icon.draw(new Canvas(bitmap));
+ return bitmap;
+ }
+ }
+
+ protected boolean isRestrictedProfileCreationInProgress() {
+ return sCreateRestrictedProfileTask != null;
+ }
+
+ @Override
+ protected int getPageId() {
+ return TvSettingsEnums.APPS_SECURITY_RESTRICTIONS;
+ }
+}
diff --git a/Settings/src/com/android/tv/settings/system/CreateRestrictedProfileActivity.java b/Settings/src/com/android/tv/settings/system/CreateRestrictedProfileActivity.java
new file mode 100644
index 000000000..7f88cb0da
--- /dev/null
+++ b/Settings/src/com/android/tv/settings/system/CreateRestrictedProfileActivity.java
@@ -0,0 +1,52 @@
+/*
+ * 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.tv.settings.system;
+
+import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_CLASSIC;
+
+import android.os.Bundle;
+
+import androidx.annotation.Keep;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.tv.settings.TvSettingsActivity;
+
+/**
+ * A subset of security settings to create a restricted profile.
+ */
+@Keep
+public class CreateRestrictedProfileActivity extends TvSettingsActivity {
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().setDimAmount(0f);
+ }
+
+ @Override
+ protected Fragment createSettingsFragment() {
+ return com.android.tv.settings.overlay.FlavorUtils.getFeatureFactory(
+ this).getSettingsFragmentProvider()
+ .newSettingsFragment(CreateRestrictedProfileFragment.class.getName(), null);
+ }
+
+ @Override
+ protected int getAvailableFlavors() {
+ return FLAVOR_CLASSIC;
+ }
+}
diff --git a/Settings/src/com/android/tv/settings/system/CreateRestrictedProfileFragment.java b/Settings/src/com/android/tv/settings/system/CreateRestrictedProfileFragment.java
new file mode 100644
index 000000000..595366727
--- /dev/null
+++ b/Settings/src/com/android/tv/settings/system/CreateRestrictedProfileFragment.java
@@ -0,0 +1,89 @@
+/*
+ * 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.tv.settings.system;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.UserManager;
+
+import androidx.annotation.Keep;
+import androidx.preference.Preference;
+
+import com.android.tv.settings.R;
+import com.android.tv.settings.users.RestrictedProfileModel;
+
+/**
+ * A subset of security settings to create a restricted profile.
+ */
+@Keep
+public class CreateRestrictedProfileFragment extends BaseSecurityFragment {
+ private static final String KEY_RESTRICTED_PROFILE_ALREADY_CREATED =
+ "restricted_profile_already_created";
+
+ private Preference mRestrictedProfileCreatePref;
+ private Preference mRestrictedProfileAlreadyCreatedPref;
+
+ public static CreateRestrictedProfileFragment newInstance() {
+ return new CreateRestrictedProfileFragment();
+ }
+
+ /**
+ * Called by other Fragments to decide whether to show or hide profile-related views.
+ */
+ public static boolean isRestrictedProfileInEffect(Context context) {
+ return new RestrictedProfileModel(context).isCurrentUser();
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ setPreferencesFromResource(R.xml.create_restricted_profile, null);
+ mRestrictedProfileCreatePref = findPreference(KEY_RESTRICTED_PROFILE_CREATE);
+ mRestrictedProfileAlreadyCreatedPref = findPreference(
+ KEY_RESTRICTED_PROFILE_ALREADY_CREATED);
+ refresh();
+ }
+
+ @Override
+ protected void refresh() {
+ mRestrictedProfileCreatePref.setEnabled(
+ UserManager.supportsMultipleUsers() && !isRestrictedProfileCreationInProgress());
+ if (mRestrictedProfile.getUser() != null) {
+ mRestrictedProfileAlreadyCreatedPref.setVisible(true);
+ mRestrictedProfileCreatePref.setVisible(false);
+ } else {
+ mRestrictedProfileAlreadyCreatedPref.setVisible(false);
+ mRestrictedProfileCreatePref.setVisible(true);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(Preference preference) {
+ final String key = preference.getKey();
+ if (KEY_RESTRICTED_PROFILE_SKIP.equals(key)) {
+ requireActivity().setResult(Activity.RESULT_CANCELED);
+ requireActivity().finish();
+ return true;
+ }
+ return super.onPreferenceTreeClick(preference);
+ }
+
+ @Override
+ protected boolean shouldExitAfterUpdatingApps() {
+ return true;
+ }
+}
diff --git a/Settings/src/com/android/tv/settings/system/EnterRestrictedProfileActivity.java b/Settings/src/com/android/tv/settings/system/EnterRestrictedProfileActivity.java
new file mode 100644
index 000000000..6587b6b64
--- /dev/null
+++ b/Settings/src/com/android/tv/settings/system/EnterRestrictedProfileActivity.java
@@ -0,0 +1,49 @@
+/*
+ * 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.tv.settings.system;
+
+import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_CLASSIC;
+
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.android.tv.settings.TvSettingsActivity;
+
+/**
+ * A subset of security settings to enter a restricted profile.
+ */
+public class EnterRestrictedProfileActivity extends TvSettingsActivity {
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().setDimAmount(0f);
+ }
+
+ @Override
+ protected Fragment createSettingsFragment() {
+ return com.android.tv.settings.overlay.FlavorUtils.getFeatureFactory(this)
+ .getSettingsFragmentProvider()
+ .newSettingsFragment(EnterRestrictedProfileFragment.class.getName(), null);
+ }
+
+ @Override
+ protected int getAvailableFlavors() {
+ return FLAVOR_CLASSIC;
+ }
+}
diff --git a/Settings/src/com/android/tv/settings/system/EnterRestrictedProfileFragment.java b/Settings/src/com/android/tv/settings/system/EnterRestrictedProfileFragment.java
new file mode 100644
index 000000000..11e0a0f45
--- /dev/null
+++ b/Settings/src/com/android/tv/settings/system/EnterRestrictedProfileFragment.java
@@ -0,0 +1,97 @@
+/*
+ * 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.tv.settings.system;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.annotation.Keep;
+import androidx.preference.Preference;
+
+import com.android.tv.settings.R;
+import com.android.tv.settings.users.RestrictedProfileModel;
+
+/**
+ * A subset of security settings to enter a restricted profile.
+ */
+@Keep
+public class EnterRestrictedProfileFragment extends BaseSecurityFragment {
+ private static final String KEY_RESTRICTED_PROFILE_NOT_FOUND =
+ "restricted_profile_not_found";
+ private static final String KEY_RESTRICTED_PROFILE_ALREADY_ENTERED =
+ "restricted_profile_already_entered";
+
+ private Preference mRestrictedProfileEnterPref;
+ private Preference mRestrictedProfileNotFoundPerf;
+ private Preference mRestrictedProfileAlreadyEnteredPerf;
+
+ public static EnterRestrictedProfileFragment newInstance() {
+ return new EnterRestrictedProfileFragment();
+ }
+
+ /**
+ * Called by other Fragments to decide whether to show or hide profile-related views.
+ */
+ public static boolean isRestrictedProfileInEffect(Context context) {
+ return new RestrictedProfileModel(context).isCurrentUser();
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ setPreferencesFromResource(R.xml.enter_restricted_profile, null);
+ mRestrictedProfileEnterPref = findPreference(KEY_RESTRICTED_PROFILE_ENTER);
+ mRestrictedProfileNotFoundPerf = findPreference(KEY_RESTRICTED_PROFILE_NOT_FOUND);
+ mRestrictedProfileAlreadyEnteredPerf = findPreference(
+ KEY_RESTRICTED_PROFILE_ALREADY_ENTERED);
+ refresh();
+ }
+
+ @Override
+ protected void refresh() {
+ if (mRestrictedProfile.isCurrentUser()) {
+ mRestrictedProfileAlreadyEnteredPerf.setVisible(true);
+ mRestrictedProfileNotFoundPerf.setVisible(false);
+ mRestrictedProfileEnterPref.setVisible(false);
+ } else if (mRestrictedProfile.getUser() == null) {
+ mRestrictedProfileAlreadyEnteredPerf.setVisible(false);
+ mRestrictedProfileNotFoundPerf.setVisible(true);
+ mRestrictedProfileEnterPref.setVisible(false);
+ } else {
+ mRestrictedProfileAlreadyEnteredPerf.setVisible(false);
+ mRestrictedProfileNotFoundPerf.setVisible(false);
+ mRestrictedProfileEnterPref.setVisible(true);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(Preference preference) {
+ final String key = preference.getKey();
+ if (KEY_RESTRICTED_PROFILE_SKIP.equals(key)) {
+ requireActivity().setResult(Activity.RESULT_CANCELED);
+ requireActivity().finish();
+ return true;
+ }
+ requireActivity().setResult(Activity.RESULT_OK); // For enter.
+ return super.onPreferenceTreeClick(preference);
+ }
+
+ @Override
+ protected boolean shouldExitAfterUpdatingApps() {
+ return true;
+ }
+}
diff --git a/Settings/src/com/android/tv/settings/system/SecurityFragment.java b/Settings/src/com/android/tv/settings/system/SecurityFragment.java
index ed53dac3a..667b44855 100644
--- a/Settings/src/com/android/tv/settings/system/SecurityFragment.java
+++ b/Settings/src/com/android/tv/settings/system/SecurityFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -16,91 +16,27 @@
package com.android.tv.settings.system;
-import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected;
-
-import android.accounts.AccountManager;
-import android.annotation.SuppressLint;
import android.app.admin.DevicePolicyManager;
-import android.app.tvsettings.TvSettingsEnums;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.UserInfo;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.UserHandle;
import android.os.UserManager;
-import android.text.TextUtils;
-import android.util.Log;
-import androidx.annotation.DrawableRes;
-import androidx.annotation.IntDef;
import androidx.annotation.Keep;
-import androidx.fragment.app.Fragment;
-import androidx.leanback.preference.LeanbackSettingsFragmentCompat;
-import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import com.android.tv.settings.R;
-import com.android.tv.settings.SettingsPreferenceFragment;
-import com.android.tv.settings.dialog.PinDialogFragment;
import com.android.tv.settings.users.AppRestrictionsFragment;
import com.android.tv.settings.users.RestrictedProfileModel;
-import com.android.tv.settings.users.RestrictedProfilePinDialogFragment;
-import com.android.tv.settings.users.RestrictedProfilePinStorage;
-import com.android.tv.settings.users.UserSwitchListenerService;
-import com.android.tv.twopanelsettings.TwoPanelSettingsFragment;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
* The security settings screen in Tv settings.
*/
@Keep
-public class SecurityFragment extends SettingsPreferenceFragment
- implements PinDialogFragment.ResultListener {
-
- private static final String TAG = "SecurityFragment";
-
- private static final String KEY_UNKNOWN_SOURCES = "unknown_sources";
- private static final String KEY_RESTRICTED_PROFILE_GROUP = "restricted_profile_group";
- private static final String KEY_RESTRICTED_PROFILE_ENTER = "restricted_profile_enter";
- private static final String KEY_RESTRICTED_PROFILE_EXIT = "restricted_profile_exit";
- private static final String KEY_RESTRICTED_PROFILE_APPS = "restricted_profile_apps";
- private static final String KEY_RESTRICTED_PROFILE_PIN = "restricted_profile_pin";
- private static final String KEY_RESTRICTED_PROFILE_CREATE = "restricted_profile_create";
- private static final String KEY_RESTRICTED_PROFILE_DELETE = "restricted_profile_delete";
- private static final String KEY_MANAGE_DEVICE_ADMIN = "manage_device_admin";
- private static final String KEY_ENTERPRISE_PRIVACY = "enterprise_privacy";
-
- private static final String ACTION_RESTRICTED_PROFILE_CREATED =
- "SecurityFragment.RESTRICTED_PROFILE_CREATED";
- private static final String EXTRA_RESTRICTED_PROFILE_INFO =
- "SecurityFragment.RESTRICTED_PROFILE_INFO";
- private static final String SAVESTATE_CREATING_RESTRICTED_PROFILE =
- "SecurityFragment.CREATING_RESTRICTED_PROFILE";
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({PIN_MODE_CHOOSE_LOCKSCREEN,
- PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT,
- PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD,
- PIN_MODE_RESTRICTED_PROFILE_DELETE})
- private @interface PinMode {}
- private static final int PIN_MODE_CHOOSE_LOCKSCREEN = 1;
- private static final int PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT = 2;
- private static final int PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD = 3;
- private static final int PIN_MODE_RESTRICTED_PROFILE_DELETE = 4;
-
+public class SecurityFragment extends BaseSecurityFragment {
private Preference mUnknownSourcesPref;
private PreferenceGroup mRestrictedProfileGroup;
private Preference mRestrictedProfileEnterPref;
@@ -113,97 +49,15 @@ public class SecurityFragment extends SettingsPreferenceFragment
private Preference mManageDeviceAdminPref;
private Preference mEnterprisePrivacyPref;
- private RestrictedProfileModel mRestrictedProfile;
-
- private boolean mCreatingRestrictedProfile;
- private RestrictedProfilePinStorage mRestrictedProfilePinStorage;
-
- @SuppressLint("StaticFieldLeak")
- private static CreateRestrictedProfileTask sCreateRestrictedProfileTask;
- private final BroadcastReceiver mRestrictedProfileReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- UserInfo result = intent.getParcelableExtra(EXTRA_RESTRICTED_PROFILE_INFO);
- if (isResumed()) {
- onRestrictedUserCreated(result);
- }
- }
- };
-
- private Handler mUiThreadHandler;
- private HandlerThread mBackgroundHandlerThread;
- private Handler mBackgroundHandler;
-
public static SecurityFragment newInstance() {
return new SecurityFragment();
}
- @Override
- public void onCreate(Bundle savedInstanceState) {
- mRestrictedProfile = new RestrictedProfileModel(getContext());
-
- super.onCreate(savedInstanceState);
- mCreatingRestrictedProfile = savedInstanceState != null
- && savedInstanceState.getBoolean(SAVESTATE_CREATING_RESTRICTED_PROFILE);
-
- mUiThreadHandler = new Handler();
- mBackgroundHandlerThread = new HandlerThread("SecurityFragmentBackgroundThread");
- mBackgroundHandlerThread.start();
- mBackgroundHandler = new Handler(mBackgroundHandlerThread.getLooper());
- }
-
- @Override
- public void onDestroy() {
- mBackgroundHandler = null;
- mBackgroundHandlerThread.quitSafely();
- mBackgroundHandlerThread = null;
- mUiThreadHandler = null;
-
- super.onDestroy();
-
- mRestrictedProfile = null;
- }
-
- @Override
- public void onResume() {
- super.onResume();
- refresh();
- LocalBroadcastManager.getInstance(getActivity())
- .registerReceiver(mRestrictedProfileReceiver,
- new IntentFilter(ACTION_RESTRICTED_PROFILE_CREATED));
- if (mCreatingRestrictedProfile) {
- UserInfo userInfo = mRestrictedProfile.getUser();
- if (userInfo != null) {
- onRestrictedUserCreated(userInfo);
- }
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- LocalBroadcastManager.getInstance(getActivity())
- .unregisterReceiver(mRestrictedProfileReceiver);
- }
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- mRestrictedProfilePinStorage = RestrictedProfilePinStorage.newInstance(getContext());
- mRestrictedProfilePinStorage.bind();
- }
-
- @Override
- public void onDetach() {
- mRestrictedProfilePinStorage.unbind();
- mRestrictedProfilePinStorage = null;
- super.onDetach();
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(SAVESTATE_CREATING_RESTRICTED_PROFILE, mCreatingRestrictedProfile);
+ /**
+ * Called by other Fragments to decide whether to show or hide profile-related views.
+ */
+ public static boolean isRestrictedProfileInEffect(Context context) {
+ return new RestrictedProfileModel(context).isCurrentUser();
}
@Override
@@ -224,7 +78,8 @@ public class SecurityFragment extends SettingsPreferenceFragment
refresh();
}
- private void refresh() {
+ @Override
+ protected void refresh() {
if (mRestrictedProfile.isCurrentUser()) {
// We are in restricted profile
mUnknownSourcesPref.setVisible(false);
@@ -249,7 +104,7 @@ public class SecurityFragment extends SettingsPreferenceFragment
mRestrictedProfileDeletePref.setVisible(true);
AppRestrictionsFragment.prepareArgs(mRestrictedProfileAppsPref.getExtras(),
- mRestrictedProfile.getUser().id, false);
+ mRestrictedProfile.getUser().id, false, false);
} else if (UserManager.supportsMultipleUsers()) {
// Not in restricted profile, and it doesn't exist
mUnknownSourcesPref.setVisible(true);
@@ -274,7 +129,8 @@ public class SecurityFragment extends SettingsPreferenceFragment
mRestrictedProfileDeletePref.setVisible(false);
}
- mRestrictedProfileCreatePref.setEnabled(sCreateRestrictedProfileTask == null);
+ mRestrictedProfileCreatePref.setEnabled(
+ !isRestrictedProfileCreationInProgress());
mUnknownSourcesPref.setEnabled(!isUnknownSourcesBlocked());
@@ -282,53 +138,6 @@ public class SecurityFragment extends SettingsPreferenceFragment
mEnterprisePrivacyPref.setVisible(isDeviceManaged());
}
- @Override
- public boolean onPreferenceTreeClick(Preference preference) {
- final String key = preference.getKey();
- if (TextUtils.isEmpty(key)) {
- return super.onPreferenceTreeClick(preference);
- }
- switch (key) {
- case KEY_RESTRICTED_PROFILE_ENTER:
- logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_ENTER_PROFILE);
- if (mRestrictedProfile.enterUser()) {
- getActivity().finish();
- }
- return true;
- case KEY_RESTRICTED_PROFILE_EXIT:
- logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_EXIT_PROFILE);
- launchPinDialog(PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT);
- return true;
- case KEY_RESTRICTED_PROFILE_PIN:
- logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_PROFILE_CHANGE_PIN);
- launchPinDialog(PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD);
- return true;
- case KEY_RESTRICTED_PROFILE_CREATE:
- logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_CREATE_PROFILE);
- createRestrictedProfile();
- return true;
- case KEY_RESTRICTED_PROFILE_DELETE:
- logEntrySelected(TvSettingsEnums.APPS_SECURITY_RESTRICTIONS_DELETE_PROFILE);
- launchPinDialog(PIN_MODE_RESTRICTED_PROFILE_DELETE);
- return true;
- }
- return super.onPreferenceTreeClick(preference);
- }
-
- private void createRestrictedProfile() {
- mBackgroundHandler.post(() -> {
- boolean pinIsSet = mRestrictedProfilePinStorage.isPinSet();
-
- mUiThreadHandler.post(() -> {
- if (pinIsSet) {
- addRestrictedUser();
- } else {
- launchPinDialog(PIN_MODE_CHOOSE_LOCKSCREEN);
- }
- });
- });
- }
-
private boolean isUnknownSourcesBlocked() {
final UserManager um = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
return um.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
@@ -346,162 +155,4 @@ public class SecurityFragment extends SettingsPreferenceFragment
final List<ComponentName> admins = devicePolicyManager.getActiveAdmins();
return (admins != null && !admins.isEmpty());
}
-
- private void launchPinDialog(@PinMode int pinMode) {
- @PinDialogFragment.PinDialogType
- int pinDialogMode;
-
- switch (pinMode) {
- case PIN_MODE_CHOOSE_LOCKSCREEN:
- pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN;
- break;
- case PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT:
- pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_ENTER_PIN;
- break;
- case PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD:
- pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_NEW_PIN;
- break;
- case PIN_MODE_RESTRICTED_PROFILE_DELETE:
- pinDialogMode = PinDialogFragment.PIN_DIALOG_TYPE_DELETE_PIN;
- break;
- default:
- throw new IllegalArgumentException("Unknown pin mode: " + pinMode);
- }
-
- RestrictedProfilePinDialogFragment restrictedProfilePinDialogFragment =
- RestrictedProfilePinDialogFragment.newInstance(pinDialogMode);
- restrictedProfilePinDialogFragment.setTargetFragment(this, pinMode);
- restrictedProfilePinDialogFragment.show(getFragmentManager(),
- PinDialogFragment.DIALOG_TAG);
- }
-
- @Override
- public void pinFragmentDone(int requestCode, boolean success) {
- if (!success) {
- Log.d(TAG, "Request " + requestCode + " unsuccessful.");
- return;
- }
-
- switch (requestCode) {
- case PIN_MODE_CHOOSE_LOCKSCREEN:
- addRestrictedUser();
- break;
- case PIN_MODE_RESTRICTED_PROFILE_SWITCH_OUT:
- mRestrictedProfile.exitUser();
- mUiThreadHandler.post(() -> getActivity().finish());
- break;
- case PIN_MODE_RESTRICTED_PROFILE_CHANGE_PASSWORD:
- // do nothing
- break;
- case PIN_MODE_RESTRICTED_PROFILE_DELETE:
- mUiThreadHandler.post(() -> {
- mRestrictedProfile.removeUser();
- UserSwitchListenerService.onUserCreatedOrDeleted(getActivity());
- refresh();
- });
- break;
- default:
- Log.d(TAG, "Pin request code not recognised: " + requestCode);
- }
- }
-
- private void addRestrictedUser() {
- if (sCreateRestrictedProfileTask == null) {
- sCreateRestrictedProfileTask = new CreateRestrictedProfileTask(getContext());
- sCreateRestrictedProfileTask.execute();
- mCreatingRestrictedProfile = true;
- }
- refresh();
- }
-
- /**
- * Called by other Fragments to decide whether to show or hide profile-related views.
- */
- public static boolean isRestrictedProfileInEffect(Context context) {
- return new RestrictedProfileModel(context).isCurrentUser();
- }
-
- private void onRestrictedUserCreated(UserInfo result) {
- int userId = result.id;
- if (result.isRestricted()
- && result.restrictedProfileParentId == UserHandle.myUserId()) {
- final AppRestrictionsFragment restrictionsFragment =
- AppRestrictionsFragment.newInstance(userId, true);
- final Fragment settingsFragment = getCallbackFragment();
- if (settingsFragment instanceof LeanbackSettingsFragmentCompat) {
- ((LeanbackSettingsFragmentCompat) settingsFragment)
- .startPreferenceFragment(restrictionsFragment);
- } else if (settingsFragment instanceof TwoPanelSettingsFragment) {
- ((TwoPanelSettingsFragment) settingsFragment)
- .startPreferenceFragment(restrictionsFragment);
- } else {
- throw new IllegalStateException("Didn't find fragment of expected type: "
- + settingsFragment);
- }
- }
- mCreatingRestrictedProfile = false;
- refresh();
- }
-
- private static class CreateRestrictedProfileTask extends AsyncTask<Void, Void, UserInfo> {
- private final Context mContext;
- private final UserManager mUserManager;
-
- CreateRestrictedProfileTask(Context context) {
- mContext = context.getApplicationContext();
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- }
-
- @Override
- protected UserInfo doInBackground(Void... params) {
- UserInfo restrictedUserInfo = mUserManager.createProfileForUser(
- mContext.getString(R.string.user_new_profile_name),
- UserManager.USER_TYPE_FULL_RESTRICTED, /* flags */ 0, UserHandle.myUserId());
- if (restrictedUserInfo == null) {
- final UserInfo existingUserInfo = new RestrictedProfileModel(mContext).getUser();
- if (existingUserInfo == null) {
- Log.wtf(TAG, "Got back a null user handle!");
- }
- return existingUserInfo;
- }
- int userId = restrictedUserInfo.id;
- UserHandle user = new UserHandle(userId);
- mUserManager.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user);
- Bitmap bitmap = createBitmapFromDrawable(R.drawable.ic_avatar_default);
- mUserManager.setUserIcon(userId, bitmap);
- // Add shared accounts
- AccountManager.get(mContext).addSharedAccountsFromParentUser(
- UserHandle.of(UserHandle.myUserId()), user);
- return restrictedUserInfo;
- }
-
- @Override
- protected void onPostExecute(UserInfo result) {
- sCreateRestrictedProfileTask = null;
- if (result == null) {
- return;
- }
- UserSwitchListenerService.onUserCreatedOrDeleted(mContext);
- LocalBroadcastManager.getInstance(mContext).sendBroadcast(
- new Intent(ACTION_RESTRICTED_PROFILE_CREATED)
- .putExtra(EXTRA_RESTRICTED_PROFILE_INFO, result));
- }
-
- private Bitmap createBitmapFromDrawable(@DrawableRes int resId) {
- Drawable icon = mContext.getDrawable(resId);
- if (icon == null) {
- throw new IllegalArgumentException("Drawable is missing!");
- }
- icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
- Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(),
- Bitmap.Config.ARGB_8888);
- icon.draw(new Canvas(bitmap));
- return bitmap;
- }
- }
-
- @Override
- protected int getPageId() {
- return TvSettingsEnums.APPS_SECURITY_RESTRICTIONS;
- }
}
diff --git a/Settings/src/com/android/tv/settings/users/AppRestrictionsFragment.java b/Settings/src/com/android/tv/settings/users/AppRestrictionsFragment.java
index 1421c3885..fd4f3545f 100644
--- a/Settings/src/com/android/tv/settings/users/AppRestrictionsFragment.java
+++ b/Settings/src/com/android/tv/settings/users/AppRestrictionsFragment.java
@@ -111,8 +111,15 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen
/** Key for extra passed in from calling fragment to indicate if this is a newly created user */
private static final String EXTRA_NEW_USER = "new_user";
+ /**
+ * Key for extra passed in from calling fragment to indicate we should exit after updating
+ * apps.
+ */
+ private static final String EXTRA_EXIT_AFTER_UPDATE = "exit_after_update";
+
private boolean mFirstTime = true;
private boolean mNewUser;
+ private boolean mExitAfterUpdate;
private boolean mAppListChanged;
private boolean mRestrictedProfile;
@@ -237,14 +244,17 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen
}
}
- public static void prepareArgs(@NonNull Bundle bundle, int userId, boolean newUser) {
+ public static void prepareArgs(@NonNull Bundle bundle, int userId, boolean newUser,
+ boolean exitAfterUpdate) {
bundle.putInt(EXTRA_USER_ID, userId);
bundle.putBoolean(EXTRA_NEW_USER, newUser);
+ bundle.putBoolean(EXTRA_EXIT_AFTER_UPDATE, exitAfterUpdate);
}
- public static AppRestrictionsFragment newInstance(int userId, boolean newUser) {
- final Bundle args = new Bundle(2);
- prepareArgs(args, userId, newUser);
+ public static AppRestrictionsFragment newInstance(int userId, boolean newUser,
+ boolean exitAfterUpdate) {
+ final Bundle args = new Bundle(3);
+ prepareArgs(args, userId, newUser, exitAfterUpdate);
AppRestrictionsFragment fragment = new AppRestrictionsFragment();
fragment.setArguments(args);
return fragment;
@@ -268,6 +278,7 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen
mUser = new UserHandle(args.getInt(EXTRA_USER_ID));
}
mNewUser = args.getBoolean(EXTRA_NEW_USER, false);
+ mExitAfterUpdate = args.getBoolean(EXTRA_EXIT_AFTER_UPDATE, false);
}
}
@@ -346,6 +357,15 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen
}
}
+ @Override
+ public void onDestroyView() {
+ if (mExitAfterUpdate) {
+ requireActivity().setResult(Activity.RESULT_OK);
+ requireActivity().finish();
+ }
+ super.onDestroyView();
+ }
+
private void onPackageChanged(Intent intent) {
String action = intent.getAction();
String packageName = intent.getData().getSchemeSpecificPart();