diff options
author | Oleg Kibirev <olegk@google.com> | 2023-12-07 00:46:49 +0000 |
---|---|---|
committer | Oleg Kibirev <olegk@google.com> | 2023-12-08 19:45:50 +0000 |
commit | e6920c7ede6abcf134750582b732cba13c11993b (patch) | |
tree | 782335eb43a1eadf8d04d3c6bc2f467fb04edaf2 | |
parent | dc52b5a3021574014c0fd0b9058751e454856abe (diff) | |
download | TvSettings-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
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(); |