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