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