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