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