1 /* 2 * Copyright (C) 2018 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.incident; 18 19 import android.app.ActivityManager; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.pm.ApplicationInfo; 24 import android.content.pm.PackageManager; 25 import android.content.pm.UserInfo; 26 import android.content.res.Resources; 27 import android.os.Binder; 28 import android.os.Build; 29 import android.os.IBinder; 30 import android.os.IIncidentAuthListener; 31 import android.os.IIncidentCompanion; 32 import android.os.IIncidentManager; 33 import android.os.IncidentManager; 34 import android.os.RemoteException; 35 import android.os.ServiceManager; 36 import android.os.UserHandle; 37 import android.os.UserManager; 38 import android.util.Log; 39 40 import com.android.internal.util.DumpUtils; 41 import com.android.server.SystemService; 42 43 import java.io.FileDescriptor; 44 import java.io.PrintWriter; 45 import java.util.List; 46 47 /** 48 * Helper service for incidentd and dumpstated to provide user feedback 49 * and authorization for bug and inicdent reports to be taken. 50 */ 51 public class IncidentCompanionService extends SystemService { 52 static final String TAG = "IncidentCompanionService"; 53 54 /** 55 * Dump argument for proxying restricted image dumps to the services 56 * listed in the config. 57 */ 58 private static String[] RESTRICTED_IMAGE_DUMP_ARGS = new String[] { 59 "--hal", "--restricted_image" }; 60 61 /** 62 * The two permissions, for sendBroadcastAsUserMultiplePermissions. 63 */ 64 private static final String[] DUMP_AND_USAGE_STATS_PERMISSIONS = new String[] { 65 android.Manifest.permission.DUMP, 66 android.Manifest.permission.PACKAGE_USAGE_STATS 67 }; 68 69 /** 70 * Tracker for reports pending approval. 71 */ 72 private PendingReports mPendingReports; 73 74 /** 75 * Implementation of the IIncidentCompanion binder interface. 76 */ 77 private final class BinderService extends IIncidentCompanion.Stub { 78 /** 79 * ONEWAY binder call to initiate authorizing the report. If you don't need 80 * IncidentCompanionService to check whether the calling UID matches then 81 * pass 0 for callingUid. Either way, the caller must have DUMP and USAGE_STATS 82 * permissions to retrieve the data, so it ends up being about the same. 83 */ 84 @Override 85 public void authorizeReport(int callingUid, final String callingPackage, 86 final String receiverClass, final String reportId, 87 final int flags, final IIncidentAuthListener listener) { 88 enforceRequestAuthorizationPermission(); 89 90 final long ident = Binder.clearCallingIdentity(); 91 try { 92 mPendingReports.authorizeReport(callingUid, callingPackage, 93 receiverClass, reportId, flags, listener); 94 } finally { 95 Binder.restoreCallingIdentity(ident); 96 } 97 } 98 99 /** 100 * ONEWAY binder call to cancel the inbound authorization request. 101 * <p> 102 * This is a oneway call, and so is authorizeReport, so the 103 * caller's ordering is preserved. The other calls on this object are synchronous, so 104 * their ordering is not guaranteed with respect to these calls. So the implementation 105 * sends out extra broadcasts to allow for eventual consistency. 106 */ 107 public void cancelAuthorization(final IIncidentAuthListener listener) { 108 enforceRequestAuthorizationPermission(); 109 110 // Caller can cancel if they don't want it anymore, and mRequestQueue elides 111 // authorize/cancel pairs. 112 final long ident = Binder.clearCallingIdentity(); 113 try { 114 mPendingReports.cancelAuthorization(listener); 115 } finally { 116 Binder.restoreCallingIdentity(ident); 117 } 118 } 119 120 /** 121 * ONEWAY implementation to send broadcast from incidentd, which is native. 122 */ 123 @Override 124 public void sendReportReadyBroadcast(String pkg, String cls) { 125 enforceRequestAuthorizationPermission(); 126 127 final long ident = Binder.clearCallingIdentity(); 128 try { 129 final Context context = getContext(); 130 131 final int primaryUser = getAndValidateUser(context); 132 if (primaryUser == UserHandle.USER_NULL) { 133 return; 134 } 135 136 final Intent intent = new Intent(Intent.ACTION_INCIDENT_REPORT_READY); 137 intent.setComponent(new ComponentName(pkg, cls)); 138 139 Log.d(TAG, "sendReportReadyBroadcast sending primaryUser=" + primaryUser 140 + " userHandle=" + UserHandle.getUserHandleForUid(primaryUser) 141 + " intent=" + intent); 142 143 // Send it to the primary user. Only they can do incident reports. 144 context.sendBroadcastAsUserMultiplePermissions(intent, 145 UserHandle.getUserHandleForUid(primaryUser), 146 DUMP_AND_USAGE_STATS_PERMISSIONS); 147 } finally { 148 Binder.restoreCallingIdentity(ident); 149 } 150 } 151 152 /** 153 * SYNCHRONOUS binder call to get the list of reports that are pending confirmation 154 * by the user. 155 */ 156 @Override 157 public List<String> getPendingReports() { 158 enforceAuthorizePermission(); 159 return mPendingReports.getPendingReports(); 160 } 161 162 /** 163 * SYNCHRONOUS binder call to mark a report as approved. 164 */ 165 @Override 166 public void approveReport(String uri) { 167 enforceAuthorizePermission(); 168 169 final long ident = Binder.clearCallingIdentity(); 170 try { 171 mPendingReports.approveReport(uri); 172 } finally { 173 Binder.restoreCallingIdentity(ident); 174 } 175 } 176 177 /** 178 * SYNCHRONOUS binder call to mark a report as NOT approved. 179 */ 180 @Override 181 public void denyReport(String uri) { 182 enforceAuthorizePermission(); 183 184 final long ident = Binder.clearCallingIdentity(); 185 try { 186 mPendingReports.denyReport(uri); 187 } finally { 188 Binder.restoreCallingIdentity(ident); 189 } 190 } 191 192 /** 193 * SYNCHRONOUS binder call to get the list of incident reports waiting for a receiver. 194 */ 195 @Override 196 public List<String> getIncidentReportList(String pkg, String cls) throws RemoteException { 197 enforceAccessReportsPermissions(null); 198 199 final long ident = Binder.clearCallingIdentity(); 200 try { 201 return getIIncidentManager().getIncidentReportList(pkg, cls); 202 } finally { 203 Binder.restoreCallingIdentity(ident); 204 } 205 } 206 207 /** 208 * SYNCHRONOUS binder call to commit an incident report 209 */ 210 @Override 211 public void deleteIncidentReports(String pkg, String cls, String id) 212 throws RemoteException { 213 if (pkg == null || cls == null || id == null 214 || pkg.length() == 0 || cls.length() == 0 || id.length() == 0) { 215 throw new RuntimeException("Invalid pkg, cls or id"); 216 } 217 enforceAccessReportsPermissions(pkg); 218 219 final long ident = Binder.clearCallingIdentity(); 220 try { 221 getIIncidentManager().deleteIncidentReports(pkg, cls, id); 222 } finally { 223 Binder.restoreCallingIdentity(ident); 224 } 225 } 226 227 /** 228 * SYNCHRONOUS binder call to delete all incident reports for a package. 229 */ 230 @Override 231 public void deleteAllIncidentReports(String pkg) throws RemoteException { 232 if (pkg == null || pkg.length() == 0) { 233 throw new RuntimeException("Invalid pkg"); 234 } 235 enforceAccessReportsPermissions(pkg); 236 237 final long ident = Binder.clearCallingIdentity(); 238 try { 239 getIIncidentManager().deleteAllIncidentReports(pkg); 240 } finally { 241 Binder.restoreCallingIdentity(ident); 242 } 243 } 244 245 /** 246 * SYNCHRONOUS binder call to get the IncidentReport object. 247 */ 248 @Override 249 public IncidentManager.IncidentReport getIncidentReport(String pkg, String cls, String id) 250 throws RemoteException { 251 if (pkg == null || cls == null || id == null 252 || pkg.length() == 0 || cls.length() == 0 || id.length() == 0) { 253 throw new RuntimeException("Invalid pkg, cls or id"); 254 } 255 enforceAccessReportsPermissions(pkg); 256 257 final long ident = Binder.clearCallingIdentity(); 258 try { 259 return getIIncidentManager().getIncidentReport(pkg, cls, id); 260 } finally { 261 Binder.restoreCallingIdentity(ident); 262 } 263 } 264 265 /** 266 * SYNCHRONOUS implementation of adb shell dumpsys debugreportcompanion. 267 */ 268 @Override 269 protected void dump(FileDescriptor fd, final PrintWriter writer, String[] args) { 270 if (!DumpUtils.checkDumpPermission(getContext(), TAG, writer)) { 271 return; 272 } 273 274 if (args.length == 1 && "--restricted_image".equals(args[0])) { 275 // Does NOT clearCallingIdentity 276 dumpRestrictedImages(fd); 277 } else { 278 // Regular dump 279 mPendingReports.dump(fd, writer, args); 280 } 281 } 282 283 /** 284 * Proxy for the restricted images section. 285 */ 286 private void dumpRestrictedImages(FileDescriptor fd) { 287 // Only supported on eng or userdebug. 288 if (!(Build.IS_ENG || Build.IS_USERDEBUG)) { 289 return; 290 } 291 292 final Resources res = getContext().getResources(); 293 final String[] services = res.getStringArray( 294 com.android.internal.R.array.config_restrictedImagesServices); 295 final int servicesCount = services.length; 296 for (int i = 0; i < servicesCount; i++) { 297 final String name = services[i]; 298 Log.d(TAG, "Looking up service " + name); 299 final IBinder service = ServiceManager.getService(name); 300 if (service != null) { 301 Log.d(TAG, "Calling dump on service: " + name); 302 try { 303 service.dump(fd, RESTRICTED_IMAGE_DUMP_ARGS); 304 } catch (RemoteException ex) { 305 Log.w(TAG, "dump --restricted_image of " + name + " threw", ex); 306 } 307 } 308 } 309 } 310 311 /** 312 * Inside the binder interface class because we want to do all of the authorization 313 * here, before calling out to the helper objects. 314 */ 315 private void enforceRequestAuthorizationPermission() { 316 getContext().enforceCallingOrSelfPermission( 317 android.Manifest.permission.REQUEST_INCIDENT_REPORT_APPROVAL, null); 318 } 319 320 /** 321 * Inside the binder interface class because we want to do all of the authorization 322 * here, before calling out to the helper objects. 323 */ 324 private void enforceAuthorizePermission() { 325 getContext().enforceCallingOrSelfPermission( 326 android.Manifest.permission.APPROVE_INCIDENT_REPORTS, null); 327 } 328 329 /** 330 * Enforce that the calling process either has APPROVE_INCIDENT_REPORTS or 331 * (DUMP and PACKAGE_USAGE_STATS). This lets the approver get, because showing 332 * information about the report is a prerequisite for letting the user decide. 333 * 334 * If pkg is null, it is not checked, so make sure that you check it for null first 335 * if you do need the packages to match. 336 * 337 * Inside the binder interface class because we want to do all of the authorization 338 * here, before calling out to the helper objects. 339 */ 340 private void enforceAccessReportsPermissions(String pkg) { 341 if (getContext().checkCallingPermission( 342 android.Manifest.permission.APPROVE_INCIDENT_REPORTS) 343 != PackageManager.PERMISSION_GRANTED) { 344 getContext().enforceCallingOrSelfPermission( 345 android.Manifest.permission.DUMP, null); 346 getContext().enforceCallingOrSelfPermission( 347 android.Manifest.permission.PACKAGE_USAGE_STATS, null); 348 if (pkg != null) { 349 enforceCallerIsSameApp(pkg); 350 } 351 } 352 } 353 354 /** 355 * Throw a SecurityException if the incoming binder call is not from pkg. 356 */ 357 private void enforceCallerIsSameApp(String pkg) throws SecurityException { 358 try { 359 final int uid = Binder.getCallingUid(); 360 final int userId = UserHandle.getCallingUserId(); 361 final ApplicationInfo ai = getContext().getPackageManager() 362 .getApplicationInfoAsUser(pkg, 0, userId); 363 if (ai == null) { 364 throw new SecurityException("Unknown package " + pkg); 365 } 366 if (!UserHandle.isSameApp(ai.uid, uid)) { 367 throw new SecurityException("Calling uid " + uid + " gave package " 368 + pkg + " which is owned by uid " + ai.uid); 369 } 370 } catch (PackageManager.NameNotFoundException re) { 371 throw new SecurityException("Unknown package " + pkg + "\n" + re); 372 } 373 } 374 } 375 376 /** 377 * Construct new IncidentCompanionService with the context. 378 */ 379 public IncidentCompanionService(Context context) { 380 super(context); 381 mPendingReports = new PendingReports(context); 382 } 383 384 /** 385 * Initialize the service. It is still not safe to do UI until 386 * onBootPhase(SystemService.PHASE_BOOT_COMPLETED). 387 */ 388 @Override 389 public void onStart() { 390 publishBinderService(Context.INCIDENT_COMPANION_SERVICE, new BinderService()); 391 } 392 393 /** 394 * Handle the boot process... Starts everything running once the system is 395 * up enough for us to do UI. 396 */ 397 @Override 398 public void onBootPhase(int phase) { 399 super.onBootPhase(phase); 400 switch (phase) { 401 case SystemService.PHASE_BOOT_COMPLETED: 402 mPendingReports.onBootCompleted(); 403 break; 404 } 405 } 406 407 /** 408 * Looks up incidentd every time, so we don't need a complex handshake between 409 * incidentd and IncidentCompanionService. 410 */ 411 private IIncidentManager getIIncidentManager() throws RemoteException { 412 return IIncidentManager.Stub.asInterface( 413 ServiceManager.getService(Context.INCIDENT_SERVICE)); 414 } 415 416 /** 417 * Check whether the current user is the primary user, and return the user id if they are. 418 * Returns UserHandle.USER_NULL if not valid. 419 */ 420 public static int getAndValidateUser(Context context) { 421 // Current user 422 UserInfo currentUser; 423 try { 424 currentUser = ActivityManager.getService().getCurrentUser(); 425 } catch (RemoteException ex) { 426 // We're already inside the system process. 427 throw new RuntimeException(ex); 428 } 429 430 // Primary user 431 final UserManager um = UserManager.get(context); 432 final UserInfo primaryUser = um.getPrimaryUser(); 433 434 // Check that we're using the right user. 435 if (currentUser == null) { 436 Log.w(TAG, "No current user. Nobody to approve the report." 437 + " The report will be denied."); 438 return UserHandle.USER_NULL; 439 } 440 if (primaryUser == null) { 441 Log.w(TAG, "No primary user. Nobody to approve the report." 442 + " The report will be denied."); 443 return UserHandle.USER_NULL; 444 } 445 if (primaryUser.id != currentUser.id) { 446 Log.w(TAG, "Only the primary user can approve bugreports, but they are not" 447 + " the current user. The report will be denied."); 448 return UserHandle.USER_NULL; 449 } 450 451 return primaryUser.id; 452 } 453 } 454 455