/* * Copyright (C) 2019 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.permissioncontroller.role.ui.specialappaccess; import android.app.Application; import android.content.pm.ApplicationInfo; import android.os.Process; import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; import androidx.annotation.NonNull; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LiveData; import androidx.lifecycle.Transformations; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; import com.android.permissioncontroller.role.model.Role; import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData; import com.android.permissioncontroller.role.ui.RoleLiveData; import com.android.permissioncontroller.role.ui.RoleSortFunction; import com.android.permissioncontroller.role.utils.UserUtils; import java.util.List; /** * {@link ViewModel} for a special app access. */ public class SpecialAppAccessViewModel extends AndroidViewModel { private static final String LOG_TAG = SpecialAppAccessViewModel.class.getSimpleName(); @NonNull private final Role mRole; @NonNull private final LiveData>> mRoleLiveData; @NonNull private final ArrayMap mManageRoleHolderStateLiveDatas = new ArrayMap<>(); public SpecialAppAccessViewModel(@NonNull Role role, @NonNull Application application) { super(application); mRole = role; UserHandle user = Process.myUserHandle(); RoleLiveData roleLiveData = new RoleLiveData(role, user, application); UserHandle workProfile = UserUtils.getWorkProfile(application); RoleSortFunction sortFunction = new RoleSortFunction(application); if (workProfile == null) { mRoleLiveData = Transformations.map(roleLiveData, sortFunction); } else { RoleLiveData workRoleLiveData = new RoleLiveData(role, workProfile, application); mRoleLiveData = Transformations.map(new MergeRoleLiveData(roleLiveData, workRoleLiveData), sortFunction); } } @NonNull public LiveData>> getRoleLiveData() { return mRoleLiveData; } /** * Observe all the {@link ManageRoleHolderStateLiveData} instances. * * @param owner the {@link LifecycleOwner} which controls the observer * @param observer the observer that will receive the events */ public void observeManageRoleHolderState(@NonNull LifecycleOwner owner, @NonNull ManageRoleHolderStateObserver observer) { int manageRoleHolderStateLiveDatasSize = mManageRoleHolderStateLiveDatas.size(); for (int i = 0; i < manageRoleHolderStateLiveDatasSize; i++) { ManageRoleHolderStateLiveData liveData = mManageRoleHolderStateLiveDatas.valueAt(i); liveData.observe(owner, state -> observer.onManageRoleHolderStateChanged(liveData, state)); } } /** * Get or create a {@link ManageRoleHolderStateLiveData} instance for the specified key. * * @param key the key for the {@link ManageRoleHolderStateLiveData} * @param owner the {@link LifecycleOwner} which controls the observer * @param observer the observer that will receive the events * * @return the {@link ManageRoleHolderStateLiveData} */ @NonNull private ManageRoleHolderStateLiveData getManageRoleHolderStateLiveData(@NonNull String key, @NonNull LifecycleOwner owner, @NonNull ManageRoleHolderStateObserver observer) { ManageRoleHolderStateLiveData liveData = mManageRoleHolderStateLiveDatas.get(key); if (liveData == null) { liveData = new ManageRoleHolderStateLiveData(); ManageRoleHolderStateLiveData finalLiveData = liveData; liveData.observe(owner, state -> observer.onManageRoleHolderStateChanged(finalLiveData, state)); mManageRoleHolderStateLiveDatas.put(key, liveData); } return liveData; } /** * Set whether an application has an special app access. * * @param packageName the package name of the application * @param allow whether the application should have the access * @param user the user of the application * @param key the key for the {@link ManageRoleHolderStateLiveData} * @param owner the {@link LifecycleOwner} which controls the observer * @param observer the observer that will receive the events */ public void setSpecialAppAccessAsUser(@NonNull String packageName, boolean allow, @NonNull UserHandle user, @NonNull String key, @NonNull LifecycleOwner owner, @NonNull ManageRoleHolderStateObserver observer) { ManageRoleHolderStateLiveData liveData = getManageRoleHolderStateLiveData(key, owner, observer); if (liveData.getValue() != ManageRoleHolderStateLiveData.STATE_IDLE) { Log.i(LOG_TAG, "Trying to set special app access while another request is on-going"); return; } liveData.setRoleHolderAsUser(mRole.getName(), packageName, allow, 0, user, getApplication()); } /** * Observer for multiple {@link ManageRoleHolderStateLiveData} instances. */ public interface ManageRoleHolderStateObserver { /** * Callback when any {@link ManageRoleHolderStateLiveData} changed. * * @param liveData the {@link ManageRoleHolderStateLiveData} that changed * @param state the state after the change */ void onManageRoleHolderStateChanged(@NonNull ManageRoleHolderStateLiveData liveData, int state); } /** * {@link ViewModelProvider.Factory} for {@link SpecialAppAccessViewModel}. */ public static class Factory implements ViewModelProvider.Factory { @NonNull private Role mRole; @NonNull private Application mApplication; public Factory(@NonNull Role role, @NonNull Application application) { mRole = role; mApplication = application; } @NonNull @Override public T create(@NonNull Class modelClass) { //noinspection unchecked return (T) new SpecialAppAccessViewModel(mRole, mApplication); } } }