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.Context;
20 import android.content.pm.PackageInfo;
21 import android.content.pm.PackageManager;
22 import android.content.pm.ServiceInfo;
23 import android.content.pm.UserInfo;
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 int mShowFlags;
76     private TextView mPackageView;
77     private boolean mFilterActivities;
78     private boolean mFilterServices;
79     private boolean mFilterPermissions;
80     private boolean mFilterSharedUid;
81 
82     @Override
onCreate(Bundle savedInstanceState)83     public void onCreate(Bundle savedInstanceState) {
84         super.onCreate(savedInstanceState);
85         mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
86         mPackageManager = getActivity().getPackageManager();
87     }
88 
89     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance)90     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
91         View view = inflater.inflate(R.layout.package_info_test, container, false);
92         setListener(view);
93 
94         return view;
95     }
96 
refreshPackages()97     private void refreshPackages() {
98         List<PackageInfo> packages = new ArrayList<PackageInfo>();
99         try {
100             packages = mPackageManager.getInstalledPackagesAsUser(PACKAGE_FLAGS, mUserToShow.id);
101             if (DEBUG) {
102                 Log.d(TAG, "total packages found: " + packages.size());
103             }
104         } catch (Exception e) {
105             if (DEBUG) {
106                 Log.e(TAG, "failed to get packages for given user: " + mUserToShow);
107             }
108             mPackageView.setText("Cannot retrieve packages for this user..");
109             return;
110         }
111 
112         mPackagesToDisableForSystemUser.clear();
113 
114         for (PackageInfo packageInfo : packages) {
115             Log.d(TAG, "checking package: " + packageInfo);
116             boolean toBlacklist = true;
117             // check share user id, show package does not have sharedUserId or not system uid
118             if (mFilterSharedUid) {
119                 if (DEBUG) {
120                     Log.d(TAG, "sharedUid flagged: " + (packageInfo.sharedUserId == null
121                                 || !packageInfo.sharedUserId.equals(SYSTEM_UID)));
122                 }
123 
124                 toBlacklist &= (packageInfo.sharedUserId == null
125                         || !packageInfo.sharedUserId.equals(SYSTEM_UID));
126             }
127 
128             // check permissions, show package does not require selected permissions
129             if (mFilterPermissions && packageInfo.requestedPermissions != null) {
130                 if (DEBUG) {
131                     for (String info : Arrays.asList(packageInfo.requestedPermissions)) {
132                         Log.d(TAG, info + " flagged: " + (!IMPORTANT_PERMISSIONS.contains(info)));
133                     }
134                 }
135 
136                 toBlacklist &= !(Arrays.asList(packageInfo.requestedPermissions).stream().anyMatch(
137                         info -> IMPORTANT_PERMISSIONS.contains(info)));
138             }
139             // check services, w/o service or service not exported and w/o single user flag
140             if (mFilterServices && packageInfo.services != null) {
141                 if (DEBUG) {
142                     for (ServiceInfo info : Arrays.asList(packageInfo.services)) {
143                         Log.d(TAG, info + " flagged: " + (!info.exported
144                                 && (info.flags & ServiceInfo.FLAG_SINGLE_USER) == 0));
145                     }
146                 }
147 
148                 toBlacklist &= Arrays.asList(packageInfo.services).stream().anyMatch(info ->
149                     !info.exported && (info.flags & ServiceInfo.FLAG_SINGLE_USER) == 0);
150             }
151             // check activities
152             if (mFilterActivities) {
153                 if (DEBUG) {
154                     Log.d(TAG, packageInfo + " contain activities only, flagged: " + (
155                             packageInfo.activities != null
156                             && packageInfo.services == null
157                             && packageInfo.providers == null));
158                 }
159                 toBlacklist &= (packageInfo.activities != null
160                     && packageInfo.services == null && packageInfo.providers == null);
161             }
162 
163             if (toBlacklist) {
164                 mPackagesToDisableForSystemUser.add(packageInfo);
165             }
166         }
167     }
168 
showPackagesOnView(TextView tv)169     private void showPackagesOnView(TextView tv) {
170         refreshPackages();
171 
172         tv.setText(mPackagesToDisableForSystemUser.size() + " Packages found ...\n");
173 
174         for (PackageInfo info : mPackagesToDisableForSystemUser) {
175             tv.append(info.packageName.toString() + "\n");
176         }
177     }
178 
setListener(View v)179     private void setListener(View v) {
180         List<UserInfo> users = mUserManager.getUsers();
181         mUserToShow = users.get(0);
182         Spinner spinner = v.findViewById(R.id.spinner);
183         ArrayAdapter<UserInfo> userArrayAdapter = new ArrayAdapter<UserInfo>(
184                     getContext(), android.R.layout.simple_spinner_item, users);
185         userArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
186         spinner.setAdapter(userArrayAdapter);
187         spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
188             @Override
189             public void onItemSelected(AdapterView<?> parent, View currentView,
190                     int position, long id) {
191                 mUserToShow = (UserInfo) parent.getItemAtPosition(position);
192             }
193             @Override
194             public void onNothingSelected(AdapterView<?> parent) {
195             }
196         });
197 
198         CheckBox activities = v.findViewById(R.id.checkbox_activities);
199         CheckBox services = v.findViewById(R.id.checkbox_services);
200         CheckBox permissions = v.findViewById(R.id.checkbox_permissions);
201         CheckBox shareduid = v.findViewById(R.id.checkbox_shareduid);
202         Button showButton = v.findViewById(R.id.button_show);
203         TextView packageView = v.findViewById(R.id.packages);
204 
205         activities.setOnClickListener(view -> mFilterActivities = ((CheckBox) view).isChecked());
206         services.setOnClickListener(view -> mFilterServices = ((CheckBox) view).isChecked());
207         permissions.setOnClickListener(view -> mFilterPermissions = ((CheckBox) view).isChecked());
208         shareduid.setOnClickListener(view -> mFilterSharedUid = ((CheckBox) view).isChecked());
209         showButton.setOnClickListener(view -> showPackagesOnView(packageView));
210     }
211 }
212