1 /* 2 * Copyright (C) 2023 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.android.settings.development; 18 19 import static android.app.Activity.RESULT_OK; 20 21 import android.app.settings.SettingsEnums; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.PackageInfo; 26 import android.content.pm.PackageManager; 27 import android.os.Build; 28 import android.os.Bundle; 29 import android.os.Process; 30 import android.os.UserHandle; 31 32 import com.android.settings.R; 33 import com.android.settings.applications.defaultapps.DefaultAppPickerFragment; 34 import com.android.settingslib.applications.DefaultAppInfo; 35 36 import java.text.Collator; 37 import java.util.ArrayList; 38 import java.util.Collections; 39 import java.util.Comparator; 40 import java.util.List; 41 42 public class DevelopmentAppPicker extends DefaultAppPickerFragment implements 43 DeveloperOptionAwareMixin { 44 public static final String EXTRA_REQUESTING_PERMISSION = "REQUESTING_PERMISSION"; 45 public static final String EXTRA_DEBUGGABLE = "DEBUGGABLE"; 46 public static final String EXTRA_SELECTING_APP = "SELECTING_APP"; 47 48 private String mPermissionName; 49 private boolean mDebuggableOnly; 50 private String mSelectingApp; 51 52 @Override onAttach(Context context)53 public void onAttach(Context context) { 54 super.onAttach(context); 55 Bundle arguments = getArguments(); 56 if (arguments == null) { 57 return; 58 } 59 mPermissionName = arguments.getString(EXTRA_REQUESTING_PERMISSION); 60 mDebuggableOnly = arguments.getBoolean(EXTRA_DEBUGGABLE); 61 mSelectingApp = arguments.getString(EXTRA_SELECTING_APP); 62 } 63 64 @Override getMetricsCategory()65 public int getMetricsCategory() { 66 return SettingsEnums.DEVELOPMENT_APP_PICKER; 67 } 68 69 @Override getPreferenceScreenResId()70 protected int getPreferenceScreenResId() { 71 return R.xml.development_app_picker; 72 } 73 74 @Override shouldShowItemNone()75 protected boolean shouldShowItemNone() { 76 return true; 77 } 78 79 @Override getCandidates()80 protected List<DefaultAppInfo> getCandidates() { 81 List<DefaultAppInfo> packageInfoList = new ArrayList<DefaultAppInfo>(); 82 Context context = getContext(); 83 List<ApplicationInfo> installedApps = mPm.getInstalledApplications(0); 84 for (ApplicationInfo ai : installedApps) { 85 if (ai.uid == Process.SYSTEM_UID) { 86 continue; 87 } 88 // Filter out apps that are not debuggable if required. 89 if (mDebuggableOnly) { 90 // On a user build, we only allow debugging of apps that 91 // are marked as debuggable, otherwise (for platform development) 92 // we allow all apps. 93 if ((ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0 94 && "user".equals(Build.TYPE)) { 95 continue; 96 } 97 } 98 99 // Filter out apps that do not request the permission if required. 100 if (mPermissionName != null) { 101 boolean requestsPermission = false; 102 try { 103 PackageInfo pi = mPm.getPackageInfo(ai.packageName, 104 PackageManager.GET_PERMISSIONS); 105 if (pi.requestedPermissions == null) { 106 continue; 107 } 108 for (String requestedPermission : pi.requestedPermissions) { 109 if (requestedPermission.equals(mPermissionName)) { 110 requestsPermission = true; 111 break; 112 } 113 } 114 if (!requestsPermission) { 115 continue; 116 } 117 } catch (PackageManager.NameNotFoundException e) { 118 continue; 119 } 120 } 121 DefaultAppInfo appInfo = new DefaultAppInfo(context, mPm, UserHandle.myUserId(), ai); 122 packageInfoList.add(appInfo); 123 } 124 Collections.sort(packageInfoList, sLabelComparator); 125 return packageInfoList; 126 } 127 128 @Override getDefaultKey()129 protected String getDefaultKey() { 130 return mSelectingApp; 131 } 132 133 @Override setDefaultKey(String key)134 protected boolean setDefaultKey(String key) { 135 DefaultAppInfo appInfo = (DefaultAppInfo) getCandidate(key); 136 Intent intent = new Intent(); 137 if (appInfo != null && appInfo.packageItemInfo != null) { 138 intent.setAction(appInfo.packageItemInfo.packageName); 139 } 140 setResult(RESULT_OK, intent); 141 finish(); 142 return true; 143 } 144 145 private static final Comparator<DefaultAppInfo> sLabelComparator = 146 new Comparator<DefaultAppInfo>() { 147 public int compare(DefaultAppInfo a, DefaultAppInfo b) { 148 return Collator.getInstance().compare(a.loadLabel(), b.loadLabel()); 149 } 150 }; 151 } 152