• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.server.am;
18 
19 import static android.app.AppOpsManager.OP_NONE;
20 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
21 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
22 
23 import android.app.Activity;
24 import android.app.BroadcastOptions;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.pm.PackageManager;
29 import android.content.pm.ResolveInfo;
30 import android.os.Binder;
31 import android.os.BugreportManager;
32 import android.os.BugreportParams;
33 import android.os.UserHandle;
34 import android.provider.Settings;
35 import android.text.TextUtils;
36 import android.util.ArraySet;
37 import android.util.Slog;
38 
39 import com.android.server.SystemConfig;
40 
41 import java.util.List;
42 
43 /**
44  * Static utility methods related to BugReportHandler.
45  */
46 public final class BugReportHandlerUtil {
47     private static final String TAG = TAG_WITH_CLASS_NAME ? "BugReportHandlerUtil" : TAG_AM;
48     private static final String SHELL_APP_PACKAGE = "com.android.shell";
49     private static final String INTENT_BUGREPORT_REQUESTED =
50             "com.android.internal.intent.action.BUGREPORT_REQUESTED";
51     private static final String INTENT_GET_BUGREPORT_HANDLER_RESPONSE =
52             "com.android.internal.intent.action.GET_BUGREPORT_HANDLER_RESPONSE";
53 
54     /**
55      * Check is BugReportHandler enabled on the device.
56      *
57      * @param context Context
58      * @return true if BugReportHandler is enabled, or false otherwise
59      */
isBugReportHandlerEnabled(Context context)60     static boolean isBugReportHandlerEnabled(Context context) {
61         return context.getResources().getBoolean(
62                 com.android.internal.R.bool.config_bugReportHandlerEnabled);
63     }
64 
65     /**
66      * Launches a bugreport-allowlisted app to handle a bugreport.
67      *
68      * <p>Allows a bug report handler app to take bugreports on the user's behalf. The handler can
69      * be predefined in the config, meant to be launched with the primary user. The user can
70      * override this with a different (or same) handler app on possibly a different user. This is
71      * useful for capturing bug reports from work profile, for instance.
72      *
73      * @param context Context
74      * @return true if there is a bugreport-allowlisted app to handle a bugreport, or false
75      * otherwise
76      */
launchBugReportHandlerApp(Context context)77     static boolean launchBugReportHandlerApp(Context context) {
78         if (!isBugReportHandlerEnabled(context)) {
79             return false;
80         }
81 
82         String handlerApp = getCustomBugReportHandlerApp(context);
83         if (isShellApp(handlerApp)) {
84             return false;
85         }
86 
87         int handlerUser = getCustomBugReportHandlerUser(context);
88         if (!isValidBugReportHandlerApp(handlerApp)) {
89             handlerApp = getDefaultBugReportHandlerApp(context);
90             handlerUser = UserHandle.USER_SYSTEM;
91         } else if (getBugReportHandlerAppReceivers(context, handlerApp, handlerUser).isEmpty()) {
92             // It looks like the settings are outdated, reset outdated settings.
93             //
94             // i.e.
95             // If user chooses which profile and which bugreport-allowlisted app in that
96             // profile to handle a bugreport, then user remove the profile.
97             // === RESULT ===
98             // The chosen bugreport handler app is outdated because the profile is removed,
99             // so reset the chosen app and profile
100             handlerApp = getDefaultBugReportHandlerApp(context);
101             handlerUser = UserHandle.USER_SYSTEM;
102             resetCustomBugreportHandlerAppAndUser(context);
103         }
104 
105         if (isShellApp(handlerApp) || !isValidBugReportHandlerApp(handlerApp)
106                 || getBugReportHandlerAppReceivers(context, handlerApp, handlerUser).isEmpty()) {
107             return false;
108         }
109 
110         if (getBugReportHandlerAppResponseReceivers(context, handlerApp, handlerUser).isEmpty()) {
111             // Just try to launch bugreport handler app to handle bugreport request
112             // because the bugreport handler app is old and not support to provide response to
113             // let BugReportHandlerUtil know it is available or not.
114             launchBugReportHandlerApp(context, handlerApp, handlerUser);
115             return true;
116         }
117 
118         Slog.i(TAG, "Getting response from bug report handler app: " + handlerApp);
119         Intent intent = new Intent(INTENT_GET_BUGREPORT_HANDLER_RESPONSE);
120         intent.setPackage(handlerApp);
121         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
122         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
123         final long identity = Binder.clearCallingIdentity();
124         try {
125             // Handler app's BroadcastReceiver should call setResultCode(Activity.RESULT_OK) to
126             // let BugreportHandlerResponseBroadcastReceiver know the handler app is available.
127             context.sendOrderedBroadcastAsUser(intent,
128                     UserHandle.of(handlerUser),
129                     android.Manifest.permission.DUMP,
130                     OP_NONE, /* options= */ null,
131                     new BugreportHandlerResponseBroadcastReceiver(handlerApp, handlerUser),
132                     /* scheduler= */ null,
133                     Activity.RESULT_CANCELED,
134                     /* initialData= */ null,
135                     /* initialExtras= */ null);
136         } catch (RuntimeException e) {
137             Slog.e(TAG, "Error while trying to get response from bug report handler app.", e);
138             return false;
139         } finally {
140             Binder.restoreCallingIdentity(identity);
141         }
142         return true;
143     }
144 
launchBugReportHandlerApp(Context context, String handlerApp, int handlerUser)145     private static void launchBugReportHandlerApp(Context context, String handlerApp,
146             int handlerUser) {
147         Slog.i(TAG, "Launching bug report handler app: " + handlerApp);
148         Intent intent = new Intent(INTENT_BUGREPORT_REQUESTED);
149         intent.setPackage(handlerApp);
150         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
151         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
152         // Send broadcast to the receiver while allowing starting activity from background
153         final BroadcastOptions options = BroadcastOptions.makeBasic();
154         options.setBackgroundActivityStartsAllowed(true);
155         final long identity = Binder.clearCallingIdentity();
156         try {
157             context.sendBroadcastAsUser(intent, UserHandle.of(handlerUser),
158                     android.Manifest.permission.DUMP,
159                     options.toBundle());
160         } catch (RuntimeException e) {
161             Slog.e(TAG, "Error while trying to launch bugreport handler app.", e);
162         } finally {
163             Binder.restoreCallingIdentity(identity);
164         }
165     }
166 
getCustomBugReportHandlerApp(Context context)167     private static String getCustomBugReportHandlerApp(Context context) {
168         // Get the package of custom bugreport handler app
169         return Settings.Global.getString(context.getContentResolver(),
170                 Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP);
171     }
172 
getCustomBugReportHandlerUser(Context context)173     private static int getCustomBugReportHandlerUser(Context context) {
174         return Settings.Global.getInt(context.getContentResolver(),
175                 Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER, UserHandle.USER_NULL);
176     }
177 
isShellApp(String app)178     private static boolean isShellApp(String app) {
179         return SHELL_APP_PACKAGE.equals(app);
180     }
181 
isValidBugReportHandlerApp(String app)182     private static boolean isValidBugReportHandlerApp(String app) {
183         return !TextUtils.isEmpty(app) && isBugreportWhitelistedApp(app);
184     }
185 
isBugreportWhitelistedApp(String app)186     private static boolean isBugreportWhitelistedApp(String app) {
187         // Verify the app is bugreport-allowlisted
188         final ArraySet<String> whitelistedApps = SystemConfig.getInstance()
189                 .getBugreportWhitelistedPackages();
190         return whitelistedApps.contains(app);
191     }
192 
getBugReportHandlerAppReceivers(Context context, String handlerApp, int handlerUser)193     private static List<ResolveInfo> getBugReportHandlerAppReceivers(Context context,
194             String handlerApp, int handlerUser) {
195         // Use the app package and the user id to retrieve the receiver that can handle a
196         // broadcast of the intent.
197         Intent intent = new Intent(INTENT_BUGREPORT_REQUESTED);
198         intent.setPackage(handlerApp);
199         return context.getPackageManager()
200                 .queryBroadcastReceiversAsUser(intent, PackageManager.MATCH_SYSTEM_ONLY,
201                         handlerUser);
202     }
203 
getBugReportHandlerAppResponseReceivers(Context context, String handlerApp, int handlerUser)204     private static List<ResolveInfo> getBugReportHandlerAppResponseReceivers(Context context,
205             String handlerApp, int handlerUser) {
206         // Use the app package and the user id to retrieve the receiver that can provide response
207         Intent intent = new Intent(INTENT_GET_BUGREPORT_HANDLER_RESPONSE);
208         intent.setPackage(handlerApp);
209         return context.getPackageManager()
210                 .queryBroadcastReceiversAsUser(intent, PackageManager.MATCH_SYSTEM_ONLY,
211                         handlerUser);
212     }
213 
getDefaultBugReportHandlerApp(Context context)214     private static String getDefaultBugReportHandlerApp(Context context) {
215         return context.getResources().getString(
216                 com.android.internal.R.string.config_defaultBugReportHandlerApp);
217     }
218 
resetCustomBugreportHandlerAppAndUser(Context context)219     private static void resetCustomBugreportHandlerAppAndUser(Context context) {
220         final long identity = Binder.clearCallingIdentity();
221         try {
222             Settings.Global.putString(context.getContentResolver(),
223                     Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP,
224                     getDefaultBugReportHandlerApp(context));
225             Settings.Global.putInt(context.getContentResolver(),
226                     Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER, UserHandle.USER_SYSTEM);
227         } finally {
228             Binder.restoreCallingIdentity(identity);
229         }
230     }
231 
232     private static class BugreportHandlerResponseBroadcastReceiver extends BroadcastReceiver {
233         private final String handlerApp;
234         private final int handlerUser;
235 
BugreportHandlerResponseBroadcastReceiver(String handlerApp, int handlerUser)236         BugreportHandlerResponseBroadcastReceiver(String handlerApp, int handlerUser) {
237             this.handlerApp = handlerApp;
238             this.handlerUser = handlerUser;
239         }
240 
241         @Override
onReceive(Context context, Intent intent)242         public void onReceive(Context context, Intent intent) {
243             if (getResultCode() == Activity.RESULT_OK) {
244                 // Try to launch bugreport handler app to handle bugreport request because the
245                 // bugreport handler app is available.
246                 launchBugReportHandlerApp(context, handlerApp, handlerUser);
247                 return;
248             }
249 
250             Slog.w(TAG, "Request bug report because no response from handler app.");
251             BugreportManager bugreportManager = context.getSystemService(BugreportManager.class);
252             bugreportManager.requestBugreport(
253                     new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE),
254                     /* shareTitle= */null, /* shareDescription= */ null);
255         }
256     }
257 }
258