1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.packageinstaller.role.ui;
18 
19 import android.app.Application;
20 import android.content.pm.ApplicationInfo;
21 import android.os.Process;
22 import android.os.UserHandle;
23 import android.util.ArrayMap;
24 import android.util.Log;
25 import android.util.Pair;
26 
27 import androidx.annotation.NonNull;
28 import androidx.lifecycle.AndroidViewModel;
29 import androidx.lifecycle.LifecycleOwner;
30 import androidx.lifecycle.LiveData;
31 import androidx.lifecycle.Transformations;
32 import androidx.lifecycle.ViewModel;
33 import androidx.lifecycle.ViewModelProvider;
34 
35 import com.android.packageinstaller.role.model.Role;
36 import com.android.packageinstaller.role.utils.UserUtils;
37 
38 import java.util.List;
39 
40 /**
41  * {@link ViewModel} for a special app access.
42  */
43 public class SpecialAppAccessViewModel extends AndroidViewModel {
44 
45     private static final String LOG_TAG = SpecialAppAccessViewModel.class.getSimpleName();
46 
47     @NonNull
48     private final Role mRole;
49 
50     @NonNull
51     private final LiveData<List<Pair<ApplicationInfo, Boolean>>> mRoleLiveData;
52 
53     @NonNull
54     private final ArrayMap<String, ManageRoleHolderStateLiveData> mManageRoleHolderStateLiveDatas =
55             new ArrayMap<>();
56 
SpecialAppAccessViewModel(@onNull Role role, @NonNull Application application)57     public SpecialAppAccessViewModel(@NonNull Role role, @NonNull Application application) {
58         super(application);
59 
60         mRole = role;
61 
62         UserHandle user = Process.myUserHandle();
63         RoleLiveData roleLiveData = new RoleLiveData(role, user, application);
64         UserHandle workProfile = UserUtils.getWorkProfile(application);
65         RoleSortFunction sortFunction = new RoleSortFunction(application);
66         if (workProfile == null) {
67             mRoleLiveData = Transformations.map(roleLiveData, sortFunction);
68         } else {
69             RoleLiveData workRoleLiveData = new RoleLiveData(role, workProfile, application);
70             mRoleLiveData = Transformations.map(new MergeRoleLiveData(roleLiveData,
71                     workRoleLiveData), sortFunction);
72         }
73     }
74 
75     @NonNull
getRoleLiveData()76     public LiveData<List<Pair<ApplicationInfo, Boolean>>> getRoleLiveData() {
77         return mRoleLiveData;
78     }
79 
80     /**
81      * Observe all the {@link ManageRoleHolderStateLiveData} instances.
82      *
83      * @param owner the {@link LifecycleOwner} which controls the observer
84      * @param observer the observer that will receive the events
85      */
observeManageRoleHolderState(@onNull LifecycleOwner owner, @NonNull ManageRoleHolderStateObserver observer)86     public void observeManageRoleHolderState(@NonNull LifecycleOwner owner,
87             @NonNull ManageRoleHolderStateObserver observer) {
88         int manageRoleHolderStateLiveDatasSize = mManageRoleHolderStateLiveDatas.size();
89         for (int i = 0; i < manageRoleHolderStateLiveDatasSize; i++) {
90             ManageRoleHolderStateLiveData liveData = mManageRoleHolderStateLiveDatas.valueAt(i);
91 
92             liveData.observe(owner, state -> observer.onManageRoleHolderStateChanged(liveData,
93                     state));
94         }
95     }
96 
97     /**
98      * Get or create a {@link ManageRoleHolderStateLiveData} instance for the specified key.
99      *
100      * @param key the key for the {@link ManageRoleHolderStateLiveData}
101      * @param owner the {@link LifecycleOwner} which controls the observer
102      * @param observer the observer that will receive the events
103      *
104      * @return the {@link ManageRoleHolderStateLiveData}
105      */
106     @NonNull
getManageRoleHolderStateLiveData(@onNull String key, @NonNull LifecycleOwner owner, @NonNull ManageRoleHolderStateObserver observer)107     private ManageRoleHolderStateLiveData getManageRoleHolderStateLiveData(@NonNull String key,
108             @NonNull LifecycleOwner owner, @NonNull ManageRoleHolderStateObserver observer) {
109         ManageRoleHolderStateLiveData liveData = mManageRoleHolderStateLiveDatas.get(key);
110         if (liveData == null) {
111             liveData = new ManageRoleHolderStateLiveData();
112             ManageRoleHolderStateLiveData finalLiveData = liveData;
113             liveData.observe(owner, state -> observer.onManageRoleHolderStateChanged(finalLiveData,
114                     state));
115             mManageRoleHolderStateLiveDatas.put(key, liveData);
116         }
117         return liveData;
118     }
119 
120     /**
121      * Set whether an application has an special app access.
122      *
123      * @param packageName the package name of the application
124      * @param allow whether the application should have the access
125      * @param user the user of the application
126      * @param key the key for the {@link ManageRoleHolderStateLiveData}
127      * @param owner the {@link LifecycleOwner} which controls the observer
128      * @param observer the observer that will receive the events
129      */
setSpecialAppAccessAsUser(@onNull String packageName, boolean allow, @NonNull UserHandle user, @NonNull String key, @NonNull LifecycleOwner owner, @NonNull ManageRoleHolderStateObserver observer)130     public void setSpecialAppAccessAsUser(@NonNull String packageName, boolean allow,
131             @NonNull UserHandle user, @NonNull String key, @NonNull LifecycleOwner owner,
132             @NonNull ManageRoleHolderStateObserver observer) {
133         ManageRoleHolderStateLiveData liveData = getManageRoleHolderStateLiveData(key, owner,
134                 observer);
135         if (liveData.getValue() != ManageRoleHolderStateLiveData.STATE_IDLE) {
136             Log.i(LOG_TAG, "Trying to set special app access while another request is on-going");
137             return;
138         }
139         liveData.setRoleHolderAsUser(mRole.getName(), packageName, allow, 0, user,
140                 getApplication());
141     }
142 
143     /**
144      * Observer for multiple {@link ManageRoleHolderStateLiveData} instances.
145      */
146     public interface ManageRoleHolderStateObserver {
147 
148         /**
149          * Callback when any {@link ManageRoleHolderStateLiveData} changed.
150          *
151          * @param liveData the {@link ManageRoleHolderStateLiveData} that changed
152          * @param state the state after the change
153          */
onManageRoleHolderStateChanged(@onNull ManageRoleHolderStateLiveData liveData, int state)154         void onManageRoleHolderStateChanged(@NonNull ManageRoleHolderStateLiveData liveData,
155                 int state);
156     }
157 
158     /**
159      * {@link ViewModelProvider.Factory} for {@link SpecialAppAccessViewModel}.
160      */
161     public static class Factory implements ViewModelProvider.Factory {
162 
163         @NonNull
164         private Role mRole;
165 
166         @NonNull
167         private Application mApplication;
168 
Factory(@onNull Role role, @NonNull Application application)169         public Factory(@NonNull Role role, @NonNull Application application) {
170             mRole = role;
171             mApplication = application;
172         }
173 
174         @NonNull
175         @Override
create(@onNull Class<T> modelClass)176         public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
177             //noinspection unchecked
178             return (T) new SpecialAppAccessViewModel(mRole, mApplication);
179         }
180     }
181 }
182