1 /*
2  * Copyright (C) 2020 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.google.android.car.kitchensink.packageinfo;
18 
19 import android.content.pm.PackageInfo;
20 import android.content.pm.PackageManager;
21 import android.content.pm.ServiceInfo;
22 import android.content.pm.UserInfo;
23 import android.multiuser.Flags;
24 import android.os.Bundle;
25 import android.os.UserManager;
26 import android.util.Log;
27 import android.view.LayoutInflater;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.widget.AdapterView;
31 import android.widget.ArrayAdapter;
32 import android.widget.Button;
33 import android.widget.CheckBox;
34 import android.widget.Spinner;
35 import android.widget.TextView;
36 
37 import androidx.fragment.app.Fragment;
38 
39 import com.google.android.car.kitchensink.R;
40 
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.List;
44 
45 /**
46  * Test fragment to check packages installed for each user
47  * <p>
48  * Options to apply conditions to filter out packages, if package
49  * <ul>
50  * <li>only have activities.
51  * <li>have service but not exported or for single user.
52  * <li>does not require any key permission
53  * (INTERACT_ACROSS_USERS, INTERACT_ACROSS_USERS_FULL, WRITE_DEVICE_CONFIG).
54  * <li>does not have sharedUserId or shardUserId is not system uid.
55  * </ul>
56  */
57 public final class PackageInfoFragment extends Fragment{
58     private static final String TAG = "PackageInfoTest";
59     private static final boolean DEBUG = true;
60     private static final int PACKAGE_FLAGS = PackageManager.GET_META_DATA
61             | PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES
62             | PackageManager.GET_PROVIDERS | PackageManager.GET_RECEIVERS
63             | PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES;
64     private static final List<String> IMPORTANT_PERMISSIONS = Arrays.asList(
65             "android.permission.INTERACT_ACROSS_USERS",
66             "android.permission.INTERACT_ACROSS_USERS_FULL",
67             "android.permission.WRITE_DEVICE_CONFIG");
68     private static final String SYSTEM_UID = "android.uid.system";
69 
70     private final List<PackageInfo> mPackagesToDisableForSystemUser = new ArrayList<>();
71 
72     private UserManager mUserManager;
73     private PackageManager mPackageManager;
74     private UserInfo mUserToShow;
75     private boolean mFilterActivities;
76     private boolean mFilterServices;
77     private boolean mFilterPermissions;
78     private boolean mFilterSharedUid;
79 
80     @Override
onCreate(Bundle savedInstanceState)81     public void onCreate(Bundle savedInstanceState) {
82         super.onCreate(savedInstanceState);
83         mUserManager = getContext().getSystemService(UserManager.class);
84         mPackageManager = getActivity().getPackageManager();
85     }
86 
87     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance)88     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
89         View view = inflater.inflate(R.layout.package_info_test, container, false);
90         setListener(view);
91 
92         return view;
93     }
94 
refreshPackages()95     private void refreshPackages() {
96         List<PackageInfo> packages = new ArrayList<PackageInfo>();
97         try {
98             packages = mPackageManager.getInstalledPackagesAsUser(PACKAGE_FLAGS, mUserToShow.id);
99             if (DEBUG) {
100                 Log.d(TAG, "total packages found: " + packages.size());
101             }
102         } catch (Exception e) {
103             if (DEBUG) {
104                 Log.e(TAG, "failed to get packages for given user: " + mUserToShow);
105             }
106             return;
107         }
108 
109         mPackagesToDisableForSystemUser.clear();
110 
111         for (PackageInfo packageInfo : packages) {
112             Log.d(TAG, "checking package: " + packageInfo);
113             boolean toDenyList = true;
114             // check share user id, show package does not have sharedUserId or not system uid
115             if (mFilterSharedUid) {
116                 if (DEBUG) {
117                     Log.d(TAG, "sharedUid flagged: " + (packageInfo.sharedUserId == null
118                                 || !packageInfo.sharedUserId.equals(SYSTEM_UID)));
119                 }
120 
121                 toDenyList &= (packageInfo.sharedUserId == null
122                         || !packageInfo.sharedUserId.equals(SYSTEM_UID));
123             }
124 
125             // check permissions, show package does not require selected permissions
126             if (mFilterPermissions && packageInfo.requestedPermissions != null) {
127                 if (DEBUG) {
128                     for (String info : Arrays.asList(packageInfo.requestedPermissions)) {
129                         Log.d(TAG, info + " flagged: " + (!IMPORTANT_PERMISSIONS.contains(info)));
130                     }
131                 }
132 
133                 toDenyList &= !(Arrays.asList(packageInfo.requestedPermissions).stream().anyMatch(
134                         info -> IMPORTANT_PERMISSIONS.contains(info)));
135             }
136             // check services, w/o service or service not exported and w/o single user flag
137             // or services is system user only
138             if (mFilterServices && packageInfo.services != null) {
139                 if (DEBUG) {
140                     for (ServiceInfo info : Arrays.asList(packageInfo.services)) {
141                         Log.d(TAG, info + " flagged as systemUserOnlyOrSingleUserService: "
142                                 + systemUserOnlyOrSingleUserService(info));
143                     }
144                 }
145 
146                 toDenyList &= Arrays.asList(packageInfo.services).stream().anyMatch(info ->
147                         systemUserOnlyOrSingleUserService(info));
148             }
149             // check activities
150             if (mFilterActivities) {
151                 if (DEBUG) {
152                     Log.d(TAG, packageInfo + " contain activities only, flagged: " + (
153                             packageInfo.activities != null
154                             && packageInfo.services == null
155                             && packageInfo.providers == null));
156                 }
157                 toDenyList &= (packageInfo.activities != null
158                     && packageInfo.services == null && packageInfo.providers == null);
159             }
160 
161             if (toDenyList) {
162                 mPackagesToDisableForSystemUser.add(packageInfo);
163             }
164         }
165     }
166 
systemUserOnlyOrSingleUserService(ServiceInfo info)167     private boolean systemUserOnlyOrSingleUserService(ServiceInfo info) {
168         return (Flags.enableSystemUserOnlyForServicesAndProviders()
169                 && (info.flags & ServiceInfo.FLAG_SYSTEM_USER_ONLY) == 0)
170                 || ((!info.exported
171                 && (info.flags & ServiceInfo.FLAG_SINGLE_USER) == 0));
172     }
173 
showPackagesOnView(TextView tv)174     private void showPackagesOnView(TextView tv) {
175         refreshPackages();
176 
177         tv.setText(mPackagesToDisableForSystemUser.size() + " Packages found ...\n");
178 
179         for (PackageInfo info : mPackagesToDisableForSystemUser) {
180             tv.append(info.packageName.toString() + "\n");
181         }
182     }
183 
setListener(View v)184     private void setListener(View v) {
185         List<UserInfo> users = mUserManager.getUsers();
186         mUserToShow = users.get(0);
187         Spinner spinner = v.findViewById(R.id.spinner);
188         ArrayAdapter<UserInfo> userArrayAdapter = new ArrayAdapter<UserInfo>(
189                     getContext(), android.R.layout.simple_spinner_item, users);
190         userArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
191         spinner.setAdapter(userArrayAdapter);
192         spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
193             @Override
194             public void onItemSelected(AdapterView<?> parent, View currentView,
195                     int position, long id) {
196                 mUserToShow = (UserInfo) parent.getItemAtPosition(position);
197             }
198             @Override
199             public void onNothingSelected(AdapterView<?> parent) {
200             }
201         });
202 
203         CheckBox activities = v.findViewById(R.id.checkbox_activities);
204         CheckBox services = v.findViewById(R.id.checkbox_services);
205         CheckBox permissions = v.findViewById(R.id.checkbox_permissions);
206         CheckBox shareduid = v.findViewById(R.id.checkbox_shareduid);
207         Button showButton = v.findViewById(R.id.button_show);
208         TextView packageView = v.findViewById(R.id.packages);
209 
210         activities.setOnClickListener(view -> mFilterActivities = ((CheckBox) view).isChecked());
211         services.setOnClickListener(view -> mFilterServices = ((CheckBox) view).isChecked());
212         permissions.setOnClickListener(view -> mFilterPermissions = ((CheckBox) view).isChecked());
213         shareduid.setOnClickListener(view -> mFilterSharedUid = ((CheckBox) view).isChecked());
214         showButton.setOnClickListener(view -> showPackagesOnView(packageView));
215     }
216 }
217